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