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