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