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