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(sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]), 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 < sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0])) 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_set_source(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) 260 { 261 FIXME("\n"); 262 return E_NOTIMPL; 263 } 264 265 static HRESULT RegExp_get_global(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 266 { 267 TRACE("\n"); 268 269 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_GLOB)); 270 return S_OK; 271 } 272 273 static HRESULT RegExp_set_global(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) 274 { 275 FIXME("\n"); 276 return E_NOTIMPL; 277 } 278 279 static HRESULT RegExp_get_ignoreCase(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 280 { 281 TRACE("\n"); 282 283 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_FOLD)); 284 return S_OK; 285 } 286 287 static HRESULT RegExp_set_ignoreCase(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) 288 { 289 FIXME("\n"); 290 return E_NOTIMPL; 291 } 292 293 static HRESULT RegExp_get_multiline(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 294 { 295 TRACE("\n"); 296 297 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_MULTILINE)); 298 return S_OK; 299 } 300 301 static HRESULT RegExp_set_multiline(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) 302 { 303 FIXME("\n"); 304 return E_NOTIMPL; 305 } 306 307 static INT index_from_val(script_ctx_t *ctx, jsval_t v) 308 { 309 double n; 310 HRESULT hres; 311 312 hres = to_number(ctx, v, &n); 313 if(FAILED(hres)) { 314 clear_ei(ctx); /* FIXME: Move ignoring exceptions to to_primitive */ 315 return 0; 316 } 317 318 n = floor(n); 319 return is_int32(n) ? n : 0; 320 } 321 322 static HRESULT RegExp_get_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 323 { 324 RegExpInstance *regexp = regexp_from_jsdisp(jsthis); 325 326 TRACE("\n"); 327 328 return jsval_copy(regexp->last_index_val, r); 329 } 330 331 static HRESULT RegExp_set_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) 332 { 333 RegExpInstance *regexp = regexp_from_jsdisp(jsthis); 334 HRESULT hres; 335 336 TRACE("\n"); 337 338 jsval_release(regexp->last_index_val); 339 hres = jsval_copy(value, ®exp->last_index_val); 340 if(FAILED(hres)) 341 return hres; 342 343 regexp->last_index = index_from_val(ctx, value); 344 return S_OK; 345 } 346 347 static HRESULT RegExp_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 348 jsval_t *r) 349 { 350 RegExpInstance *regexp; 351 unsigned len, f; 352 jsstr_t *ret; 353 WCHAR *ptr; 354 355 TRACE("\n"); 356 357 if(!is_vclass(jsthis, JSCLASS_REGEXP)) { 358 FIXME("Not a RegExp\n"); 359 return E_NOTIMPL; 360 } 361 362 regexp = regexp_from_vdisp(jsthis); 363 364 if(!r) 365 return S_OK; 366 367 len = jsstr_length(regexp->str) + 2; 368 369 f = regexp->jsregexp->flags; 370 if(f & REG_FOLD) 371 len++; 372 if(f & REG_GLOB) 373 len++; 374 if(f & REG_MULTILINE) 375 len++; 376 377 ret = jsstr_alloc_buf(len, &ptr); 378 if(!ret) 379 return E_OUTOFMEMORY; 380 381 *ptr++ = '/'; 382 ptr += jsstr_flush(regexp->str, ptr); 383 *ptr++ = '/'; 384 385 if(f & REG_FOLD) 386 *ptr++ = 'i'; 387 if(f & REG_GLOB) 388 *ptr++ = 'g'; 389 if(f & REG_MULTILINE) 390 *ptr++ = 'm'; 391 392 *r = jsval_string(ret); 393 return S_OK; 394 } 395 396 static HRESULT create_match_array(script_ctx_t *ctx, jsstr_t *input_str, 397 const match_state_t *result, IDispatch **ret) 398 { 399 const WCHAR *input; 400 jsdisp_t *array; 401 jsstr_t *str; 402 DWORD i; 403 HRESULT hres = S_OK; 404 405 static const WCHAR indexW[] = {'i','n','d','e','x',0}; 406 static const WCHAR inputW[] = {'i','n','p','u','t',0}; 407 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; 408 static const WCHAR zeroW[] = {'0',0}; 409 410 input = jsstr_flatten(input_str); 411 if(!input) 412 return E_OUTOFMEMORY; 413 414 hres = create_array(ctx, result->paren_count+1, &array); 415 if(FAILED(hres)) 416 return hres; 417 418 for(i=0; i < result->paren_count; i++) { 419 if(result->parens[i].index != -1) 420 str = jsstr_substr(input_str, result->parens[i].index, result->parens[i].length); 421 else 422 str = jsstr_empty(); 423 if(!str) { 424 hres = E_OUTOFMEMORY; 425 break; 426 } 427 428 hres = jsdisp_propput_idx(array, i+1, jsval_string(str)); 429 jsstr_release(str); 430 if(FAILED(hres)) 431 break; 432 } 433 434 while(SUCCEEDED(hres)) { 435 hres = jsdisp_propput_name(array, indexW, jsval_number(result->cp-input-result->match_len)); 436 if(FAILED(hres)) 437 break; 438 439 hres = jsdisp_propput_name(array, lastIndexW, jsval_number(result->cp-input)); 440 if(FAILED(hres)) 441 break; 442 443 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr_addref(input_str))); 444 if(FAILED(hres)) 445 break; 446 447 str = jsstr_alloc_len(result->cp-result->match_len, result->match_len); 448 if(!str) { 449 hres = E_OUTOFMEMORY; 450 break; 451 } 452 hres = jsdisp_propput_name(array, zeroW, jsval_string(str)); 453 jsstr_release(str); 454 break; 455 } 456 457 if(FAILED(hres)) { 458 jsdisp_release(array); 459 return hres; 460 } 461 462 *ret = to_disp(array); 463 return S_OK; 464 } 465 466 static HRESULT run_exec(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t arg, 467 jsstr_t **input, match_state_t **result, BOOL *ret) 468 { 469 RegExpInstance *regexp; 470 match_state_t *match; 471 DWORD last_index = 0; 472 const WCHAR *string; 473 jsstr_t *jsstr; 474 HRESULT hres; 475 476 if(!is_vclass(jsthis, JSCLASS_REGEXP)) { 477 FIXME("Not a RegExp\n"); 478 return E_NOTIMPL; 479 } 480 481 regexp = regexp_from_vdisp(jsthis); 482 483 hres = to_flat_string(ctx, arg, &jsstr, &string); 484 if(FAILED(hres)) 485 return hres; 486 487 if(regexp->jsregexp->flags & REG_GLOB) { 488 if(regexp->last_index < 0) { 489 jsstr_release(jsstr); 490 set_last_index(regexp, 0); 491 *ret = FALSE; 492 if(input) 493 *input = jsstr_empty(); 494 return S_OK; 495 } 496 497 last_index = regexp->last_index; 498 } 499 500 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, string+last_index); 501 if(!match) { 502 jsstr_release(jsstr); 503 return E_OUTOFMEMORY; 504 } 505 506 hres = regexp_match_next(ctx, ®exp->dispex, REM_RESET_INDEX, jsstr, &match); 507 if(FAILED(hres)) { 508 jsstr_release(jsstr); 509 return hres; 510 } 511 512 *result = match; 513 *ret = hres == S_OK; 514 if(input) 515 *input = jsstr; 516 else 517 jsstr_release(jsstr); 518 return S_OK; 519 } 520 521 static HRESULT RegExp_exec(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 522 jsval_t *r) 523 { 524 match_state_t *match; 525 heap_pool_t *mark; 526 BOOL b; 527 jsstr_t *string; 528 HRESULT hres; 529 530 TRACE("\n"); 531 532 mark = heap_pool_mark(&ctx->tmp_heap); 533 534 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(jsstr_empty()), &string, &match, &b); 535 if(FAILED(hres)) { 536 heap_pool_clear(mark); 537 return hres; 538 } 539 540 if(r) { 541 if(b) { 542 IDispatch *ret; 543 544 hres = create_match_array(ctx, string, match, &ret); 545 if(SUCCEEDED(hres)) 546 *r = jsval_disp(ret); 547 }else { 548 *r = jsval_null(); 549 } 550 } 551 552 heap_pool_clear(mark); 553 jsstr_release(string); 554 return hres; 555 } 556 557 static HRESULT RegExp_test(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 558 jsval_t *r) 559 { 560 match_state_t *match; 561 jsstr_t *undef_str; 562 heap_pool_t *mark; 563 BOOL b; 564 HRESULT hres; 565 566 TRACE("\n"); 567 568 mark = heap_pool_mark(&ctx->tmp_heap); 569 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(undef_str = jsstr_undefined()), NULL, &match, &b); 570 heap_pool_clear(mark); 571 if(!argc) 572 jsstr_release(undef_str); 573 if(FAILED(hres)) 574 return hres; 575 576 if(r) 577 *r = jsval_bool(b); 578 return S_OK; 579 } 580 581 static HRESULT RegExp_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 582 jsval_t *r) 583 { 584 TRACE("\n"); 585 586 switch(flags) { 587 case INVOKE_FUNC: 588 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL); 589 default: 590 FIXME("unimplemented flags %x\n", flags); 591 return E_NOTIMPL; 592 } 593 594 return S_OK; 595 } 596 597 static void RegExp_destructor(jsdisp_t *dispex) 598 { 599 RegExpInstance *This = regexp_from_jsdisp(dispex); 600 601 if(This->jsregexp) 602 regexp_destroy(This->jsregexp); 603 jsval_release(This->last_index_val); 604 jsstr_release(This->str); 605 heap_free(This); 606 } 607 608 static const builtin_prop_t RegExp_props[] = { 609 {execW, RegExp_exec, PROPF_METHOD|1}, 610 {globalW, NULL,0, RegExp_get_global, RegExp_set_global}, 611 {ignoreCaseW, NULL,0, RegExp_get_ignoreCase, RegExp_set_ignoreCase}, 612 {lastIndexW, NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex}, 613 {multilineW, NULL,0, RegExp_get_multiline, RegExp_set_multiline}, 614 {sourceW, NULL,0, RegExp_get_source, RegExp_set_source}, 615 {testW, RegExp_test, PROPF_METHOD|1}, 616 {toStringW, RegExp_toString, PROPF_METHOD} 617 }; 618 619 static const builtin_info_t RegExp_info = { 620 JSCLASS_REGEXP, 621 {NULL, RegExp_value, 0}, 622 sizeof(RegExp_props)/sizeof(*RegExp_props), 623 RegExp_props, 624 RegExp_destructor, 625 NULL 626 }; 627 628 static const builtin_prop_t RegExpInst_props[] = { 629 {globalW, NULL,0, RegExp_get_global, RegExp_set_global}, 630 {ignoreCaseW, NULL,0, RegExp_get_ignoreCase, RegExp_set_ignoreCase}, 631 {lastIndexW, NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex}, 632 {multilineW, NULL,0, RegExp_get_multiline, RegExp_set_multiline}, 633 {sourceW, NULL,0, RegExp_get_source, RegExp_set_source} 634 }; 635 636 static const builtin_info_t RegExpInst_info = { 637 JSCLASS_REGEXP, 638 {NULL, RegExp_value, 0}, 639 sizeof(RegExpInst_props)/sizeof(*RegExpInst_props), 640 RegExpInst_props, 641 RegExp_destructor, 642 NULL 643 }; 644 645 static HRESULT alloc_regexp(script_ctx_t *ctx, jsdisp_t *object_prototype, RegExpInstance **ret) 646 { 647 RegExpInstance *regexp; 648 HRESULT hres; 649 650 regexp = heap_alloc_zero(sizeof(RegExpInstance)); 651 if(!regexp) 652 return E_OUTOFMEMORY; 653 654 if(object_prototype) 655 hres = init_dispex(®exp->dispex, ctx, &RegExp_info, object_prototype); 656 else 657 hres = init_dispex_from_constr(®exp->dispex, ctx, &RegExpInst_info, ctx->regexp_constr); 658 659 if(FAILED(hres)) { 660 heap_free(regexp); 661 return hres; 662 } 663 664 *ret = regexp; 665 return S_OK; 666 } 667 668 HRESULT create_regexp(script_ctx_t *ctx, jsstr_t *src, DWORD flags, jsdisp_t **ret) 669 { 670 RegExpInstance *regexp; 671 const WCHAR *str; 672 HRESULT hres; 673 674 TRACE("%s %x\n", debugstr_jsstr(src), flags); 675 676 str = jsstr_flatten(src); 677 if(!str) 678 return E_OUTOFMEMORY; 679 680 hres = alloc_regexp(ctx, NULL, ®exp); 681 if(FAILED(hres)) 682 return hres; 683 684 regexp->str = jsstr_addref(src); 685 regexp->last_index_val = jsval_number(0); 686 687 regexp->jsregexp = regexp_new(ctx, &ctx->tmp_heap, str, jsstr_length(regexp->str), flags, FALSE); 688 if(!regexp->jsregexp) { 689 WARN("regexp_new failed\n"); 690 jsdisp_release(®exp->dispex); 691 return E_FAIL; 692 } 693 694 *ret = ®exp->dispex; 695 return S_OK; 696 } 697 698 HRESULT create_regexp_var(script_ctx_t *ctx, jsval_t src_arg, jsval_t *flags_arg, jsdisp_t **ret) 699 { 700 unsigned flags, opt_len = 0; 701 const WCHAR *opt = NULL; 702 jsstr_t *src; 703 HRESULT hres; 704 705 if(is_object_instance(src_arg)) { 706 jsdisp_t *obj; 707 708 obj = iface_to_jsdisp(get_object(src_arg)); 709 if(obj) { 710 if(is_class(obj, JSCLASS_REGEXP)) { 711 RegExpInstance *regexp = regexp_from_jsdisp(obj); 712 713 hres = create_regexp(ctx, regexp->str, regexp->jsregexp->flags, ret); 714 jsdisp_release(obj); 715 return hres; 716 } 717 718 jsdisp_release(obj); 719 } 720 } 721 722 if(!is_string(src_arg)) { 723 FIXME("src_arg = %s\n", debugstr_jsval(src_arg)); 724 return E_NOTIMPL; 725 } 726 727 src = get_string(src_arg); 728 729 if(flags_arg) { 730 jsstr_t *opt_str; 731 732 if(!is_string(*flags_arg)) { 733 FIXME("unimplemented for %s\n", debugstr_jsval(*flags_arg)); 734 return E_NOTIMPL; 735 } 736 737 opt_str = get_string(*flags_arg); 738 opt = jsstr_flatten(opt_str); 739 if(!opt) 740 return E_OUTOFMEMORY; 741 opt_len = jsstr_length(opt_str); 742 } 743 744 hres = parse_regexp_flags(opt, opt_len, &flags); 745 if(FAILED(hres)) 746 return hres; 747 748 return create_regexp(ctx, src, flags, ret); 749 } 750 751 HRESULT regexp_string_match(script_ctx_t *ctx, jsdisp_t *re, jsstr_t *jsstr, jsval_t *r) 752 { 753 static const WCHAR indexW[] = {'i','n','d','e','x',0}; 754 static const WCHAR inputW[] = {'i','n','p','u','t',0}; 755 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; 756 757 RegExpInstance *regexp = regexp_from_jsdisp(re); 758 match_result_t *match_result; 759 unsigned match_cnt, i; 760 const WCHAR *str; 761 jsdisp_t *array; 762 HRESULT hres; 763 764 str = jsstr_flatten(jsstr); 765 if(!str) 766 return E_OUTOFMEMORY; 767 768 if(!(regexp->jsregexp->flags & REG_GLOB)) { 769 match_state_t *match; 770 heap_pool_t *mark; 771 772 mark = heap_pool_mark(&ctx->tmp_heap); 773 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, str); 774 if(!match) { 775 heap_pool_clear(mark); 776 return E_OUTOFMEMORY; 777 } 778 779 hres = regexp_match_next(ctx, ®exp->dispex, 0, jsstr, &match); 780 if(FAILED(hres)) { 781 heap_pool_clear(mark); 782 return hres; 783 } 784 785 if(r) { 786 if(hres == S_OK) { 787 IDispatch *ret; 788 789 hres = create_match_array(ctx, jsstr, match, &ret); 790 if(SUCCEEDED(hres)) 791 *r = jsval_disp(ret); 792 }else { 793 *r = jsval_null(); 794 } 795 } 796 797 heap_pool_clear(mark); 798 return S_OK; 799 } 800 801 hres = regexp_match(ctx, ®exp->dispex, jsstr, FALSE, &match_result, &match_cnt); 802 if(FAILED(hres)) 803 return hres; 804 805 if(!match_cnt) { 806 TRACE("no match\n"); 807 808 if(r) 809 *r = jsval_null(); 810 return S_OK; 811 } 812 813 hres = create_array(ctx, match_cnt, &array); 814 if(FAILED(hres)) 815 return hres; 816 817 for(i=0; i < match_cnt; i++) { 818 jsstr_t *tmp_str; 819 820 tmp_str = jsstr_substr(jsstr, match_result[i].index, match_result[i].length); 821 if(!tmp_str) { 822 hres = E_OUTOFMEMORY; 823 break; 824 } 825 826 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str)); 827 jsstr_release(tmp_str); 828 if(FAILED(hres)) 829 break; 830 } 831 832 while(SUCCEEDED(hres)) { 833 hres = jsdisp_propput_name(array, indexW, jsval_number(match_result[match_cnt-1].index)); 834 if(FAILED(hres)) 835 break; 836 837 hres = jsdisp_propput_name(array, lastIndexW, 838 jsval_number(match_result[match_cnt-1].index + match_result[match_cnt-1].length)); 839 if(FAILED(hres)) 840 break; 841 842 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr)); 843 break; 844 } 845 846 heap_free(match_result); 847 848 if(SUCCEEDED(hres) && r) 849 *r = jsval_obj(array); 850 else 851 jsdisp_release(array); 852 return hres; 853 } 854 855 static HRESULT global_idx(script_ctx_t *ctx, DWORD idx, jsval_t *r) 856 { 857 jsstr_t *ret; 858 859 ret = jsstr_substr(ctx->last_match, ctx->match_parens[idx].index, ctx->match_parens[idx].length); 860 if(!ret) 861 return E_OUTOFMEMORY; 862 863 *r = jsval_string(ret); 864 return S_OK; 865 } 866 867 static HRESULT RegExpConstr_get_idx1(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 868 { 869 TRACE("\n"); 870 return global_idx(ctx, 0, r); 871 } 872 873 static HRESULT RegExpConstr_get_idx2(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 874 { 875 TRACE("\n"); 876 return global_idx(ctx, 1, r); 877 } 878 879 static HRESULT RegExpConstr_get_idx3(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 880 { 881 TRACE("\n"); 882 return global_idx(ctx, 2, r); 883 } 884 885 static HRESULT RegExpConstr_get_idx4(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 886 { 887 TRACE("\n"); 888 return global_idx(ctx, 3, r); 889 } 890 891 static HRESULT RegExpConstr_get_idx5(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 892 { 893 TRACE("\n"); 894 return global_idx(ctx, 4, r); 895 } 896 897 static HRESULT RegExpConstr_get_idx6(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 898 { 899 TRACE("\n"); 900 return global_idx(ctx, 5, r); 901 } 902 903 static HRESULT RegExpConstr_get_idx7(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 904 { 905 TRACE("\n"); 906 return global_idx(ctx, 6, r); 907 } 908 909 static HRESULT RegExpConstr_get_idx8(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 910 { 911 TRACE("\n"); 912 return global_idx(ctx, 7, r); 913 } 914 915 static HRESULT RegExpConstr_get_idx9(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 916 { 917 TRACE("\n"); 918 return global_idx(ctx, 8, r); 919 } 920 921 static HRESULT RegExpConstr_get_leftContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 922 { 923 jsstr_t *ret; 924 925 TRACE("\n"); 926 927 ret = jsstr_substr(ctx->last_match, 0, ctx->last_match_index); 928 if(!ret) 929 return E_OUTOFMEMORY; 930 931 *r = jsval_string(ret); 932 return S_OK; 933 } 934 935 static HRESULT RegExpConstr_get_rightContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 936 { 937 jsstr_t *ret; 938 939 TRACE("\n"); 940 941 ret = jsstr_substr(ctx->last_match, ctx->last_match_index+ctx->last_match_length, 942 jsstr_length(ctx->last_match) - ctx->last_match_index - ctx->last_match_length); 943 if(!ret) 944 return E_OUTOFMEMORY; 945 946 *r = jsval_string(ret); 947 return S_OK; 948 } 949 950 static HRESULT RegExpConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 951 jsval_t *r) 952 { 953 TRACE("\n"); 954 955 switch(flags) { 956 case DISPATCH_METHOD: 957 if(argc) { 958 if(is_object_instance(argv[0])) { 959 jsdisp_t *jsdisp = iface_to_jsdisp(get_object(argv[0])); 960 if(jsdisp) { 961 if(is_class(jsdisp, JSCLASS_REGEXP)) { 962 if(argc > 1 && !is_undefined(argv[1])) { 963 jsdisp_release(jsdisp); 964 return throw_regexp_error(ctx, JS_E_REGEXP_SYNTAX, NULL); 965 } 966 967 if(r) 968 *r = jsval_obj(jsdisp); 969 else 970 jsdisp_release(jsdisp); 971 return S_OK; 972 } 973 jsdisp_release(jsdisp); 974 } 975 } 976 } 977 /* fall through */ 978 case DISPATCH_CONSTRUCT: { 979 jsdisp_t *ret; 980 HRESULT hres; 981 982 if(!argc) { 983 FIXME("no args\n"); 984 return E_NOTIMPL; 985 } 986 987 hres = create_regexp_var(ctx, argv[0], argc > 1 ? argv+1 : NULL, &ret); 988 if(FAILED(hres)) 989 return hres; 990 991 if(r) 992 *r = jsval_obj(ret); 993 else 994 jsdisp_release(ret); 995 return S_OK; 996 } 997 default: 998 FIXME("unimplemented flags: %x\n", flags); 999 return E_NOTIMPL; 1000 } 1001 1002 return S_OK; 1003 } 1004 1005 static const builtin_prop_t RegExpConstr_props[] = { 1006 {idx1W, NULL,0, RegExpConstr_get_idx1, builtin_set_const}, 1007 {idx2W, NULL,0, RegExpConstr_get_idx2, builtin_set_const}, 1008 {idx3W, NULL,0, RegExpConstr_get_idx3, builtin_set_const}, 1009 {idx4W, NULL,0, RegExpConstr_get_idx4, builtin_set_const}, 1010 {idx5W, NULL,0, RegExpConstr_get_idx5, builtin_set_const}, 1011 {idx6W, NULL,0, RegExpConstr_get_idx6, builtin_set_const}, 1012 {idx7W, NULL,0, RegExpConstr_get_idx7, builtin_set_const}, 1013 {idx8W, NULL,0, RegExpConstr_get_idx8, builtin_set_const}, 1014 {idx9W, NULL,0, RegExpConstr_get_idx9, builtin_set_const}, 1015 {leftContextW, NULL,0, RegExpConstr_get_leftContext, builtin_set_const}, 1016 {rightContextW, NULL,0, RegExpConstr_get_rightContext, builtin_set_const} 1017 }; 1018 1019 static const builtin_info_t RegExpConstr_info = { 1020 JSCLASS_FUNCTION, 1021 DEFAULT_FUNCTION_VALUE, 1022 sizeof(RegExpConstr_props)/sizeof(*RegExpConstr_props), 1023 RegExpConstr_props, 1024 NULL, 1025 NULL 1026 }; 1027 1028 HRESULT create_regexp_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret) 1029 { 1030 RegExpInstance *regexp; 1031 HRESULT hres; 1032 1033 static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0}; 1034 1035 hres = alloc_regexp(ctx, object_prototype, ®exp); 1036 if(FAILED(hres)) 1037 return hres; 1038 1039 hres = create_builtin_constructor(ctx, RegExpConstr_value, RegExpW, &RegExpConstr_info, 1040 PROPF_CONSTR|2, ®exp->dispex, ret); 1041 1042 jsdisp_release(®exp->dispex); 1043 return hres; 1044 } 1045 1046 HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret) 1047 { 1048 const WCHAR *p; 1049 DWORD flags = 0; 1050 1051 for (p = str; p < str+str_len; p++) { 1052 switch (*p) { 1053 case 'g': 1054 flags |= REG_GLOB; 1055 break; 1056 case 'i': 1057 flags |= REG_FOLD; 1058 break; 1059 case 'm': 1060 flags |= REG_MULTILINE; 1061 break; 1062 case 'y': 1063 flags |= REG_STICKY; 1064 break; 1065 default: 1066 WARN("wrong flag %c\n", *p); 1067 return E_FAIL; 1068 } 1069 } 1070 1071 *ret = flags; 1072 return S_OK; 1073 } 1074