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
regexp_from_jsdisp(jsdisp_t * jsdisp)61 static inline RegExpInstance *regexp_from_jsdisp(jsdisp_t *jsdisp)
62 {
63 return CONTAINING_RECORD(jsdisp, RegExpInstance, dispex);
64 }
65
regexp_from_vdisp(vdisp_t * vdisp)66 static inline RegExpInstance *regexp_from_vdisp(vdisp_t *vdisp)
67 {
68 return regexp_from_jsdisp(vdisp->u.jsdisp);
69 }
70
set_last_index(RegExpInstance * This,DWORD last_index)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
do_regexp_match_next(script_ctx_t * ctx,RegExpInstance * regexp,DWORD rem_flags,jsstr_t * jsstr,const WCHAR * str,match_state_t * ret)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
regexp_match_next(script_ctx_t * ctx,jsdisp_t * dispex,DWORD rem_flags,jsstr_t * jsstr,match_state_t ** ret)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
regexp_match(script_ctx_t * ctx,jsdisp_t * dispex,jsstr_t * jsstr,BOOL gflag,match_result_t ** match_result,DWORD * result_cnt)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
RegExp_get_source(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExp_get_global(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExp_get_ignoreCase(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExp_get_multiline(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
index_from_val(script_ctx_t * ctx,jsval_t v)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
RegExp_get_lastIndex(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExp_set_lastIndex(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t value)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, ®exp->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
RegExp_toString(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
create_match_array(script_ctx_t * ctx,jsstr_t * input_str,const match_state_t * result,IDispatch ** ret)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
run_exec(script_ctx_t * ctx,vdisp_t * jsthis,jsval_t arg,jsstr_t ** input,match_state_t ** result,BOOL * ret)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, ®exp->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
RegExp_exec(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
RegExp_test(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
RegExp_value(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
RegExp_destructor(jsdisp_t * dispex)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
alloc_regexp(script_ctx_t * ctx,jsdisp_t * object_prototype,RegExpInstance ** ret)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(®exp->dispex, ctx, &RegExp_info, object_prototype);
632 else
633 hres = init_dispex_from_constr(®exp->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
create_regexp(script_ctx_t * ctx,jsstr_t * src,DWORD flags,jsdisp_t ** ret)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, ®exp);
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(®exp->dispex);
667 return E_FAIL;
668 }
669
670 *ret = ®exp->dispex;
671 return S_OK;
672 }
673
create_regexp_var(script_ctx_t * ctx,jsval_t src_arg,jsval_t * flags_arg,jsdisp_t ** ret)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
regexp_string_match(script_ctx_t * ctx,jsdisp_t * re,jsstr_t * jsstr,jsval_t * r)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, ®exp->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, ®exp->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
global_idx(script_ctx_t * ctx,DWORD idx,jsval_t * r)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
RegExpConstr_get_idx1(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx2(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx3(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx4(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx5(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx6(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx7(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx8(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_idx9(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_leftContext(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_get_rightContext(script_ctx_t * ctx,jsdisp_t * jsthis,jsval_t * r)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
RegExpConstr_value(script_ctx_t * ctx,vdisp_t * jsthis,WORD flags,unsigned argc,jsval_t * argv,jsval_t * r)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
create_regexp_constr(script_ctx_t * ctx,jsdisp_t * object_prototype,jsdisp_t ** ret)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, ®exp);
1012 if(FAILED(hres))
1013 return hres;
1014
1015 hres = create_builtin_constructor(ctx, RegExpConstr_value, RegExpW, &RegExpConstr_info,
1016 PROPF_CONSTR|2, ®exp->dispex, ret);
1017
1018 jsdisp_release(®exp->dispex);
1019 return hres;
1020 }
1021
parse_regexp_flags(const WCHAR * str,DWORD str_len,DWORD * ret)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