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