mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 11:43:31 +00:00
aa18bbedea
It already increases refcount. Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
1017 lines
26 KiB
C
1017 lines
26 KiB
C
/*
|
|
* Copyright 2008 Jacek Caban for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <math.h>
|
|
|
|
#include "jscript.h"
|
|
#include "regexp.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(jscript);
|
|
|
|
typedef struct {
|
|
jsdisp_t dispex;
|
|
|
|
regexp_t *jsregexp;
|
|
jsstr_t *str;
|
|
INT last_index;
|
|
jsval_t last_index_val;
|
|
} RegExpInstance;
|
|
|
|
static inline RegExpInstance *regexp_from_jsdisp(jsdisp_t *jsdisp)
|
|
{
|
|
return CONTAINING_RECORD(jsdisp, RegExpInstance, dispex);
|
|
}
|
|
|
|
static inline RegExpInstance *regexp_this(jsval_t vthis)
|
|
{
|
|
jsdisp_t *jsdisp = is_object_instance(vthis) ? to_jsdisp(get_object(vthis)) : NULL;
|
|
return (jsdisp && is_class(jsdisp, JSCLASS_REGEXP)) ? regexp_from_jsdisp(jsdisp) : NULL;
|
|
}
|
|
|
|
static void set_last_index(RegExpInstance *This, DWORD last_index)
|
|
{
|
|
This->last_index = last_index;
|
|
jsval_release(This->last_index_val);
|
|
This->last_index_val = jsval_number(last_index);
|
|
}
|
|
|
|
static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp,
|
|
DWORD rem_flags, jsstr_t *jsstr, const WCHAR *str, match_state_t *ret)
|
|
{
|
|
HRESULT hres;
|
|
|
|
hres = regexp_execute(regexp->jsregexp, ctx, &ctx->tmp_heap,
|
|
str, jsstr_length(jsstr), ret);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
if(hres == S_FALSE) {
|
|
if(rem_flags & REM_RESET_INDEX)
|
|
set_last_index(regexp, 0);
|
|
return S_FALSE;
|
|
}
|
|
|
|
if(!(rem_flags & REM_NO_CTX_UPDATE) && ctx->last_match != jsstr) {
|
|
jsstr_release(ctx->last_match);
|
|
ctx->last_match = jsstr_addref(jsstr);
|
|
}
|
|
|
|
if(!(rem_flags & REM_NO_CTX_UPDATE)) {
|
|
DWORD i, n = min(ARRAY_SIZE(ctx->match_parens), ret->paren_count);
|
|
|
|
for(i=0; i < n; i++) {
|
|
if(ret->parens[i].index == -1) {
|
|
ctx->match_parens[i].index = 0;
|
|
ctx->match_parens[i].length = 0;
|
|
}else {
|
|
ctx->match_parens[i].index = ret->parens[i].index;
|
|
ctx->match_parens[i].length = ret->parens[i].length;
|
|
}
|
|
}
|
|
|
|
if(n < ARRAY_SIZE(ctx->match_parens))
|
|
memset(ctx->match_parens+n, 0, sizeof(ctx->match_parens) - n*sizeof(ctx->match_parens[0]));
|
|
}
|
|
|
|
set_last_index(regexp, ret->cp-str);
|
|
|
|
if(!(rem_flags & REM_NO_CTX_UPDATE)) {
|
|
ctx->last_match_index = ret->cp-str-ret->match_len;
|
|
ctx->last_match_length = ret->match_len;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT regexp_match_next(script_ctx_t *ctx, jsdisp_t *dispex,
|
|
DWORD rem_flags, jsstr_t *jsstr, match_state_t **ret)
|
|
{
|
|
RegExpInstance *regexp = regexp_from_jsdisp(dispex);
|
|
match_state_t *match;
|
|
heap_pool_t *mark;
|
|
const WCHAR *str;
|
|
HRESULT hres;
|
|
|
|
if((rem_flags & REM_CHECK_GLOBAL) && !(regexp->jsregexp->flags & REG_GLOB)) {
|
|
if(rem_flags & REM_ALLOC_RESULT)
|
|
*ret = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
str = jsstr_flatten(jsstr);
|
|
if(!str)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if(rem_flags & REM_ALLOC_RESULT) {
|
|
match = alloc_match_state(regexp->jsregexp, NULL, str);
|
|
if(!match)
|
|
return E_OUTOFMEMORY;
|
|
*ret = match;
|
|
}
|
|
|
|
mark = heap_pool_mark(&ctx->tmp_heap);
|
|
|
|
if(rem_flags & REM_NO_PARENS) {
|
|
match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, NULL);
|
|
if(!match) {
|
|
heap_pool_clear(mark);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
match->cp = (*ret)->cp;
|
|
match->match_len = (*ret)->match_len;
|
|
}else {
|
|
match = *ret;
|
|
}
|
|
|
|
hres = do_regexp_match_next(ctx, regexp, rem_flags, jsstr, str, match);
|
|
|
|
if(rem_flags & REM_NO_PARENS) {
|
|
(*ret)->cp = match->cp;
|
|
(*ret)->match_len = match->match_len;
|
|
}
|
|
|
|
heap_pool_clear(mark);
|
|
|
|
if(hres != S_OK && (rem_flags & REM_ALLOC_RESULT)) {
|
|
free(match);
|
|
*ret = NULL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT regexp_match(script_ctx_t *ctx, jsdisp_t *dispex, jsstr_t *jsstr, BOOL gflag,
|
|
match_result_t **match_result, DWORD *result_cnt)
|
|
{
|
|
RegExpInstance *This = regexp_from_jsdisp(dispex);
|
|
match_result_t *ret = NULL;
|
|
match_state_t *result;
|
|
DWORD i=0, ret_size = 0;
|
|
heap_pool_t *mark;
|
|
const WCHAR *str;
|
|
HRESULT hres;
|
|
|
|
mark = heap_pool_mark(&ctx->tmp_heap);
|
|
|
|
str = jsstr_flatten(jsstr);
|
|
if(!str)
|
|
return E_OUTOFMEMORY;
|
|
|
|
result = alloc_match_state(This->jsregexp, &ctx->tmp_heap, str);
|
|
if(!result) {
|
|
heap_pool_clear(mark);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
while(1) {
|
|
hres = do_regexp_match_next(ctx, This, 0, jsstr, str, result);
|
|
if(hres == S_FALSE) {
|
|
hres = S_OK;
|
|
break;
|
|
}
|
|
|
|
if(FAILED(hres))
|
|
break;
|
|
|
|
if(ret_size == i) {
|
|
if(ret) {
|
|
match_result_t *old_ret = ret;
|
|
|
|
ret = realloc(old_ret, (ret_size <<= 1) * sizeof(match_result_t));
|
|
if(!ret)
|
|
free(old_ret);
|
|
}else {
|
|
ret = malloc((ret_size=4) * sizeof(match_result_t));
|
|
}
|
|
if(!ret) {
|
|
hres = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret[i].index = result->cp - str - result->match_len;
|
|
ret[i++].length = result->match_len;
|
|
|
|
if(!gflag && !(This->jsregexp->flags & REG_GLOB)) {
|
|
hres = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
heap_pool_clear(mark);
|
|
if(FAILED(hres)) {
|
|
free(ret);
|
|
return hres;
|
|
}
|
|
|
|
*match_result = ret;
|
|
*result_cnt = i;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExp_get_source(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
|
|
*r = jsval_string(jsstr_addref(regexp_from_jsdisp(jsthis)->str));
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExp_get_global(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
|
|
*r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_GLOB));
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExp_get_ignoreCase(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
|
|
*r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_FOLD));
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExp_get_multiline(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
|
|
*r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_MULTILINE));
|
|
return S_OK;
|
|
}
|
|
|
|
static INT index_from_val(script_ctx_t *ctx, jsval_t v)
|
|
{
|
|
double n;
|
|
HRESULT hres;
|
|
|
|
hres = to_number(ctx, v, &n);
|
|
if(FAILED(hres))
|
|
return 0;
|
|
|
|
n = floor(n);
|
|
return is_int32(n) ? n : 0;
|
|
}
|
|
|
|
static HRESULT RegExp_get_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
RegExpInstance *regexp = regexp_from_jsdisp(jsthis);
|
|
|
|
TRACE("\n");
|
|
|
|
return jsval_copy(regexp->last_index_val, r);
|
|
}
|
|
|
|
static HRESULT RegExp_set_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
|
|
{
|
|
RegExpInstance *regexp = regexp_from_jsdisp(jsthis);
|
|
HRESULT hres;
|
|
|
|
TRACE("\n");
|
|
|
|
jsval_release(regexp->last_index_val);
|
|
hres = jsval_copy(value, ®exp->last_index_val);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
regexp->last_index = index_from_val(ctx, value);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExp_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
|
|
jsval_t *r)
|
|
{
|
|
RegExpInstance *regexp;
|
|
unsigned len, f;
|
|
jsstr_t *ret;
|
|
WCHAR *ptr;
|
|
|
|
TRACE("\n");
|
|
|
|
if(!(regexp = regexp_this(vthis))) {
|
|
WARN("Not a RegExp\n");
|
|
return JS_E_REGEXP_EXPECTED;
|
|
}
|
|
|
|
|
|
if(!r)
|
|
return S_OK;
|
|
|
|
len = jsstr_length(regexp->str) + 2;
|
|
|
|
f = regexp->jsregexp->flags;
|
|
if(f & REG_FOLD)
|
|
len++;
|
|
if(f & REG_GLOB)
|
|
len++;
|
|
if(f & REG_MULTILINE)
|
|
len++;
|
|
|
|
ret = jsstr_alloc_buf(len, &ptr);
|
|
if(!ret)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*ptr++ = '/';
|
|
ptr += jsstr_flush(regexp->str, ptr);
|
|
*ptr++ = '/';
|
|
|
|
if(f & REG_FOLD)
|
|
*ptr++ = 'i';
|
|
if(f & REG_GLOB)
|
|
*ptr++ = 'g';
|
|
if(f & REG_MULTILINE)
|
|
*ptr++ = 'm';
|
|
|
|
*r = jsval_string(ret);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT create_match_array(script_ctx_t *ctx, jsstr_t *input_str,
|
|
const match_state_t *result, IDispatch **ret)
|
|
{
|
|
const WCHAR *input;
|
|
jsdisp_t *array;
|
|
jsstr_t *str;
|
|
DWORD i;
|
|
HRESULT hres = S_OK;
|
|
|
|
input = jsstr_flatten(input_str);
|
|
if(!input)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hres = create_array(ctx, result->paren_count+1, &array);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
for(i=0; i < result->paren_count; i++) {
|
|
jsval_t val;
|
|
|
|
if(result->parens[i].index != -1) {
|
|
if(!(str = jsstr_substr(input_str, result->parens[i].index, result->parens[i].length))) {
|
|
hres = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
val = jsval_string(str);
|
|
}else if(ctx->version < SCRIPTLANGUAGEVERSION_ES5) {
|
|
val = jsval_string(jsstr_empty());
|
|
}else {
|
|
val = jsval_undefined();
|
|
}
|
|
|
|
hres = jsdisp_propput_idx(array, i+1, val);
|
|
jsval_release(val);
|
|
if(FAILED(hres))
|
|
break;
|
|
}
|
|
|
|
while(SUCCEEDED(hres)) {
|
|
hres = jsdisp_propput_name(array, L"index", jsval_number(result->cp-input-result->match_len));
|
|
if(FAILED(hres))
|
|
break;
|
|
|
|
hres = jsdisp_propput_name(array, L"lastIndex", jsval_number(result->cp-input));
|
|
if(FAILED(hres))
|
|
break;
|
|
|
|
hres = jsdisp_propput_name(array, L"input", jsval_string(input_str));
|
|
if(FAILED(hres))
|
|
break;
|
|
|
|
str = jsstr_alloc_len(result->cp-result->match_len, result->match_len);
|
|
if(!str) {
|
|
hres = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
hres = jsdisp_propput_name(array, L"0", jsval_string(str));
|
|
jsstr_release(str);
|
|
break;
|
|
}
|
|
|
|
if(FAILED(hres)) {
|
|
jsdisp_release(array);
|
|
return hres;
|
|
}
|
|
|
|
*ret = to_disp(array);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT run_exec(script_ctx_t *ctx, jsval_t vthis, jsval_t arg,
|
|
jsstr_t **input, match_state_t **result, BOOL *ret)
|
|
{
|
|
RegExpInstance *regexp;
|
|
match_state_t *match;
|
|
DWORD last_index = 0;
|
|
const WCHAR *string;
|
|
jsstr_t *jsstr;
|
|
HRESULT hres;
|
|
|
|
if(!(regexp = regexp_this(vthis))) {
|
|
WARN("Not a RegExp\n");
|
|
return JS_E_REGEXP_EXPECTED;
|
|
}
|
|
|
|
hres = to_flat_string(ctx, arg, &jsstr, &string);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
if(regexp->jsregexp->flags & REG_GLOB) {
|
|
if(regexp->last_index < 0) {
|
|
jsstr_release(jsstr);
|
|
set_last_index(regexp, 0);
|
|
*ret = FALSE;
|
|
if(input)
|
|
*input = jsstr_empty();
|
|
return S_OK;
|
|
}
|
|
|
|
last_index = regexp->last_index;
|
|
}
|
|
|
|
match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, string+last_index);
|
|
if(!match) {
|
|
jsstr_release(jsstr);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hres = regexp_match_next(ctx, ®exp->dispex, REM_RESET_INDEX, jsstr, &match);
|
|
if(FAILED(hres)) {
|
|
jsstr_release(jsstr);
|
|
return hres;
|
|
}
|
|
|
|
*result = match;
|
|
*ret = hres == S_OK;
|
|
if(input)
|
|
*input = jsstr;
|
|
else
|
|
jsstr_release(jsstr);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExp_exec(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
|
|
jsval_t *r)
|
|
{
|
|
match_state_t *match;
|
|
heap_pool_t *mark;
|
|
BOOL b;
|
|
jsstr_t *string;
|
|
HRESULT hres;
|
|
|
|
TRACE("\n");
|
|
|
|
mark = heap_pool_mark(&ctx->tmp_heap);
|
|
|
|
hres = run_exec(ctx, vthis, argc ? argv[0] : jsval_string(jsstr_empty()), &string, &match, &b);
|
|
if(FAILED(hres)) {
|
|
heap_pool_clear(mark);
|
|
return hres;
|
|
}
|
|
|
|
if(r) {
|
|
if(b) {
|
|
IDispatch *ret;
|
|
|
|
hres = create_match_array(ctx, string, match, &ret);
|
|
if(SUCCEEDED(hres))
|
|
*r = jsval_disp(ret);
|
|
}else {
|
|
*r = jsval_null();
|
|
}
|
|
}
|
|
|
|
heap_pool_clear(mark);
|
|
jsstr_release(string);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT RegExp_test(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
|
|
jsval_t *r)
|
|
{
|
|
match_state_t *match;
|
|
jsstr_t *undef_str;
|
|
heap_pool_t *mark;
|
|
BOOL b;
|
|
HRESULT hres;
|
|
|
|
TRACE("\n");
|
|
|
|
mark = heap_pool_mark(&ctx->tmp_heap);
|
|
hres = run_exec(ctx, vthis, argc ? argv[0] : jsval_string(undef_str = jsstr_undefined()), NULL, &match, &b);
|
|
heap_pool_clear(mark);
|
|
if(!argc)
|
|
jsstr_release(undef_str);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
if(r)
|
|
*r = jsval_bool(b);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExp_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
|
|
jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
|
|
switch(flags) {
|
|
case INVOKE_FUNC:
|
|
return JS_E_FUNCTION_EXPECTED;
|
|
default:
|
|
FIXME("unimplemented flags %x\n", flags);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static void RegExp_destructor(jsdisp_t *dispex)
|
|
{
|
|
RegExpInstance *This = regexp_from_jsdisp(dispex);
|
|
|
|
if(This->jsregexp)
|
|
regexp_destroy(This->jsregexp);
|
|
jsval_release(This->last_index_val);
|
|
jsstr_release(This->str);
|
|
free(This);
|
|
}
|
|
|
|
static HRESULT RegExp_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex)
|
|
{
|
|
return gc_process_linked_val(gc_ctx, op, dispex, ®exp_from_jsdisp(dispex)->last_index_val);
|
|
}
|
|
|
|
static const builtin_prop_t RegExp_props[] = {
|
|
{L"exec", RegExp_exec, PROPF_METHOD|1},
|
|
{L"global", NULL,0, RegExp_get_global},
|
|
{L"ignoreCase", NULL,0, RegExp_get_ignoreCase},
|
|
{L"lastIndex", NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex},
|
|
{L"multiline", NULL,0, RegExp_get_multiline},
|
|
{L"source", NULL,0, RegExp_get_source},
|
|
{L"test", RegExp_test, PROPF_METHOD|1},
|
|
{L"toString", RegExp_toString, PROPF_METHOD}
|
|
};
|
|
|
|
static const builtin_info_t RegExp_info = {
|
|
JSCLASS_REGEXP,
|
|
RegExp_value,
|
|
ARRAY_SIZE(RegExp_props),
|
|
RegExp_props,
|
|
RegExp_destructor,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
RegExp_gc_traverse
|
|
};
|
|
|
|
static const builtin_prop_t RegExpInst_props[] = {
|
|
{L"global", NULL,0, RegExp_get_global},
|
|
{L"ignoreCase", NULL,0, RegExp_get_ignoreCase},
|
|
{L"lastIndex", NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex},
|
|
{L"multiline", NULL,0, RegExp_get_multiline},
|
|
{L"source", NULL,0, RegExp_get_source}
|
|
};
|
|
|
|
static const builtin_info_t RegExpInst_info = {
|
|
JSCLASS_REGEXP,
|
|
RegExp_value,
|
|
ARRAY_SIZE(RegExpInst_props),
|
|
RegExpInst_props,
|
|
RegExp_destructor,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
RegExp_gc_traverse
|
|
};
|
|
|
|
static HRESULT alloc_regexp(script_ctx_t *ctx, jsstr_t *str, jsdisp_t *object_prototype, RegExpInstance **ret)
|
|
{
|
|
RegExpInstance *regexp;
|
|
HRESULT hres;
|
|
|
|
regexp = calloc(1, sizeof(RegExpInstance));
|
|
if(!regexp)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if(object_prototype)
|
|
hres = init_dispex(®exp->dispex, ctx, &RegExp_info, object_prototype);
|
|
else
|
|
hres = init_dispex_from_constr(®exp->dispex, ctx, &RegExpInst_info, ctx->regexp_constr);
|
|
|
|
if(FAILED(hres)) {
|
|
free(regexp);
|
|
return hres;
|
|
}
|
|
|
|
regexp->str = jsstr_addref(str);
|
|
regexp->last_index_val = jsval_number(0);
|
|
|
|
*ret = regexp;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT create_regexp(script_ctx_t *ctx, jsstr_t *src, DWORD flags, jsdisp_t **ret)
|
|
{
|
|
RegExpInstance *regexp;
|
|
const WCHAR *str;
|
|
HRESULT hres;
|
|
|
|
str = jsstr_flatten(src);
|
|
if(!str)
|
|
return E_OUTOFMEMORY;
|
|
|
|
TRACE("%s %lx\n", debugstr_wn(str, jsstr_length(src)), flags);
|
|
|
|
hres = alloc_regexp(ctx, src, NULL, ®exp);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
regexp->jsregexp = regexp_new(ctx, &ctx->tmp_heap, str, jsstr_length(regexp->str), flags, FALSE);
|
|
if(!regexp->jsregexp) {
|
|
WARN("regexp_new failed\n");
|
|
jsdisp_release(®exp->dispex);
|
|
return DISP_E_EXCEPTION;
|
|
}
|
|
|
|
*ret = ®exp->dispex;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT create_regexp_var(script_ctx_t *ctx, jsval_t src_arg, jsval_t *flags_arg, jsdisp_t **ret)
|
|
{
|
|
DWORD flags = 0;
|
|
const WCHAR *opt = NULL;
|
|
jsstr_t *src;
|
|
HRESULT hres = S_OK;
|
|
|
|
if(is_object_instance(src_arg)) {
|
|
jsdisp_t *obj;
|
|
|
|
obj = to_jsdisp(get_object(src_arg));
|
|
if(obj) {
|
|
if(is_class(obj, JSCLASS_REGEXP)) {
|
|
RegExpInstance *regexp = regexp_from_jsdisp(obj);
|
|
|
|
hres = create_regexp(ctx, regexp->str, regexp->jsregexp->flags, ret);
|
|
return hres;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(is_undefined(src_arg))
|
|
src = jsstr_empty();
|
|
else
|
|
hres = to_string(ctx, src_arg, &src);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
if(flags_arg && !is_undefined(*flags_arg)) {
|
|
jsstr_t *opt_str;
|
|
|
|
hres = to_string(ctx, *flags_arg, &opt_str);
|
|
if(SUCCEEDED(hres)) {
|
|
opt = jsstr_flatten(opt_str);
|
|
if(opt)
|
|
hres = parse_regexp_flags(opt, jsstr_length(opt_str), &flags);
|
|
else
|
|
hres = E_OUTOFMEMORY;
|
|
jsstr_release(opt_str);
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hres))
|
|
hres = create_regexp(ctx, src, flags, ret);
|
|
jsstr_release(src);
|
|
return hres;
|
|
}
|
|
|
|
HRESULT regexp_string_match(script_ctx_t *ctx, jsdisp_t *re, jsstr_t *jsstr, jsval_t *r)
|
|
{
|
|
RegExpInstance *regexp = regexp_from_jsdisp(re);
|
|
match_result_t *match_result;
|
|
DWORD match_cnt, i;
|
|
const WCHAR *str;
|
|
jsdisp_t *array;
|
|
HRESULT hres;
|
|
|
|
str = jsstr_flatten(jsstr);
|
|
if(!str)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if(!(regexp->jsregexp->flags & REG_GLOB)) {
|
|
match_state_t *match;
|
|
heap_pool_t *mark;
|
|
|
|
mark = heap_pool_mark(&ctx->tmp_heap);
|
|
match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, str);
|
|
if(!match) {
|
|
heap_pool_clear(mark);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hres = regexp_match_next(ctx, ®exp->dispex, 0, jsstr, &match);
|
|
if(FAILED(hres)) {
|
|
heap_pool_clear(mark);
|
|
return hres;
|
|
}
|
|
|
|
if(r) {
|
|
if(hres == S_OK) {
|
|
IDispatch *ret;
|
|
|
|
hres = create_match_array(ctx, jsstr, match, &ret);
|
|
if(SUCCEEDED(hres))
|
|
*r = jsval_disp(ret);
|
|
}else {
|
|
*r = jsval_null();
|
|
}
|
|
}
|
|
|
|
heap_pool_clear(mark);
|
|
return S_OK;
|
|
}
|
|
|
|
hres = regexp_match(ctx, ®exp->dispex, jsstr, FALSE, &match_result, &match_cnt);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
if(!match_cnt) {
|
|
TRACE("no match\n");
|
|
|
|
if(r)
|
|
*r = jsval_null();
|
|
return S_OK;
|
|
}
|
|
|
|
hres = create_array(ctx, match_cnt, &array);
|
|
if(FAILED(hres)) {
|
|
free(match_result);
|
|
return hres;
|
|
}
|
|
|
|
for(i=0; i < match_cnt; i++) {
|
|
jsstr_t *tmp_str;
|
|
|
|
tmp_str = jsstr_substr(jsstr, match_result[i].index, match_result[i].length);
|
|
if(!tmp_str) {
|
|
hres = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
|
|
jsstr_release(tmp_str);
|
|
if(FAILED(hres))
|
|
break;
|
|
}
|
|
|
|
while(SUCCEEDED(hres)) {
|
|
hres = jsdisp_propput_name(array, L"index", jsval_number(match_result[match_cnt-1].index));
|
|
if(FAILED(hres))
|
|
break;
|
|
|
|
hres = jsdisp_propput_name(array, L"lastIndex",
|
|
jsval_number(match_result[match_cnt-1].index + match_result[match_cnt-1].length));
|
|
if(FAILED(hres))
|
|
break;
|
|
|
|
hres = jsdisp_propput_name(array, L"input", jsval_string(jsstr));
|
|
break;
|
|
}
|
|
|
|
free(match_result);
|
|
|
|
if(SUCCEEDED(hres) && r)
|
|
*r = jsval_obj(array);
|
|
else
|
|
jsdisp_release(array);
|
|
return hres;
|
|
}
|
|
|
|
static HRESULT global_idx(script_ctx_t *ctx, DWORD idx, jsval_t *r)
|
|
{
|
|
jsstr_t *ret;
|
|
|
|
ret = jsstr_substr(ctx->last_match, ctx->match_parens[idx].index, ctx->match_parens[idx].length);
|
|
if(!ret)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*r = jsval_string(ret);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx1(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 0, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx2(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 1, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx3(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 2, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx4(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 3, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx5(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 4, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx6(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 5, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx7(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 6, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx8(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 7, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_idx9(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
return global_idx(ctx, 8, r);
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_leftContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
jsstr_t *ret;
|
|
|
|
TRACE("\n");
|
|
|
|
ret = jsstr_substr(ctx->last_match, 0, ctx->last_match_index);
|
|
if(!ret)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*r = jsval_string(ret);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExpConstr_get_rightContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
|
|
{
|
|
jsstr_t *ret;
|
|
|
|
TRACE("\n");
|
|
|
|
ret = jsstr_substr(ctx->last_match, ctx->last_match_index+ctx->last_match_length,
|
|
jsstr_length(ctx->last_match) - ctx->last_match_index - ctx->last_match_length);
|
|
if(!ret)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*r = jsval_string(ret);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT RegExpConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
|
|
jsval_t *r)
|
|
{
|
|
TRACE("\n");
|
|
|
|
switch(flags) {
|
|
case DISPATCH_METHOD:
|
|
if(argc) {
|
|
if(is_object_instance(argv[0])) {
|
|
jsdisp_t *jsdisp = to_jsdisp(get_object(argv[0]));
|
|
if(jsdisp) {
|
|
if(is_class(jsdisp, JSCLASS_REGEXP)) {
|
|
if(argc > 1 && !is_undefined(argv[1]))
|
|
return JS_E_REGEXP_SYNTAX;
|
|
|
|
if(r)
|
|
*r = jsval_obj(jsdisp_addref(jsdisp));
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* fall through */
|
|
case DISPATCH_CONSTRUCT: {
|
|
jsdisp_t *ret;
|
|
HRESULT hres;
|
|
|
|
hres = create_regexp_var(ctx, argc ? argv[0] : jsval_undefined(), argc > 1 ? argv+1 : NULL, &ret);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
if(r)
|
|
*r = jsval_obj(ret);
|
|
else
|
|
jsdisp_release(ret);
|
|
return S_OK;
|
|
}
|
|
default:
|
|
FIXME("unimplemented flags: %x\n", flags);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static const builtin_prop_t RegExpConstr_props[] = {
|
|
{L"$1", NULL,0, RegExpConstr_get_idx1, builtin_set_const},
|
|
{L"$2", NULL,0, RegExpConstr_get_idx2, builtin_set_const},
|
|
{L"$3", NULL,0, RegExpConstr_get_idx3, builtin_set_const},
|
|
{L"$4", NULL,0, RegExpConstr_get_idx4, builtin_set_const},
|
|
{L"$5", NULL,0, RegExpConstr_get_idx5, builtin_set_const},
|
|
{L"$6", NULL,0, RegExpConstr_get_idx6, builtin_set_const},
|
|
{L"$7", NULL,0, RegExpConstr_get_idx7, builtin_set_const},
|
|
{L"$8", NULL,0, RegExpConstr_get_idx8, builtin_set_const},
|
|
{L"$9", NULL,0, RegExpConstr_get_idx9, builtin_set_const},
|
|
{L"leftContext", NULL,0, RegExpConstr_get_leftContext, builtin_set_const},
|
|
{L"rightContext", NULL,0, RegExpConstr_get_rightContext, builtin_set_const}
|
|
};
|
|
|
|
static const builtin_info_t RegExpConstr_info = {
|
|
JSCLASS_FUNCTION,
|
|
Function_value,
|
|
ARRAY_SIZE(RegExpConstr_props),
|
|
RegExpConstr_props,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
HRESULT create_regexp_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
|
|
{
|
|
jsstr_t *str = jsstr_empty();
|
|
RegExpInstance *regexp;
|
|
HRESULT hres;
|
|
|
|
hres = alloc_regexp(ctx, str, object_prototype, ®exp);
|
|
jsstr_release(str);
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
hres = create_builtin_constructor(ctx, RegExpConstr_value, L"RegExp", &RegExpConstr_info,
|
|
PROPF_CONSTR|2, ®exp->dispex, ret);
|
|
|
|
jsdisp_release(®exp->dispex);
|
|
return hres;
|
|
}
|
|
|
|
HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret)
|
|
{
|
|
const WCHAR *p;
|
|
DWORD flags = 0;
|
|
|
|
for (p = str; p < str+str_len; p++) {
|
|
switch (*p) {
|
|
case 'g':
|
|
flags |= REG_GLOB;
|
|
break;
|
|
case 'i':
|
|
flags |= REG_FOLD;
|
|
break;
|
|
case 'm':
|
|
flags |= REG_MULTILINE;
|
|
break;
|
|
case 'y':
|
|
flags |= REG_STICKY;
|
|
break;
|
|
default:
|
|
WARN("wrong flag %c\n", *p);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
*ret = flags;
|
|
return S_OK;
|
|
}
|