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