Commit db69b121 authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

jscript: Added String.match implementation.

RegExp part of patch is based on Mozilla regexp implementation.
parent 9a752be1
......@@ -184,6 +184,13 @@ HRESULT create_object_constr(script_ctx_t*,DispatchEx**);
HRESULT create_regexp_constr(script_ctx_t*,DispatchEx**);
HRESULT create_string_constr(script_ctx_t*,DispatchEx**);
typedef struct {
const WCHAR *str;
DWORD len;
} match_result_t;
HRESULT regexp_match(DispatchEx*,const WCHAR*,DWORD,BOOL,match_result_t**,DWORD*);
static inline VARIANT *get_arg(DISPPARAMS *dp, DWORD i)
{
return dp->rgvarg + dp->cArgs-i-1;
......
......@@ -3116,6 +3116,79 @@ good:
return x;
}
static REMatchState *MatchRegExp(REGlobalData *gData, REMatchState *x)
{
REMatchState *result;
const WCHAR *cp = x->cp;
const WCHAR *cp2;
UINT j;
/*
* Have to include the position beyond the last character
* in order to detect end-of-input/line condition.
*/
for (cp2 = cp; cp2 <= gData->cpend; cp2++) {
gData->skipped = cp2 - cp;
x->cp = cp2;
for (j = 0; j < gData->regexp->parenCount; j++)
x->parens[j].index = -1;
result = ExecuteREBytecode(gData, x);
if (!gData->ok || result || (gData->regexp->flags & JSREG_STICKY))
return result;
gData->backTrackSP = gData->backTrackStack;
gData->cursz = 0;
gData->stateStackTop = 0;
cp2 = cp + gData->skipped;
}
return NULL;
}
#define MIN_BACKTRACK_LIMIT 400000
static REMatchState *InitMatch(script_ctx_t *cx, REGlobalData *gData, JSRegExp *re, size_t length)
{
REMatchState *result;
UINT i;
gData->backTrackStackSize = INITIAL_BACKTRACK;
gData->backTrackStack = jsheap_alloc(gData->pool, INITIAL_BACKTRACK);
if (!gData->backTrackStack)
goto bad;
gData->backTrackSP = gData->backTrackStack;
gData->cursz = 0;
gData->backTrackCount = 0;
gData->backTrackLimit = 0;
gData->stateStackLimit = INITIAL_STATESTACK;
gData->stateStack = jsheap_alloc(gData->pool, sizeof(REProgState) * INITIAL_STATESTACK);
if (!gData->stateStack)
goto bad;
gData->stateStackTop = 0;
gData->cx = cx;
gData->regexp = re;
gData->ok = TRUE;
result = jsheap_alloc(gData->pool, offsetof(REMatchState, parens) + re->parenCount * sizeof(RECapture));
if (!result)
goto bad;
for (i = 0; i < re->classCount; i++) {
if (!re->classList[i].converted &&
!ProcessCharSet(gData, &re->classList[i])) {
return NULL;
}
}
return result;
bad:
js_ReportOutOfScriptQuota(cx);
gData->ok = FALSE;
return NULL;
}
static void
js_DestroyRegExp(JSRegExp *re)
{
......@@ -3224,6 +3297,84 @@ out:
return re;
}
HRESULT regexp_match(DispatchEx *dispex, const WCHAR *str, DWORD len, BOOL gflag, match_result_t **match_result,
DWORD *result_cnt)
{
RegExpInstance *This = (RegExpInstance*)dispex;
match_result_t *ret = NULL;
const WCHAR *cp = str;
REGlobalData gData;
REMatchState *x, *result;
DWORD matchlen;
DWORD i=0, ret_size = 0;
jsheap_t *mark;
size_t length;
HRESULT hres = E_FAIL;
length = len;
mark = jsheap_mark(&This->dispex.ctx->tmp_heap);
gData.pool = &This->dispex.ctx->tmp_heap;
while(1) {
gData.cpbegin = cp;
gData.cpend = str + len;
gData.start = cp-str;
gData.skipped = 0;
x = InitMatch(NULL, &gData, This->jsregexp, length);
if(!x) {
WARN("InitMatch failed\n");
break;
}
x->cp = cp;
result = MatchRegExp(&gData, x);
if(!gData.ok) {
WARN("MatchRegExp failed\n");
break;
}
if(!result) {
hres = S_OK;
break;
}
matchlen = (result->cp-cp) - gData.skipped;
if(ret)
ret = heap_realloc(ret, (ret_size <<= 1) * sizeof(match_result_t));
else if(ret_size == i)
ret = heap_alloc((ret_size=4) * sizeof(match_result_t));
if(!ret) {
hres = E_OUTOFMEMORY;
break;
}
ret[i].str = result->cp-matchlen;
ret[i].len = matchlen;
length -= result->cp-cp;
cp = result->cp;
i++;
if(!gflag && !(This->jsregexp->flags & JSREG_GLOB)) {
hres = S_OK;
break;
}
}
jsheap_clear(mark);
if(FAILED(hres)) {
heap_free(ret);
return hres;
}
*match_result = ret;
*result_cnt = i;
return S_OK;
}
static HRESULT RegExp_source(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
{
......
......@@ -189,8 +189,77 @@ static HRESULT String_link(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS
static HRESULT String_match(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
{
FIXME("\n");
StringInstance *This = (StringInstance*)dispex;
match_result_t *match_result;
DispatchEx *array;
VARIANT var, *arg_var;
DWORD match_cnt, i;
HRESULT hres = S_OK;
TRACE("\n");
if(dp->cArgs - dp->cNamedArgs != 1) {
FIXME("unsupported args\n");
return E_NOTIMPL;
}
arg_var = get_arg(dp, 0);
switch(V_VT(arg_var)) {
case VT_DISPATCH: {
DispatchEx *regexp;
regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
if(regexp) {
if(regexp->builtin_info->class == JSCLASS_REGEXP) {
hres = regexp_match(regexp, This->str, This->length, FALSE, &match_result, &match_cnt);
jsdisp_release(regexp);
if(FAILED(hres))
return hres;
break;
}
jsdisp_release(regexp);
}
}
default:
FIXME("implemented only for regexp args\n");
return E_NOTIMPL;
}
if(!match_cnt) {
TRACE("no match\n");
if(retv)
V_VT(retv) = VT_NULL;
return S_OK;
}
hres = create_array(dispex->ctx, match_cnt, &array);
if(FAILED(hres))
return hres;
V_VT(&var) = VT_BSTR;
for(i=0; i < match_cnt; i++) {
V_BSTR(&var) = SysAllocStringLen(match_result[i].str, match_result[i].len);
if(!V_BSTR(&var)) {
hres = E_OUTOFMEMORY;
break;
}
hres = jsdisp_propput_idx(array, i, lcid, &var, ei, NULL/*FIXME*/);
SysFreeString(V_BSTR(&var));
if(FAILED(hres))
break;
}
if(FAILED(hres)) {
jsdisp_release(array);
return hres;
}
V_VT(retv) = VT_DISPATCH;
V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
return S_OK;
}
static HRESULT String_replace(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment