jscript: Moved generic regular expressions code to separate file.
[wine/multimedia.git] / dlls / jscript / jsregexp.c
blobbda9886737534690a8caf7197a1845d32746e785
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <math.h>
21 #include "jscript.h"
22 #include "regexp.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
28 typedef struct {
29 jsdisp_t dispex;
31 JSRegExp *jsregexp;
32 jsstr_t *str;
33 INT last_index;
34 jsval_t last_index_val;
35 } RegExpInstance;
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};
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};
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};
61 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0};
62 static const WCHAR emptyW[] = {0};
64 static inline RegExpInstance *regexp_from_vdisp(vdisp_t *vdisp)
66 return (RegExpInstance*)vdisp->u.jsdisp;
69 static void set_last_index(RegExpInstance *This, DWORD last_index)
71 This->last_index = last_index;
72 jsval_release(This->last_index_val);
73 This->last_index_val = jsval_number(last_index);
76 static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp, DWORD rem_flags,
77 jsstr_t *str, const WCHAR **cp, match_result_t **parens, DWORD *parens_size,
78 DWORD *parens_cnt, match_result_t *ret)
80 REMatchState *result;
81 DWORD matchlen;
82 HRESULT hres;
84 hres = MatchRegExpNext(regexp->jsregexp, str->str, jsstr_length(str),
85 cp, &ctx->tmp_heap, &result, &matchlen);
86 if(FAILED(hres))
87 return hres;
88 if(hres == S_FALSE) {
89 if(rem_flags & REM_RESET_INDEX)
90 set_last_index(regexp, 0);
91 return S_FALSE;
94 if(parens) {
95 if(regexp->jsregexp->parenCount > *parens_size) {
96 match_result_t *new_parens;
98 if(*parens)
99 new_parens = heap_realloc(*parens, sizeof(match_result_t)*regexp->jsregexp->parenCount);
100 else
101 new_parens = heap_alloc(sizeof(match_result_t)*regexp->jsregexp->parenCount);
102 if(!new_parens)
103 return E_OUTOFMEMORY;
105 *parens_size = regexp->jsregexp->parenCount;
106 *parens = new_parens;
110 if(!(rem_flags & REM_NO_CTX_UPDATE) && ctx->last_match != str) {
111 jsstr_release(ctx->last_match);
112 ctx->last_match = jsstr_addref(str);
115 if(parens) {
116 DWORD i;
118 *parens_cnt = regexp->jsregexp->parenCount;
120 for(i=0; i < regexp->jsregexp->parenCount; i++) {
121 if(result->parens[i].index == -1) {
122 (*parens)[i].str = NULL;
123 (*parens)[i].len = 0;
124 }else {
125 (*parens)[i].str = str->str + result->parens[i].index;
126 (*parens)[i].len = result->parens[i].length;
131 if(!(rem_flags & REM_NO_CTX_UPDATE)) {
132 DWORD i, n = min(sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]), regexp->jsregexp->parenCount);
134 for(i=0; i < n; i++) {
135 if(result->parens[i].index == -1) {
136 ctx->match_parens[i].str = NULL;
137 ctx->match_parens[i].len = 0;
138 }else {
139 ctx->match_parens[i].str = ctx->last_match->str + result->parens[i].index;
140 ctx->match_parens[i].len = result->parens[i].length;
144 if(n < sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]))
145 memset(ctx->match_parens+n, 0, sizeof(ctx->match_parens) - n*sizeof(ctx->match_parens[0]));
148 ret->str = result->cp-matchlen;
149 ret->len = matchlen;
150 set_last_index(regexp, result->cp-str->str);
152 if(!(rem_flags & REM_NO_CTX_UPDATE)) {
153 ctx->last_match_index = ret->str-str->str;
154 ctx->last_match_length = matchlen;
157 return S_OK;
160 HRESULT regexp_match_next(script_ctx_t *ctx, jsdisp_t *dispex, DWORD rem_flags, jsstr_t *str,
161 const WCHAR **cp, match_result_t **parens, DWORD *parens_size, DWORD *parens_cnt,
162 match_result_t *ret)
164 RegExpInstance *regexp = (RegExpInstance*)dispex;
165 heap_pool_t *mark;
166 HRESULT hres;
168 if((rem_flags & REM_CHECK_GLOBAL) && !(regexp->jsregexp->flags & JSREG_GLOB))
169 return S_FALSE;
171 mark = heap_pool_mark(&ctx->tmp_heap);
173 hres = do_regexp_match_next(ctx, regexp, rem_flags, str, cp, parens, parens_size, parens_cnt, ret);
175 heap_pool_clear(mark);
176 return hres;
179 static HRESULT regexp_match(script_ctx_t *ctx, jsdisp_t *dispex, jsstr_t *str, BOOL gflag,
180 match_result_t **match_result, DWORD *result_cnt)
182 RegExpInstance *This = (RegExpInstance*)dispex;
183 match_result_t *ret = NULL, cres;
184 const WCHAR *cp = str->str;
185 DWORD i=0, ret_size = 0;
186 heap_pool_t *mark;
187 HRESULT hres;
189 mark = heap_pool_mark(&ctx->tmp_heap);
191 while(1) {
192 hres = do_regexp_match_next(ctx, This, 0, str, &cp, NULL, NULL, NULL, &cres);
193 if(hres == S_FALSE) {
194 hres = S_OK;
195 break;
198 if(FAILED(hres))
199 break;
201 if(ret_size == i) {
202 if(ret) {
203 match_result_t *old_ret = ret;
205 ret = heap_realloc(old_ret, (ret_size <<= 1) * sizeof(match_result_t));
206 if(!ret)
207 heap_free(old_ret);
208 }else {
209 ret = heap_alloc((ret_size=4) * sizeof(match_result_t));
211 if(!ret) {
212 hres = E_OUTOFMEMORY;
213 break;
217 ret[i++] = cres;
219 if(!gflag && !(This->jsregexp->flags & JSREG_GLOB)) {
220 hres = S_OK;
221 break;
225 heap_pool_clear(mark);
226 if(FAILED(hres)) {
227 heap_free(ret);
228 return hres;
231 *match_result = ret;
232 *result_cnt = i;
233 return S_OK;
236 static HRESULT RegExp_source(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
237 jsval_t *r)
239 TRACE("\n");
241 switch(flags) {
242 case DISPATCH_PROPERTYGET: {
243 RegExpInstance *This = regexp_from_vdisp(jsthis);
244 *r = jsval_string(jsstr_addref(This->str));
245 break;
247 default:
248 FIXME("Unimplemented flags %x\n", flags);
249 return E_NOTIMPL;
252 return S_OK;
255 static HRESULT RegExp_global(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
256 jsval_t *r)
258 FIXME("\n");
259 return E_NOTIMPL;
262 static HRESULT RegExp_ignoreCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
263 jsval_t *r)
265 FIXME("\n");
266 return E_NOTIMPL;
269 static HRESULT RegExp_multiline(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
270 jsval_t *r)
272 FIXME("\n");
273 return E_NOTIMPL;
276 static INT index_from_val(script_ctx_t *ctx, jsval_t v)
278 double n;
279 HRESULT hres;
281 hres = to_number(ctx, v, &n);
282 if(FAILED(hres)) {
283 clear_ei(ctx); /* FIXME: Move ignoring exceptions to to_primitive */
284 return 0;
287 n = floor(n);
288 return is_int32(n) ? n : 0;
291 static HRESULT RegExp_lastIndex(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
292 jsval_t *r)
294 TRACE("\n");
296 switch(flags) {
297 case DISPATCH_PROPERTYGET: {
298 RegExpInstance *regexp = regexp_from_vdisp(jsthis);
300 return jsval_copy(regexp->last_index_val, r);
302 case DISPATCH_PROPERTYPUT: {
303 RegExpInstance *regexp = regexp_from_vdisp(jsthis);
304 HRESULT hres;
306 hres = jsval_copy(argv[0], &regexp->last_index_val);
307 if(FAILED(hres))
308 return hres;
310 regexp->last_index = index_from_val(ctx, argv[0]);
311 break;
313 default:
314 FIXME("unimplemented flags: %x\n", flags);
315 return E_NOTIMPL;
318 return S_OK;
321 static HRESULT RegExp_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
322 jsval_t *r)
324 FIXME("\n");
325 return E_NOTIMPL;
328 static HRESULT create_match_array(script_ctx_t *ctx, jsstr_t *input, const match_result_t *result,
329 const match_result_t *parens, DWORD parens_cnt, IDispatch **ret)
331 jsdisp_t *array;
332 jsstr_t *str;
333 DWORD i;
334 HRESULT hres = S_OK;
336 static const WCHAR indexW[] = {'i','n','d','e','x',0};
337 static const WCHAR inputW[] = {'i','n','p','u','t',0};
338 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
339 static const WCHAR zeroW[] = {'0',0};
341 hres = create_array(ctx, parens_cnt+1, &array);
342 if(FAILED(hres))
343 return hres;
345 for(i=0; i < parens_cnt; i++) {
346 str = jsstr_alloc_len(parens[i].str, parens[i].len);
347 if(!str) {
348 hres = E_OUTOFMEMORY;
349 break;
352 hres = jsdisp_propput_idx(array, i+1, jsval_string(str));
353 jsstr_release(str);
354 if(FAILED(hres))
355 break;
358 while(SUCCEEDED(hres)) {
359 hres = jsdisp_propput_name(array, indexW, jsval_number(result->str-input->str));
360 if(FAILED(hres))
361 break;
363 hres = jsdisp_propput_name(array, lastIndexW, jsval_number(result->str-input->str+result->len));
364 if(FAILED(hres))
365 break;
367 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr_addref(input)));
368 if(FAILED(hres))
369 break;
371 str = jsstr_alloc_len(result->str, result->len);
372 if(!str) {
373 hres = E_OUTOFMEMORY;
374 break;
376 hres = jsdisp_propput_name(array, zeroW, jsval_string(str));
377 jsstr_release(str);
378 break;
381 if(FAILED(hres)) {
382 jsdisp_release(array);
383 return hres;
386 *ret = to_disp(array);
387 return S_OK;
390 static HRESULT run_exec(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t arg, jsstr_t **input,
391 match_result_t *match, match_result_t **parens, DWORD *parens_cnt, BOOL *ret)
393 RegExpInstance *regexp;
394 DWORD parens_size = 0, last_index = 0;
395 const WCHAR *cp;
396 jsstr_t *string;
397 HRESULT hres;
399 if(!is_vclass(jsthis, JSCLASS_REGEXP)) {
400 FIXME("Not a RegExp\n");
401 return E_NOTIMPL;
404 regexp = regexp_from_vdisp(jsthis);
406 hres = to_string(ctx, arg, &string);
407 if(FAILED(hres))
408 return hres;
410 if(regexp->jsregexp->flags & JSREG_GLOB) {
411 if(regexp->last_index < 0) {
412 jsstr_release(string);
413 set_last_index(regexp, 0);
414 *ret = FALSE;
415 if(input)
416 *input = jsstr_empty();
417 return S_OK;
420 last_index = regexp->last_index;
423 cp = string->str + last_index;
424 hres = regexp_match_next(ctx, &regexp->dispex, REM_RESET_INDEX, string, &cp, parens,
425 parens ? &parens_size : NULL, parens_cnt, match);
426 if(FAILED(hres)) {
427 jsstr_release(string);
428 return hres;
431 *ret = hres == S_OK;
432 if(input)
433 *input = string;
434 else
435 jsstr_release(string);
436 return S_OK;
439 static HRESULT RegExp_exec(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
440 jsval_t *r)
442 match_result_t *parens = NULL, match;
443 DWORD parens_cnt = 0;
444 BOOL b;
445 jsstr_t *string;
446 HRESULT hres;
448 TRACE("\n");
450 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(jsstr_empty()), &string, &match, &parens, &parens_cnt, &b);
451 if(FAILED(hres)) {
452 heap_free(parens);
453 return hres;
456 if(r) {
457 if(b) {
458 IDispatch *ret;
460 hres = create_match_array(ctx, string, &match, parens, parens_cnt, &ret);
461 if(SUCCEEDED(hres))
462 *r = jsval_disp(ret);
463 }else {
464 *r = jsval_null();
468 heap_free(parens);
469 jsstr_release(string);
470 return hres;
473 static HRESULT RegExp_test(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
474 jsval_t *r)
476 match_result_t match;
477 jsstr_t *undef_str;
478 BOOL b;
479 HRESULT hres;
481 TRACE("\n");
483 if(!argc) {
484 undef_str = jsstr_alloc(undefinedW);
485 if(!undef_str)
486 return E_OUTOFMEMORY;
489 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(undef_str), NULL, &match, NULL, NULL, &b);
490 if(!argc)
491 jsstr_release(undef_str);
492 if(FAILED(hres))
493 return hres;
495 if(r)
496 *r = jsval_bool(b);
497 return S_OK;
500 static HRESULT RegExp_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
501 jsval_t *r)
503 TRACE("\n");
505 switch(flags) {
506 case INVOKE_FUNC:
507 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
508 default:
509 FIXME("unimplemented flags %x\n", flags);
510 return E_NOTIMPL;
513 return S_OK;
516 static void RegExp_destructor(jsdisp_t *dispex)
518 RegExpInstance *This = (RegExpInstance*)dispex;
520 if(This->jsregexp)
521 js_DestroyRegExp(This->jsregexp);
522 jsval_release(This->last_index_val);
523 jsstr_release(This->str);
524 heap_free(This);
527 static const builtin_prop_t RegExp_props[] = {
528 {execW, RegExp_exec, PROPF_METHOD|1},
529 {globalW, RegExp_global, 0},
530 {ignoreCaseW, RegExp_ignoreCase, 0},
531 {lastIndexW, RegExp_lastIndex, 0},
532 {multilineW, RegExp_multiline, 0},
533 {sourceW, RegExp_source, 0},
534 {testW, RegExp_test, PROPF_METHOD|1},
535 {toStringW, RegExp_toString, PROPF_METHOD}
538 static const builtin_info_t RegExp_info = {
539 JSCLASS_REGEXP,
540 {NULL, RegExp_value, 0},
541 sizeof(RegExp_props)/sizeof(*RegExp_props),
542 RegExp_props,
543 RegExp_destructor,
544 NULL
547 static const builtin_prop_t RegExpInst_props[] = {
548 {globalW, RegExp_global, 0},
549 {ignoreCaseW, RegExp_ignoreCase, 0},
550 {lastIndexW, RegExp_lastIndex, 0},
551 {multilineW, RegExp_multiline, 0},
552 {sourceW, RegExp_source, 0}
555 static const builtin_info_t RegExpInst_info = {
556 JSCLASS_REGEXP,
557 {NULL, RegExp_value, 0},
558 sizeof(RegExpInst_props)/sizeof(*RegExpInst_props),
559 RegExpInst_props,
560 RegExp_destructor,
561 NULL
564 static HRESULT alloc_regexp(script_ctx_t *ctx, jsdisp_t *object_prototype, RegExpInstance **ret)
566 RegExpInstance *regexp;
567 HRESULT hres;
569 regexp = heap_alloc_zero(sizeof(RegExpInstance));
570 if(!regexp)
571 return E_OUTOFMEMORY;
573 if(object_prototype)
574 hres = init_dispex(&regexp->dispex, ctx, &RegExp_info, object_prototype);
575 else
576 hres = init_dispex_from_constr(&regexp->dispex, ctx, &RegExpInst_info, ctx->regexp_constr);
578 if(FAILED(hres)) {
579 heap_free(regexp);
580 return hres;
583 *ret = regexp;
584 return S_OK;
587 HRESULT create_regexp(script_ctx_t *ctx, jsstr_t *src, DWORD flags, jsdisp_t **ret)
589 RegExpInstance *regexp;
590 HRESULT hres;
592 TRACE("%s %x\n", debugstr_jsstr(src), flags);
594 hres = alloc_regexp(ctx, NULL, &regexp);
595 if(FAILED(hres))
596 return hres;
598 regexp->str = jsstr_addref(src);
599 regexp->last_index_val = jsval_number(0);
601 regexp->jsregexp = js_NewRegExp(ctx, &ctx->tmp_heap, regexp->str->str,
602 jsstr_length(regexp->str), flags, FALSE);
603 if(!regexp->jsregexp) {
604 WARN("js_NewRegExp failed\n");
605 jsdisp_release(&regexp->dispex);
606 return E_FAIL;
609 *ret = &regexp->dispex;
610 return S_OK;
613 HRESULT create_regexp_var(script_ctx_t *ctx, jsval_t src_arg, jsval_t *flags_arg, jsdisp_t **ret)
615 jsstr_t *src, *opt = NULL;
616 DWORD flags;
617 HRESULT hres;
619 if(is_object_instance(src_arg)) {
620 jsdisp_t *obj;
622 obj = iface_to_jsdisp((IUnknown*)get_object(src_arg));
623 if(obj) {
624 if(is_class(obj, JSCLASS_REGEXP)) {
625 RegExpInstance *regexp = (RegExpInstance*)obj;
627 hres = create_regexp(ctx, regexp->str, regexp->jsregexp->flags, ret);
628 jsdisp_release(obj);
629 return hres;
632 jsdisp_release(obj);
636 if(!is_string(src_arg)) {
637 FIXME("src_arg = %s\n", debugstr_jsval(src_arg));
638 return E_NOTIMPL;
641 src = get_string(src_arg);
643 if(flags_arg) {
644 if(!is_string(*flags_arg)) {
645 FIXME("unimplemented for %s\n", debugstr_jsval(*flags_arg));
646 return E_NOTIMPL;
649 opt = get_string(*flags_arg);
652 hres = parse_regexp_flags(opt ? opt->str : NULL, opt ? jsstr_length(opt) : 0, &flags);
653 if(FAILED(hres))
654 return hres;
656 return create_regexp(ctx, src, flags, ret);
659 HRESULT regexp_string_match(script_ctx_t *ctx, jsdisp_t *re, jsstr_t *str, jsval_t *r)
661 static const WCHAR indexW[] = {'i','n','d','e','x',0};
662 static const WCHAR inputW[] = {'i','n','p','u','t',0};
663 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
665 RegExpInstance *regexp = (RegExpInstance*)re;
666 match_result_t *match_result;
667 unsigned match_cnt, i;
668 jsdisp_t *array;
669 HRESULT hres;
671 if(!(regexp->jsregexp->flags & JSREG_GLOB)) {
672 match_result_t match, *parens = NULL;
673 DWORD parens_cnt, parens_size = 0;
674 const WCHAR *cp = str->str;
676 hres = regexp_match_next(ctx, &regexp->dispex, 0, str, &cp, &parens, &parens_size, &parens_cnt, &match);
677 if(FAILED(hres)) {
678 heap_free(parens);
679 return hres;
682 if(r) {
683 if(hres == S_OK) {
684 IDispatch *ret;
686 hres = create_match_array(ctx, str, &match, parens, parens_cnt, &ret);
687 if(SUCCEEDED(hres))
688 *r = jsval_disp(ret);
689 }else {
690 *r = jsval_null();
694 heap_free(parens);
695 return S_OK;
698 hres = regexp_match(ctx, &regexp->dispex, str, FALSE, &match_result, &match_cnt);
699 if(FAILED(hres))
700 return hres;
702 if(!match_cnt) {
703 TRACE("no match\n");
705 if(r)
706 *r = jsval_null();
707 return S_OK;
710 hres = create_array(ctx, match_cnt, &array);
711 if(FAILED(hres))
712 return hres;
714 for(i=0; i < match_cnt; i++) {
715 jsstr_t *tmp_str;
717 tmp_str = jsstr_alloc_len(match_result[i].str, match_result[i].len);
718 if(!tmp_str) {
719 hres = E_OUTOFMEMORY;
720 break;
723 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
724 jsstr_release(tmp_str);
725 if(FAILED(hres))
726 break;
729 while(SUCCEEDED(hres)) {
730 hres = jsdisp_propput_name(array, indexW, jsval_number(match_result[match_cnt-1].str-str->str));
731 if(FAILED(hres))
732 break;
734 hres = jsdisp_propput_name(array, lastIndexW,
735 jsval_number(match_result[match_cnt-1].str-str->str+match_result[match_cnt-1].len));
736 if(FAILED(hres))
737 break;
739 hres = jsdisp_propput_name(array, inputW, jsval_string(str));
740 break;
743 heap_free(match_result);
745 if(SUCCEEDED(hres) && r)
746 *r = jsval_obj(array);
747 else
748 jsdisp_release(array);
749 return hres;
752 static HRESULT global_idx(script_ctx_t *ctx, DWORD flags, DWORD idx, jsval_t *r)
754 switch(flags) {
755 case DISPATCH_PROPERTYGET: {
756 jsstr_t *ret;
758 ret = jsstr_alloc_len(ctx->match_parens[idx].str, ctx->match_parens[idx].len);
759 if(!ret)
760 return E_OUTOFMEMORY;
762 *r = jsval_string(ret);
763 break;
765 case DISPATCH_PROPERTYPUT:
766 break;
767 default:
768 FIXME("unsupported flags\n");
769 return E_NOTIMPL;
772 return S_OK;
775 static HRESULT RegExpConstr_idx1(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
776 unsigned argc, jsval_t *argv, jsval_t *r)
778 TRACE("\n");
779 return global_idx(ctx, flags, 0, r);
782 static HRESULT RegExpConstr_idx2(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
783 unsigned argc, jsval_t *argv, jsval_t *r)
785 TRACE("\n");
786 return global_idx(ctx, flags, 1, r);
789 static HRESULT RegExpConstr_idx3(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
790 unsigned argc, jsval_t *argv, jsval_t *r)
792 TRACE("\n");
793 return global_idx(ctx, flags, 2, r);
796 static HRESULT RegExpConstr_idx4(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
797 unsigned argc, jsval_t *argv, jsval_t *r)
799 TRACE("\n");
800 return global_idx(ctx, flags, 3, r);
803 static HRESULT RegExpConstr_idx5(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
804 unsigned argc, jsval_t *argv, jsval_t *r)
806 TRACE("\n");
807 return global_idx(ctx, flags, 4, r);
810 static HRESULT RegExpConstr_idx6(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
811 unsigned argc, jsval_t *argv, jsval_t *r)
813 TRACE("\n");
814 return global_idx(ctx, flags, 5, r);
817 static HRESULT RegExpConstr_idx7(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
818 unsigned argc, jsval_t *argv, jsval_t *r)
820 TRACE("\n");
821 return global_idx(ctx, flags, 6, r);
824 static HRESULT RegExpConstr_idx8(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
825 unsigned argc, jsval_t *argv, jsval_t *r)
827 TRACE("\n");
828 return global_idx(ctx, flags, 7, r);
831 static HRESULT RegExpConstr_idx9(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
832 unsigned argc, jsval_t *argv, jsval_t *r)
834 TRACE("\n");
835 return global_idx(ctx, flags, 8, r);
838 static HRESULT RegExpConstr_leftContext(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
839 unsigned argc, jsval_t *argv, jsval_t *r)
841 TRACE("\n");
843 switch(flags) {
844 case DISPATCH_PROPERTYGET: {
845 jsstr_t *ret;
847 ret = jsstr_alloc_len(ctx->last_match->str, ctx->last_match_index);
848 if(!ret)
849 return E_OUTOFMEMORY;
851 *r = jsval_string(ret);
852 break;
854 case DISPATCH_PROPERTYPUT:
855 break;
856 default:
857 FIXME("unsupported flags\n");
858 return E_NOTIMPL;
861 return S_OK;
864 static HRESULT RegExpConstr_rightContext(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
865 unsigned argc, jsval_t *argv, jsval_t *r)
867 TRACE("\n");
869 switch(flags) {
870 case DISPATCH_PROPERTYGET: {
871 jsstr_t *ret;
873 ret = jsstr_alloc(ctx->last_match->str+ctx->last_match_index+ctx->last_match_length);
874 if(!ret)
875 return E_OUTOFMEMORY;
877 *r = jsval_string(ret);
878 break;
880 case DISPATCH_PROPERTYPUT:
881 break;
882 default:
883 FIXME("unsupported flags\n");
884 return E_NOTIMPL;
887 return S_OK;
890 static HRESULT RegExpConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
891 jsval_t *r)
893 TRACE("\n");
895 switch(flags) {
896 case DISPATCH_METHOD:
897 if(argc) {
898 if(is_object_instance(argv[0])) {
899 jsdisp_t *jsdisp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
900 if(jsdisp) {
901 if(is_class(jsdisp, JSCLASS_REGEXP)) {
902 if(argc > 1 && !is_undefined(argv[1])) {
903 jsdisp_release(jsdisp);
904 return throw_regexp_error(ctx, JS_E_REGEXP_SYNTAX, NULL);
907 if(r)
908 *r = jsval_obj(jsdisp);
909 else
910 jsdisp_release(jsdisp);
911 return S_OK;
913 jsdisp_release(jsdisp);
917 /* fall through */
918 case DISPATCH_CONSTRUCT: {
919 jsdisp_t *ret;
920 HRESULT hres;
922 if(!argc) {
923 FIXME("no args\n");
924 return E_NOTIMPL;
927 hres = create_regexp_var(ctx, argv[0], argc > 1 ? argv+1 : NULL, &ret);
928 if(FAILED(hres))
929 return hres;
931 if(r)
932 *r = jsval_obj(ret);
933 else
934 jsdisp_release(ret);
935 return S_OK;
937 default:
938 FIXME("unimplemented flags: %x\n", flags);
939 return E_NOTIMPL;
942 return S_OK;
945 static const builtin_prop_t RegExpConstr_props[] = {
946 {idx1W, RegExpConstr_idx1, 0},
947 {idx2W, RegExpConstr_idx2, 0},
948 {idx3W, RegExpConstr_idx3, 0},
949 {idx4W, RegExpConstr_idx4, 0},
950 {idx5W, RegExpConstr_idx5, 0},
951 {idx6W, RegExpConstr_idx6, 0},
952 {idx7W, RegExpConstr_idx7, 0},
953 {idx8W, RegExpConstr_idx8, 0},
954 {idx9W, RegExpConstr_idx9, 0},
955 {leftContextW, RegExpConstr_leftContext, 0},
956 {rightContextW, RegExpConstr_rightContext, 0}
959 static const builtin_info_t RegExpConstr_info = {
960 JSCLASS_FUNCTION,
961 {NULL, Function_value, 0},
962 sizeof(RegExpConstr_props)/sizeof(*RegExpConstr_props),
963 RegExpConstr_props,
964 NULL,
965 NULL
968 HRESULT create_regexp_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
970 RegExpInstance *regexp;
971 HRESULT hres;
973 static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0};
975 hres = alloc_regexp(ctx, object_prototype, &regexp);
976 if(FAILED(hres))
977 return hres;
979 hres = create_builtin_constructor(ctx, RegExpConstr_value, RegExpW, &RegExpConstr_info,
980 PROPF_CONSTR|2, &regexp->dispex, ret);
982 jsdisp_release(&regexp->dispex);
983 return hres;
986 HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret)
988 const WCHAR *p;
989 DWORD flags = 0;
991 for (p = str; p < str+str_len; p++) {
992 switch (*p) {
993 case 'g':
994 flags |= JSREG_GLOB;
995 break;
996 case 'i':
997 flags |= JSREG_FOLD;
998 break;
999 case 'm':
1000 flags |= JSREG_MULTILINE;
1001 break;
1002 case 'y':
1003 flags |= JSREG_STICKY;
1004 break;
1005 default:
1006 WARN("wrong flag %c\n", *p);
1007 return E_FAIL;
1011 *ret = flags;
1012 return S_OK;