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