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