From bebbc18989470f685971aa7086f207aa297b9206 Mon Sep 17 00:00:00 2001 From: Jan Zerebecki Date: Wed, 17 Sep 2008 02:01:29 +0200 Subject: [PATCH] push 08a4e89daf1dcb63399dc60ee1e4a8641e1eb101 --- dlls/comctl32/tests/trackbar.c | 7 +- dlls/imm32/imm.c | 419 ++-- dlls/imm32/tests/imm32.c | 28 + dlls/inetcomm/mimeintl.c | 49 +- dlls/inetcomm/tests/mimeintl.c | 30 + dlls/jscript/array.c | 104 +- dlls/jscript/engine.h | 12 +- dlls/jscript/jscript.c | 2 + dlls/jscript/jscript.h | 51 +- dlls/jscript/jsutils.c | 36 + dlls/jscript/lex.c | 46 + dlls/jscript/parser.y | 13 +- dlls/jscript/regexp.c | 3459 +++++++++++++++++++++++++++++++- dlls/jscript/string.c | 116 +- dlls/jscript/tests/{rsrc.rc => api.js} | 23 +- dlls/jscript/tests/lang.js | 3 + dlls/jscript/tests/regexp.js | 54 + dlls/jscript/tests/rsrc.rc | 6 + dlls/jscript/tests/run.c | 2 + dlls/ntdll/file.c | 12 +- dlls/oleaut32/tests/tmarshal.c | 35 +- dlls/oleaut32/tests/tmarshal.idl | 9 + dlls/oleaut32/tests/tmarshal_dispids.h | 1 + dlls/oleaut32/tmarshal.c | 37 +- dlls/oleaut32/typelib.c | 6 +- dlls/winhttp/Makefile.in | 1 + dlls/winhttp/cookie.c | 281 +++ dlls/winhttp/request.c | 56 +- dlls/winhttp/session.c | 152 +- dlls/winhttp/tests/winhttp.c | 150 ++ dlls/winhttp/winhttp_private.h | 26 + programs/regedit/framewnd.c | 4 +- programs/regedit/regedit.c | 4 +- programs/regedit/regproc.c | 184 +- programs/regedit/regproc.h | 5 +- tools/widl/client.c | 143 +- tools/widl/expr.c | 44 +- tools/widl/expr.h | 3 +- tools/widl/header.c | 4 +- tools/widl/proxy.c | 228 +-- tools/widl/server.c | 146 +- tools/widl/typegen.c | 293 ++- tools/widl/typegen.h | 11 +- 43 files changed, 5527 insertions(+), 768 deletions(-) copy dlls/jscript/tests/{rsrc.rc => api.js} (52%) create mode 100644 dlls/jscript/tests/regexp.js create mode 100644 dlls/winhttp/cookie.c diff --git a/dlls/comctl32/tests/trackbar.c b/dlls/comctl32/tests/trackbar.c index d82cd9850d4..00f7024ab5e 100644 --- a/dlls/comctl32/tests/trackbar.c +++ b/dlls/comctl32/tests/trackbar.c @@ -38,9 +38,9 @@ static const struct message create_parent_wnd_seq[] = { { WM_NCCREATE, sent }, { WM_NCCALCSIZE, sent|wparam, 0 }, { WM_CREATE, sent }, - { WM_QUERYNEWPALETTE, sent|optional }, { WM_SHOWWINDOW, sent|wparam, 1 }, { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, + { WM_QUERYNEWPALETTE, sent|optional }, { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, { WM_ACTIVATEAPP, sent|wparam, 1 }, { WM_NCACTIVATE, sent|wparam, 1 }, @@ -394,8 +394,9 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP LRESULT ret; struct message msg; - /* do not log painting messages */ - if (message != WM_PAINT && + /* log system messages, except for painting */ + if (message < WM_USER && + message != WM_PAINT && message != WM_ERASEBKGND && message != WM_NCPAINT && message != WM_NCHITTEST && diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 94b775201f7..abcff551a1b 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -985,158 +985,172 @@ BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf) return TRUE; } -/*********************************************************************** - * ImmGetCompositionStringA (IMM32.@) - */ -LONG WINAPI ImmGetCompositionStringA( - HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen) -{ - BOOL isString = FALSE; - LPBYTE buffer = NULL; - CHAR *buf = NULL; - LONG rc = 0; - InputContextData *data = (InputContextData*)hIMC; - LPCOMPOSITIONSTRING compstr; - LPBYTE compdata; - TRACE("(%p, 0x%x, %p, %d)\n", hIMC, dwIndex, lpBuf, dwBufLen); +/* Helpers for the GetCompositionString functions */ - if (!data) - return FALSE; +static INT CopyCompStringIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE target, INT tlen, + BOOL unicode ) +{ + INT rc; - if (!data->IMC.hCompStr) - return FALSE; + if (is_himc_ime_unicode(data) && !unicode) + rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)source, slen, (LPSTR)target, tlen, NULL, NULL); + else if (!is_himc_ime_unicode(data) && unicode) + rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)source, slen, (LPWSTR)target, tlen) * sizeof(WCHAR); + else + { + int dlen = (unicode)?sizeof(WCHAR):sizeof(CHAR); + memcpy( target, source, min(slen,tlen)*dlen); + rc = slen*dlen; + } - compdata = ImmLockIMCC(data->IMC.hCompStr); - compstr = (LPCOMPOSITIONSTRING)compdata; + return rc; +} - switch (dwIndex) +static INT CopyCompAttrIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource, INT sslen, + LPBYTE target, INT tlen, BOOL unicode ) +{ + INT rc; + + if (is_himc_ime_unicode(data) && !unicode) { - case GCS_RESULTSTR: - if (compstr->dwResultStrLen > 0 && compstr->dwResultStrOffset > 0) - { - isString = TRUE; - buffer = compdata + compstr->dwResultStrOffset; - rc = compstr->dwResultStrLen; - TRACE("GCS_RESULTSTR %p %i\n", buffer, rc); - } - break; - case GCS_COMPSTR: - if (compstr->dwCompStrLen > 0 && compstr->dwCompStrOffset > 0) - { - isString = TRUE; - buffer = compdata + compstr->dwCompStrOffset; - rc = compstr->dwCompStrLen; - TRACE("GCS_COMPSTR %p %i\n", buffer, rc); - } - break; - case GCS_COMPATTR: - if (compstr->dwCompAttrLen > 0 && compstr->dwCompAttrOffset > 0) - { - buffer = compdata + compstr->dwCompAttrOffset; - rc = compstr->dwCompAttrLen; - TRACE("GCS_COMPATTR %p %i\n", buffer, rc); - } - break; - case GCS_COMPCLAUSE: - if (compstr->dwCompClauseLen > 0 && compstr->dwCompClauseOffset > 0) - { - buffer = compdata + compstr->dwCompClauseOffset; - rc = compstr->dwCompClauseLen; - TRACE("GCS_COMPCLAUSE %p %i\n", buffer, rc); - } - break; - case GCS_RESULTCLAUSE: - if (compstr->dwResultClauseLen > 0 && compstr->dwResultClauseOffset > 0) - { - buffer = compdata + compstr->dwResultClauseOffset; - rc = compstr->dwResultClauseLen; - TRACE("GCS_RESULTCLAUSE %p %i\n", buffer, rc); - } - break; - case GCS_RESULTREADSTR: - if (compstr->dwResultReadStrLen > 0 && compstr->dwResultReadStrOffset > 0) + rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, sslen, NULL, 0, NULL, NULL); + if (tlen) { - isString = TRUE; - buffer = compdata + compstr->dwResultReadStrOffset; - rc = compstr->dwResultReadStrLen; - TRACE("GCS_RESULTREADSTR %p %i\n",buffer, rc); - } - break; - case GCS_RESULTREADCLAUSE: - if (compstr->dwResultReadClauseLen > 0 && compstr->dwResultReadClauseOffset > 0) - { - buffer = compdata + compstr->dwResultReadClauseOffset; - rc = compstr->dwResultReadClauseLen; - TRACE("GCS_RESULTREADCLAUSE %p %i\n", buffer, rc); + const BYTE *src = source; + LPBYTE dst = target; + int i, j = 0, k = 0; + + if (rc < tlen) + tlen = rc; + for (i = 0; i < sslen; ++i) + { + int len; + + len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)ssource + i, 1, + NULL, 0, NULL, NULL); + for (; len > 0; --len) + { + dst[j++] = src[k]; + + if (j >= tlen) + goto end; + } + ++k; + } + end: + rc = j; } - break; - case GCS_COMPREADSTR: - if (compstr->dwCompReadStrLen > 0 && compstr->dwCompReadStrOffset > 0) + } + else if (!is_himc_ime_unicode(data) && unicode) + { + rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, sslen, NULL, 0); + if (tlen) { - isString = TRUE; - buffer = compdata + compstr->dwCompReadStrOffset; - rc = compstr->dwCompReadStrLen; - TRACE("GCS_COMPREADSTR %p %i\n", buffer, rc); + const BYTE *src = source; + LPBYTE dst = target; + int i, j = 0; + + if (rc < tlen) + tlen = rc; + for (i = 0; i < sslen; ++i) + { + if (IsDBCSLeadByte(((LPSTR)ssource)[i])) + continue; + + dst[j++] = src[i]; + + if (j >= tlen) + break; + } + rc = j; } - break; - case GCS_COMPREADATTR: - if (compstr->dwCompReadAttrLen > 0 && compstr->dwCompReadAttrOffset > 0) + } + else + { + memcpy( target, source, min(slen,tlen)); + rc = slen; + } + + return rc; +} + +static INT CopyCompClauseIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource, INT sslen, + LPBYTE target, INT tlen, BOOL unicode ) +{ + INT rc; + + if (is_himc_ime_unicode(data) && !unicode) + { + if (tlen) { - buffer = compdata + compstr->dwCompReadAttrOffset; - rc = compstr->dwCompReadAttrLen; - TRACE("GCS_COMPREADATTR %p %i\n", buffer, rc); + int i; + + if (slen < tlen) + tlen = slen; + tlen /= sizeof (DWORD); + for (i = 0; i < tlen; ++i) + { + ((DWORD *)target)[i] = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, + ((DWORD *)source)[i], + NULL, 0, + NULL, NULL); + } + rc = sizeof (DWORD) * i; } - break; - case GCS_COMPREADCLAUSE: - if (compstr->dwCompReadClauseLen > 0 && compstr->dwCompReadClauseOffset > 0) + else + rc = slen; + } + else if (!is_himc_ime_unicode(data) && unicode) + { + if (tlen) { - buffer = compdata + compstr->dwCompReadClauseOffset; - rc = compstr->dwCompReadClauseLen; - TRACE("GCS_COMPREADCLAUSE %p %i\n", buffer, rc); + int i; + + if (slen < tlen) + tlen = slen; + tlen /= sizeof (DWORD); + for (i = 0; i < tlen; ++i) + { + ((DWORD *)target)[i] = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, + ((DWORD *)source)[i], + NULL, 0); + } + rc = sizeof (DWORD) * i; } - break; - case GCS_CURSORPOS: - TRACE("GCS_CURSORPOS\n"); - rc = compstr->dwCursorPos; - break; - case GCS_DELTASTART: - TRACE("GCS_DELTASTART\n"); - rc = compstr->dwDeltaStart; - break; - default: - FIXME("Unhandled index 0x%x\n",dwIndex); - break; + else + rc = slen; } - - if ( isString && buffer && is_himc_ime_unicode(data)) + else { - INT len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)buffer, rc, NULL, 0, NULL, NULL); - buf = HeapAlloc( GetProcessHeap(), 0, len ); - if ( buf ) - rc = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)buffer, rc, buf, len, NULL, NULL); - buffer = (LPBYTE)buf; + memcpy( target, source, min(slen,tlen)); + rc = slen; } - if ( lpBuf && buffer && dwBufLen >= rc) - memcpy(lpBuf, buffer, rc); + return rc; +} - HeapFree( GetProcessHeap(), 0, buf ); - ImmUnlockIMCC(data->IMC.hCompStr); +static INT CopyCompOffsetIMEtoClient(InputContextData *data, DWORD offset, LPBYTE ssource, BOOL unicode) +{ + int rc; + + if (is_himc_ime_unicode(data) && !unicode) + { + rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL); + } + else if (!is_himc_ime_unicode(data) && unicode) + { + rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0); + } + else + rc = offset; return rc; } -/*********************************************************************** - * ImmGetCompositionStringW (IMM32.@) - */ -LONG WINAPI ImmGetCompositionStringW( - HIMC hIMC, DWORD dwIndex, - LPVOID lpBuf, DWORD dwBufLen) +static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, + DWORD dwBufLen, BOOL unicode) { - BOOL isString = FALSE; - LPBYTE buffer = NULL; - WCHAR *buf = NULL; LONG rc = 0; InputContextData *data = (InputContextData*)hIMC; LPCOMPOSITIONSTRING compstr; @@ -1156,125 +1170,96 @@ LONG WINAPI ImmGetCompositionStringW( switch (dwIndex) { case GCS_RESULTSTR: - if (compstr->dwResultStrLen > 0 && compstr->dwResultStrOffset > 0) - { - isString = TRUE; - buffer = compdata + compstr->dwResultStrOffset; - rc = compstr->dwResultStrLen; - TRACE("GCS_RESULTSTR %p %i\n", buffer, rc); - } - break; - case GCS_RESULTREADSTR: - if (compstr->dwResultReadStrLen > 0 && compstr->dwResultReadStrOffset > 0) - { - isString = TRUE; - buffer = compdata + compstr->dwResultReadStrOffset; - rc = compstr->dwResultReadStrLen; - TRACE("GCS_RESULTREADSTR %p %i\n",buffer, rc); - } + TRACE("GCS_RESULTSTR\n"); + rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPSTR: - if (compstr->dwCompStrLen > 0 && compstr->dwCompStrOffset > 0) - { - isString = TRUE; - buffer = compdata + compstr->dwCompStrOffset; - rc = compstr->dwCompStrLen; - TRACE("GCS_COMPSTR %p %i\n", buffer, rc); - } + TRACE("GCS_COMPSTR\n"); + rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPATTR: - if (compstr->dwCompAttrLen > 0 && compstr->dwCompAttrOffset > 0) - { - buffer = compdata + compstr->dwCompAttrOffset; - rc = compstr->dwCompAttrLen; - TRACE("GCS_COMPATTR %p %i\n", buffer, rc); - } + TRACE("GCS_COMPATTR\n"); + rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen, + compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, + lpBuf, dwBufLen, unicode); break; case GCS_COMPCLAUSE: - if (compstr->dwCompClauseLen > 0 && compstr->dwCompClauseOffset > 0) - { - buffer = compdata + compstr->dwCompClauseOffset; - rc = compstr->dwCompClauseLen; - TRACE("GCS_COMPCLAUSE %p %i\n", buffer, rc); - } + TRACE("GCS_COMPCLAUSE\n"); + rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen, + compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, + lpBuf, dwBufLen, unicode); + break; + case GCS_RESULTCLAUSE: + TRACE("GCS_RESULTCLAUSE\n"); + rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen, + compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, + lpBuf, dwBufLen, unicode); + break; + case GCS_RESULTREADSTR: + TRACE("GCS_RESULTREADSTR\n"); + rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode); + break; + case GCS_RESULTREADCLAUSE: + TRACE("GCS_RESULTREADCLAUSE\n"); + rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen, + compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, + lpBuf, dwBufLen, unicode); break; case GCS_COMPREADSTR: - if (compstr->dwCompReadStrLen > 0 && compstr->dwCompReadStrOffset > 0) - { - isString = TRUE; - buffer = compdata + compstr->dwCompReadStrOffset; - rc = compstr->dwCompReadStrLen; - TRACE("GCS_COMPREADSTR %p %i\n", buffer, rc); - } + TRACE("GCS_COMPREADSTR\n"); + rc = CopyCompStringIMEtoClient(data, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode); break; case GCS_COMPREADATTR: - if (compstr->dwCompReadAttrLen > 0 && compstr->dwCompReadAttrOffset > 0) - { - buffer = compdata + compstr->dwCompReadAttrOffset; - rc = compstr->dwCompReadAttrLen; - TRACE("GCS_COMPREADATTR %p %i\n", buffer, rc); - } + TRACE("GCS_COMPREADATTR\n"); + rc = CopyCompAttrIMEtoClient(data, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen, + compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, + lpBuf, dwBufLen, unicode); break; case GCS_COMPREADCLAUSE: - if (compstr->dwCompReadClauseLen > 0 && compstr->dwCompReadClauseOffset > 0) - { - buffer = compdata + compstr->dwCompReadClauseOffset; - rc = compstr->dwCompReadClauseLen; - TRACE("GCS_COMPREADCLAUSE %p %i\n", buffer, rc); - } - break; - case GCS_RESULTREADCLAUSE: - if (compstr->dwResultReadClauseLen > 0 && compstr->dwResultReadClauseOffset > 0) - { - buffer = compdata + compstr->dwResultReadClauseOffset; - rc = compstr->dwResultReadClauseLen; - TRACE("GCS_RESULTREADCLAUSE %p %i\n", buffer, rc); - } - break; - case GCS_RESULTCLAUSE: - if (compstr->dwResultClauseLen > 0 && compstr->dwResultClauseOffset > 0) - { - buffer = compdata + compstr->dwResultClauseOffset; - rc = compstr->dwResultClauseLen; - TRACE("GCS_RESULTCLAUSE %p %i\n", buffer, rc); - } + TRACE("GCS_COMPREADCLAUSE\n"); + rc = CopyCompClauseIMEtoClient(data, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen, + compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, + lpBuf, dwBufLen, unicode); break; case GCS_CURSORPOS: TRACE("GCS_CURSORPOS\n"); - rc = compstr->dwCursorPos; + rc = CopyCompOffsetIMEtoClient(data, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode); break; case GCS_DELTASTART: TRACE("GCS_DELTASTART\n"); - rc = compstr->dwDeltaStart; + rc = CopyCompOffsetIMEtoClient(data, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode); break; default: FIXME("Unhandled index 0x%x\n",dwIndex); break; } - if ( isString && buffer ) - { - if ( !is_himc_ime_unicode(data) ) - { - INT len = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)buffer, rc, NULL, 0 ); - buf = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); - if ( buf ) - rc = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)buffer, rc, buf, len ); - buffer = (LPBYTE)buf; - } - rc *= sizeof(WCHAR); - } - - if ( lpBuf && buffer && dwBufLen >= rc ) - memcpy( lpBuf, buffer, rc ); - - HeapFree( GetProcessHeap(), 0, buf ); ImmUnlockIMCC(data->IMC.hCompStr); return rc; } /*********************************************************************** + * ImmGetCompositionStringA (IMM32.@) + */ +LONG WINAPI ImmGetCompositionStringA( + HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen) +{ + return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, FALSE); +} + + +/*********************************************************************** + * ImmGetCompositionStringW (IMM32.@) + */ +LONG WINAPI ImmGetCompositionStringW( + HIMC hIMC, DWORD dwIndex, + LPVOID lpBuf, DWORD dwBufLen) +{ + return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, TRUE); +} + +/*********************************************************************** * ImmGetCompositionWindow (IMM32.@) */ BOOL WINAPI ImmGetCompositionWindow(HIMC hIMC, LPCOMPOSITIONFORM lpCompForm) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 591feb337fd..d0ced5f9215 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -220,8 +220,36 @@ static int test_ImmNotifyIME(void) { return 0; } +static int test_ImmGetCompositionString(void) +{ + HIMC imc; + static const WCHAR string[] = {'w','i','n','e',0x65e5,0x672c,0x8a9e}; + char cstring[20]; + WCHAR wstring[20]; + DWORD len; + DWORD alen,wlen; + + imc = ImmGetContext(hwnd); + ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0); + alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); + wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); + /* windows machines without any IME installed just return 0 above */ + if( alen && wlen) + { + len = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0); + ok(len*sizeof(WCHAR)==wlen,"GCS_COMPATTR(W) not returning correct count\n"); + len = ImmGetCompositionStringA(imc, GCS_COMPATTR, NULL, 0); + ok(len==alen,"GCS_COMPATTR(A) not returning correct count\n"); + } + ImmReleaseContext(hwnd, imc); + return 0; +} + START_TEST(imm32) { if (init()) + { test_ImmNotifyIME(); + test_ImmGetCompositionString(); + } cleanup(); } diff --git a/dlls/inetcomm/mimeintl.c b/dlls/inetcomm/mimeintl.c index 626ad62ebce..48deebf7781 100644 --- a/dlls/inetcomm/mimeintl.c +++ b/dlls/inetcomm/mimeintl.c @@ -346,13 +346,52 @@ static HRESULT WINAPI MimeInternat_EncodeHeader(IMimeInternational *iface, HCHAR } static HRESULT WINAPI MimeInternat_ConvertBuffer(IMimeInternational *iface, CODEPAGEID cpiSource, - CODEPAGEID cpiDest, - LPBLOB pIn, - LPBLOB pOut, + CODEPAGEID cpiDest, LPBLOB pIn, LPBLOB pOut, ULONG *pcbRead) { - FIXME("stub\n"); - return E_NOTIMPL; + HRESULT hr; + IMultiLanguage *ml; + + TRACE("(%p)->(%d, %d, %p, %p, %p)\n", iface, cpiSource, cpiDest, pIn, pOut, pcbRead); + + *pcbRead = 0; + pOut->cbSize = 0; + + /* Could call mlang.ConvertINetString() to avoid the COM overhead if need be. */ + + hr = get_mlang(&ml); + if(SUCCEEDED(hr)) + { + DWORD mode = 0; + UINT in_size = pIn->cbSize, out_size; + + hr = IMultiLanguage_ConvertString(ml, &mode, cpiSource, cpiDest, pIn->pBlobData, &in_size, + NULL, &out_size); + if(hr == S_OK) /* S_FALSE means the conversion could not be performed */ + { + pOut->pBlobData = CoTaskMemAlloc(out_size); + if(!pOut->pBlobData) + hr = E_OUTOFMEMORY; + else + { + mode = 0; + in_size = pIn->cbSize; + hr = IMultiLanguage_ConvertString(ml, &mode, cpiSource, cpiDest, pIn->pBlobData, &in_size, + pOut->pBlobData, &out_size); + + if(hr == S_OK) + { + *pcbRead = in_size; + pOut->cbSize = out_size; + } + else + CoTaskMemFree(pOut->pBlobData); + } + } + IMultiLanguage_Release(ml); + } + + return hr; } static HRESULT WINAPI MimeInternat_ConvertString(IMimeInternational *iface, CODEPAGEID cpiSource, diff --git a/dlls/inetcomm/tests/mimeintl.c b/dlls/inetcomm/tests/mimeintl.c index 0fc514fe5d2..0de56b2a4d7 100644 --- a/dlls/inetcomm/tests/mimeintl.c +++ b/dlls/inetcomm/tests/mimeintl.c @@ -210,11 +210,41 @@ static void test_defaultcharset(void) IMimeInternational_Release(internat); } +static void test_convert(void) +{ + IMimeInternational *internat; + HRESULT hr; + BLOB src, dst; + ULONG read; + static const char test_string[] = "test string"; + + hr = MimeOleGetInternat(&internat); + ok(hr == S_OK, "ret %08x\n", hr); + + src.pBlobData = (BYTE*)test_string; + src.cbSize = sizeof(test_string); + hr = IMimeInternational_ConvertBuffer(internat, 1252, 28591, &src, &dst, &read); + ok(hr == S_OK, "ret %08x\n", hr); + ok(read == sizeof(test_string), "got %d\n", read); + ok(dst.cbSize == sizeof(test_string), "got %d\n", dst.cbSize); + CoTaskMemFree(dst.pBlobData); + + src.cbSize = 2; + hr = IMimeInternational_ConvertBuffer(internat, 1252, 28591, &src, &dst, &read); + ok(hr == S_OK, "ret %08x\n", hr); + ok(read == 2, "got %d\n", read); + ok(dst.cbSize == 2, "got %d\n", dst.cbSize); + CoTaskMemFree(dst.pBlobData); + + IMimeInternational_Release(internat); +} + START_TEST(mimeintl) { OleInitialize(NULL); test_create(); test_charset(); test_defaultcharset(); + test_convert(); OleUninitialize(); } diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index 31249f093e1..c131865be11 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -24,6 +24,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(jscript); typedef struct { DispatchEx dispex; + + DWORD length; } ArrayInstance; static const WCHAR lengthW[] = {'l','e','n','g','t','h',0}; @@ -49,8 +51,21 @@ static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p',' static HRESULT Array_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) { - FIXME("\n"); - return E_NOTIMPL; + ArrayInstance *This = (ArrayInstance*)dispex; + + TRACE("%p %d\n", This, This->length); + + switch(flags) { + case DISPATCH_PROPERTYGET: + V_VT(retv) = VT_I4; + V_I4(retv) = This->length; + break; + default: + FIXME("unimplemented flags %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; } static HRESULT Array_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, @@ -179,7 +194,23 @@ static void Array_destructor(DispatchEx *dispex) static void Array_on_put(DispatchEx *dispex, const WCHAR *name) { - FIXME("\n"); + ArrayInstance *array = (ArrayInstance*)dispex; + const WCHAR *ptr = name; + DWORD id = 0; + + if(!isdigitW(*ptr)) + return; + + while(*ptr && isdigitW(*ptr)) { + id = id*10 + (*ptr-'0'); + ptr++; + } + + if(*ptr) + return; + + if(id >= array->length) + array->length = id+1; } static const builtin_prop_t Array_props[] = { @@ -212,10 +243,56 @@ static const builtin_info_t Array_info = { }; static HRESULT ArrayConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, - VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) { - FIXME("\n"); - return E_NOTIMPL; + DispatchEx *obj; + VARIANT *arg_var; + DWORD i; + HRESULT hres; + + TRACE("\n"); + + switch(flags) { + case DISPATCH_CONSTRUCT: { + if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) { + if(V_I4(arg_var) < 0) { + FIXME("throw RangeError\n"); + return E_FAIL; + } + + hres = create_array(dispex->ctx, V_I4(arg_var), &obj); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj); + return S_OK; + } + + hres = create_array(dispex->ctx, arg_cnt(dp), &obj); + if(FAILED(hres)) + return hres; + + for(i=0; i < arg_cnt(dp); i++) { + hres = jsdisp_propput_idx(obj, i, lcid, get_arg(dp, i), ei, caller); + if(FAILED(hres)) + break; + } + if(FAILED(hres)) { + jsdisp_release(obj); + return hres; + } + + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj); + break; + } + default: + FIXME("unimplemented flags: %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; } static HRESULT alloc_array(script_ctx_t *ctx, BOOL use_constr, ArrayInstance **ret) @@ -251,3 +328,18 @@ HRESULT create_array_constr(script_ctx_t *ctx, DispatchEx **ret) IDispatchEx_Release(_IDispatchEx_(&array->dispex)); return hres; } + +HRESULT create_array(script_ctx_t *ctx, DWORD length, DispatchEx **ret) +{ + ArrayInstance *array; + HRESULT hres; + + hres = alloc_array(ctx, TRUE, &array); + if(FAILED(hres)) + return hres; + + array->length = length; + + *ret = &array->dispex; + return S_OK; +} diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index 57a68c2812b..c37bc243443 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -18,6 +18,11 @@ typedef struct _source_elements_t source_elements_t; +typedef struct _obj_literal_t { + DispatchEx *obj; + struct _obj_literal_t *next; +} obj_literal_t; + typedef struct _parser_ctx_t { LONG ref; @@ -30,9 +35,10 @@ typedef struct _parser_ctx_t { BOOL nl; HRESULT hres; - jsheap_t tmp_heap; jsheap_t heap; + obj_literal_t *obj_literals; + struct _parser_ctx_t *next; } parser_ctx_t; @@ -53,7 +59,7 @@ static inline void *parser_alloc(parser_ctx_t *ctx, DWORD size) static inline void *parser_alloc_tmp(parser_ctx_t *ctx, DWORD size) { - return jsheap_alloc(&ctx->tmp_heap, size); + return jsheap_alloc(&ctx->script->tmp_heap, size); } typedef struct _scope_chain_t { @@ -105,6 +111,8 @@ typedef struct { } u; } literal_t; +literal_t *parse_regexp(parser_ctx_t*); + typedef struct _variable_declaration_t { const WCHAR *identifier; expression_t *expr; diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 899f59d4aaa..7c7def07468 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -54,6 +54,7 @@ void script_release(script_ctx_t *ctx) if(--ctx->ref) return; + jsheap_free(&ctx->tmp_heap); heap_free(ctx); } @@ -507,6 +508,7 @@ static HRESULT WINAPI JScriptParse_InitNew(IActiveScriptParse *iface) ctx->ref = 1; ctx->state = SCRIPTSTATE_UNINITIALIZED; + jsheap_init(&ctx->tmp_heap); ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL); if(ctx) { diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index ee1215ef1eb..a098b82c992 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -40,6 +40,22 @@ typedef struct { VARIANT var; } jsexcept_t; +typedef struct { + void **blocks; + DWORD block_cnt; + DWORD last_block; + DWORD offset; + BOOL mark; + struct list custom_blocks; +} jsheap_t; + +void jsheap_init(jsheap_t*); +void *jsheap_alloc(jsheap_t*,DWORD); +void *jsheap_grow(jsheap_t*,void*,DWORD,DWORD); +void jsheap_clear(jsheap_t*); +void jsheap_free(jsheap_t*); +jsheap_t *jsheap_mark(jsheap_t*); + typedef struct DispatchEx DispatchEx; #define PROPF_ARGMASK 0x00ff @@ -115,6 +131,9 @@ HRESULT create_builtin_function(script_ctx_t*,builtin_invoke_t,DWORD,DispatchEx* HRESULT create_object(script_ctx_t*,DispatchEx*,DispatchEx**); HRESULT create_math(script_ctx_t*,DispatchEx**); +HRESULT create_array(script_ctx_t*,DWORD,DispatchEx**); +HRESULT create_regexp_str(script_ctx_t*,const WCHAR*,DWORD,const WCHAR*,DWORD,DispatchEx**); +HRESULT create_string(script_ctx_t*,const WCHAR*,DWORD,DispatchEx**); HRESULT to_primitive(script_ctx_t*,VARIANT*,jsexcept_t*,VARIANT*); HRESULT to_boolean(VARIANT*,VARIANT_BOOL*); @@ -137,6 +156,8 @@ struct _script_ctx_t { named_item_t *named_items; LCID lcid; + jsheap_t tmp_heap; + DispatchEx *script_disp; DispatchEx *global; DispatchEx *array_constr; @@ -163,22 +184,26 @@ HRESULT create_object_constr(script_ctx_t*,DispatchEx**); HRESULT create_regexp_constr(script_ctx_t*,DispatchEx**); HRESULT create_string_constr(script_ctx_t*,DispatchEx**); -const char *debugstr_variant(const VARIANT*); +typedef struct { + const WCHAR *str; + DWORD len; +} match_result_t; -HRESULT WINAPI JScriptFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**); +HRESULT regexp_match(DispatchEx*,const WCHAR*,DWORD,BOOL,match_result_t**,DWORD*); -typedef struct { - void **blocks; - DWORD block_cnt; - DWORD last_block; - DWORD offset; - struct list custom_blocks; -} jsheap_t; +static inline VARIANT *get_arg(DISPPARAMS *dp, DWORD i) +{ + return dp->rgvarg + dp->cArgs-i-1; +} -void jsheap_init(jsheap_t*); -void *jsheap_alloc(jsheap_t*,DWORD); -void jsheap_clear(jsheap_t*); -void jsheap_free(jsheap_t*); +static inline DWORD arg_cnt(const DISPPARAMS *dp) +{ + return dp->cArgs - dp->cNamedArgs; +} + +const char *debugstr_variant(const VARIANT*); + +HRESULT WINAPI JScriptFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**); extern LONG module_ref; diff --git a/dlls/jscript/jsutils.c b/dlls/jscript/jsutils.c index a193b9ef425..2b0c31e783b 100644 --- a/dlls/jscript/jsutils.c +++ b/dlls/jscript/jsutils.c @@ -17,6 +17,7 @@ */ #include "jscript.h" +#include "engine.h" #include "wine/debug.h" @@ -110,14 +111,30 @@ void *jsheap_alloc(jsheap_t *heap, DWORD size) return list+1; } +void *jsheap_grow(jsheap_t *heap, void *mem, DWORD size, DWORD inc) +{ + if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size + && heap->offset+inc < block_size(heap->last_block)) { + heap->offset += inc; + return mem; + } + + return jsheap_alloc(heap, size+inc); +} + void jsheap_clear(jsheap_t *heap) { struct list *tmp; + if(!heap) + return; + while((tmp = list_next(&heap->custom_blocks, &heap->custom_blocks))) { list_remove(tmp); heap_free(tmp); } + + heap->last_block = heap->offset = 0; } void jsheap_free(jsheap_t *heap) @@ -133,6 +150,15 @@ void jsheap_free(jsheap_t *heap) jsheap_init(heap); } +jsheap_t *jsheap_mark(jsheap_t *heap) +{ + if(heap->mark) + return NULL; + + heap->mark = TRUE; + return heap; +} + /* ECMA-262 3rd Edition 9.1 */ HRESULT to_primitive(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret) { @@ -223,7 +249,17 @@ HRESULT to_string(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, BSTR *str) /* ECMA-262 3rd Edition 9.9 */ HRESULT to_object(exec_ctx_t *ctx, VARIANT *v, IDispatch **disp) { + DispatchEx *dispex; + HRESULT hres; + switch(V_VT(v)) { + case VT_BSTR: + hres = create_string(ctx->parser->script, V_BSTR(v), SysStringLen(V_BSTR(v)), &dispex); + if(FAILED(hres)) + return hres; + + *disp = (IDispatch*)_IDispatchEx_(dispex); + break; case VT_DISPATCH: IDispatch_AddRef(V_DISPATCH(v)); *disp = V_DISPATCH(v); diff --git a/dlls/jscript/lex.c b/dlls/jscript/lex.c index 8ae15d3fc6d..0aea118224a 100644 --- a/dlls/jscript/lex.c +++ b/dlls/jscript/lex.c @@ -684,3 +684,49 @@ int parser_lex(void *lval, parser_ctx_t *ctx) WARN("unexpected char '%c' %d\n", *ctx->ptr, *ctx->ptr); return 0; } + +static void add_object_literal(parser_ctx_t *ctx, DispatchEx *obj) +{ + obj_literal_t *literal = parser_alloc(ctx, sizeof(obj_literal_t)); + + literal->obj = obj; + literal->next = ctx->obj_literals; + ctx->obj_literals = literal; +} + +literal_t *parse_regexp(parser_ctx_t *ctx) +{ + const WCHAR *re, *flags; + DispatchEx *regexp; + literal_t *ret; + DWORD re_len; + HRESULT hres; + + TRACE("\n"); + + re = ctx->ptr; + while(ctx->ptr < ctx->end && (*ctx->ptr != '/' || *(ctx->ptr-1) == '\\')) + ctx->ptr++; + + if(ctx->ptr == ctx->end) { + WARN("unexpected end of file\n"); + return NULL; + } + + re_len = ctx->ptr-re; + + flags = ++ctx->ptr; + while(ctx->ptr < ctx->end && isalnumW(*ctx->ptr)) + ctx->ptr++; + + hres = create_regexp_str(ctx->script, re, re_len, flags, ctx->ptr-flags, ®exp); + if(FAILED(hres)) + return NULL; + + add_object_literal(ctx, regexp); + + ret = parser_alloc(ctx, sizeof(literal_t)); + ret->vt = VT_DISPATCH; + ret->u.disp = (IDispatch*)_IDispatchEx_(regexp); + return ret; +} diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index cc4ee7bbf52..f721a8ed4f9 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -775,7 +775,8 @@ Literal | BooleanLiteral { $$ = $1; } | tNumericLiteral { $$ = $1; } | tStringLiteral { $$ = new_string_literal(ctx, $1); } - | '/' { FIXME("RegExp literal\n"); YYABORT; } + | '/' { $$ = parse_regexp(ctx); + if(!$$) YYABORT; } /* ECMA-262 3rd Edition 7.8.2 */ BooleanLiteral @@ -1509,9 +1510,14 @@ static void program_parsed(parser_ctx_t *ctx, source_elements_t *source) void parser_release(parser_ctx_t *ctx) { + obj_literal_t *iter; + if(--ctx->ref) return; + for(iter = ctx->obj_literals; iter; iter = iter->next) + jsdisp_release(iter->obj); + jsheap_free(&ctx->heap); heap_free(ctx); } @@ -1519,6 +1525,7 @@ void parser_release(parser_ctx_t *ctx) HRESULT script_parse(script_ctx_t *ctx, const WCHAR *code, parser_ctx_t **ret) { parser_ctx_t *parser_ctx; + jsheap_t *mark; HRESULT hres; parser_ctx = heap_alloc_zero(sizeof(parser_ctx_t)); @@ -1534,11 +1541,11 @@ HRESULT script_parse(script_ctx_t *ctx, const WCHAR *code, parser_ctx_t **ret) script_addref(ctx); parser_ctx->script = ctx; - jsheap_init(&parser_ctx->tmp_heap); + mark = jsheap_mark(&ctx->tmp_heap); jsheap_init(&parser_ctx->heap); parser_parse(parser_ctx); - jsheap_free(&parser_ctx->tmp_heap); + jsheap_clear(mark); if(FAILED(parser_ctx->hres)) { hres = parser_ctx->hres; parser_release(parser_ctx); diff --git a/dlls/jscript/regexp.c b/dlls/jscript/regexp.c index 8bbaa6cb1b5..e8e9bea3618 100644 --- a/dlls/jscript/regexp.c +++ b/dlls/jscript/regexp.c @@ -16,22 +16,3399 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +/* + * Code in this file is based on files: + * js/src/jsregexp.h + * js/src/jsregexp.c + * from Mozilla project, released under LGPL 2.1 or later. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + */ + +#include + #include "jscript.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(jscript); +#define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ +#define JSREG_GLOB 0x02 /* global exec, creates array of matches */ +#define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ +#define JSREG_STICKY 0x08 /* only match starting at lastIndex */ + +typedef BYTE JSPackedBool; +typedef BYTE jsbytecode; + +/* + * This struct holds a bitmap representation of a class from a regexp. + * There's a list of these referenced by the classList field in the JSRegExp + * struct below. The initial state has startIndex set to the offset in the + * original regexp source of the beginning of the class contents. The first + * use of the class converts the source representation into a bitmap. + * + */ +typedef struct RECharSet { + JSPackedBool converted; + JSPackedBool sense; + WORD length; + union { + BYTE *bits; + struct { + size_t startIndex; + size_t length; + } src; + } u; +} RECharSet; + +typedef struct { + WORD flags; /* flags, see jsapi.h's JSREG_* defines */ + size_t parenCount; /* number of parenthesized submatches */ + size_t classCount; /* count [...] bitmaps */ + RECharSet *classList; /* list of [...] bitmaps */ + BSTR source; /* locked source string, sans // */ + jsbytecode program[1]; /* regular expression bytecode */ +} JSRegExp; + typedef struct { DispatchEx dispex; + + JSRegExp *jsregexp; + BSTR str; } RegExpInstance; +static const WCHAR sourceW[] = {'s','o','u','r','c','e',0}; +static const WCHAR globalW[] = {'g','l','o','b','a','l',0}; +static const WCHAR ignoreCaseW[] = {'i','g','n','o','r','e','C','a','s','e',0}; +static const WCHAR multilineW[] = {'m','u','l','t','i','l','i','n','e',0}; +static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0}; static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; static const WCHAR propertyIsEnumerableW[] = {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; +static const WCHAR execW[] = {'e','x','e','c',0}; + +static const WCHAR emptyW[] = {0}; + +/* FIXME: Better error handling */ +#define ReportRegExpError(a,b,c) +#define ReportRegExpErrorHelper(a,b,c,d) +#define JS_ReportErrorNumber(a,b,c,d) +#define JS_ReportErrorFlagsAndNumber(a,b,c,d,e,f) +#define js_ReportOutOfScriptQuota(a) +#define JS_ReportOutOfMemory(a) +#define JS_COUNT_OPERATION(a,b) + +#define JSMSG_MIN_TOO_BIG 47 +#define JSMSG_MAX_TOO_BIG 48 +#define JSMSG_OUT_OF_ORDER 49 +#define JSMSG_OUT_OF_MEMORY 137 + +#define LINE_SEPARATOR 0x2028 +#define PARA_SEPARATOR 0x2029 + +#define RE_IS_LETTER(c) (((c >= 'A') && (c <= 'Z')) || \ + ((c >= 'a') && (c <= 'z')) ) +#define RE_IS_LINE_TERM(c) ((c == '\n') || (c == '\r') || \ + (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR)) + +#define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) + +#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) +#define JS7_UNDEC(c) ((c) - '0') + +typedef enum REOp { + REOP_EMPTY, + REOP_BOL, + REOP_EOL, + REOP_WBDRY, + REOP_WNONBDRY, + REOP_DOT, + REOP_DIGIT, + REOP_NONDIGIT, + REOP_ALNUM, + REOP_NONALNUM, + REOP_SPACE, + REOP_NONSPACE, + REOP_BACKREF, + REOP_FLAT, + REOP_FLAT1, + REOP_FLATi, + REOP_FLAT1i, + REOP_UCFLAT1, + REOP_UCFLAT1i, + REOP_UCFLAT, + REOP_UCFLATi, + REOP_CLASS, + REOP_NCLASS, + REOP_ALT, + REOP_QUANT, + REOP_STAR, + REOP_PLUS, + REOP_OPT, + REOP_LPAREN, + REOP_RPAREN, + REOP_JUMP, + REOP_DOTSTAR, + REOP_LPARENNON, + REOP_ASSERT, + REOP_ASSERT_NOT, + REOP_ASSERTTEST, + REOP_ASSERTNOTTEST, + REOP_MINIMALSTAR, + REOP_MINIMALPLUS, + REOP_MINIMALOPT, + REOP_MINIMALQUANT, + REOP_ENDCHILD, + REOP_REPEAT, + REOP_MINIMALREPEAT, + REOP_ALTPREREQ, + REOP_ALTPREREQ2, + REOP_ENDALT, + REOP_CONCAT, + REOP_END, + REOP_LIMIT /* META: no operator >= to this */ +} REOp; + +#define REOP_IS_SIMPLE(op) ((op) <= REOP_NCLASS) + +const char *reop_names[] = { + "empty", + "bol", + "eol", + "wbdry", + "wnonbdry", + "dot", + "digit", + "nondigit", + "alnum", + "nonalnum", + "space", + "nonspace", + "backref", + "flat", + "flat1", + "flati", + "flat1i", + "ucflat1", + "ucflat1i", + "ucflat", + "ucflati", + "class", + "nclass", + "alt", + "quant", + "star", + "plus", + "opt", + "lparen", + "rparen", + "jump", + "dotstar", + "lparennon", + "assert", + "assert_not", + "asserttest", + "assertnottest", + "minimalstar", + "minimalplus", + "minimalopt", + "minimalquant", + "endchild", + "repeat", + "minimalrepeat", + "altprereq", + "alrprereq2", + "endalt", + "concat", + "end", + NULL +}; + +typedef struct RECapture { + ptrdiff_t index; /* start of contents, -1 for empty */ + size_t length; /* length of capture */ +} RECapture; + +typedef struct REMatchState { + const WCHAR *cp; + RECapture parens[1]; /* first of 're->parenCount' captures, + allocated at end of this struct */ +} REMatchState; + +typedef struct REProgState { + jsbytecode *continue_pc; /* current continuation data */ + jsbytecode continue_op; + ptrdiff_t index; /* progress in text */ + size_t parenSoFar; /* highest indexed paren started */ + union { + struct { + UINT min; /* current quantifier limits */ + UINT max; + } quantifier; + struct { + size_t top; /* backtrack stack state */ + size_t sz; + } assertion; + } u; +} REProgState; + +typedef struct REBackTrackData { + size_t sz; /* size of previous stack entry */ + jsbytecode *backtrack_pc; /* where to backtrack to */ + jsbytecode backtrack_op; + const WCHAR *cp; /* index in text of match at backtrack */ + size_t parenIndex; /* start index of saved paren contents */ + size_t parenCount; /* # of saved paren contents */ + size_t saveStateStackTop; /* number of parent states */ + /* saved parent states follow */ + /* saved paren contents follow */ +} REBackTrackData; + +#define INITIAL_STATESTACK 100 +#define INITIAL_BACKTRACK 8000 + +typedef struct REGlobalData { + script_ctx_t *cx; + JSRegExp *regexp; /* the RE in execution */ + BOOL ok; /* runtime error (out_of_memory only?) */ + size_t start; /* offset to start at */ + ptrdiff_t skipped; /* chars skipped anchoring this r.e. */ + const WCHAR *cpbegin; /* text base address */ + const WCHAR *cpend; /* text limit address */ + + REProgState *stateStack; /* stack of state of current parents */ + size_t stateStackTop; + size_t stateStackLimit; + + REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ + REBackTrackData *backTrackSP; + size_t backTrackStackSize; + size_t cursz; /* size of current stack entry */ + size_t backTrackCount; /* how many times we've backtracked */ + size_t backTrackLimit; /* upper limit on backtrack states */ + + jsheap_t *pool; /* It's faster to use one malloc'd pool + than to malloc/free the three items + that are allocated from this pool */ +} REGlobalData; + +typedef struct RENode RENode; +struct RENode { + REOp op; /* r.e. op bytecode */ + RENode *next; /* next in concatenation order */ + void *kid; /* first operand */ + union { + void *kid2; /* second operand */ + INT num; /* could be a number */ + size_t parenIndex; /* or a parenthesis index */ + struct { /* or a quantifier range */ + UINT min; + UINT max; + JSPackedBool greedy; + } range; + struct { /* or a character class */ + size_t startIndex; + size_t kidlen; /* length of string at kid, in jschars */ + size_t index; /* index into class list */ + WORD bmsize; /* bitmap size, based on max char code */ + JSPackedBool sense; + } ucclass; + struct { /* or a literal sequence */ + WCHAR chr; /* of one character */ + size_t length; /* or many (via the kid) */ + } flat; + struct { + RENode *kid2; /* second operand from ALT */ + WCHAR ch1; /* match char for ALTPREREQ */ + WCHAR ch2; /* ditto, or class index for ALTPREREQ2 */ + } altprereq; + } u; +}; + +#define CLASS_CACHE_SIZE 4 + +typedef struct CompilerState { + script_ctx_t *context; + const WCHAR *cpbegin; + const WCHAR *cpend; + const WCHAR *cp; + size_t parenCount; + size_t classCount; /* number of [] encountered */ + size_t treeDepth; /* maximum depth of parse tree */ + size_t progLength; /* estimated bytecode length */ + RENode *result; + size_t classBitmapsMem; /* memory to hold all class bitmaps */ + struct { + const WCHAR *start; /* small cache of class strings */ + size_t length; /* since they're often the same */ + size_t index; + } classCache[CLASS_CACHE_SIZE]; + WORD flags; +} CompilerState; + +typedef struct EmitStateStackEntry { + jsbytecode *altHead; /* start of REOP_ALT* opcode */ + jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */ + jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */ + jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */ + RENode *continueNode; /* original REOP_ALT* node being stacked */ + jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */ + JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to + avoid 16-bit unsigned offset overflow */ +} EmitStateStackEntry; + +/* + * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h, + * the getters and setters take the pc of the offset, not of the opcode before + * the offset. + */ +#define ARG_LEN 2 +#define GET_ARG(pc) ((WORD)(((pc)[0] << 8) | (pc)[1])) +#define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \ + (pc)[1] = (jsbytecode) (arg)) + +#define OFFSET_LEN ARG_LEN +#define OFFSET_MAX ((1 << (ARG_LEN * 8)) - 1) +#define GET_OFFSET(pc) GET_ARG(pc) + +static BOOL ParseRegExp(CompilerState*); + +/* + * Maximum supported tree depth is maximum size of EmitStateStackEntry stack. + * For sanity, we limit it to 2^24 bytes. + */ +#define TREE_DEPTH_MAX ((1 << 24) / sizeof(EmitStateStackEntry)) + +/* + * The maximum memory that can be allocated for class bitmaps. + * For sanity, we limit it to 2^24 bytes. + */ +#define CLASS_BITMAPS_MEM_LIMIT (1 << 24) + +/* + * Functions to get size and write/read bytecode that represent small indexes + * compactly. + * Each byte in the code represent 7-bit chunk of the index. 8th bit when set + * indicates that the following byte brings more bits to the index. Otherwise + * this is the last byte in the index bytecode representing highest index bits. + */ +static size_t +GetCompactIndexWidth(size_t index) +{ + size_t width; + + for (width = 1; (index >>= 7) != 0; ++width) { } + return width; +} + +static inline jsbytecode * +WriteCompactIndex(jsbytecode *pc, size_t index) +{ + size_t next; + + while ((next = index >> 7) != 0) { + *pc++ = (jsbytecode)(index | 0x80); + index = next; + } + *pc++ = (jsbytecode)index; + return pc; +} + +static inline jsbytecode * +ReadCompactIndex(jsbytecode *pc, size_t *result) +{ + size_t nextByte; + + nextByte = *pc++; + if ((nextByte & 0x80) == 0) { + /* + * Short-circuit the most common case when compact index <= 127. + */ + *result = nextByte; + } else { + size_t shift = 7; + *result = 0x7F & nextByte; + do { + nextByte = *pc++; + *result |= (nextByte & 0x7F) << shift; + shift += 7; + } while ((nextByte & 0x80) != 0); + } + return pc; +} + +/* Construct and initialize an RENode, returning NULL for out-of-memory */ +static RENode * +NewRENode(CompilerState *state, REOp op) +{ + RENode *ren; + + ren = jsheap_alloc(&state->context->tmp_heap, sizeof(*ren)); + if (!ren) { + /* js_ReportOutOfScriptQuota(cx); */ + return NULL; + } + ren->op = op; + ren->next = NULL; + ren->kid = NULL; + return ren; +} + +/* + * Validates and converts hex ascii value. + */ +static BOOL +isASCIIHexDigit(WCHAR c, UINT *digit) +{ + UINT cv = c; + + if (cv < '0') + return FALSE; + if (cv <= '9') { + *digit = cv - '0'; + return TRUE; + } + cv |= 0x20; + if (cv >= 'a' && cv <= 'f') { + *digit = cv - 'a' + 10; + return TRUE; + } + return FALSE; +} + +typedef struct { + REOp op; + const WCHAR *errPos; + size_t parenIndex; +} REOpData; + +#define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) +#define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) + +static BOOL +SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target) +{ + ptrdiff_t offset = target - jump; + + /* Check that target really points forward. */ + assert(offset >= 2); + if ((size_t)offset > OFFSET_MAX) + return FALSE; + + jump[0] = JUMP_OFFSET_HI(offset); + jump[1] = JUMP_OFFSET_LO(offset); + return TRUE; +} + +/* + * Generate bytecode for the tree rooted at t using an explicit stack instead + * of recursion. + */ +static jsbytecode * +EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth, + jsbytecode *pc, RENode *t) +{ + EmitStateStackEntry *emitStateSP, *emitStateStack; + RECharSet *charSet; + REOp op; + + if (treeDepth == 0) { + emitStateStack = NULL; + } else { + emitStateStack = heap_alloc(sizeof(EmitStateStackEntry) * treeDepth); + if (!emitStateStack) + return NULL; + } + emitStateSP = emitStateStack; + op = t->op; + assert(op < REOP_LIMIT); + + for (;;) { + *pc++ = op; + switch (op) { + case REOP_EMPTY: + --pc; + break; + + case REOP_ALTPREREQ2: + case REOP_ALTPREREQ: + assert(emitStateSP); + emitStateSP->altHead = pc - 1; + emitStateSP->endTermFixup = pc; + pc += OFFSET_LEN; + SET_ARG(pc, t->u.altprereq.ch1); + pc += ARG_LEN; + SET_ARG(pc, t->u.altprereq.ch2); + pc += ARG_LEN; + + emitStateSP->nextAltFixup = pc; /* offset to next alternate */ + pc += OFFSET_LEN; + + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_JUMP; + emitStateSP->jumpToJumpFlag = FALSE; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *) t->kid; + op = t->op; + assert(op < REOP_LIMIT); + continue; + + case REOP_JUMP: + emitStateSP->nextTermFixup = pc; /* offset to following term */ + pc += OFFSET_LEN; + if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc)) + goto jump_too_big; + emitStateSP->continueOp = REOP_ENDALT; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *) t->u.kid2; + op = t->op; + assert(op < REOP_LIMIT); + continue; + + case REOP_ENDALT: + /* + * If we already patched emitStateSP->nextTermFixup to jump to + * a nearer jump, to avoid 16-bit immediate offset overflow, we + * are done here. + */ + if (emitStateSP->jumpToJumpFlag) + break; + + /* + * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT. + * REOP_ENDALT is executed only on successful match of the last + * alternate in a group. + */ + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; + if (t->op != REOP_ALT) { + if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc)) + goto jump_too_big; + } + + /* + * If the program is bigger than the REOP_JUMP offset range, then + * we must check for alternates before this one that are part of + * the same group, and fix up their jump offsets to target jumps + * close enough to fit in a 16-bit unsigned offset immediate. + */ + if ((size_t)(pc - re->program) > OFFSET_MAX && + emitStateSP > emitStateStack) { + EmitStateStackEntry *esp, *esp2; + jsbytecode *alt, *jump; + ptrdiff_t span, header; + + esp2 = emitStateSP; + alt = esp2->altHead; + for (esp = esp2 - 1; esp >= emitStateStack; --esp) { + if (esp->continueOp == REOP_ENDALT && + !esp->jumpToJumpFlag && + esp->nextTermFixup + OFFSET_LEN == alt && + (size_t)(pc - ((esp->continueNode->op != REOP_ALT) + ? esp->endTermFixup + : esp->nextTermFixup)) > OFFSET_MAX) { + alt = esp->altHead; + jump = esp->nextTermFixup; + + /* + * The span must be 1 less than the distance from + * jump offset to jump offset, so we actually jump + * to a REOP_JUMP bytecode, not to its offset! + */ + for (;;) { + assert(jump < esp2->nextTermFixup); + span = esp2->nextTermFixup - jump - 1; + if ((size_t)span <= OFFSET_MAX) + break; + do { + if (--esp2 == esp) + goto jump_too_big; + } while (esp2->continueOp != REOP_ENDALT); + } + + jump[0] = JUMP_OFFSET_HI(span); + jump[1] = JUMP_OFFSET_LO(span); + + if (esp->continueNode->op != REOP_ALT) { + /* + * We must patch the offset at esp->endTermFixup + * as well, for the REOP_ALTPREREQ{,2} opcodes. + * If we're unlucky and endTermFixup is more than + * OFFSET_MAX bytes from its target, we cheat by + * jumping 6 bytes to the jump whose offset is at + * esp->nextTermFixup, which has the same target. + */ + jump = esp->endTermFixup; + header = esp->nextTermFixup - jump; + span += header; + if ((size_t)span > OFFSET_MAX) + span = header; + + jump[0] = JUMP_OFFSET_HI(span); + jump[1] = JUMP_OFFSET_LO(span); + } + + esp->jumpToJumpFlag = TRUE; + } + } + } + break; + + case REOP_ALT: + assert(emitStateSP); + emitStateSP->altHead = pc - 1; + emitStateSP->nextAltFixup = pc; /* offset to next alternate */ + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_JUMP; + emitStateSP->jumpToJumpFlag = FALSE; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *) t->kid; + op = t->op; + assert(op < REOP_LIMIT); + continue; + + case REOP_FLAT: + /* + * Coalesce FLATs if possible and if it would not increase bytecode + * beyond preallocated limit. The latter happens only when bytecode + * size for coalesced string with offset p and length 2 exceeds 6 + * bytes preallocated for 2 single char nodes, i.e. when + * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or + * GetCompactIndexWidth(p) > 4. + * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more + * nodes strictly decreases bytecode size, the check has to be + * done only for the first coalescing. + */ + if (t->kid && + GetCompactIndexWidth((WCHAR*)t->kid - state->cpbegin) <= 4) + { + while (t->next && + t->next->op == REOP_FLAT && + (WCHAR*)t->kid + t->u.flat.length == + (WCHAR*)t->next->kid) { + t->u.flat.length += t->next->u.flat.length; + t->next = t->next->next; + } + } + if (t->kid && t->u.flat.length > 1) { + pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT; + pc = WriteCompactIndex(pc, (WCHAR*)t->kid - state->cpbegin); + pc = WriteCompactIndex(pc, t->u.flat.length); + } else if (t->u.flat.chr < 256) { + pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; + *pc++ = (jsbytecode) t->u.flat.chr; + } else { + pc[-1] = (state->flags & JSREG_FOLD) + ? REOP_UCFLAT1i + : REOP_UCFLAT1; + SET_ARG(pc, t->u.flat.chr); + pc += ARG_LEN; + } + break; + + case REOP_LPAREN: + assert(emitStateSP); + pc = WriteCompactIndex(pc, t->u.parenIndex); + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_RPAREN; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *) t->kid; + op = t->op; + continue; + + case REOP_RPAREN: + pc = WriteCompactIndex(pc, t->u.parenIndex); + break; + + case REOP_BACKREF: + pc = WriteCompactIndex(pc, t->u.parenIndex); + break; + + case REOP_ASSERT: + assert(emitStateSP); + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ASSERTTEST; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *) t->kid; + op = t->op; + continue; + + case REOP_ASSERTTEST: + case REOP_ASSERTNOTTEST: + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; + break; + + case REOP_ASSERT_NOT: + assert(emitStateSP); + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ASSERTNOTTEST; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *) t->kid; + op = t->op; + continue; + + case REOP_QUANT: + assert(emitStateSP); + if (t->u.range.min == 0 && t->u.range.max == (UINT)-1) { + pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR; + } else if (t->u.range.min == 0 && t->u.range.max == 1) { + pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT; + } else if (t->u.range.min == 1 && t->u.range.max == (UINT) -1) { + pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS; + } else { + if (!t->u.range.greedy) + pc[-1] = REOP_MINIMALQUANT; + pc = WriteCompactIndex(pc, t->u.range.min); + /* + * Write max + 1 to avoid using size_t(max) + 1 bytes + * for (UINT)-1 sentinel. + */ + pc = WriteCompactIndex(pc, t->u.range.max + 1); + } + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ENDCHILD; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = (RENode *) t->kid; + op = t->op; + continue; + + case REOP_ENDCHILD: + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; + break; + + case REOP_CLASS: + if (!t->u.ucclass.sense) + pc[-1] = REOP_NCLASS; + pc = WriteCompactIndex(pc, t->u.ucclass.index); + charSet = &re->classList[t->u.ucclass.index]; + charSet->converted = FALSE; + charSet->length = t->u.ucclass.bmsize; + charSet->u.src.startIndex = t->u.ucclass.startIndex; + charSet->u.src.length = t->u.ucclass.kidlen; + charSet->sense = t->u.ucclass.sense; + break; + + default: + break; + } + + t = t->next; + if (t) { + op = t->op; + } else { + if (emitStateSP == emitStateStack) + break; + --emitStateSP; + t = emitStateSP->continueNode; + op = (REOp) emitStateSP->continueOp; + } + } + + cleanup: + heap_free(emitStateStack); + return pc; + + jump_too_big: + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + pc = NULL; + goto cleanup; +} + +/* + * Process the op against the two top operands, reducing them to a single + * operand in the penultimate slot. Update progLength and treeDepth. + */ +static BOOL +ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, + INT operandSP) +{ + RENode *result; + + switch (opData->op) { + case REOP_ALT: + result = NewRENode(state, REOP_ALT); + if (!result) + return FALSE; + result->kid = operandStack[operandSP - 2]; + result->u.kid2 = operandStack[operandSP - 1]; + operandStack[operandSP - 2] = result; + + if (state->treeDepth == TREE_DEPTH_MAX) { + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + return FALSE; + } + ++state->treeDepth; + + /* + * Look at both alternates to see if there's a FLAT or a CLASS at + * the start of each. If so, use a prerequisite match. + */ + if (((RENode *) result->kid)->op == REOP_FLAT && + ((RENode *) result->u.kid2)->op == REOP_FLAT && + (state->flags & JSREG_FOLD) == 0) { + result->op = REOP_ALTPREREQ; + result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; + result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr; + /* ALTPREREQ, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else + if (((RENode *) result->kid)->op == REOP_CLASS && + ((RENode *) result->kid)->u.ucclass.index < 256 && + ((RENode *) result->u.kid2)->op == REOP_FLAT && + (state->flags & JSREG_FOLD) == 0) { + result->op = REOP_ALTPREREQ2; + result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr; + result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index; + /* ALTPREREQ2, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else + if (((RENode *) result->kid)->op == REOP_FLAT && + ((RENode *) result->u.kid2)->op == REOP_CLASS && + ((RENode *) result->u.kid2)->u.ucclass.index < 256 && + (state->flags & JSREG_FOLD) == 0) { + result->op = REOP_ALTPREREQ2; + result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; + result->u.altprereq.ch2 = + ((RENode *) result->u.kid2)->u.ucclass.index; + /* ALTPREREQ2, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else { + /* ALT, , ..., JUMP, ... ENDALT */ + state->progLength += 7; + } + break; + + case REOP_CONCAT: + result = operandStack[operandSP - 2]; + while (result->next) + result = result->next; + result->next = operandStack[operandSP - 1]; + break; + + case REOP_ASSERT: + case REOP_ASSERT_NOT: + case REOP_LPARENNON: + case REOP_LPAREN: + /* These should have been processed by a close paren. */ + ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_MISSING_PAREN, + opData->errPos); + return FALSE; + + default:; + } + return TRUE; +} + +/* + * Hack two bits in CompilerState.flags, for use within FindParenCount to flag + * its being on the stack, and to propagate errors to its callers. + */ +#define JSREG_FIND_PAREN_COUNT 0x8000 +#define JSREG_FIND_PAREN_ERROR 0x4000 + +/* + * Magic return value from FindParenCount and GetDecimalValue, to indicate + * overflow beyond GetDecimalValue's max parameter, or a computed maximum if + * its findMax parameter is non-null. + */ +#define OVERFLOW_VALUE ((UINT)-1) + +static UINT +FindParenCount(CompilerState *state) +{ + CompilerState temp; + int i; + + if (state->flags & JSREG_FIND_PAREN_COUNT) + return OVERFLOW_VALUE; + + /* + * Copy state into temp, flag it so we never report an invalid backref, + * and reset its members to parse the entire regexp. This is obviously + * suboptimal, but GetDecimalValue calls us only if a backref appears to + * refer to a forward parenthetical, which is rare. + */ + temp = *state; + temp.flags |= JSREG_FIND_PAREN_COUNT; + temp.cp = temp.cpbegin; + temp.parenCount = 0; + temp.classCount = 0; + temp.progLength = 0; + temp.treeDepth = 0; + temp.classBitmapsMem = 0; + for (i = 0; i < CLASS_CACHE_SIZE; i++) + temp.classCache[i].start = NULL; + + if (!ParseRegExp(&temp)) { + state->flags |= JSREG_FIND_PAREN_ERROR; + return OVERFLOW_VALUE; + } + return temp.parenCount; +} + +/* + * Extract and return a decimal value at state->cp. The initial character c + * has already been read. Return OVERFLOW_VALUE if the result exceeds max. + * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in + * state->flags to discover whether an error occurred under findMax. + */ +static UINT +GetDecimalValue(WCHAR c, UINT max, UINT (*findMax)(CompilerState *state), + CompilerState *state) +{ + UINT value = JS7_UNDEC(c); + BOOL overflow = (value > max && (!findMax || value > findMax(state))); + + /* The following restriction allows simpler overflow checks. */ + assert(max <= ((UINT)-1 - 9) / 10); + while (state->cp < state->cpend) { + c = *state->cp; + if (!JS7_ISDEC(c)) + break; + value = 10 * value + JS7_UNDEC(c); + if (!overflow && value > max && (!findMax || value > findMax(state))) + overflow = TRUE; + ++state->cp; + } + return overflow ? OVERFLOW_VALUE : value; +} + +/* + * Calculate the total size of the bitmap required for a class expression. + */ +static BOOL +CalculateBitmapSize(CompilerState *state, RENode *target, const WCHAR *src, + const WCHAR *end) +{ + UINT max = 0; + BOOL inRange = FALSE; + WCHAR c, rangeStart = 0; + UINT n, digit, nDigits, i; + + target->u.ucclass.bmsize = 0; + target->u.ucclass.sense = TRUE; + + if (src == end) + return TRUE; + + if (*src == '^') { + ++src; + target->u.ucclass.sense = FALSE; + } + + while (src != end) { + BOOL canStartRange = TRUE; + UINT localMax = 0; + + switch (*src) { + case '\\': + ++src; + c = *src++; + switch (c) { + case 'b': + localMax = 0x8; + break; + case 'f': + localMax = 0xC; + break; + case 'n': + localMax = 0xA; + break; + case 'r': + localMax = 0xD; + break; + case 't': + localMax = 0x9; + break; + case 'v': + localMax = 0xB; + break; + case 'c': + if (src < end && RE_IS_LETTER(*src)) { + localMax = (UINT) (*src++) & 0x1F; + } else { + --src; + localMax = '\\'; + } + break; + case 'x': + nDigits = 2; + goto lexHex; + case 'u': + nDigits = 4; +lexHex: + n = 0; + for (i = 0; (i < nDigits) && (src < end); i++) { + c = *src++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original + *'\' as a literal. + */ + src -= i + 1; + n = '\\'; + break; + } + n = (n << 4) | digit; + } + localMax = n; + break; + case 'd': + canStartRange = FALSE; + if (inRange) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return FALSE; + } + localMax = '9'; + break; + case 'D': + case 's': + case 'S': + case 'w': + case 'W': + canStartRange = FALSE; + if (inRange) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return FALSE; + } + max = 65535; + + /* + * If this is the start of a range, ensure that it's less than + * the end. + */ + localMax = 0; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + /* + * This is a non-ECMA extension - decimal escapes (in this + * case, octal!) are supposed to be an error inside class + * ranges, but supported here for backwards compatibility. + * + */ + n = JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + n = 8 * n + JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + i = 8 * n + JS7_UNDEC(c); + if (i <= 0377) + n = i; + else + src--; + } + } + localMax = n; + break; + + default: + localMax = c; + break; + } + break; + default: + localMax = *src++; + break; + } + + if (inRange) { + /* Throw a SyntaxError here, per ECMA-262, 15.10.2.15. */ + if (rangeStart > localMax) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return FALSE; + } + inRange = FALSE; + } else { + if (canStartRange && src < end - 1) { + if (*src == '-') { + ++src; + inRange = TRUE; + rangeStart = (WCHAR)localMax; + continue; + } + } + if (state->flags & JSREG_FOLD) + rangeStart = localMax; /* one run of the uc/dc loop below */ + } + + if (state->flags & JSREG_FOLD) { + WCHAR maxch = localMax; + + for (i = rangeStart; i <= localMax; i++) { + WCHAR uch, dch; + + uch = toupperW(i); + dch = tolowerW(i); + if(maxch < uch) + maxch = uch; + if(maxch < dch) + maxch = dch; + } + localMax = maxch; + } + + if (localMax > max) + max = localMax; + } + target->u.ucclass.bmsize = max; + return TRUE; +} + +static INT +ParseMinMaxQuantifier(CompilerState *state, BOOL ignoreValues) +{ + UINT min, max; + WCHAR c; + const WCHAR *errp = state->cp++; + + c = *state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + min = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; + + if (!ignoreValues && min == OVERFLOW_VALUE) + return JSMSG_MIN_TOO_BIG; + + if (c == ',') { + c = *++state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + max = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; + if (!ignoreValues && max == OVERFLOW_VALUE) + return JSMSG_MAX_TOO_BIG; + if (!ignoreValues && min > max) + return JSMSG_OUT_OF_ORDER; + } else { + max = (UINT)-1; + } + } else { + max = min; + } + if (c == '}') { + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return JSMSG_OUT_OF_MEMORY; + state->result->u.range.min = min; + state->result->u.range.max = max; + /* + * QUANT, , , ... + * where is written as compact(max+1) to make + * (UINT)-1 sentinel to occupy 1 byte, not width_of(max)+1. + */ + state->progLength += (1 + GetCompactIndexWidth(min) + + GetCompactIndexWidth(max + 1) + +3); + return 0; + } + } + + state->cp = errp; + return -1; +} + +static BOOL +ParseQuantifier(CompilerState *state) +{ + RENode *term; + term = state->result; + if (state->cp < state->cpend) { + switch (*state->cp) { + case '+': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return FALSE; + state->result->u.range.min = 1; + state->result->u.range.max = (UINT)-1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '*': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return FALSE; + state->result->u.range.min = 0; + state->result->u.range.max = (UINT)-1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '?': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return FALSE; + state->result->u.range.min = 0; + state->result->u.range.max = 1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '{': /* balance '}' */ + { + INT err; + + err = ParseMinMaxQuantifier(state, FALSE); + if (err == 0) + goto quantifier; + if (err == -1) + return TRUE; + + ReportRegExpErrorHelper(state, JSREPORT_ERROR, err, errp); + return FALSE; + } + default:; + } + } + return TRUE; + +quantifier: + if (state->treeDepth == TREE_DEPTH_MAX) { + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + return FALSE; + } + + ++state->treeDepth; + ++state->cp; + state->result->kid = term; + if (state->cp < state->cpend && *state->cp == '?') { + ++state->cp; + state->result->u.range.greedy = FALSE; + } else { + state->result->u.range.greedy = TRUE; + } + return TRUE; +} + +/* + * item: assertion An item is either an assertion or + * quantatom a quantified atom. + * + * assertion: '^' Assertions match beginning of string + * (or line if the class static property + * RegExp.multiline is true). + * '$' End of string (or line if the class + * static property RegExp.multiline is + * true). + * '\b' Word boundary (between \w and \W). + * '\B' Word non-boundary. + * + * quantatom: atom An unquantified atom. + * quantatom '{' n ',' m '}' + * Atom must occur between n and m times. + * quantatom '{' n ',' '}' Atom must occur at least n times. + * quantatom '{' n '}' Atom must occur exactly n times. + * quantatom '*' Zero or more times (same as {0,}). + * quantatom '+' One or more times (same as {1,}). + * quantatom '?' Zero or one time (same as {0,1}). + * + * any of which can be optionally followed by '?' for ungreedy + * + * atom: '(' regexp ')' A parenthesized regexp (what matched + * can be addressed using a backreference, + * see '\' n below). + * '.' Matches any char except '\n'. + * '[' classlist ']' A character class. + * '[' '^' classlist ']' A negated character class. + * '\f' Form Feed. + * '\n' Newline (Line Feed). + * '\r' Carriage Return. + * '\t' Horizontal Tab. + * '\v' Vertical Tab. + * '\d' A digit (same as [0-9]). + * '\D' A non-digit. + * '\w' A word character, [0-9a-z_A-Z]. + * '\W' A non-word character. + * '\s' A whitespace character, [ \b\f\n\r\t\v]. + * '\S' A non-whitespace character. + * '\' n A backreference to the nth (n decimal + * and positive) parenthesized expression. + * '\' octal An octal escape sequence (octal must be + * two or three digits long, unless it is + * 0 for the null character). + * '\x' hex A hex escape (hex must be two digits). + * '\u' unicode A unicode escape (must be four digits). + * '\c' ctrl A control character, ctrl is a letter. + * '\' literalatomchar Any character except one of the above + * that follow '\' in an atom. + * otheratomchar Any character not first among the other + * atom right-hand sides. + */ +static BOOL +ParseTerm(CompilerState *state) +{ + WCHAR c = *state->cp++; + UINT nDigits; + UINT num, tmp, n, i; + const WCHAR *termStart; + + switch (c) { + /* assertions and atoms */ + case '^': + state->result = NewRENode(state, REOP_BOL); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + case '$': + state->result = NewRENode(state, REOP_EOL); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + case '\\': + if (state->cp >= state->cpend) { + /* a trailing '\' is an error */ + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_TRAILING_SLASH); + return FALSE; + } + c = *state->cp++; + switch (c) { + /* assertion escapes */ + case 'b' : + state->result = NewRENode(state, REOP_WBDRY); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + case 'B': + state->result = NewRENode(state, REOP_WNONBDRY); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + /* Decimal escape */ + case '0': + /* Give a strict warning. See also the note below. */ + WARN("non-octal digit in an escape sequence that doesn't match a back-reference\n"); + doOctal: + num = 0; + while (state->cp < state->cpend) { + c = *state->cp; + if (c < '0' || '7' < c) + break; + state->cp++; + tmp = 8 * num + (UINT)JS7_UNDEC(c); + if (tmp > 0377) + break; + num = tmp; + } + c = (WCHAR)num; + doFlat: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->progLength += 3; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + termStart = state->cp - 1; + num = GetDecimalValue(c, state->parenCount, FindParenCount, state); + if (state->flags & JSREG_FIND_PAREN_ERROR) + return FALSE; + if (num == OVERFLOW_VALUE) { + /* Give a strict mode warning. */ + WARN("back-reference exceeds number of capturing parentheses\n"); + + /* + * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax + * error here. However, for compatibility with IE, we treat the + * whole backref as flat if the first character in it is not a + * valid octal character, and as an octal escape otherwise. + */ + state->cp = termStart; + if (c >= '8') { + /* Treat this as flat. termStart - 1 is the \. */ + c = '\\'; + goto asFlat; + } + + /* Treat this as an octal escape. */ + goto doOctal; + } + assert(1 <= num && num <= 0x10000); + state->result = NewRENode(state, REOP_BACKREF); + if (!state->result) + return FALSE; + state->result->u.parenIndex = num - 1; + state->progLength + += 1 + GetCompactIndexWidth(state->result->u.parenIndex); + break; + /* Control escape */ + case 'f': + c = 0xC; + goto doFlat; + case 'n': + c = 0xA; + goto doFlat; + case 'r': + c = 0xD; + goto doFlat; + case 't': + c = 0x9; + goto doFlat; + case 'v': + c = 0xB; + goto doFlat; + /* Control letter */ + case 'c': + if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) { + c = (WCHAR) (*state->cp++ & 0x1F); + } else { + /* back off to accepting the original '\' as a literal */ + --state->cp; + c = '\\'; + } + goto doFlat; + /* HexEscapeSequence */ + case 'x': + nDigits = 2; + goto lexHex; + /* UnicodeEscapeSequence */ + case 'u': + nDigits = 4; +lexHex: + n = 0; + for (i = 0; i < nDigits && state->cp < state->cpend; i++) { + UINT digit; + c = *state->cp++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original 'u' or 'x' as a + * literal. + */ + state->cp -= i + 2; + n = *state->cp++; + break; + } + n = (n << 4) | digit; + } + c = (WCHAR) n; + goto doFlat; + /* Character class escapes */ + case 'd': + state->result = NewRENode(state, REOP_DIGIT); +doSimple: + if (!state->result) + return FALSE; + state->progLength++; + break; + case 'D': + state->result = NewRENode(state, REOP_NONDIGIT); + goto doSimple; + case 's': + state->result = NewRENode(state, REOP_SPACE); + goto doSimple; + case 'S': + state->result = NewRENode(state, REOP_NONSPACE); + goto doSimple; + case 'w': + state->result = NewRENode(state, REOP_ALNUM); + goto doSimple; + case 'W': + state->result = NewRENode(state, REOP_NONALNUM); + goto doSimple; + /* IdentityEscape */ + default: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->result->kid = (void *) (state->cp - 1); + state->progLength += 3; + break; + } + break; + case '[': + state->result = NewRENode(state, REOP_CLASS); + if (!state->result) + return FALSE; + termStart = state->cp; + state->result->u.ucclass.startIndex = termStart - state->cpbegin; + for (;;) { + if (state->cp == state->cpend) { + ReportRegExpErrorHelper(state, JSREPORT_ERROR, + JSMSG_UNTERM_CLASS, termStart); + + return FALSE; + } + if (*state->cp == '\\') { + state->cp++; + if (state->cp != state->cpend) + state->cp++; + continue; + } + if (*state->cp == ']') { + state->result->u.ucclass.kidlen = state->cp - termStart; + break; + } + state->cp++; + } + for (i = 0; i < CLASS_CACHE_SIZE; i++) { + if (!state->classCache[i].start) { + state->classCache[i].start = termStart; + state->classCache[i].length = state->result->u.ucclass.kidlen; + state->classCache[i].index = state->classCount; + break; + } + if (state->classCache[i].length == + state->result->u.ucclass.kidlen) { + for (n = 0; ; n++) { + if (n == state->classCache[i].length) { + state->result->u.ucclass.index + = state->classCache[i].index; + goto claim; + } + if (state->classCache[i].start[n] != termStart[n]) + break; + } + } + } + state->result->u.ucclass.index = state->classCount++; + + claim: + /* + * Call CalculateBitmapSize now as we want any errors it finds + * to be reported during the parse phase, not at execution. + */ + if (!CalculateBitmapSize(state, state->result, termStart, state->cp++)) + return FALSE; + /* + * Update classBitmapsMem with number of bytes to hold bmsize bits, + * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8 + * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize. + */ + n = (state->result->u.ucclass.bmsize >> 3) + 1; + if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) { + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + return FALSE; + } + state->classBitmapsMem += n; + /* CLASS, */ + state->progLength + += 1 + GetCompactIndexWidth(state->result->u.ucclass.index); + break; + + case '.': + state->result = NewRENode(state, REOP_DOT); + goto doSimple; + + case '{': + { + const WCHAR *errp = state->cp--; + INT err; + + err = ParseMinMaxQuantifier(state, TRUE); + state->cp = errp; + + if (err < 0) + goto asFlat; + + /* FALL THROUGH */ + } + case '*': + case '+': + case '?': + ReportRegExpErrorHelper(state, JSREPORT_ERROR, + JSMSG_BAD_QUANTIFIER, state->cp - 1); + return FALSE; + default: +asFlat: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->result->kid = (void *) (state->cp - 1); + state->progLength += 3; + break; + } + return ParseQuantifier(state); +} + +/* + * Top-down regular expression grammar, based closely on Perl4. + * + * regexp: altern A regular expression is one or more + * altern '|' regexp alternatives separated by vertical bar. + */ +#define INITIAL_STACK_SIZE 128 + +static BOOL +ParseRegExp(CompilerState *state) +{ + size_t parenIndex; + RENode *operand; + REOpData *operatorStack; + RENode **operandStack; + REOp op; + INT i; + BOOL result = FALSE; + + INT operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; + INT operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; + + /* Watch out for empty regexp */ + if (state->cp == state->cpend) { + state->result = NewRENode(state, REOP_EMPTY); + return (state->result != NULL); + } + + operatorStack = heap_alloc(sizeof(REOpData) * operatorStackSize); + if (!operatorStack) + return FALSE; + + operandStack = heap_alloc(sizeof(RENode *) * operandStackSize); + if (!operandStack) + goto out; + + for (;;) { + parenIndex = state->parenCount; + if (state->cp == state->cpend) { + /* + * If we are at the end of the regexp and we're short one or more + * operands, the regexp must have the form /x|/ or some such, with + * left parentheses making us short more than one operand. + */ + if (operatorSP >= operandSP) { + operand = NewRENode(state, REOP_EMPTY); + if (!operand) + goto out; + goto pushOperand; + } + } else { + switch (*state->cp) { + case '(': + ++state->cp; + if (state->cp + 1 < state->cpend && + *state->cp == '?' && + (state->cp[1] == '=' || + state->cp[1] == '!' || + state->cp[1] == ':')) { + switch (state->cp[1]) { + case '=': + op = REOP_ASSERT; + /* ASSERT, , ... ASSERTTEST */ + state->progLength += 4; + break; + case '!': + op = REOP_ASSERT_NOT; + /* ASSERTNOT, , ... ASSERTNOTTEST */ + state->progLength += 4; + break; + default: + op = REOP_LPARENNON; + break; + } + state->cp += 2; + } else { + op = REOP_LPAREN; + /* LPAREN, , ... RPAREN, */ + state->progLength + += 2 * (1 + GetCompactIndexWidth(parenIndex)); + state->parenCount++; + if (state->parenCount == 65535) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_TOO_MANY_PARENS); + goto out; + } + } + goto pushOperator; + + case ')': + /* + * If there's no stacked open parenthesis, throw syntax error. + */ + for (i = operatorSP - 1; ; i--) { + if (i < 0) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + if (operatorStack[i].op == REOP_ASSERT || + operatorStack[i].op == REOP_ASSERT_NOT || + operatorStack[i].op == REOP_LPARENNON || + operatorStack[i].op == REOP_LPAREN) { + break; + } + } + /* FALL THROUGH */ + + case '|': + /* Expected an operand before these, so make an empty one */ + operand = NewRENode(state, REOP_EMPTY); + if (!operand) + goto out; + goto pushOperand; + + default: + if (!ParseTerm(state)) + goto out; + operand = state->result; +pushOperand: + if (operandSP == operandStackSize) { + RENode **tmp; + operandStackSize += operandStackSize; + tmp = heap_realloc(operandStack, sizeof(RENode *) * operandStackSize); + if (!tmp) + goto out; + operandStack = tmp; + } + operandStack[operandSP++] = operand; + break; + } + } + + /* At the end; process remaining operators. */ +restartOperator: + if (state->cp == state->cpend) { + while (operatorSP) { + --operatorSP; + if (!ProcessOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) + goto out; + --operandSP; + } + assert(operandSP == 1); + state->result = operandStack[0]; + result = TRUE; + goto out; + } + + switch (*state->cp) { + case '|': + /* Process any stacked 'concat' operators */ + ++state->cp; + while (operatorSP && + operatorStack[operatorSP - 1].op == REOP_CONCAT) { + --operatorSP; + if (!ProcessOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) { + goto out; + } + --operandSP; + } + op = REOP_ALT; + goto pushOperator; + + case ')': + /* + * If there's no stacked open parenthesis, throw syntax error. + */ + for (i = operatorSP - 1; ; i--) { + if (i < 0) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + if (operatorStack[i].op == REOP_ASSERT || + operatorStack[i].op == REOP_ASSERT_NOT || + operatorStack[i].op == REOP_LPARENNON || + operatorStack[i].op == REOP_LPAREN) { + break; + } + } + ++state->cp; + + /* Process everything on the stack until the open parenthesis. */ + for (;;) { + assert(operatorSP); + --operatorSP; + switch (operatorStack[operatorSP].op) { + case REOP_ASSERT: + case REOP_ASSERT_NOT: + case REOP_LPAREN: + operand = NewRENode(state, operatorStack[operatorSP].op); + if (!operand) + goto out; + operand->u.parenIndex = + operatorStack[operatorSP].parenIndex; + assert(operandSP); + operand->kid = operandStack[operandSP - 1]; + operandStack[operandSP - 1] = operand; + if (state->treeDepth == TREE_DEPTH_MAX) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_REGEXP_TOO_COMPLEX); + goto out; + } + ++state->treeDepth; + /* FALL THROUGH */ + + case REOP_LPARENNON: + state->result = operandStack[operandSP - 1]; + if (!ParseQuantifier(state)) + goto out; + operandStack[operandSP - 1] = state->result; + goto restartOperator; + default: + if (!ProcessOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) + goto out; + --operandSP; + break; + } + } + break; + + case '{': + { + const WCHAR *errp = state->cp; + + if (ParseMinMaxQuantifier(state, TRUE) < 0) { + /* + * This didn't even scan correctly as a quantifier, so we should + * treat it as flat. + */ + op = REOP_CONCAT; + goto pushOperator; + } + + state->cp = errp; + /* FALL THROUGH */ + } + + case '+': + case '*': + case '?': + ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER, + state->cp); + result = FALSE; + goto out; + + default: + /* Anything else is the start of the next term. */ + op = REOP_CONCAT; +pushOperator: + if (operatorSP == operatorStackSize) { + REOpData *tmp; + operatorStackSize += operatorStackSize; + tmp = heap_realloc(operatorStack, sizeof(REOpData) * operatorStackSize); + if (!tmp) + goto out; + operatorStack = tmp; + } + operatorStack[operatorSP].op = op; + operatorStack[operatorSP].errPos = state->cp; + operatorStack[operatorSP++].parenIndex = parenIndex; + break; + } + } +out: + heap_free(operatorStack); + heap_free(operandStack); + return result; +} + +/* + * Save the current state of the match - the position in the input + * text as well as the position in the bytecode. The state of any + * parent expressions is also saved (preceding state). + * Contents of parenCount parentheses from parenIndex are also saved. + */ +static REBackTrackData * +PushBackTrackState(REGlobalData *gData, REOp op, + jsbytecode *target, REMatchState *x, const WCHAR *cp, + size_t parenIndex, size_t parenCount) +{ + size_t i; + REBackTrackData *result = + (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz); + + size_t sz = sizeof(REBackTrackData) + + gData->stateStackTop * sizeof(REProgState) + + parenCount * sizeof(RECapture); + + ptrdiff_t btsize = gData->backTrackStackSize; + ptrdiff_t btincr = ((char *)result + sz) - + ((char *)gData->backTrackStack + btsize); + + TRACE("\tBT_Push: %lu,%lu\n", (unsigned long) parenIndex, (unsigned long) parenCount); + + JS_COUNT_OPERATION(gData->cx, JSOW_JUMP * (1 + parenCount)); + if (btincr > 0) { + ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; + + JS_COUNT_OPERATION(gData->cx, JSOW_ALLOCATION); + btincr = ((btincr+btsize-1)/btsize)*btsize; + gData->backTrackStack = jsheap_grow(gData->pool, gData->backTrackStack, btsize, btincr); + if (!gData->backTrackStack) { + js_ReportOutOfScriptQuota(gData->cx); + gData->ok = FALSE; + return NULL; + } + gData->backTrackStackSize = btsize + btincr; + result = (REBackTrackData *) ((char *)gData->backTrackStack + offset); + } + gData->backTrackSP = result; + result->sz = gData->cursz; + gData->cursz = sz; + + result->backtrack_op = op; + result->backtrack_pc = target; + result->cp = cp; + result->parenCount = parenCount; + result->parenIndex = parenIndex; + + result->saveStateStackTop = gData->stateStackTop; + assert(gData->stateStackTop); + memcpy(result + 1, gData->stateStack, + sizeof(REProgState) * result->saveStateStackTop); + + if (parenCount != 0) { + memcpy((char *)(result + 1) + + sizeof(REProgState) * result->saveStateStackTop, + &x->parens[parenIndex], + sizeof(RECapture) * parenCount); + for (i = 0; i != parenCount; i++) + x->parens[parenIndex + i].index = -1; + } + + return result; +} + +static inline REMatchState * +FlatNIMatcher(REGlobalData *gData, REMatchState *x, WCHAR *matchChars, + size_t length) +{ + size_t i; + assert(gData->cpend >= x->cp); + if (length > (size_t)(gData->cpend - x->cp)) + return NULL; + for (i = 0; i != length; i++) { + if (toupperW(matchChars[i]) != toupperW(x->cp[i])) + return NULL; + } + x->cp += length; + return x; +} + +/* + * 1. Evaluate DecimalEscape to obtain an EscapeValue E. + * 2. If E is not a character then go to step 6. + * 3. Let ch be E's character. + * 4. Let A be a one-element RECharSet containing the character ch. + * 5. Call CharacterSetMatcher(A, false) and return its Matcher result. + * 6. E must be an integer. Let n be that integer. + * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. + * 8. Return an internal Matcher closure that takes two arguments, a State x + * and a Continuation c, and performs the following: + * 1. Let cap be x's captures internal array. + * 2. Let s be cap[n]. + * 3. If s is undefined, then call c(x) and return its result. + * 4. Let e be x's endIndex. + * 5. Let len be s's length. + * 6. Let f be e+len. + * 7. If f>InputLength, return failure. + * 8. If there exists an integer i between 0 (inclusive) and len (exclusive) + * such that Canonicalize(s[i]) is not the same character as + * Canonicalize(Input [e+i]), then return failure. + * 9. Let y be the State (f, cap). + * 10. Call c(y) and return its result. + */ +static REMatchState * +BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex) +{ + size_t len, i; + const WCHAR *parenContent; + RECapture *cap = &x->parens[parenIndex]; + + if (cap->index == -1) + return x; + + len = cap->length; + if (x->cp + len > gData->cpend) + return NULL; + + parenContent = &gData->cpbegin[cap->index]; + if (gData->regexp->flags & JSREG_FOLD) { + for (i = 0; i < len; i++) { + if (toupperW(parenContent[i]) != toupperW(x->cp[i])) + return NULL; + } + } else { + for (i = 0; i < len; i++) { + if (parenContent[i] != x->cp[i]) + return NULL; + } + } + x->cp += len; + return x; +} + +/* Add a single character to the RECharSet */ +static void +AddCharacterToCharSet(RECharSet *cs, WCHAR c) +{ + UINT byteIndex = (UINT)(c >> 3); + assert(c <= cs->length); + cs->u.bits[byteIndex] |= 1 << (c & 0x7); +} + + +/* Add a character range, c1 to c2 (inclusive) to the RECharSet */ +static void +AddCharacterRangeToCharSet(RECharSet *cs, UINT c1, UINT c2) +{ + UINT i; + + UINT byteIndex1 = c1 >> 3; + UINT byteIndex2 = c2 >> 3; + + assert(c2 <= cs->length && c1 <= c2); + + c1 &= 0x7; + c2 &= 0x7; + + if (byteIndex1 == byteIndex2) { + cs->u.bits[byteIndex1] |= ((BYTE)0xFF >> (7 - (c2 - c1))) << c1; + } else { + cs->u.bits[byteIndex1] |= 0xFF << c1; + for (i = byteIndex1 + 1; i < byteIndex2; i++) + cs->u.bits[i] = 0xFF; + cs->u.bits[byteIndex2] |= (BYTE)0xFF >> (7 - c2); + } +} + +/* Compile the source of the class into a RECharSet */ +static BOOL +ProcessCharSet(REGlobalData *gData, RECharSet *charSet) +{ + const WCHAR *src, *end; + BOOL inRange = FALSE; + WCHAR rangeStart = 0; + UINT byteLength, n; + WCHAR c, thisCh; + INT nDigits, i; + + assert(!charSet->converted); + /* + * Assert that startIndex and length points to chars inside [] inside + * source string. + */ + assert(1 <= charSet->u.src.startIndex); + assert(charSet->u.src.startIndex + < SysStringLen(gData->regexp->source)); + assert(charSet->u.src.length <= SysStringLen(gData->regexp->source) + - 1 - charSet->u.src.startIndex); + + charSet->converted = TRUE; + src = gData->regexp->source + charSet->u.src.startIndex; + + end = src + charSet->u.src.length; + + assert(src[-1] == '[' && end[0] == ']'); + + byteLength = (charSet->length >> 3) + 1; + charSet->u.bits = heap_alloc(byteLength); + if (!charSet->u.bits) { + JS_ReportOutOfMemory(gData->cx); + gData->ok = FALSE; + return FALSE; + } + memset(charSet->u.bits, 0, byteLength); + + if (src == end) + return TRUE; + + if (*src == '^') { + assert(charSet->sense == FALSE); + ++src; + } else { + assert(charSet->sense == TRUE); + } + + while (src != end) { + switch (*src) { + case '\\': + ++src; + c = *src++; + switch (c) { + case 'b': + thisCh = 0x8; + break; + case 'f': + thisCh = 0xC; + break; + case 'n': + thisCh = 0xA; + break; + case 'r': + thisCh = 0xD; + break; + case 't': + thisCh = 0x9; + break; + case 'v': + thisCh = 0xB; + break; + case 'c': + if (src < end && JS_ISWORD(*src)) { + thisCh = (WCHAR)(*src++ & 0x1F); + } else { + --src; + thisCh = '\\'; + } + break; + case 'x': + nDigits = 2; + goto lexHex; + case 'u': + nDigits = 4; + lexHex: + n = 0; + for (i = 0; (i < nDigits) && (src < end); i++) { + UINT digit; + c = *src++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original '\' + * as a literal + */ + src -= i + 1; + n = '\\'; + break; + } + n = (n << 4) | digit; + } + thisCh = (WCHAR)n; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + /* + * This is a non-ECMA extension - decimal escapes (in this + * case, octal!) are supposed to be an error inside class + * ranges, but supported here for backwards compatibility. + */ + n = JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + n = 8 * n + JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + i = 8 * n + JS7_UNDEC(c); + if (i <= 0377) + n = i; + else + src--; + } + } + thisCh = (WCHAR)n; + break; + + case 'd': + AddCharacterRangeToCharSet(charSet, '0', '9'); + continue; /* don't need range processing */ + case 'D': + AddCharacterRangeToCharSet(charSet, 0, '0' - 1); + AddCharacterRangeToCharSet(charSet, + (WCHAR)('9' + 1), + (WCHAR)charSet->length); + continue; + case 's': + for (i = (INT)charSet->length; i >= 0; i--) + if (isspaceW(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + case 'S': + for (i = (INT)charSet->length; i >= 0; i--) + if (!isspaceW(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + case 'w': + for (i = (INT)charSet->length; i >= 0; i--) + if (JS_ISWORD(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + case 'W': + for (i = (INT)charSet->length; i >= 0; i--) + if (!JS_ISWORD(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + default: + thisCh = c; + break; + + } + break; + + default: + thisCh = *src++; + break; + + } + if (inRange) { + if (gData->regexp->flags & JSREG_FOLD) { + int i; + + assert(rangeStart <= thisCh); + for (i = rangeStart; i <= thisCh; i++) { + WCHAR uch, dch; + + AddCharacterToCharSet(charSet, i); + uch = toupperW(i); + dch = tolowerW(i); + if (i != uch) + AddCharacterToCharSet(charSet, uch); + if (i != dch) + AddCharacterToCharSet(charSet, dch); + } + } else { + AddCharacterRangeToCharSet(charSet, rangeStart, thisCh); + } + inRange = FALSE; + } else { + if (gData->regexp->flags & JSREG_FOLD) { + AddCharacterToCharSet(charSet, toupperW(thisCh)); + AddCharacterToCharSet(charSet, tolowerW(thisCh)); + } else { + AddCharacterToCharSet(charSet, thisCh); + } + if (src < end - 1) { + if (*src == '-') { + ++src; + inRange = TRUE; + rangeStart = thisCh; + } + } + } + } + return TRUE; +} + +static BOOL +ReallocStateStack(REGlobalData *gData) +{ + size_t limit = gData->stateStackLimit; + size_t sz = sizeof(REProgState) * limit; + + gData->stateStack = jsheap_grow(gData->pool, gData->stateStack, sz, sz); + if (!gData->stateStack) { + js_ReportOutOfScriptQuota(gData->cx); + gData->ok = FALSE; + return FALSE; + } + gData->stateStackLimit = limit + limit; + return TRUE; +} + +#define PUSH_STATE_STACK(data) \ + do { \ + ++(data)->stateStackTop; \ + if ((data)->stateStackTop == (data)->stateStackLimit && \ + !ReallocStateStack((data))) { \ + return NULL; \ + } \ + }while(0) + +/* + * Apply the current op against the given input to see if it's going to match + * or fail. Return false if we don't get a match, true if we do. If updatecp is + * true, then update the current state's cp. Always update startpc to the next + * op. + */ +static inline REMatchState * +SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, + jsbytecode **startpc, BOOL updatecp) +{ + REMatchState *result = NULL; + WCHAR matchCh; + size_t parenIndex; + size_t offset, length, index; + jsbytecode *pc = *startpc; /* pc has already been incremented past op */ + WCHAR *source; + const WCHAR *startcp = x->cp; + WCHAR ch; + RECharSet *charSet; + + const char *opname = reop_names[op]; + TRACE("\n%06d: %*s%s\n", pc - gData->regexp->program, + gData->stateStackTop * 2, "", opname); + + switch (op) { + case REOP_EMPTY: + result = x; + break; + case REOP_BOL: + if (x->cp != gData->cpbegin) { + if (/*!gData->cx->regExpStatics.multiline && FIXME !!! */ + !(gData->regexp->flags & JSREG_MULTILINE)) { + break; + } + if (!RE_IS_LINE_TERM(x->cp[-1])) + break; + } + result = x; + break; + case REOP_EOL: + if (x->cp != gData->cpend) { + if (/*!gData->cx->regExpStatics.multiline &&*/ + !(gData->regexp->flags & JSREG_MULTILINE)) { + break; + } + if (!RE_IS_LINE_TERM(*x->cp)) + break; + } + result = x; + break; + case REOP_WBDRY: + if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ + !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) { + result = x; + } + break; + case REOP_WNONBDRY: + if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ + (x->cp != gData->cpend && JS_ISWORD(*x->cp))) { + result = x; + } + break; + case REOP_DOT: + if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_DIGIT: + if (x->cp != gData->cpend && JS7_ISDEC(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONDIGIT: + if (x->cp != gData->cpend && !JS7_ISDEC(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_ALNUM: + if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONALNUM: + if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_SPACE: + if (x->cp != gData->cpend && isspaceW(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONSPACE: + if (x->cp != gData->cpend && !isspaceW(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_BACKREF: + pc = ReadCompactIndex(pc, &parenIndex); + assert(parenIndex < gData->regexp->parenCount); + result = BackrefMatcher(gData, x, parenIndex); + break; + case REOP_FLAT: + pc = ReadCompactIndex(pc, &offset); + assert(offset < SysStringLen(gData->regexp->source)); + pc = ReadCompactIndex(pc, &length); + assert(1 <= length); + assert(length <= SysStringLen(gData->regexp->source) - offset); + if (length <= (size_t)(gData->cpend - x->cp)) { + source = gData->regexp->source + offset; + TRACE("%s\n", debugstr_wn(source, length)); + for (index = 0; index != length; index++) { + if (source[index] != x->cp[index]) + return NULL; + } + x->cp += length; + result = x; + } + break; + case REOP_FLAT1: + matchCh = *pc++; + TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp); + if (x->cp != gData->cpend && *x->cp == matchCh) { + result = x; + result->cp++; + } + break; + case REOP_FLATi: + pc = ReadCompactIndex(pc, &offset); + assert(offset < SysStringLen(gData->regexp->source)); + pc = ReadCompactIndex(pc, &length); + assert(1 <= length); + assert(length <= SysStringLen(gData->regexp->source) - offset); + source = gData->regexp->source; + result = FlatNIMatcher(gData, x, source + offset, length); + break; + case REOP_FLAT1i: + matchCh = *pc++; + if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) { + result = x; + result->cp++; + } + break; + case REOP_UCFLAT1: + matchCh = GET_ARG(pc); + TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp); + pc += ARG_LEN; + if (x->cp != gData->cpend && *x->cp == matchCh) { + result = x; + result->cp++; + } + break; + case REOP_UCFLAT1i: + matchCh = GET_ARG(pc); + pc += ARG_LEN; + if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) { + result = x; + result->cp++; + } + break; + case REOP_CLASS: + pc = ReadCompactIndex(pc, &index); + assert(index < gData->regexp->classCount); + if (x->cp != gData->cpend) { + charSet = &gData->regexp->classList[index]; + assert(charSet->converted); + ch = *x->cp; + index = ch >> 3; + if (charSet->length != 0 && + ch <= charSet->length && + (charSet->u.bits[index] & (1 << (ch & 0x7)))) { + result = x; + result->cp++; + } + } + break; + case REOP_NCLASS: + pc = ReadCompactIndex(pc, &index); + assert(index < gData->regexp->classCount); + if (x->cp != gData->cpend) { + charSet = &gData->regexp->classList[index]; + assert(charSet->converted); + ch = *x->cp; + index = ch >> 3; + if (charSet->length == 0 || + ch > charSet->length || + !(charSet->u.bits[index] & (1 << (ch & 0x7)))) { + result = x; + result->cp++; + } + } + break; + + default: + assert(FALSE); + } + if (result) { + if (!updatecp) + x->cp = startcp; + *startpc = pc; + TRACE(" * \n"); + return result; + } + x->cp = startcp; + return NULL; +} + +static inline REMatchState * +ExecuteREBytecode(REGlobalData *gData, REMatchState *x) +{ + REMatchState *result = NULL; + REBackTrackData *backTrackData; + jsbytecode *nextpc, *testpc; + REOp nextop; + RECapture *cap; + REProgState *curState; + const WCHAR *startcp; + size_t parenIndex, k; + size_t parenSoFar = 0; + + WCHAR matchCh1, matchCh2; + RECharSet *charSet; + + BOOL anchor; + jsbytecode *pc = gData->regexp->program; + REOp op = (REOp) *pc++; + + /* + * If the first node is a simple match, step the index into the string + * until that match is made, or fail if it can't be found at all. + */ + if (REOP_IS_SIMPLE(op) && !(gData->regexp->flags & JSREG_STICKY)) { + anchor = FALSE; + while (x->cp <= gData->cpend) { + nextpc = pc; /* reset back to start each time */ + result = SimpleMatch(gData, x, op, &nextpc, TRUE); + if (result) { + anchor = TRUE; + x = result; + pc = nextpc; /* accept skip to next opcode */ + op = (REOp) *pc++; + assert(op < REOP_LIMIT); + break; + } + gData->skipped++; + x->cp++; + } + if (!anchor) + goto bad; + } + + for (;;) { + const char *opname = reop_names[op]; + TRACE("\n%06d: %*s%s\n", pc - gData->regexp->program, + gData->stateStackTop * 2, "", opname); + + if (REOP_IS_SIMPLE(op)) { + result = SimpleMatch(gData, x, op, &pc, TRUE); + } else { + curState = &gData->stateStack[gData->stateStackTop]; + switch (op) { + case REOP_END: + goto good; + case REOP_ALTPREREQ2: + nextpc = pc + GET_OFFSET(pc); /* start of next op */ + pc += ARG_LEN; + matchCh2 = GET_ARG(pc); + pc += ARG_LEN; + k = GET_ARG(pc); + pc += ARG_LEN; + + if (x->cp != gData->cpend) { + if (*x->cp == matchCh2) + goto doAlt; + + charSet = &gData->regexp->classList[k]; + if (!charSet->converted && !ProcessCharSet(gData, charSet)) + goto bad; + matchCh1 = *x->cp; + k = matchCh1 >> 3; + if ((charSet->length == 0 || + matchCh1 > charSet->length || + !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^ + charSet->sense) { + goto doAlt; + } + } + result = NULL; + break; + + case REOP_ALTPREREQ: + nextpc = pc + GET_OFFSET(pc); /* start of next op */ + pc += ARG_LEN; + matchCh1 = GET_ARG(pc); + pc += ARG_LEN; + matchCh2 = GET_ARG(pc); + pc += ARG_LEN; + if (x->cp == gData->cpend || + (*x->cp != matchCh1 && *x->cp != matchCh2)) { + result = NULL; + break; + } + /* else false thru... */ + + case REOP_ALT: + doAlt: + nextpc = pc + GET_OFFSET(pc); /* start of next alternate */ + pc += ARG_LEN; /* start of this alternate */ + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + op = (REOp) *pc++; + startcp = x->cp; + if (REOP_IS_SIMPLE(op)) { + if (!SimpleMatch(gData, x, op, &pc, TRUE)) { + op = (REOp) *nextpc++; + pc = nextpc; + continue; + } + result = x; + op = (REOp) *pc++; + } + nextop = (REOp) *nextpc++; + if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0)) + goto bad; + continue; + + /* + * Occurs at (successful) end of REOP_ALT, + */ + case REOP_JUMP: + /* + * If we have not gotten a result here, it is because of an + * empty match. Do the same thing REOP_EMPTY would do. + */ + if (!result) + result = x; + + --gData->stateStackTop; + pc += GET_OFFSET(pc); + op = (REOp) *pc++; + continue; + + /* + * Occurs at last (successful) end of REOP_ALT, + */ + case REOP_ENDALT: + /* + * If we have not gotten a result here, it is because of an + * empty match. Do the same thing REOP_EMPTY would do. + */ + if (!result) + result = x; + + --gData->stateStackTop; + op = (REOp) *pc++; + continue; + + case REOP_LPAREN: + pc = ReadCompactIndex(pc, &parenIndex); + TRACE("[ %lu ]\n", (unsigned long) parenIndex); + assert(parenIndex < gData->regexp->parenCount); + if (parenIndex + 1 > parenSoFar) + parenSoFar = parenIndex + 1; + x->parens[parenIndex].index = x->cp - gData->cpbegin; + x->parens[parenIndex].length = 0; + op = (REOp) *pc++; + continue; + + case REOP_RPAREN: + { + ptrdiff_t delta; + + pc = ReadCompactIndex(pc, &parenIndex); + assert(parenIndex < gData->regexp->parenCount); + cap = &x->parens[parenIndex]; + delta = x->cp - (gData->cpbegin + cap->index); + cap->length = (delta < 0) ? 0 : (size_t) delta; + op = (REOp) *pc++; + + if (!result) + result = x; + continue; + } + case REOP_ASSERT: + nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ + pc += ARG_LEN; /* start of ASSERT child */ + op = (REOp) *pc++; + testpc = pc; + if (REOP_IS_SIMPLE(op) && + !SimpleMatch(gData, x, op, &testpc, FALSE)) { + result = NULL; + break; + } + curState->u.assertion.top = + (char *)gData->backTrackSP - (char *)gData->backTrackStack; + curState->u.assertion.sz = gData->cursz; + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (!PushBackTrackState(gData, REOP_ASSERTTEST, + nextpc, x, x->cp, 0, 0)) { + goto bad; + } + continue; + + case REOP_ASSERT_NOT: + nextpc = pc + GET_OFFSET(pc); + pc += ARG_LEN; + op = (REOp) *pc++; + testpc = pc; + if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ && + SimpleMatch(gData, x, op, &testpc, FALSE) && + *testpc == REOP_ASSERTNOTTEST) { + result = NULL; + break; + } + curState->u.assertion.top + = (char *)gData->backTrackSP - + (char *)gData->backTrackStack; + curState->u.assertion.sz = gData->cursz; + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST, + nextpc, x, x->cp, 0, 0)) { + goto bad; + } + continue; + + case REOP_ASSERTTEST: + --gData->stateStackTop; + --curState; + x->cp = gData->cpbegin + curState->index; + gData->backTrackSP = + (REBackTrackData *) ((char *)gData->backTrackStack + + curState->u.assertion.top); + gData->cursz = curState->u.assertion.sz; + if (result) + result = x; + break; + + case REOP_ASSERTNOTTEST: + --gData->stateStackTop; + --curState; + x->cp = gData->cpbegin + curState->index; + gData->backTrackSP = + (REBackTrackData *) ((char *)gData->backTrackStack + + curState->u.assertion.top); + gData->cursz = curState->u.assertion.sz; + result = (!result) ? x : NULL; + break; + case REOP_STAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = (UINT)-1; + goto quantcommon; + case REOP_PLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = (UINT)-1; + goto quantcommon; + case REOP_OPT: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = 1; + goto quantcommon; + case REOP_QUANT: + pc = ReadCompactIndex(pc, &k); + curState->u.quantifier.min = k; + pc = ReadCompactIndex(pc, &k); + /* max is k - 1 to use one byte for (UINT)-1 sentinel. */ + curState->u.quantifier.max = k - 1; + assert(curState->u.quantifier.min <= curState->u.quantifier.max); + quantcommon: + if (curState->u.quantifier.max == 0) { + pc = pc + GET_OFFSET(pc); + op = (REOp) *pc++; + result = x; + continue; + } + /* Step over */ + nextpc = pc + ARG_LEN; + op = (REOp) *nextpc++; + startcp = x->cp; + if (REOP_IS_SIMPLE(op)) { + if (!SimpleMatch(gData, x, op, &nextpc, TRUE)) { + if (curState->u.quantifier.min == 0) + result = x; + else + result = NULL; + pc = pc + GET_OFFSET(pc); + break; + } + op = (REOp) *nextpc++; + result = x; + } + curState->index = startcp - gData->cpbegin; + curState->continue_op = REOP_REPEAT; + curState->continue_pc = pc; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min == 0 && + !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp, + 0, 0)) { + goto bad; + } + pc = nextpc; + continue; + + case REOP_ENDCHILD: /* marks the end of a quantifier child */ + pc = curState[-1].continue_pc; + op = (REOp) curState[-1].continue_op; + + if (!result) + result = x; + continue; + + case REOP_REPEAT: + --curState; + do { + --gData->stateStackTop; + if (!result) { + /* Failed, see if we have enough children. */ + if (curState->u.quantifier.min == 0) + goto repeatDone; + goto break_switch; + } + if (curState->u.quantifier.min == 0 && + x->cp == gData->cpbegin + curState->index) { + /* matched an empty string, that'll get us nowhere */ + result = NULL; + goto break_switch; + } + if (curState->u.quantifier.min != 0) + curState->u.quantifier.min--; + if (curState->u.quantifier.max != (UINT) -1) + curState->u.quantifier.max--; + if (curState->u.quantifier.max == 0) + goto repeatDone; + nextpc = pc + ARG_LEN; + nextop = (REOp) *nextpc; + startcp = x->cp; + if (REOP_IS_SIMPLE(nextop)) { + nextpc++; + if (!SimpleMatch(gData, x, nextop, &nextpc, TRUE)) { + if (curState->u.quantifier.min == 0) + goto repeatDone; + result = NULL; + goto break_switch; + } + result = x; + } + curState->index = startcp - gData->cpbegin; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min == 0 && + !PushBackTrackState(gData, REOP_REPEAT, + pc, x, startcp, + curState->parenSoFar, + parenSoFar - + curState->parenSoFar)) { + goto bad; + } + } while (*nextpc == REOP_ENDCHILD); + pc = nextpc; + op = (REOp) *pc++; + parenSoFar = curState->parenSoFar; + continue; + + repeatDone: + result = x; + pc += GET_OFFSET(pc); + goto break_switch; + + case REOP_MINIMALSTAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = (UINT)-1; + goto minimalquantcommon; + case REOP_MINIMALPLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = (UINT)-1; + goto minimalquantcommon; + case REOP_MINIMALOPT: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = 1; + goto minimalquantcommon; + case REOP_MINIMALQUANT: + pc = ReadCompactIndex(pc, &k); + curState->u.quantifier.min = k; + pc = ReadCompactIndex(pc, &k); + /* See REOP_QUANT comments about k - 1. */ + curState->u.quantifier.max = k - 1; + assert(curState->u.quantifier.min + <= curState->u.quantifier.max); + minimalquantcommon: + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min != 0) { + curState->continue_op = REOP_MINIMALREPEAT; + curState->continue_pc = pc; + /* step over */ + pc += OFFSET_LEN; + op = (REOp) *pc++; + } else { + if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, + pc, x, x->cp, 0, 0)) { + goto bad; + } + --gData->stateStackTop; + pc = pc + GET_OFFSET(pc); + op = (REOp) *pc++; + } + continue; + + case REOP_MINIMALREPEAT: + --gData->stateStackTop; + --curState; + + TRACE("{%d,%d}\n", curState->u.quantifier.min, curState->u.quantifier.max); +#define PREPARE_REPEAT() \ + do { \ + curState->index = x->cp - gData->cpbegin; \ + curState->continue_op = REOP_MINIMALREPEAT; \ + curState->continue_pc = pc; \ + pc += ARG_LEN; \ + for (k = curState->parenSoFar; k < parenSoFar; k++) \ + x->parens[k].index = -1; \ + PUSH_STATE_STACK(gData); \ + op = (REOp) *pc++; \ + assert(op < REOP_LIMIT); \ + }while(0) + + if (!result) { + TRACE(" - \n"); + /* + * Non-greedy failure - try to consume another child. + */ + if (curState->u.quantifier.max == (UINT) -1 || + curState->u.quantifier.max > 0) { + PREPARE_REPEAT(); + continue; + } + /* Don't need to adjust pc since we're going to pop. */ + break; + } + if (curState->u.quantifier.min == 0 && + x->cp == gData->cpbegin + curState->index) { + /* Matched an empty string, that'll get us nowhere. */ + result = NULL; + break; + } + if (curState->u.quantifier.min != 0) + curState->u.quantifier.min--; + if (curState->u.quantifier.max != (UINT) -1) + curState->u.quantifier.max--; + if (curState->u.quantifier.min != 0) { + PREPARE_REPEAT(); + continue; + } + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, + pc, x, x->cp, + curState->parenSoFar, + parenSoFar - curState->parenSoFar)) { + goto bad; + } + --gData->stateStackTop; + pc = pc + GET_OFFSET(pc); + op = (REOp) *pc++; + assert(op < REOP_LIMIT); + continue; + default: + assert(FALSE); + result = NULL; + } + break_switch:; + } + + /* + * If the match failed and there's a backtrack option, take it. + * Otherwise this is a complete and utter failure. + */ + if (!result) { + if (gData->cursz == 0) + return NULL; + + /* Potentially detect explosive regex here. */ + gData->backTrackCount++; + if (gData->backTrackLimit && + gData->backTrackCount >= gData->backTrackLimit) { + JS_ReportErrorNumber(gData->cx, js_GetErrorMessage, NULL, + JSMSG_REGEXP_TOO_COMPLEX); + gData->ok = FALSE; + return NULL; + } + + backTrackData = gData->backTrackSP; + gData->cursz = backTrackData->sz; + gData->backTrackSP = + (REBackTrackData *) ((char *)backTrackData - backTrackData->sz); + x->cp = backTrackData->cp; + pc = backTrackData->backtrack_pc; + op = (REOp) backTrackData->backtrack_op; + assert(op < REOP_LIMIT); + gData->stateStackTop = backTrackData->saveStateStackTop; + assert(gData->stateStackTop); + + memcpy(gData->stateStack, backTrackData + 1, + sizeof(REProgState) * backTrackData->saveStateStackTop); + curState = &gData->stateStack[gData->stateStackTop - 1]; + + if (backTrackData->parenCount) { + memcpy(&x->parens[backTrackData->parenIndex], + (char *)(backTrackData + 1) + + sizeof(REProgState) * backTrackData->saveStateStackTop, + sizeof(RECapture) * backTrackData->parenCount); + parenSoFar = backTrackData->parenIndex + backTrackData->parenCount; + } else { + for (k = curState->parenSoFar; k < parenSoFar; k++) + x->parens[k].index = -1; + parenSoFar = curState->parenSoFar; + } + + TRACE("\tBT_Pop: %ld,%ld\n", + (unsigned long) backTrackData->parenIndex, + (unsigned long) backTrackData->parenCount); + continue; + } + x = result; + + /* + * Continue with the expression. + */ + op = (REOp)*pc++; + assert(op < REOP_LIMIT); + } + +bad: + TRACE("\n"); + return NULL; + +good: + TRACE("\n"); + 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) +{ + if (re->classList) { + UINT i; + for (i = 0; i < re->classCount; i++) { + if (re->classList[i].converted) + heap_free(re->classList[i].u.bits); + re->classList[i].u.bits = NULL; + } + heap_free(re->classList); + } + heap_free(re); +} + +static JSRegExp * +js_NewRegExp(script_ctx_t *cx, BSTR str, UINT flags, BOOL flat) +{ + JSRegExp *re; + jsheap_t *mark; + CompilerState state; + size_t resize; + jsbytecode *endPC; + UINT i; + size_t len; + + re = NULL; + mark = jsheap_mark(&cx->tmp_heap); + len = SysStringLen(str); + + state.context = cx; + state.cp = str; + if (!state.cp) + goto out; + state.cpbegin = state.cp; + state.cpend = state.cp + len; + state.flags = flags; + state.parenCount = 0; + state.classCount = 0; + state.progLength = 0; + state.treeDepth = 0; + state.classBitmapsMem = 0; + for (i = 0; i < CLASS_CACHE_SIZE; i++) + state.classCache[i].start = NULL; + + if (len != 0 && flat) { + state.result = NewRENode(&state, REOP_FLAT); + if (!state.result) + goto out; + state.result->u.flat.chr = *state.cpbegin; + state.result->u.flat.length = len; + state.result->kid = (void *) state.cpbegin; + /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ + state.progLength += 1 + GetCompactIndexWidth(0) + + GetCompactIndexWidth(len); + } else { + if (!ParseRegExp(&state)) + goto out; + } + resize = offsetof(JSRegExp, program) + state.progLength + 1; + re = heap_alloc(resize); + if (!re) + goto out; + + assert(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT); + re->classCount = state.classCount; + if (re->classCount) { + re->classList = heap_alloc(re->classCount * sizeof(RECharSet)); + if (!re->classList) { + js_DestroyRegExp(re); + re = NULL; + goto out; + } + for (i = 0; i < re->classCount; i++) + re->classList[i].converted = FALSE; + } else { + re->classList = NULL; + } + endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); + if (!endPC) { + js_DestroyRegExp(re); + re = NULL; + goto out; + } + *endPC++ = REOP_END; + /* + * Check whether size was overestimated and shrink using realloc. + * This is safe since no pointers to newly parsed regexp or its parts + * besides re exist here. + */ + if ((size_t)(endPC - re->program) != state.progLength + 1) { + JSRegExp *tmp; + assert((size_t)(endPC - re->program) < state.progLength + 1); + resize = offsetof(JSRegExp, program) + (endPC - re->program); + tmp = heap_realloc(re, resize); + if (tmp) + re = tmp; + } + + re->flags = flags; + re->parenCount = state.parenCount; + re->source = str; + +out: + jsheap_clear(mark); + 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) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_global(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_ignoreCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_multiline(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_lastIndex(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} static HRESULT RegExp_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) @@ -68,6 +3445,13 @@ static HRESULT RegExp_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, D return E_NOTIMPL; } +static HRESULT RegExp_exec(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + static HRESULT RegExp_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) { @@ -77,13 +3461,24 @@ static HRESULT RegExp_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAM static void RegExp_destructor(DispatchEx *dispex) { - heap_free(dispex); + RegExpInstance *This = (RegExpInstance*)dispex; + + if(This->jsregexp) + js_DestroyRegExp(This->jsregexp); + SysFreeString(This->str); + heap_free(This); } static const builtin_prop_t RegExp_props[] = { + {execW, RegExp_exec, PROPF_METHOD}, + {globalW, RegExp_global, 0}, {hasOwnPropertyW, RegExp_hasOwnProperty, PROPF_METHOD}, + {ignoreCaseW, RegExp_ignoreCase, 0}, {isPrototypeOfW, RegExp_isPrototypeOf, PROPF_METHOD}, + {lastIndexW, RegExp_lastIndex, 0}, + {multilineW, RegExp_multiline, 0}, {propertyIsEnumerableW, RegExp_propertyIsEnumerable, PROPF_METHOD}, + {sourceW, RegExp_source, 0}, {toLocaleStringW, RegExp_toLocaleString, PROPF_METHOD}, {toStringW, RegExp_toString, PROPF_METHOD} }; @@ -120,6 +3515,37 @@ static HRESULT alloc_regexp(script_ctx_t *ctx, BOOL use_constr, RegExpInstance * return S_OK; } +static HRESULT create_regexp(script_ctx_t *ctx, const WCHAR *exp, int len, DWORD flags, DispatchEx **ret) +{ + RegExpInstance *regexp; + HRESULT hres; + + TRACE("%s %x\n", debugstr_w(exp), flags); + + hres = alloc_regexp(ctx, TRUE, ®exp); + if(FAILED(hres)) + return hres; + + if(len == -1) + regexp->str = SysAllocString(exp); + else + regexp->str = SysAllocStringLen(exp, len); + if(!regexp->str) { + jsdisp_release(®exp->dispex); + return E_OUTOFMEMORY; + } + + regexp->jsregexp = js_NewRegExp(ctx, regexp->str, flags, FALSE); + if(!regexp->jsregexp) { + WARN("js_NewRegExp failed\n"); + jsdisp_release(®exp->dispex); + return E_FAIL; + } + + *ret = ®exp->dispex; + return S_OK; +} + static HRESULT RegExpConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) { @@ -141,3 +3567,34 @@ HRESULT create_regexp_constr(script_ctx_t *ctx, DispatchEx **ret) jsdisp_release(®exp->dispex); return hres; } + +HRESULT create_regexp_str(script_ctx_t *ctx, const WCHAR *exp, DWORD exp_len, const WCHAR *opt, + DWORD opt_len, DispatchEx **ret) +{ + const WCHAR *p; + DWORD flags = 0; + + if(opt) { + for (p = opt; p < opt+opt_len; p++) { + switch (*p) { + case 'g': + flags |= JSREG_GLOB; + break; + case 'i': + flags |= JSREG_FOLD; + break; + case 'm': + flags |= JSREG_MULTILINE; + break; + case 'y': + flags |= JSREG_STICKY; + break; + default: + WARN("wrong flag %c\n", *p); + return E_FAIL; + } + } + } + + return create_regexp(ctx, exp, exp_len, flags, ret); +} diff --git a/dlls/jscript/string.c b/dlls/jscript/string.c index 26623ca7fdb..b310cabec32 100644 --- a/dlls/jscript/string.c +++ b/dlls/jscript/string.c @@ -24,6 +24,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(jscript); typedef struct { DispatchEx dispex; + + WCHAR *str; + DWORD length; } StringInstance; static const WCHAR lengthW[] = {'l','e','n','g','t','h',0}; @@ -64,11 +67,6 @@ static const WCHAR propertyIsEnumerableW[] = {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; -static void String_destructor(DispatchEx *dispex) -{ - FIXME("\n"); -} - static HRESULT String_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) { @@ -191,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"); - return E_NOTIMPL; + 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, @@ -328,6 +395,14 @@ static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAM return E_NOTIMPL; } +static void String_destructor(DispatchEx *dispex) +{ + StringInstance *This = (StringInstance*)dispex; + + heap_free(This->str); + heap_free(This); +} + static const builtin_prop_t String_props[] = { {anchorW, String_anchor, PROPF_METHOD}, {bigW, String_big, PROPF_METHOD}, @@ -419,3 +494,30 @@ HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret) jsdisp_release(&string->dispex); return hres; } + +HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret) +{ + StringInstance *string; + HRESULT hres; + + hres = string_alloc(ctx, TRUE, &string); + if(FAILED(hres)) + return hres; + + if(len == -1) + len = strlenW(str); + + string->length = len; + string->str = heap_alloc((len+1)*sizeof(WCHAR)); + if(!string->str) { + jsdisp_release(&string->dispex); + return E_OUTOFMEMORY; + } + + memcpy(string->str, str, len*sizeof(WCHAR)); + string->str[len] = 0; + + *ret = &string->dispex; + return S_OK; + +} diff --git a/dlls/jscript/tests/rsrc.rc b/dlls/jscript/tests/api.js similarity index 52% copy from dlls/jscript/tests/rsrc.rc copy to dlls/jscript/tests/api.js index ba7a46041de..eed48dc2056 100644 --- a/dlls/jscript/tests/rsrc.rc +++ b/dlls/jscript/tests/api.js @@ -16,5 +16,24 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -/* @makedep: lang.js */ -lang.js 40 "lang.js" +var arr = new Array(); +ok(typeof(arr) === "object", "arr () is not object"); +ok((arr.length === 0), "arr.length is not 0"); +ok(arr["0"] === undefined, "arr[0] is not undefined"); + +var arr = new Array(1, 2, "test"); +ok(typeof(arr) === "object", "arr (1,2,test) is not object"); +ok((arr.length === 3), "arr.length is not 3"); +ok(arr["0"] === 1, "arr[0] is not 1"); +ok(arr["1"] === 2, "arr[1] is not 2"); +ok(arr["2"] === "test", "arr[2] is not \"test\""); + +arr["7"] = true; +ok((arr.length === 8), "arr.length is not 8"); + +var arr = new Array(6); +ok(typeof(arr) === "object", "arr (6) is not object"); +ok((arr.length === 6), "arr.length is not 6"); +ok(arr["0"] === undefined, "arr[0] is not undefined"); + +reportSuccess(); diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js index fef840308b0..7a4e4cda715 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -233,4 +233,7 @@ ok(tmp === 2, "incremented tmp(1) is not 2"); ok(tmp-- === 2, "tmp-- (2) is not 2"); ok(tmp === 1, "decremented tmp is not 1"); +String.prototype.test = true; +ok("".test === true, "\"\",test is not true"); + reportSuccess(); diff --git a/dlls/jscript/tests/regexp.js b/dlls/jscript/tests/regexp.js new file mode 100644 index 00000000000..209ee22b0fb --- /dev/null +++ b/dlls/jscript/tests/regexp.js @@ -0,0 +1,54 @@ +/* + * 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 + */ + + +var m; + +m = "abcabc".match(/ca/); +ok(typeof(m) === "object", "typeof m is not object"); +ok(m.length === 1, "m.length is not 1"); +ok(m["0"] === "ca", "m[0] is not \"ca\""); + +m = "abcabc".match(/ab/); +ok(typeof(m) === "object", "typeof m is not object"); +ok(m.length === 1, "m.length is not 1"); +ok(m["0"] === "ab", "m[0] is not \"ab\""); + +m = "abcabc".match(/ab/g); +ok(typeof(m) === "object", "typeof m is not object"); +ok(m.length === 2, "m.length is not 1"); +ok(m["0"] === "ab", "m[0] is not \"ab\""); +ok(m["1"] === "ab", "m[1] is not \"ab\""); + +m = "abcabc".match(/Ab/g); +ok(typeof(m) === "object", "typeof m is not object"); +ok(m === null, "m is not null"); + +m = "abcabc".match(/Ab/gi); +ok(typeof(m) === "object", "typeof m is not object"); +ok(m.length === 2, "m.length is not 1"); +ok(m["0"] === "ab", "m[0] is not \"ab\""); +ok(m["1"] === "ab", "m[1] is not \"ab\""); + +m = "aaabcabc".match(/a+b/g); +ok(typeof(m) === "object", "typeof m is not object"); +ok(m.length === 2, "m.length is not 1"); +ok(m["0"] === "aaab", "m[0] is not \"ab\""); +ok(m["1"] === "ab", "m[1] is not \"ab\""); + +reportSuccess(); diff --git a/dlls/jscript/tests/rsrc.rc b/dlls/jscript/tests/rsrc.rc index ba7a46041de..ecb421289d2 100644 --- a/dlls/jscript/tests/rsrc.rc +++ b/dlls/jscript/tests/rsrc.rc @@ -16,5 +16,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +/* @makedep: api.js */ +api.js 40 "api.js" + /* @makedep: lang.js */ lang.js 40 "lang.js" + +/* @makedep: regexp.js */ +regexp.js 40 "regexp.js" diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index 3409b7e63a8..29947fe140b 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -582,6 +582,8 @@ static void run_tests(void) CHECK_CALLED(global_success_i); run_from_res("lang.js"); + run_from_res("api.js"); + run_from_res("regexp.js"); } START_TEST(run) diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index 57460234a9e..ffc2399d348 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -2244,6 +2244,7 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io } else { + ULONGLONG bsize; /* Linux's fstatvfs is buggy */ #if !defined(linux) || !defined(HAVE_FSTATFS) struct statvfs stfs; @@ -2253,7 +2254,7 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io io->u.Status = FILE_GetNtStatus(); break; } - info->BytesPerSector = stfs.f_frsize; + bsize = stfs.f_frsize; #else struct statfs stfs; if (fstatfs( fd, &stfs ) < 0) @@ -2261,11 +2262,12 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io io->u.Status = FILE_GetNtStatus(); break; } - info->BytesPerSector = stfs.f_bsize; + bsize = stfs.f_bsize; #endif - info->TotalAllocationUnits.QuadPart = stfs.f_blocks; - info->AvailableAllocationUnits.QuadPart = stfs.f_bavail; - info->SectorsPerAllocationUnit = 1; + info->BytesPerSector = 512; + info->SectorsPerAllocationUnit = 8; + info->TotalAllocationUnits.QuadPart = bsize * stfs.f_blocks / (512 * 8); + info->AvailableAllocationUnits.QuadPart = bsize * stfs.f_bavail / (512 * 8); io->Information = sizeof(*info); io->u.Status = STATUS_SUCCESS; } diff --git a/dlls/oleaut32/tests/tmarshal.c b/dlls/oleaut32/tests/tmarshal.c index f705722f09f..04f0d4de22e 100644 --- a/dlls/oleaut32/tests/tmarshal.c +++ b/dlls/oleaut32/tests/tmarshal.c @@ -24,13 +24,26 @@ #include #include -#include +#include "wine/test.h" #include "tmarshal.h" #include "tmarshal_dispids.h" #define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08lx\n", (unsigned long int)hr) +/* ULL suffix is not portable */ +#define ULL_CONST(dw1, dw2) ((((ULONGLONG)dw1) << 32) | (ULONGLONG)dw2) + +const MYSTRUCT MYSTRUCT_BYVAL = {0x12345678, ULL_CONST(0xdeadbeef, 0x98765432)}; +const MYSTRUCT MYSTRUCT_BYPTR = {0x91827364, ULL_CONST(0x88776655, 0x44332211)}; +const MYSTRUCT MYSTRUCT_ARRAY[5] = { + {0x1a1b1c1d, ULL_CONST(0x1e1f1011, 0x12131415)}, + {0x2a2b2c2d, ULL_CONST(0x2e2f2021, 0x22232425)}, + {0x3a3b3c3d, ULL_CONST(0x3e3f3031, 0x32333435)}, + {0x4a4b4c4d, ULL_CONST(0x4e4f4041, 0x42434445)}, + {0x5a5b5c5d, ULL_CONST(0x5e5f5051, 0x52535455)}, +}; + /* Debugging functions from wine/libs/wine/debug.c */ /* allocate some tmp string space */ @@ -575,6 +588,18 @@ void WINAPI Widget_VarArg( ok(hr == S_OK, "SafeArrayUnaccessData failed with %x\n", hr); } +void WINAPI Widget_StructArgs( + IWidget * iface, + MYSTRUCT byval, + MYSTRUCT *byptr, + MYSTRUCT arr[5]) +{ + ok(memcmp(&byval, &MYSTRUCT_BYVAL, sizeof(MYSTRUCT))==0, "Struct parameter passed by value corrupted\n"); + ok(memcmp(byptr, &MYSTRUCT_BYPTR, sizeof(MYSTRUCT))==0, "Struct parameter passed by pointer corrupted\n"); + ok(memcmp(arr, MYSTRUCT_ARRAY, sizeof(MYSTRUCT_ARRAY))==0, "Array of structs corrupted\n"); +} + + HRESULT WINAPI Widget_Error( IWidget __RPC_FAR * iface) { @@ -616,6 +641,7 @@ static const struct IWidgetVtbl Widget_VTable = Widget_VariantArrayPtr, Widget_Variant, Widget_VarArg, + Widget_StructArgs, Widget_Error, Widget_CloneInterface }; @@ -932,6 +958,8 @@ static void test_typelibmarshal(void) DWORD tid; BSTR bstr; ITypeInfo *pTypeInfo; + MYSTRUCT mystruct; + MYSTRUCT mystructArray[5]; ok(pKEW != NULL, "Widget creation failed\n"); @@ -1071,6 +1099,11 @@ static void test_typelibmarshal(void) ok_ole_success(hr, IDispatch_Invoke); VariantClear(&varresult); + /* call StructArgs (direct) */ + mystruct = MYSTRUCT_BYPTR; + memcpy(mystructArray, MYSTRUCT_ARRAY, sizeof(mystructArray)); + IWidget_StructArgs(pWidget, MYSTRUCT_BYVAL, &mystruct, mystructArray); + /* call Clone */ dispparams.cNamedArgs = 0; dispparams.cArgs = 0; diff --git a/dlls/oleaut32/tests/tmarshal.idl b/dlls/oleaut32/tests/tmarshal.idl index 2367f2894f1..ea10797d410 100644 --- a/dlls/oleaut32/tests/tmarshal.idl +++ b/dlls/oleaut32/tests/tmarshal.idl @@ -35,6 +35,12 @@ library TestTypelib STATE_WIDGETIFIED } STATE; + typedef struct tagMYSTRUCT + { + INT field1; + ULONGLONG field2; + } MYSTRUCT; + coclass ApplicationObject2; [ @@ -111,6 +117,9 @@ library TestTypelib [vararg, id(DISPID_TM_VARARG)] void VarArg([in] int numexpect, [in] SAFEARRAY(VARIANT) values); + [id(DISPID_TM_STRUCTARGS)] + void StructArgs([in] MYSTRUCT byval, [in] MYSTRUCT *byptr, [in] MYSTRUCT arr[5]); + [id(DISPID_TM_ERROR)] HRESULT Error(); diff --git a/dlls/oleaut32/tests/tmarshal_dispids.h b/dlls/oleaut32/tests/tmarshal_dispids.h index 803b94be9e4..8fcf10909b9 100644 --- a/dlls/oleaut32/tests/tmarshal_dispids.h +++ b/dlls/oleaut32/tests/tmarshal_dispids.h @@ -33,5 +33,6 @@ #define DISPID_TM_ERROR 14 #define DISPID_TM_CLONEINTERFACE 15 #define DISPID_TM_TESTDUAL 16 +#define DISPID_TM_STRUCTARGS 17 #define DISPID_NOA_BSTRRET 1 diff --git a/dlls/oleaut32/tmarshal.c b/dlls/oleaut32/tmarshal.c index 0123e52eb38..fdfe07ce7c9 100644 --- a/dlls/oleaut32/tmarshal.c +++ b/dlls/oleaut32/tmarshal.c @@ -524,6 +524,22 @@ _argsize(TYPEDESC *tdesc, ITypeInfo *tinfo) { return (sizeof(DECIMAL)+3)/sizeof(DWORD); case VT_VARIANT: return (sizeof(VARIANT)+3)/sizeof(DWORD); + case VT_USERDEFINED: + { + ITypeInfo *tinfo2; + TYPEATTR *tattr; + HRESULT hres; + DWORD ret; + + hres = ITypeInfo_GetRefTypeInfo(tinfo,tdesc->u.hreftype,&tinfo2); + if (FAILED(hres)) + return 0; /* should fail critically in serialize_param */ + ITypeInfo_GetTypeAttr(tinfo2,&tattr); + ret = (tattr->cbSizeInstance+3)/sizeof(DWORD); + ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); + ITypeInfo_Release(tinfo2); + return ret; + } default: return 1; } @@ -558,6 +574,22 @@ _xsize(const TYPEDESC *td, ITypeInfo *tinfo) { case VT_UI1: case VT_I1: return 1; + case VT_USERDEFINED: + { + ITypeInfo *tinfo2; + TYPEATTR *tattr; + HRESULT hres; + DWORD ret; + + hres = ITypeInfo_GetRefTypeInfo(tinfo,td->u.hreftype,&tinfo2); + if (FAILED(hres)) + return 0; + ITypeInfo_GetTypeAttr(tinfo2,&tattr); + ret = tattr->cbSizeInstance; + ITypeInfo_ReleaseTypeAttr(tinfo2, tattr); + ITypeInfo_Release(tinfo2); + return ret; + } default: return 4; } @@ -1149,9 +1181,6 @@ deserialize_param( case TKIND_RECORD: { int i; - if (alloc) - *arg = (DWORD)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,tattr->cbSizeInstance); - if (debugout) TRACE_(olerelay)("{"); for (i=0;icVars;i++) { VARDESC *vdesc; @@ -1169,7 +1198,7 @@ deserialize_param( debugout, alloc, &vdesc->elemdescVar.tdesc, - (DWORD*)(((LPBYTE)*arg)+vdesc->u.oInst), + (DWORD*)(((LPBYTE)arg)+vdesc->u.oInst), buf ); ITypeInfo2_ReleaseVarDesc(tinfo2, vdesc); diff --git a/dlls/oleaut32/typelib.c b/dlls/oleaut32/typelib.c index fb572925be6..ceb229528b1 100644 --- a/dlls/oleaut32/typelib.c +++ b/dlls/oleaut32/typelib.c @@ -3146,7 +3146,7 @@ static void SLTG_DoVars(char *pBlk, char *pFirstItem, ITypeInfoImpl *pTI, unsign sizeof(VARIANT)); V_VT((*ppVarDesc)->vardesc.u.lpvarValue) = VT_INT; if (pItem->flags & 0x08) - V_UNION((*ppVarDesc)->vardesc.u.lpvarValue, intVal) = pItem->byte_offs; + V_INT((*ppVarDesc)->vardesc.u.lpvarValue) = pItem->byte_offs; else { switch ((*ppVarDesc)->vardesc.elemdescVar.tdesc.vt) { @@ -3172,7 +3172,9 @@ static void SLTG_DoVars(char *pBlk, char *pFirstItem, ITypeInfoImpl *pTI, unsign case VT_UI2: case VT_I4: case VT_UI4: - V_UNION((*ppVarDesc)->vardesc.u.lpvarValue, intVal) = + case VT_INT: + case VT_UINT: + V_INT((*ppVarDesc)->vardesc.u.lpvarValue) = *(INT*)(pBlk + pItem->byte_offs); break; default: diff --git a/dlls/winhttp/Makefile.in b/dlls/winhttp/Makefile.in index 5617a1dc80d..cf188c7217c 100644 --- a/dlls/winhttp/Makefile.in +++ b/dlls/winhttp/Makefile.in @@ -8,6 +8,7 @@ IMPORTS = wininet kernel32 DELAYIMPORTS = crypt32 C_SRCS = \ + cookie.c \ handle.c \ main.c \ net.c \ diff --git a/dlls/winhttp/cookie.c b/dlls/winhttp/cookie.c new file mode 100644 index 00000000000..6c0f8b448e5 --- /dev/null +++ b/dlls/winhttp/cookie.c @@ -0,0 +1,281 @@ +/* + * Copyright 2008 Hans Leidekker 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 "config.h" +#include + +#include "wine/debug.h" +#include "wine/list.h" + +#include "windef.h" +#include "winbase.h" +#include "winhttp.h" + +#include "winhttp_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winhttp); + +static domain_t *add_domain( session_t *session, WCHAR *name ) +{ + domain_t *domain; + + if (!(domain = heap_alloc_zero( sizeof(domain_t) ))) return NULL; + + list_init( &domain->entry ); + list_init( &domain->cookies ); + + domain->name = name; + list_add_tail( &session->cookie_cache, &domain->entry ); + + TRACE("%s\n", debugstr_w(domain->name)); + return domain; +} + +static cookie_t *find_cookie( domain_t *domain, const WCHAR *path, const WCHAR *name ) +{ + struct list *item; + cookie_t *cookie; + + LIST_FOR_EACH( item, &domain->cookies ) + { + cookie = LIST_ENTRY( item, cookie_t, entry ); + if (!strcmpW( cookie->path, path ) && !strcmpiW( cookie->name, name )) + { + TRACE("found %s=%s\n", debugstr_w(cookie->name), debugstr_w(cookie->value)); + return cookie; + } + } + return NULL; +} + +static BOOL domain_match( const WCHAR *name, domain_t *domain, BOOL partial ) +{ + TRACE("comparing %s with %s\n", debugstr_w(name), debugstr_w(domain->name)); + + if (partial && !strstrW( name, domain->name )) return FALSE; + else if (!partial && strcmpW( name, domain->name )) return FALSE; + return TRUE; +} + +static void free_cookie( cookie_t *cookie ) +{ + heap_free( cookie->name ); + heap_free( cookie->value ); + heap_free( cookie->path ); + heap_free( cookie ); +} + +static void delete_cookie( cookie_t *cookie ) +{ + list_remove( &cookie->entry ); + free_cookie( cookie ); +} + +void delete_domain( domain_t *domain ) +{ + cookie_t *cookie; + struct list *item, *next; + + LIST_FOR_EACH_SAFE( item, next, &domain->cookies ) + { + cookie = LIST_ENTRY( item, cookie_t, entry ); + delete_cookie( cookie ); + } + + list_remove( &domain->entry ); + heap_free( domain->name ); + heap_free( domain ); +} + +static BOOL add_cookie( session_t *session, cookie_t *cookie, WCHAR *domain_name, WCHAR *path ) +{ + domain_t *domain = NULL; + cookie_t *old_cookie; + struct list *item; + + LIST_FOR_EACH( item, &session->cookie_cache ) + { + domain = LIST_ENTRY( item, domain_t, entry ); + if (domain_match( domain_name, domain, FALSE )) break; + domain = NULL; + } + if (!domain) + { + if (!(domain = add_domain( session, domain_name ))) return FALSE; + } + else if ((old_cookie = find_cookie( domain, path, cookie->name ))) delete_cookie( old_cookie ); + + cookie->path = path; + list_add_tail( &domain->cookies, &cookie->entry ); + + TRACE("domain %s path %s <- %s=%s\n", debugstr_w(domain_name), debugstr_w(cookie->path), + debugstr_w(cookie->name), debugstr_w(cookie->value)); + return TRUE; +} + +static cookie_t *parse_cookie( const WCHAR *string ) +{ + cookie_t *cookie; + const WCHAR *p; + int len; + + if (!(cookie = heap_alloc_zero( sizeof(cookie_t) ))) return NULL; + + list_init( &cookie->entry ); + + if (!(p = strchrW( string, '=' ))) + { + WARN("no '=' in %s\n", debugstr_w(string)); + return NULL; + } + if (p == string) + { + WARN("empty cookie name in %s\n", debugstr_w(string)); + return NULL; + } + len = p - string; + if (!(cookie->name = heap_alloc( (len + 1) * sizeof(WCHAR) ))) + { + heap_free( cookie ); + return NULL; + } + memcpy( cookie->name, string, len * sizeof(WCHAR) ); + cookie->name[len] = 0; + + p++; /* skip '=' */ + while (*p == ' ') p++; + + len = strlenW( p ); + if (!(cookie->value = heap_alloc( (len + 1) * sizeof(WCHAR) ))) + { + free_cookie( cookie ); + return NULL; + } + memcpy( cookie->value, p, len * sizeof(WCHAR) ); + cookie->value[len] = 0; + + return cookie; +} + +BOOL set_cookies( request_t *request, const WCHAR *cookies ) +{ + static const WCHAR pathW[] = {'p','a','t','h',0}; + static const WCHAR domainW[] = {'d','o','m','a','i','n',0}; + + BOOL ret = FALSE; + WCHAR *buffer, *p, *q, *r; + WCHAR *cookie_domain = NULL, *cookie_path = NULL; + session_t *session = request->connect->session; + cookie_t *cookie; + int len; + + len = strlenW( cookies ); + if (!(buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; + strcpyW( buffer, cookies ); + + p = buffer; + while (*p && *p != ';') p++; + if (*p == ';') *p++ = 0; + if (!(cookie = parse_cookie( buffer ))) + { + heap_free( buffer ); + return FALSE; + } + if ((q = strstrW( p, domainW ))) /* FIXME: do real attribute parsing */ + { + while (*q && *q != '=') q++; + if (!*q) goto end; + + r = ++q; + while (*r && *r != ';') r++; + len = r - q; + + if (!(cookie_domain = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end; + memcpy( cookie_domain, q, len * sizeof(WCHAR) ); + cookie_domain[len] = 0; + + } + if ((q = strstrW( p, pathW ))) + { + while (*q && *q != '=') q++; + if (!*q) goto end; + + r = ++q; + while (*r && *r != ';') r++; + len = r - q; + + if (!(cookie_path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end; + memcpy( cookie_path, q, len * sizeof(WCHAR) ); + cookie_path[len] = 0; + } + if (!cookie_domain && !(cookie_domain = strdupW( request->connect->servername ))) goto end; + if (!cookie_path && !(cookie_path = strdupW( request->path ))) goto end; + + if ((p = strrchrW( cookie_path, '/' )) && p != cookie_path) *p = 0; + ret = add_cookie( session, cookie, cookie_domain, cookie_path ); + +end: + if (!ret) + { + free_cookie( cookie ); + heap_free( cookie_domain ); + heap_free( cookie_path ); + } + heap_free( buffer ); + return ret; +} + +BOOL add_cookie_headers( request_t *request ) +{ + struct list *domain_cursor; + session_t *session = request->connect->session; + + LIST_FOR_EACH( domain_cursor, &session->cookie_cache ) + { + domain_t *domain = LIST_ENTRY( domain_cursor, domain_t, entry ); + if (domain_match( request->connect->servername, domain, TRUE )) + { + struct list *cookie_cursor; + TRACE("found domain %s\n", debugstr_w(domain->name)); + + LIST_FOR_EACH( cookie_cursor, &domain->cookies ) + { + cookie_t *cookie = LIST_ENTRY( cookie_cursor, cookie_t, entry ); + + TRACE("comparing path %s with %s\n", debugstr_w(request->path), debugstr_w(cookie->path)); + + if (strstrW( request->path, cookie->path ) == request->path) + { + const WCHAR format[] = {'C','o','o','k','i','e',':',' ','%','s','=','%','s',0}; + int len; + WCHAR *header; + + len = strlenW( cookie->name ) + strlenW( format ) + strlenW( cookie->value ); + if (!(header = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return FALSE; + + sprintfW( header, format, cookie->name, cookie->value ); + + TRACE("%s\n", debugstr_w(header)); + add_request_headers( request, header, len, WINHTTP_ADDREQ_FLAG_ADD ); + heap_free( header ); + } + } + } + } + return TRUE; +} diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index c149aca3664..44689631047 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -404,7 +404,7 @@ static BOOL process_header( request_t *request, LPCWSTR field, LPCWSTR value, DW return TRUE; } -static BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags ) +BOOL add_request_headers( request_t *request, LPCWSTR headers, DWORD len, DWORD flags ) { BOOL ret = FALSE; WCHAR *buffer, *p, *q; @@ -480,28 +480,20 @@ static WCHAR *build_request_string( request_t *request ) static const WCHAR crlf[] = {'\r','\n',0}; static const WCHAR colon[] = {':',' ',0}; static const WCHAR twocrlf[] = {'\r','\n','\r','\n',0}; - static const WCHAR get[] = {'G','E','T',0}; - static const WCHAR slash[] = {'/',0}; - static const WCHAR http1_1[] = {'H','T','T','P','/','1','.','1',0}; WCHAR *ret; const WCHAR **headers, **p; - const WCHAR *verb = get, *path = slash, *version = http1_1; unsigned int len, i = 0, j; - if (request->verb && request->verb[0]) verb = request->verb; - if (request->path && request->path[0]) path = request->path; - if (request->version && request->version[0]) version = request->version; - /* allocate space for an array of all the string pointers to be added */ len = request->num_headers * 4 + 7; if (!(headers = heap_alloc( len * sizeof(LPCWSTR) ))) return NULL; - headers[i++] = verb; + headers[i++] = request->verb; headers[i++] = space; - headers[i++] = path; + headers[i++] = request->path; headers[i++] = space; - headers[i++] = version; + headers[i++] = request->version; for (j = 0; j < request->num_headers; j++) { @@ -797,7 +789,6 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len static const WCHAR keep_alive[] = {'K','e','e','p','-','A','l','i','v','e',0}; static const WCHAR no_cache[] = {'n','o','-','c','a','c','h','e',0}; static const WCHAR length_fmt[] = {'%','l','d',0}; - static const WCHAR post[] = {'P','O','S','T',0}; BOOL ret = FALSE; connect_t *connect = request->connect; @@ -813,7 +804,7 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len if (connect->hostname) add_host_header( request, connect->hostname, connect->hostport, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW ); - if (total_len || (request->verb && !strcmpW( request->verb, post ))) + if (total_len || (request->verb && !strcmpW( request->verb, postW ))) { WCHAR length[21]; /* decimal long int + null */ sprintfW( length, length_fmt, total_len ); @@ -833,6 +824,11 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len TRACE("failed to add request headers\n"); return FALSE; } + if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && !add_cookie_headers( request )) + { + WARN("failed to add cookie headers\n"); + return FALSE; + } if (!(ret = open_connection( request ))) goto end; if (!(req = build_request_string( request ))) goto end; @@ -1114,6 +1110,7 @@ static BOOL handle_redirect( request_t *request ) if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end; strcpyW( request->path, uc.lpszUrlPath ); } + else request->path = strdupW( slashW ); } /* remove content-type/length headers */ @@ -1122,7 +1119,7 @@ static BOOL handle_redirect( request_t *request ) /* redirects are always GET requests */ heap_free( request->verb ); - request->verb = NULL; + request->verb = strdupW( getW ); ret = TRUE; end: @@ -1226,6 +1223,13 @@ static BOOL receive_data_chunked( request_t *request, void *buffer, DWORD size, return TRUE; } +static void finished_reading( request_t *request ) +{ + /* FIXME: close connection here if necessary */ + request->content_length = ~0UL; + request->content_read = 0; +} + static BOOL read_data( request_t *request, void *buffer, DWORD to_read, DWORD *read, BOOL async ) { static const WCHAR chunked[] = {'c','h','u','n','k','e','d',0}; @@ -1253,7 +1257,11 @@ static BOOL read_data( request_t *request, void *buffer, DWORD to_read, DWORD *r send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); } } - if (ret && read) *read = num_bytes; + if (ret) + { + if (read) *read = num_bytes; + if (!num_bytes) finished_reading( request ); + } return ret; } @@ -1263,22 +1271,23 @@ static void drain_content( request_t *request ) DWORD bytes_read; char buffer[2048]; + if (!request->content_length) return; for (;;) { if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return; } } -/* copy cookies from response headers to request headers */ -static void add_cookies( request_t *request ) +static void record_cookies( request_t *request ) { unsigned int i; for (i = 0; i < request->num_headers; i++) { - if (!strcmpiW( request->headers[i].field, attr_set_cookie ) && !request->headers[i].is_request) + header_t *set_cookie = &request->headers[i]; + if (!strcmpiW( set_cookie->field, attr_set_cookie ) && !set_cookie->is_request) { - process_header( request, attr_cookie, request->headers[i].value, WINHTTP_ADDREQ_FLAG_ADD, TRUE ); + set_cookies( request, set_cookie->value ); } } } @@ -1301,13 +1310,14 @@ static BOOL receive_response( request_t *request, BOOL async ) if (!query_headers( request, query, NULL, &request->content_length, &size, NULL )) request->content_length = ~0UL; + if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request ); + if (status == 301 || status == 302) { if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS) break; - drain_content( request ); + drain_content( request ); if (!(ret = handle_redirect( request ))) break; - if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) add_cookies( request ); clear_response_headers( request ); ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */ @@ -1523,7 +1533,7 @@ static BOOL write_data( request_t *request, LPCVOID buffer, DWORD to_write, LPDW if (async) { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) ); + if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) ); else { WINHTTP_ASYNC_RESULT result; diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 927c34708a8..4967aff2369 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -65,9 +65,16 @@ BOOL WINAPI WinHttpCheckPlatform( void ) static void session_destroy( object_header_t *hdr ) { session_t *session = (session_t *)hdr; + struct list *item, *next; + domain_t *domain; TRACE("%p\n", session); + LIST_FOR_EACH_SAFE( item, next, &session->cookie_cache ) + { + domain = LIST_ENTRY( item, domain_t, entry ); + delete_domain( domain ); + } heap_free( session->agent ); heap_free( session->proxy_server ); heap_free( session->proxy_bypass ); @@ -76,8 +83,44 @@ static void session_destroy( object_header_t *hdr ) heap_free( session ); } +static BOOL session_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen ) +{ + if (!buflen) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + switch (option) + { + case WINHTTP_OPTION_REDIRECT_POLICY: + { + if (!buffer || *buflen < sizeof(DWORD)) + { + *buflen = sizeof(DWORD); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + *(DWORD *)buffer = hdr->redirect_policy; + *buflen = sizeof(DWORD); + return TRUE; + } + default: + FIXME("unimplemented option %u\n", option); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } +} + static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen ) { + if (!buffer) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + switch (option) { case WINHTTP_OPTION_PROXY: @@ -89,22 +132,33 @@ static BOOL session_set_option( object_header_t *hdr, DWORD option, LPVOID buffe } case WINHTTP_OPTION_REDIRECT_POLICY: { - DWORD policy = *(DWORD *)buffer; + DWORD policy; + if (buflen != sizeof(policy)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + policy = *(DWORD *)buffer; TRACE("0x%x\n", policy); hdr->redirect_policy = policy; return TRUE; } + case WINHTTP_OPTION_DISABLE_FEATURE: + SetLastError(ERROR_WINHTTP_INCORRECT_HANDLE_TYPE); + return FALSE; default: FIXME("unimplemented option %u\n", option); - return TRUE; + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; } } static const object_vtbl_t session_vtbl = { session_destroy, - NULL, + session_query_option, session_set_option }; @@ -125,6 +179,8 @@ HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWST session->hdr.flags = flags; session->hdr.refs = 1; session->access = access; + session->hdr.redirect_policy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP; + list_init( &session->cookie_cache ); if (agent && !(session->agent = strdupW( agent ))) goto end; if (proxy && !(session->proxy_server = strdupW( proxy ))) goto end; @@ -254,15 +310,29 @@ static void request_destroy( object_header_t *hdr ) static BOOL request_query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPDWORD buflen ) { + if (!buflen) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + switch (option) { case WINHTTP_OPTION_SECURITY_FLAGS: { - DWORD flags = 0; + DWORD flags; + if (!buffer || *buflen < sizeof(flags)) + { + *buflen = sizeof(flags); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + + flags = 0; if (hdr->flags & WINHTTP_FLAG_SECURE) flags |= SECURITY_FLAG_SECURE; *(DWORD *)buffer = flags; - *buflen = sizeof(DWORD); + *buflen = sizeof(flags); return TRUE; } case WINHTTP_OPTION_SERVER_CERT_CONTEXT: @@ -270,6 +340,13 @@ static BOOL request_query_option( object_header_t *hdr, DWORD option, LPVOID buf const CERT_CONTEXT *cert; request_t *request = (request_t *)hdr; + if (!buffer || *buflen < sizeof(cert)) + { + *buflen = sizeof(cert); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + if (!(cert = netconn_get_certificate( &request->netconn ))) return FALSE; *(CERT_CONTEXT **)buffer = (CERT_CONTEXT *)cert; *buflen = sizeof(cert); @@ -277,18 +354,32 @@ static BOOL request_query_option( object_header_t *hdr, DWORD option, LPVOID buf } case WINHTTP_OPTION_SECURITY_KEY_BITNESS: { + if (!buffer || *buflen < sizeof(DWORD)) + { + *buflen = sizeof(DWORD); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + *(DWORD *)buffer = 128; /* FIXME */ *buflen = sizeof(DWORD); return TRUE; } default: FIXME("unimplemented option %u\n", option); + SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } } static BOOL request_set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD buflen ) { + if (!buffer) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + switch (option) { case WINHTTP_OPTION_PROXY: @@ -302,14 +393,26 @@ static BOOL request_set_option( object_header_t *hdr, DWORD option, LPVOID buffe { DWORD disable = *(DWORD *)buffer; + if (buflen != sizeof(DWORD)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + TRACE("0x%x\n", disable); - hdr->disable_flags &= disable; + hdr->disable_flags |= disable; return TRUE; } case WINHTTP_OPTION_AUTOLOGON_POLICY: { DWORD policy = *(DWORD *)buffer; + if (buflen != sizeof(DWORD)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + TRACE("0x%x\n", policy); hdr->logon_policy = policy; return TRUE; @@ -318,12 +421,19 @@ static BOOL request_set_option( object_header_t *hdr, DWORD option, LPVOID buffe { DWORD policy = *(DWORD *)buffer; + if (buflen != sizeof(DWORD)) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + TRACE("0x%x\n", policy); hdr->redirect_policy = policy; return TRUE; } default: FIXME("unimplemented option %u\n", option); + SetLastError(ERROR_INVALID_PARAMETER); return TRUE; } } @@ -378,9 +488,13 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o if (!netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE )) goto end; - if (verb && !(request->verb = strdupW( verb ))) goto end; - if (object && !(request->path = strdupW( object ))) goto end; - if (version && !(request->version = strdupW( version ))) goto end; + if (!verb || !verb[0]) verb = getW; + if (!object || !object[0]) object = slashW; + if (!version || !version[0]) version = http1_1; + + if (!(request->verb = strdupW( verb ))) goto end; + if (!(request->path = strdupW( object ))) goto end; + if (!(request->version = strdupW( version ))) goto end; if (!(hrequest = alloc_handle( &request->hdr ))) goto end; request->hdr.handle = hrequest; @@ -426,10 +540,14 @@ static BOOL query_option( object_header_t *hdr, DWORD option, LPVOID buffer, LPD return TRUE; } default: - { if (hdr->vtbl->query_option) ret = hdr->vtbl->query_option( hdr, option, buffer, buflen ); - else FIXME("unimplemented option %u\n", option); - } + else + { + FIXME("unimplemented option %u\n", option); + SetLastError(ERROR_WINHTTP_INCORRECT_HANDLE_TYPE); + return FALSE; + } + break; } return ret; } @@ -468,10 +586,14 @@ static BOOL set_option( object_header_t *hdr, DWORD option, LPVOID buffer, DWORD return TRUE; } default: - { if (hdr->vtbl->set_option) ret = hdr->vtbl->set_option( hdr, option, buffer, buflen ); - else FIXME("unimplemented option %u\n", option); - } + else + { + FIXME("unimplemented option %u\n", option); + SetLastError(ERROR_WINHTTP_INCORRECT_HANDLE_TYPE); + return FALSE; + } + break; } return ret; } diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 79148053309..a4e33b47086 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -31,6 +31,154 @@ static const WCHAR test_useragent[] = {'W','i','n','e',' ','R','e','g','r','e','s','s','i','o','n',' ','T','e','s','t',0}; static const WCHAR test_server[] = {'w','i','n','e','h','q','.','o','r','g',0}; +static void test_QueryOption(void) +{ + BOOL ret; + HINTERNET session, request, connection; + DWORD feature, size; + + SetLastError(0xdeadbeef); + session = WinHttpOpen(test_useragent, 0, 0, 0, 0); + ok(session != NULL, "WinHttpOpen failed to open session, error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(session, WINHTTP_OPTION_REDIRECT_POLICY, NULL, NULL); + ok(!ret, "should fail to set redirect policy %u\n", GetLastError()); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + + size = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(session, WINHTTP_OPTION_REDIRECT_POLICY, NULL, &size); + ok(!ret, "should fail to query option\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %u\n", GetLastError()); + ok(size == 4, "expected 4, got %u\n", size); + + feature = 0xdeadbeef; + size = sizeof(feature) - 1; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(session, WINHTTP_OPTION_REDIRECT_POLICY, &feature, &size); + ok(!ret, "should fail to query option\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %u\n", GetLastError()); + ok(size == 4, "expected 4, got %u\n", size); + + feature = 0xdeadbeef; + size = sizeof(feature) + 1; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(session, WINHTTP_OPTION_REDIRECT_POLICY, &feature, &size); + ok(ret, "failed to query option %u\n", GetLastError()); + ok(size == sizeof(feature), "WinHttpQueryOption should set the size: %u\n", size); + ok(feature == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP, + "expected WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP, got %#x\n", feature); + + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(session, WINHTTP_OPTION_REDIRECT_POLICY, NULL, sizeof(feature)); + ok(!ret, "should fail to set redirect policy %u\n", GetLastError()); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + + feature = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS; + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(session, WINHTTP_OPTION_REDIRECT_POLICY, &feature, sizeof(feature) - 1); + ok(!ret, "should fail to set redirect policy %u\n", GetLastError()); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %u\n", GetLastError()); + + feature = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS; + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(session, WINHTTP_OPTION_REDIRECT_POLICY, &feature, sizeof(feature) + 1); + ok(!ret, "should fail to set redirect policy %u\n", GetLastError()); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %u\n", GetLastError()); + + feature = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS; + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(session, WINHTTP_OPTION_REDIRECT_POLICY, &feature, sizeof(feature)); + ok(ret, "failed to set redirect policy %u\n", GetLastError()); + + feature = 0xdeadbeef; + size = sizeof(feature); + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(session, WINHTTP_OPTION_REDIRECT_POLICY, &feature, &size); + ok(ret, "failed to query option %u\n", GetLastError()); + ok(feature == WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS, + "expected WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS, got %#x\n", feature); + + feature = WINHTTP_DISABLE_COOKIES; + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(session, WINHTTP_OPTION_DISABLE_FEATURE, &feature, sizeof(feature)); + ok(!ret, "should fail to set disable feature for a session\n"); + ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, + "expected ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, got %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + connection = WinHttpConnect(session, test_server, INTERNET_DEFAULT_HTTP_PORT, 0); + ok(connection != NULL, "WinHttpConnect failed to open a connection, error: %u\n", GetLastError()); + + feature = WINHTTP_DISABLE_COOKIES; + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(connection, WINHTTP_OPTION_DISABLE_FEATURE, &feature, sizeof(feature)); + ok(!ret, "should fail to set disable feature for a connection\n"); + ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, + "expected ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, got %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + request = WinHttpOpenRequest(connection, NULL, NULL, NULL, WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, 0); + if (request == NULL && GetLastError() == ERROR_WINHTTP_NAME_NOT_RESOLVED) + { + skip("Network unreachable, skipping the test\n"); + goto done; + } + + feature = 0xdeadbeef; + size = sizeof(feature); + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(request, WINHTTP_OPTION_DISABLE_FEATURE, &feature, &size); + ok(!ret, "should fail to query disable feature for a request\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + + feature = 0; + size = sizeof(feature); + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(request, WINHTTP_OPTION_DISABLE_FEATURE, &feature, sizeof(feature)); + ok(ret, "failed to set feature %u\n", GetLastError()); + + feature = 0xffffffff; + size = sizeof(feature); + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(request, WINHTTP_OPTION_DISABLE_FEATURE, &feature, sizeof(feature)); + ok(ret, "failed to set feature %u\n", GetLastError()); + + feature = WINHTTP_DISABLE_COOKIES; + size = sizeof(feature); + SetLastError(0xdeadbeef); + ret = WinHttpSetOption(request, WINHTTP_OPTION_DISABLE_FEATURE, &feature, sizeof(feature)); + ok(ret, "failed to set feature %u\n", GetLastError()); + + size = 0; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(request, WINHTTP_OPTION_DISABLE_FEATURE, NULL, &size); + ok(!ret, "should fail to query disable feature for a request\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = WinHttpCloseHandle(request); + ok(ret, "WinHttpCloseHandle failed on closing request: %u\n", GetLastError()); + +done: + SetLastError(0xdeadbeef); + ret = WinHttpCloseHandle(connection); + ok(ret, "WinHttpCloseHandle failed on closing connection: %u\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = WinHttpCloseHandle(session); + ok(ret, "WinHttpCloseHandle failed on closing session: %u\n", GetLastError()); +} + static void test_OpenRequest (void) { BOOL ret; @@ -273,6 +421,7 @@ static void test_WinHttpAddHeaders(void) */ index = 0; len = 5*sizeof(WCHAR); + memset(check_buffer, 0xab, sizeof(check_buffer)); memcpy(buffer, check_buffer, sizeof(buffer)); ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, test_header_name, buffer, &len, &index); @@ -677,4 +826,5 @@ START_TEST (winhttp) test_WinHttpAddHeaders(); test_secure_connection(); test_request_parameter_defaults(); + test_QueryOption(); } diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 6508b38d482..0270ea83232 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -36,6 +36,11 @@ # include #endif +static const WCHAR getW[] = {'G','E','T',0}; +static const WCHAR postW[] = {'P','O','S','T',0}; +static const WCHAR slashW[] = {'/',0}; +static const WCHAR http1_1[] = {'H','T','T','P','/','1','.','1',0}; + typedef struct _object_header_t object_header_t; typedef struct @@ -65,6 +70,21 @@ struct _object_header_t typedef struct { + struct list entry; + WCHAR *name; + struct list cookies; +} domain_t; + +typedef struct +{ + struct list entry; + WCHAR *name; + WCHAR *value; + WCHAR *path; +} cookie_t; + +typedef struct +{ object_header_t hdr; LPWSTR agent; DWORD access; @@ -72,6 +92,7 @@ typedef struct LPWSTR proxy_bypass; LPWSTR proxy_username; LPWSTR proxy_password; + struct list cookie_cache; } session_t; typedef struct @@ -190,6 +211,11 @@ BOOL netconn_secure_connect( netconn_t * ); BOOL netconn_send( netconn_t *, const void *, size_t, int, int * ); const void *netconn_get_certificate( netconn_t * ); +BOOL set_cookies( request_t *, const WCHAR * ); +BOOL add_cookie_headers( request_t * ); +BOOL add_request_headers( request_t *, LPCWSTR, DWORD, DWORD ); +void delete_domain( domain_t * ); + static inline void *heap_alloc( SIZE_T size ) { return HeapAlloc( GetProcessHeap(), 0, size ); diff --git a/programs/regedit/framewnd.c b/programs/regedit/framewnd.c index c08831e7f95..4d8056e94ec 100644 --- a/programs/regedit/framewnd.c +++ b/programs/regedit/framewnd.c @@ -306,7 +306,7 @@ static BOOL InitOpenFileName(HWND hWnd, OPENFILENAMEW *pofn) if (FilterBuffer[0] == 0) LoadStringW(hInst, IDS_FILEDIALOG_FILTER, FilterBuffer, _MAX_PATH); pofn->lpstrFilter = FilterBuffer; - pofn->nFilterIndex = 1; + pofn->nFilterIndex = 2; pofn->lpstrFile = FileNameBuffer; pofn->nMaxFile = _MAX_PATH; pofn->lpstrFileTitle = FileTitleBuffer; @@ -372,7 +372,7 @@ static BOOL ExportRegistryFile(HWND hWnd, BOOL export_branch) ofn.lpTemplateName = MAKEINTRESOURCEW(IDD_EXPORT_TEMPLATE); if (GetSaveFileNameW(&ofn)) { BOOL result; - result = export_registry_key(ofn.lpstrFile, (LPWSTR)ofn.lCustData); + result = export_registry_key(ofn.lpstrFile, (LPWSTR)ofn.lCustData, ofn.nFilterIndex); if (!result) { /*printf("Can't open file \"%s\"\n", ofn.lpstrFile);*/ return FALSE; diff --git a/programs/regedit/regedit.c b/programs/regedit/regedit.c index 148d40b2978..0de71cfbffc 100644 --- a/programs/regedit/regedit.c +++ b/programs/regedit/regedit.c @@ -213,10 +213,10 @@ static BOOL PerformRegAction(REGEDIT_ACTION action, LPSTR s) get_file_name(&s, reg_key_name); reg_key_nameW = GetWideString(reg_key_name); - export_registry_key(filenameW, reg_key_nameW); + export_registry_key(filenameW, reg_key_nameW, REG_FORMAT_4); HeapFree(GetProcessHeap(), 0, reg_key_nameW); } else { - export_registry_key(filenameW, NULL); + export_registry_key(filenameW, NULL, REG_FORMAT_4); } HeapFree(GetProcessHeap(), 0, filenameW); break; diff --git a/programs/regedit/regproc.c b/programs/regedit/regproc.c index ff4918c2739..3ee7370dbbe 100644 --- a/programs/regedit/regproc.c +++ b/programs/regedit/regproc.c @@ -4,6 +4,7 @@ * * Copyright 1999 Sylvain St-Germain * Copyright 2002 Andriy Palamarchuk + * Copyright 2008 Alexander N. Sørnes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -878,31 +879,69 @@ static void REGPROC_resize_char_buffer(WCHAR **buffer, DWORD *len, DWORD require /****************************************************************************** * Prints string str to file */ -static void REGPROC_export_string(FILE *file, WCHAR *str) +static void REGPROC_export_string(WCHAR **line_buf, DWORD *line_buf_size, DWORD *line_size, WCHAR *str) { - CHAR* strA = GetMultiByteString(str); - size_t len = strlen(strA); - size_t i; + DWORD len = lstrlenW(str); + DWORD i; + DWORD extra = 0; + + REGPROC_resize_char_buffer(line_buf, line_buf_size, len + 10); /* escaping characters */ for (i = 0; i < len; i++) { - CHAR c = str[i]; + WCHAR c = str[i]; switch (c) { case '\\': - fputs("\\\\", file); + { + const WCHAR escape[] = {'\\','\\'}; + + extra++; + REGPROC_resize_char_buffer(line_buf, line_buf_size, len + extra); + memcpy(*line_buf + *line_size - 1, escape, 2 * sizeof(WCHAR)); break; + } case '\"': - fputs("\\\"", file); + { + const WCHAR escape[] = {'\\','"'}; + + extra++; + REGPROC_resize_char_buffer(line_buf, line_buf_size, len + extra); + memcpy(*line_buf + *line_size - 1, escape, 2 * sizeof(WCHAR)); break; + } case '\n': - fputs("\\\n", file); + { + const WCHAR escape[] = {'\\','\n'}; + + extra++; + REGPROC_resize_char_buffer(line_buf, line_buf_size, len + extra); + memcpy(*line_buf + *line_size - 1, escape, 2 * sizeof(WCHAR)); break; + } default: - fputc(c, file); + memcpy(*line_buf + *line_size - 1, &c, sizeof(WCHAR)); break; } + *line_size += 1; + } + *(*line_buf + *line_size - 1) = 0; + *line_size += extra; +} + +/****************************************************************************** + * Writes the given line to a file, in multi-byte or wide characters + */ +static void REGPROC_write_line(FILE *file, const WCHAR* str, BOOL unicode) +{ + if(unicode) + { + fwrite(str, sizeof(WCHAR), lstrlenW(str), file); + } else + { + char* strA = GetMultiByteString(str); + fprintf(file, strA); + HeapFree(GetProcessHeap(), 0, strA); } - HeapFree(GetProcessHeap(), 0, strA); } /****************************************************************************** @@ -924,7 +963,9 @@ static void REGPROC_export_string(FILE *file, WCHAR *str) static void export_hkey(FILE *file, HKEY key, WCHAR **reg_key_name_buf, DWORD *reg_key_name_len, WCHAR **val_name_buf, DWORD *val_name_len, - BYTE **val_buf, DWORD *val_size) + BYTE **val_buf, DWORD *val_size, + WCHAR **line_buf, DWORD *line_buf_size, + BOOL unicode) { DWORD max_sub_key_len; DWORD max_val_name_len; @@ -933,7 +974,8 @@ static void export_hkey(FILE *file, HKEY key, DWORD i; BOOL more_data; LONG ret; - CHAR* bufA; + WCHAR key_format[] = {'\n','[','%','s',']','\n',0}; + DWORD line_pos; /* get size information and resize the buffers if necessary */ if (RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, @@ -954,12 +996,11 @@ static void export_hkey(FILE *file, HKEY key, CHECK_ENOUGH_MEMORY(val_buf); } + REGPROC_resize_char_buffer(line_buf, line_buf_size, lstrlenW(*reg_key_name_buf) + 4); /* output data for the current key */ - fputs("\n[", file); - bufA = GetMultiByteString(*reg_key_name_buf); - fputs(bufA, file); - HeapFree(GetProcessHeap(), 0, bufA); - fputs("]\n", file); + wsprintfW(*line_buf, key_format, *reg_key_name_buf); + REGPROC_write_line(file, *line_buf, unicode); + /* print all the values */ i = 0; more_data = TRUE; @@ -967,6 +1008,7 @@ static void export_hkey(FILE *file, HKEY key, DWORD value_type; DWORD val_name_len1 = *val_name_len; DWORD val_size1 = *val_size; + DWORD line_size = 0; ret = RegEnumValueW(key, i, *val_name_buf, &val_name_len1, NULL, &value_type, *val_buf, &val_size1); if (ret != ERROR_SUCCESS) { @@ -978,24 +1020,48 @@ static void export_hkey(FILE *file, HKEY key, i++; if ((*val_name_buf)[0]) { - fputs("\"", file); - REGPROC_export_string(file, *val_name_buf); - fputs("\"=", file); + const WCHAR val_start[] = {'"','%','s','"','=',0}; + + line_size = 4 + lstrlenW(*val_name_buf); + REGPROC_resize_char_buffer(line_buf, line_buf_size, line_size); + wsprintfW(*line_buf, val_start, *val_name_buf); + line_pos = lstrlenW(*line_buf); } else { - fputs("@=", file); + const WCHAR std_val[] = {'@','=',0}; + line_size = 3; + REGPROC_resize_char_buffer(line_buf, line_buf_size, line_size); + lstrcpyW(*line_buf, std_val); + line_pos = lstrlenW(*line_buf); } switch (value_type) { case REG_SZ: case REG_EXPAND_SZ: - fputs("\"", file); - if (val_size1) REGPROC_export_string(file, (WCHAR*) *val_buf); - fputs("\"\n", file); + { + const WCHAR start[] = {'"',0}; + const WCHAR end[] = {'"','\n',0}; + + line_size += lstrlenW(start); + REGPROC_resize_char_buffer(line_buf, line_buf_size, line_size); + lstrcatW(*line_buf, start); + + if (val_size1) REGPROC_export_string(line_buf, line_buf_size, &line_size, (WCHAR*) *val_buf); + + line_size += lstrlenW(end); + REGPROC_resize_char_buffer(line_buf, line_buf_size, line_size); + lstrcatW(*line_buf, end); break; + } case REG_DWORD: - fprintf(file, "dword:%08x\n", *((DWORD *)*val_buf)); + { + WCHAR format[] = {'d','w','o','r','d',':','%','0','8','x','\n',0}; + + line_size += 20; + REGPROC_resize_char_buffer(line_buf, line_buf_size, line_size); + wsprintfW(*line_buf + line_pos, format, *((DWORD *)*val_buf)); break; + } default: fprintf(stderr,"%s: warning - unsupported registry format '%d', " @@ -1013,7 +1079,10 @@ static void export_hkey(FILE *file, HKEY key, int cur_pos; const WCHAR hex[] = {'h','e','x',':',0}; const WCHAR delim[] = {'"','"','=',0}; - CHAR* hex_prefixA; + const WCHAR format[] = {'%','0','2','x',0}; + const WCHAR comma[] = {',',0}; + const WCHAR concat[] = {'\\','\n',' ',' ',0}; + const WCHAR newline[] = {'\n',0}; BYTE* val_buf1 = *val_buf; DWORD val_buf1_size = val_size1; @@ -1023,7 +1092,7 @@ static void export_hkey(FILE *file, HKEY key, const WCHAR hex_format[] = {'h','e','x','(','%','d',')',':',0}; hex_prefix = buf; wsprintfW(buf, hex_format, value_type); - if(value_type == REG_MULTI_SZ) + if(value_type == REG_MULTI_SZ && !unicode) val_buf1 = (BYTE*)GetMultiByteStringN((WCHAR*)*val_buf, val_size1 / sizeof(WCHAR), &val_buf1_size); } @@ -1032,28 +1101,34 @@ static void export_hkey(FILE *file, HKEY key, cur_pos = lstrlenW(delim) + lstrlenW(hex) + lstrlenW(*val_name_buf); - hex_prefixA = GetMultiByteString(hex_prefix); - fputs(hex_prefixA, file); - HeapFree(GetProcessHeap(), 0, hex_prefixA); + line_size += lstrlenW(hex_prefix); + line_size += val_buf1_size * 3 + lstrlenW(concat) * ((int)((float)val_buf1_size * 3.0 / (float)REG_FILE_HEX_LINE_LEN) + 1 ) + 1; + REGPROC_resize_char_buffer(line_buf, line_buf_size, line_size); + lstrcatW(*line_buf, hex_prefix); + line_pos += lstrlenW(hex_prefix); for (i1 = 0; i1 < val_buf1_size; i1++) { - fprintf(file, "%02x", (unsigned int)(val_buf1)[i1]); + wsprintfW(*line_buf + line_pos, format, (unsigned int)(val_buf1)[i1]); + line_pos += 2; if (i1 + 1 < val_buf1_size) { - fputs(",", file); + lstrcpyW(*line_buf + line_pos, comma); + line_pos++; } cur_pos += 3; /* wrap the line */ if (cur_pos > REG_FILE_HEX_LINE_LEN) { - fputs("\\\n ", file); + lstrcpyW(*line_buf + line_pos, concat); + line_pos += lstrlenW(concat); cur_pos = 2; } } - if(value_type == REG_MULTI_SZ) + if(value_type == REG_MULTI_SZ && !unicode) HeapFree(GetProcessHeap(), 0, val_buf1); - fputs("\n", file); + lstrcpyW(*line_buf + line_pos, newline); break; } } + REGPROC_write_line(file, *line_buf, unicode); } } @@ -1077,7 +1152,8 @@ static void export_hkey(FILE *file, HKEY key, if (RegOpenKeyW(key, *reg_key_name_buf + curr_len + 1, &subkey) == ERROR_SUCCESS) { export_hkey(file, subkey, reg_key_name_buf, reg_key_name_len, - val_name_buf, val_name_len, val_buf, val_size); + val_name_buf, val_name_len, val_buf, val_size, + line_buf, line_buf_size, unicode); RegCloseKey(subkey); } else { REGPROC_print_error(); @@ -1090,7 +1166,7 @@ static void export_hkey(FILE *file, HKEY key, /****************************************************************************** * Open file for export. */ -static FILE *REGPROC_open_export_file(WCHAR *file_name) +static FILE *REGPROC_open_export_file(WCHAR *file_name, BOOL unicode) { FILE *file; WCHAR dash = '-'; @@ -1109,7 +1185,17 @@ static FILE *REGPROC_open_export_file(WCHAR *file_name) } HeapFree(GetProcessHeap(), 0, file_nameA); } - fputs("REGEDIT4\n", file); + if(unicode) + { + const BYTE unicode_seq[] = {0xff,0xfe}; + const WCHAR header[] = {'W','i','n','d','o','w','s',' ','R','e','g','i','s','t','r','y',' ','E','d','i','t','o','r',' ','V','e','r','s','i','o','n',' ','5','.','0','0','\n'}; + fwrite(unicode_seq, sizeof(BYTE), sizeof(unicode_seq)/sizeof(unicode_seq[0]), file); + fwrite(header, sizeof(WCHAR), sizeof(header)/sizeof(header[0]), file); + } else + { + fputs("REGEDIT4\n", file); + } + return file; } @@ -1121,21 +1207,25 @@ static FILE *REGPROC_open_export_file(WCHAR *file_name) * reg_key_name - registry branch to export. The whole registry is exported if * reg_key_name is NULL or contains an empty string. */ -BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name) +BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name, DWORD format) { WCHAR *reg_key_name_buf; WCHAR *val_name_buf; BYTE *val_buf; + WCHAR *line_buf; DWORD reg_key_name_len = KEY_MAX_LEN; DWORD val_name_len = KEY_MAX_LEN; DWORD val_size = REG_VAL_BUF_SIZE; + DWORD line_buf_size = KEY_MAX_LEN + REG_VAL_BUF_SIZE; FILE *file = NULL; + BOOL unicode = (format == REG_FORMAT_5); reg_key_name_buf = HeapAlloc(GetProcessHeap(), 0, reg_key_name_len * sizeof(*reg_key_name_buf)); val_name_buf = HeapAlloc(GetProcessHeap(), 0, val_name_len * sizeof(*val_name_buf)); val_buf = HeapAlloc(GetProcessHeap(), 0, val_size); + line_buf = HeapAlloc(GetProcessHeap(), 0, line_buf_size); CHECK_ENOUGH_MEMORY(reg_key_name_buf && val_name_buf && val_buf); if (reg_key_name && reg_key_name[0]) { @@ -1157,17 +1247,19 @@ BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name) } if (!branch_name[0]) { /* no branch - registry class is specified */ - file = REGPROC_open_export_file(file_name); + file = REGPROC_open_export_file(file_name, unicode); export_hkey(file, reg_key_class, ®_key_name_buf, ®_key_name_len, &val_name_buf, &val_name_len, - &val_buf, &val_size); + &val_buf, &val_size, &line_buf, + &line_buf_size, unicode); } else if (RegOpenKeyW(reg_key_class, branch_name, &key) == ERROR_SUCCESS) { - file = REGPROC_open_export_file(file_name); + file = REGPROC_open_export_file(file_name, unicode); export_hkey(file, key, ®_key_name_buf, ®_key_name_len, &val_name_buf, &val_name_len, - &val_buf, &val_size); + &val_buf, &val_size, &line_buf, + &line_buf_size, unicode); RegCloseKey(key); } else { CHAR* key_nameA = GetMultiByteString(reg_key_name); @@ -1180,7 +1272,7 @@ BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name) unsigned int i; /* export all registry classes */ - file = REGPROC_open_export_file(file_name); + file = REGPROC_open_export_file(file_name, unicode); for (i = 0; i < REG_CLASS_NUMBER; i++) { /* do not export HKEY_CLASSES_ROOT */ if (reg_class_keys[i] != HKEY_CLASSES_ROOT && @@ -1191,7 +1283,8 @@ BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name) export_hkey(file, reg_class_keys[i], ®_key_name_buf, ®_key_name_len, &val_name_buf, &val_name_len, - &val_buf, &val_size); + &val_buf, &val_size, &line_buf, + &line_buf_size, unicode); } } } @@ -1202,6 +1295,7 @@ BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name) HeapFree(GetProcessHeap(), 0, reg_key_name); HeapFree(GetProcessHeap(), 0, val_name_buf); HeapFree(GetProcessHeap(), 0, val_buf); + HeapFree(GetProcessHeap(), 0, line_buf); return TRUE; } diff --git a/programs/regedit/regproc.h b/programs/regedit/regproc.h index d063506197e..9655dd85c25 100644 --- a/programs/regedit/regproc.h +++ b/programs/regedit/regproc.h @@ -19,9 +19,12 @@ #define KEY_MAX_LEN 1024 +#define REG_FORMAT_5 1 +#define REG_FORMAT_4 2 + const CHAR *getAppName(void); -BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name); +BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name, DWORD format); BOOL import_registry_file(FILE *in); void delete_registry_key(WCHAR *reg_key_name); WCHAR* GetWideString(const char* strA); diff --git a/tools/widl/client.c b/tools/widl/client.c index b2f971c6271..40788954426 100644 --- a/tools/widl/client.c +++ b/tools/widl/client.c @@ -99,6 +99,50 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) context_handle_var = get_context_handle_var(func); } + print_client( "struct __frame_%s%s\n{\n", prefix_client, get_name(def) ); + indent++; + print_client( "__DECL_EXCEPTION_FRAME\n" ); + print_client("MIDL_STUB_MESSAGE _StubMsg;\n"); + if (implicit_handle || explicit_handle_var || explicit_generic_handle_var || context_handle_var) + { + if (!implicit_handle && explicit_generic_handle_var) + print_client("%s %s;\n", + get_explicit_generic_handle_type(explicit_generic_handle_var)->name, + explicit_generic_handle_var->name ); + print_client("RPC_BINDING_HANDLE _Handle;\n"); + } + + if (!is_void(get_func_return_type(func)) && decl_indirect(get_func_return_type(func))) + { + print_client("void *_p_%s;\n", "_RetVal" ); + } + indent--; + print_client( "};\n\n" ); + + print_client( "static void __finally_%s%s(", prefix_client, get_name(def) ); + print_client( " struct __frame_%s%s *__frame )\n{\n", prefix_client, get_name(def) ); + indent++; + + /* FIXME: emit client finally code */ + + if (has_full_pointer) + write_full_pointer_free(client, indent, func); + + print_client("NdrFreeBuffer(&__frame->_StubMsg);\n"); + + if (!implicit_handle && explicit_generic_handle_var) + { + fprintf(client, "\n"); + print_client("if (__frame->_Handle)\n"); + indent++; + print_client("%s_unbind(__frame->%s, __frame->_Handle);\n", + get_explicit_generic_handle_type(explicit_generic_handle_var)->name, + explicit_generic_handle_var->name); + indent--; + } + indent--; + print_client( "}\n\n" ); + write_type_decl_left(client, get_func_return_type(func)); if (needs_space_after(get_func_return_type(func))) fprintf(client, " "); @@ -115,6 +159,7 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) /* write the functions body */ fprintf(client, "{\n"); indent++; + print_client( "struct __frame_%s%s __f, * const __frame = &__f;\n", prefix_client, get_name(def) ); /* declare return value '_RetVal' */ if (!is_void(get_func_return_type(func))) @@ -123,19 +168,24 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) write_type_decl_left(client, get_func_return_type(func)); fprintf(client, " _RetVal;\n"); } + print_client("RPC_MESSAGE _RpcMessage;\n"); if (implicit_handle || explicit_handle_var || explicit_generic_handle_var || context_handle_var) - print_client("RPC_BINDING_HANDLE _Handle = 0;\n"); - - print_client("RPC_MESSAGE _RpcMessage;\n"); - print_client("MIDL_STUB_MESSAGE _StubMsg;\n"); + { + print_client( "__frame->_Handle = 0;\n" ); + if (!implicit_handle && explicit_generic_handle_var) + print_client("__frame->%s = %s;\n", + explicit_generic_handle_var->name, explicit_generic_handle_var->name ); + } if (!is_void(get_func_return_type(func)) && decl_indirect(get_func_return_type(func))) { - print_client("void *_p_%s = &%s;\n", + print_client("__frame->_p_%s = &%s;\n", "_RetVal", "_RetVal"); } fprintf(client, "\n"); + print_client( "RpcExceptionInit( 0, __finally_%s%s );\n", prefix_client, get_name(def) ); + if (has_full_pointer) write_full_pointer_init(client, indent, func, FALSE); @@ -146,14 +196,8 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) print_client("{\n"); indent++; - print_client("NdrClientInitializeNew(\n"); - indent++; - print_client("(PRPC_MESSAGE)&_RpcMessage,\n"); - print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); - print_client("(PMIDL_STUB_DESC)&%s_StubDesc,\n", iface->name); - print_client("%d);\n", method_count); - indent--; - fprintf(client, "\n"); + print_client("NdrClientInitializeNew(&_RpcMessage, &__frame->_StubMsg, &%s_StubDesc, %d);\n", + iface->name, method_count); if (is_attr(def->attrs, ATTR_IDEMPOTENT) || is_attr(def->attrs, ATTR_BROADCAST)) { @@ -167,12 +211,12 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) if (explicit_handle_var) { - print_client("_Handle = %s;\n", explicit_handle_var->name); + print_client("__frame->_Handle = %s;\n", explicit_handle_var->name); fprintf(client, "\n"); } else if (explicit_generic_handle_var) { - print_client("_Handle = %s_bind(%s);\n", + print_client("__frame->_Handle = %s_bind(%s);\n", get_explicit_generic_handle_type(explicit_generic_handle_var)->name, explicit_generic_handle_var->name); fprintf(client, "\n"); @@ -185,7 +229,8 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) int is_ch_ptr = is_aliaschain_attr(context_handle_var->type, ATTR_CONTEXTHANDLE) ? FALSE : TRUE; print_client("if (%s%s != 0)\n", is_ch_ptr ? "*" : "", context_handle_var->name); indent++; - print_client("_Handle = NDRCContextBinding(%s%s);\n", is_ch_ptr ? "*" : "", context_handle_var->name); + print_client("__frame->_Handle = NDRCContextBinding(%s%s);\n", + is_ch_ptr ? "*" : "", context_handle_var->name); indent--; if (is_attr(context_handle_var->attrs, ATTR_IN) && !is_attr(context_handle_var->attrs, ATTR_OUT)) @@ -199,38 +244,29 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) } else if (implicit_handle) { - print_client("_Handle = %s;\n", implicit_handle); + print_client("__frame->_Handle = %s;\n", implicit_handle); fprintf(client, "\n"); } - write_remoting_arguments(client, indent, func, PASS_IN, PHASE_BUFFERSIZE); + write_remoting_arguments(client, indent, func, "", PASS_IN, PHASE_BUFFERSIZE); - print_client("NdrGetBuffer(\n"); - indent++; - print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); - print_client("_StubMsg.BufferLength,\n"); + print_client("NdrGetBuffer(&__frame->_StubMsg, __frame->_StubMsg.BufferLength, "); if (implicit_handle || explicit_handle_var || explicit_generic_handle_var || context_handle_var) - print_client("_Handle);\n"); + fprintf(client, "__frame->_Handle);\n\n"); else - print_client("%s__MIDL_AutoBindHandle);\n", iface->name); - indent--; - fprintf(client, "\n"); + fprintf(client,"%s__MIDL_AutoBindHandle);\n\n", iface->name); /* marshal arguments */ - write_remoting_arguments(client, indent, func, PASS_IN, PHASE_MARSHAL); + write_remoting_arguments(client, indent, func, "", PASS_IN, PHASE_MARSHAL); /* send/receive message */ /* print_client("NdrNsSendReceive(\n"); */ - /* print_client("(unsigned char *)_StubMsg.Buffer,\n"); */ + /* print_client("(unsigned char *)__frame->_StubMsg.Buffer,\n"); */ /* print_client("(RPC_BINDING_HANDLE *) &%s__MIDL_AutoBindHandle);\n", iface->name); */ - print_client("NdrSendReceive(\n"); - indent++; - print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); - print_client("(unsigned char *)_StubMsg.Buffer);\n\n"); - indent--; + print_client("NdrSendReceive(&__frame->_StubMsg, __frame->_StubMsg.Buffer);\n\n"); - print_client("_StubMsg.BufferStart = (unsigned char *)_RpcMessage.Buffer;\n"); - print_client("_StubMsg.BufferEnd = _StubMsg.BufferStart + _RpcMessage.BufferLength;\n"); + print_client("__frame->_StubMsg.BufferStart = _RpcMessage.Buffer;\n"); + print_client("__frame->_StubMsg.BufferEnd = __frame->_StubMsg.BufferStart + _RpcMessage.BufferLength;\n"); if (has_out_arg_or_return(func)) { @@ -238,16 +274,14 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) print_client("if ((_RpcMessage.DataRepresentation & 0x0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION)\n"); indent++; - print_client("NdrConvert(\n"); - indent++; - print_client("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); - print_client("(PFORMAT_STRING)&__MIDL_ProcFormatString.Format[%u]);\n", *proc_offset); - indent -= 2; + print_client("NdrConvert(&__frame->_StubMsg, (PFORMAT_STRING)&__MIDL_ProcFormatString.Format[%u]);\n", + *proc_offset); + indent--; } /* unmarshall arguments */ fprintf(client, "\n"); - write_remoting_arguments(client, indent, func, PASS_OUT, PHASE_UNMARSHAL); + write_remoting_arguments(client, indent, func, "", PASS_OUT, PHASE_UNMARSHAL); /* unmarshal return value */ if (!is_void(get_func_return_type(func))) @@ -256,7 +290,7 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) print_client("MIDL_memset(&%s, 0, sizeof(%s));\n", "_RetVal", "_RetVal"); else if (is_ptr(get_func_return_type(func)) || is_array(get_func_return_type(func))) print_client("%s = 0;\n", "_RetVal"); - write_remoting_arguments(client, indent, func, PASS_RETURN, PHASE_UNMARSHAL); + write_remoting_arguments(client, indent, func, "", PASS_RETURN, PHASE_UNMARSHAL); } /* update proc_offset */ @@ -275,26 +309,7 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) print_client("RpcFinally\n"); print_client("{\n"); indent++; - - - /* FIXME: emit client finally code */ - - if (has_full_pointer) - write_full_pointer_free(client, indent, func); - - print_client("NdrFreeBuffer((PMIDL_STUB_MESSAGE)&_StubMsg);\n"); - - if (!implicit_handle && explicit_generic_handle_var) - { - fprintf(client, "\n"); - print_client("if (_Handle)\n"); - indent++; - print_client("%s_unbind(%s, _Handle);\n", - get_explicit_generic_handle_type(explicit_generic_handle_var)->name, - explicit_generic_handle_var->name); - indent--; - } - + print_client( "__finally_%s%s( __frame );\n", prefix_client, get_name(def) ); indent--; print_client("}\n"); print_client("RpcEndFinally\n"); @@ -434,7 +449,9 @@ static void init_client(void) print_client("#endif\n"); fprintf(client, "\n"); print_client("#include \"%s\"\n", header_name); - fprintf(client, "\n"); + print_client( "\n"); + write_exceptions( client ); + print_client( "\n"); } diff --git a/tools/widl/expr.c b/tools/widl/expr.c index dac5d9af2be..3b668ef07e7 100644 --- a/tools/widl/expr.c +++ b/tools/widl/expr.c @@ -576,7 +576,7 @@ const type_t *expr_resolve_type(const struct expr_loc *expr_loc, const type_t *c void write_expr(FILE *h, const expr_t *e, int brackets, int toplevel, const char *toplevel_prefix, - const type_t *cont_type) + const type_t *cont_type, const char *local_var_prefix) { switch (e->type) { @@ -602,9 +602,13 @@ void write_expr(FILE *h, const expr_t *e, int brackets, { int found_in_cont_type; find_identifier(e->u.sval, cont_type, &found_in_cont_type); - if (found_in_cont_type) fprintf(h, "%s", toplevel_prefix); + if (found_in_cont_type) + { + fprintf(h, "%s%s", toplevel_prefix, e->u.sval); + break; + } } - fprintf(h, "%s", e->u.sval); + fprintf(h, "%s%s", local_var_prefix, e->u.sval); break; case EXPR_STRLIT: fprintf(h, "\"%s\"", e->u.sval); @@ -614,33 +618,33 @@ void write_expr(FILE *h, const expr_t *e, int brackets, break; case EXPR_LOGNOT: fprintf(h, "!"); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); break; case EXPR_NOT: fprintf(h, "~"); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); break; case EXPR_POS: fprintf(h, "+"); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); break; case EXPR_NEG: fprintf(h, "-"); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); break; case EXPR_ADDRESSOF: fprintf(h, "&"); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); break; case EXPR_PPTR: fprintf(h, "*"); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); break; case EXPR_CAST: fprintf(h, "("); write_type_decl(h, e->u.tref, NULL); fprintf(h, ")"); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); break; case EXPR_SIZEOF: fprintf(h, "sizeof("); @@ -666,7 +670,7 @@ void write_expr(FILE *h, const expr_t *e, int brackets, case EXPR_GTREQL: case EXPR_LESSEQL: if (brackets) fprintf(h, "("); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); switch (e->type) { case EXPR_SHL: fprintf(h, " << "); break; @@ -689,38 +693,38 @@ void write_expr(FILE *h, const expr_t *e, int brackets, case EXPR_LESSEQL: fprintf(h, " <= "); break; default: break; } - write_expr(h, e->u.ext, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->u.ext, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); if (brackets) fprintf(h, ")"); break; case EXPR_MEMBER: if (brackets) fprintf(h, "("); if (e->ref->type == EXPR_PPTR) { - write_expr(h, e->ref->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); fprintf(h, "->"); } else { - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); fprintf(h, "."); } - write_expr(h, e->u.ext, 1, 0, toplevel_prefix, cont_type); + write_expr(h, e->u.ext, 1, 0, toplevel_prefix, cont_type, ""); if (brackets) fprintf(h, ")"); break; case EXPR_COND: if (brackets) fprintf(h, "("); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); fprintf(h, " ? "); - write_expr(h, e->u.ext, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->u.ext, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); fprintf(h, " : "); - write_expr(h, e->ext2, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ext2, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); if (brackets) fprintf(h, ")"); break; case EXPR_ARRAY: if (brackets) fprintf(h, "("); - write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type); + write_expr(h, e->ref, 1, toplevel, toplevel_prefix, cont_type, local_var_prefix); fprintf(h, "["); - write_expr(h, e->u.ext, 1, 1, toplevel_prefix, cont_type); + write_expr(h, e->u.ext, 1, 1, toplevel_prefix, cont_type, local_var_prefix); fprintf(h, "]"); if (brackets) fprintf(h, ")"); break; diff --git a/tools/widl/expr.h b/tools/widl/expr.h index 1b773e0a33d..31ce31d3e9e 100644 --- a/tools/widl/expr.h +++ b/tools/widl/expr.h @@ -37,4 +37,5 @@ extern expr_t *make_expr3(enum expr_type type, expr_t *expr1, expr_t *expr2, exp extern const type_t *expr_resolve_type(const struct expr_loc *expr_loc, const type_t *cont_type, const expr_t *expr); extern int compare_expr(const expr_t *a, const expr_t *b); -extern void write_expr(FILE *h, const expr_t *e, int brackets, int toplevel, const char *toplevel_prefix, const type_t *cont_type); +extern void write_expr(FILE *h, const expr_t *e, int brackets, int toplevel, const char *toplevel_prefix, + const type_t *cont_type, const char *local_var_prefix); diff --git a/tools/widl/header.c b/tools/widl/header.c index 0a2a98a8a27..0d9832d30e1 100644 --- a/tools/widl/header.c +++ b/tools/widl/header.c @@ -194,7 +194,7 @@ static void write_enums(FILE *h, var_list_t *enums) fprintf(h, "%s", get_name(v)); if (v->eval) { fprintf(h, " = "); - write_expr(h, v->eval, 0, 1, NULL, NULL); + write_expr(h, v->eval, 0, 1, NULL, NULL, ""); } } if (list_next( enums, &v->entry )) fprintf(h, ",\n"); @@ -499,7 +499,7 @@ void write_declaration(const var_t *v, int is_in_interface) if (is_const_decl(v) && v->eval) { fprintf(header, "#define %s (", v->name); - write_expr(header, v->eval, 0, 1, NULL, NULL); + write_expr(header, v->eval, 0, 1, NULL, NULL, ""); fprintf(header, ")\n\n"); } else if (v->type->type != RPC_FC_FUNCTION || !is_in_interface) diff --git a/tools/widl/proxy.c b/tools/widl/proxy.c index a7d267af6de..6f1d0ee6734 100644 --- a/tools/widl/proxy.c +++ b/tools/widl/proxy.c @@ -109,101 +109,20 @@ static void init_proxy(const statement_list_t *stmts) print_proxy( "\n"); print_proxy( "#include \"%s\"\n", header_name); print_proxy( "\n"); - print_proxy( "#ifndef USE_COMPILER_EXCEPTIONS\n"); - print_proxy( "\n"); - print_proxy( "#include \"wine/exception.h\"\n"); - print_proxy( "#undef RpcTryExcept\n"); - print_proxy( "#undef RpcExcept\n"); - print_proxy( "#undef RpcEndExcept\n"); - print_proxy( "#undef RpcTryFinally\n"); - print_proxy( "#undef RpcFinally\n"); - print_proxy( "#undef RpcEndFinally\n"); - print_proxy( "#undef RpcExceptionCode\n"); + write_exceptions( proxy ); print_proxy( "\n"); print_proxy( "struct __proxy_frame\n"); print_proxy( "{\n"); - print_proxy( " EXCEPTION_REGISTRATION_RECORD frame;\n"); - print_proxy( " sigjmp_buf jmp;\n"); - print_proxy( " DWORD code;\n"); - print_proxy( " MIDL_STUB_MESSAGE *stub;\n"); - print_proxy( " void *this;\n"); - print_proxy( " int fullptr;\n"); + print_proxy( " __DECL_EXCEPTION_FRAME;\n"); + print_proxy( " MIDL_STUB_MESSAGE _StubMsg;\n"); + print_proxy( " void *This;\n"); print_proxy( "};\n"); print_proxy( "\n"); - print_proxy("static void __proxy_finally_handler( struct __proxy_frame *frame )\n"); - print_proxy( "{\n"); - print_proxy( " if (frame->fullptr) NdrFullPointerXlatFree( frame->stub->FullPtrXlatTables );\n"); - print_proxy( " if (frame->this) NdrProxyFreeBuffer( frame->this, frame->stub );\n" ); - print_proxy( "}\n\n"); - print_proxy( "static DWORD __proxy_exception_handler( EXCEPTION_RECORD *record,\n"); - print_proxy( " EXCEPTION_REGISTRATION_RECORD *frame,\n"); - print_proxy( " CONTEXT *context,\n"); - print_proxy( " EXCEPTION_REGISTRATION_RECORD **pdispatcher )\n"); + print_proxy("static int __proxy_filter( struct __proxy_frame *__frame )\n"); print_proxy( "{\n"); - print_proxy( " struct __proxy_frame *proxy_frame = (struct __proxy_frame *)frame;\n"); - print_proxy( "\n"); - print_proxy( " if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))\n"); - print_proxy( " {\n" ); - print_proxy( " if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))\n"); - print_proxy( " __proxy_finally_handler( proxy_frame );\n"); - print_proxy( " return ExceptionContinueSearch;\n"); - print_proxy( " }\n" ); - print_proxy( " if (proxy_frame->stub->dwStubPhase == PROXY_SENDRECEIVE)\n"); - print_proxy( " return ExceptionContinueSearch;\n"); - print_proxy( "\n"); - print_proxy( " proxy_frame->code = record->ExceptionCode;\n"); - print_proxy( " __wine_rtl_unwind( frame, record );\n"); - print_proxy( " __proxy_finally_handler( proxy_frame );\n"); - print_proxy( " __wine_pop_frame( frame );\n"); - print_proxy( " siglongjmp( proxy_frame->jmp, 1 );\n"); + print_proxy( " return (__frame->_StubMsg.dwStubPhase != PROXY_SENDRECEIVE);\n"); print_proxy( "}\n"); print_proxy( "\n"); - print_proxy( "#define RpcTryExcept \\\n"); - print_proxy( " do { \\\n"); - print_proxy( " struct __proxy_frame __proxy_frame; \\\n"); - print_proxy( " __proxy_frame.frame.Handler = __proxy_exception_handler; \\\n"); - print_proxy( " __proxy_frame.stub = &_StubMsg; \\\n"); - print_proxy( " if (!sigsetjmp( __proxy_frame.jmp, 0 )) \\\n"); - print_proxy( " { \\\n"); - print_proxy( " __wine_push_frame( &__proxy_frame.frame ); \\\n"); - print_proxy( " {\n"); - print_proxy( "\n"); - print_proxy( "#define RpcExcept(expr) \\\n"); - print_proxy( " } \\\n"); - print_proxy( " __wine_pop_frame( &__proxy_frame.frame ); \\\n"); - print_proxy( " } \\\n"); - print_proxy( " else \\\n"); - print_proxy( " {\n"); - print_proxy( "\n"); - print_proxy( "#define RpcEndExcept \\\n"); - print_proxy( " } \\\n"); - print_proxy( " } while(0);\n"); - print_proxy( "\n"); - print_proxy( "#define RpcExceptionCode() (__proxy_frame.code)\n"); - print_proxy( "\n"); - print_proxy( "#define RpcTryFinallyProxy(ptr) \\\n"); - print_proxy(" __proxy_frame.this = This; \\\n"); - print_proxy(" __proxy_frame.fullptr = ptr;\n"); - print_proxy( "\n"); - print_proxy( "#define RpcFinallyProxy \\\n"); - print_proxy(" __proxy_frame.this = 0; \\\n"); - print_proxy(" __proxy_frame.fullptr = 0;\n"); - print_proxy( "\n"); - print_proxy( "#define RpcTryFinallyStub\n"); - print_proxy( "\n"); - print_proxy( "#define RpcFinallyStub\n"); - print_proxy( "\n"); - print_proxy( "#define RpcEndFinally\n"); - print_proxy( "\n"); - print_proxy( "#else /* USE_COMPILER_EXCEPTIONS */\n"); - print_proxy( "\n"); - print_proxy( "#define RpcTryFinallyProxy(ptr) RpcTryFinally\n"); - print_proxy( "#define RpcTryFinallyStub RpcTryFinally\n"); - print_proxy( "#define RpcFinallyProxy RpcFinally\n"); - print_proxy( "#define RpcFinallyStub RpcFinally\n"); - print_proxy( "\n"); - print_proxy( "#endif /* USE_COMPILER_EXCEPTIONS */\n"); - print_proxy( "\n"); write_formatstringsdecl(proxy, indent, stmts, need_proxy); write_stubdescproto(); } @@ -284,7 +203,7 @@ static void proxy_check_pointers( const var_list_t *args ) } } -static void free_variable( const var_t *arg ) +static void free_variable( const var_t *arg, const char *local_var_prefix ) { unsigned int type_offset = arg->type->typestring_offset; expr_t *iid; @@ -293,10 +212,10 @@ static void free_variable( const var_t *arg ) if (size) { - print_proxy( "_StubMsg.MaxCount = " ); - write_expr(proxy, size, 0, 1, NULL, NULL); + print_proxy( "__frame->_StubMsg.MaxCount = " ); + write_expr(proxy, size, 0, 1, NULL, NULL, local_var_prefix); fprintf(proxy, ";\n\n"); - print_proxy( "NdrClearOutParameters( &_StubMsg, "); + print_proxy( "NdrClearOutParameters( &__frame->_StubMsg, "); fprintf(proxy, "&__MIDL_TypeFormatString.Format[%u], ", type_offset ); fprintf(proxy, "(void*)%s );\n", arg->name ); return; @@ -321,11 +240,11 @@ static void free_variable( const var_t *arg ) iid = get_attrp( arg->attrs, ATTR_IIDIS ); if( iid ) { - print_proxy( "_StubMsg.MaxCount = (unsigned long) " ); - write_expr(proxy, iid, 1, 1, NULL, NULL); + print_proxy( "__frame->_StubMsg.MaxCount = (unsigned long) " ); + write_expr(proxy, iid, 1, 1, NULL, NULL, local_var_prefix); print_proxy( ";\n\n" ); } - print_proxy( "NdrClearOutParameters( &_StubMsg, "); + print_proxy( "NdrClearOutParameters( &__frame->_StubMsg, "); fprintf(proxy, "&__MIDL_TypeFormatString.Format[%u], ", type_offset ); fprintf(proxy, "(void*)%s );\n", arg->name ); break; @@ -335,7 +254,7 @@ static void free_variable( const var_t *arg ) } } -static void proxy_free_variables( var_list_t *args ) +static void proxy_free_variables( var_list_t *args, const char *local_var_prefix ) { const var_t *arg; @@ -343,7 +262,7 @@ static void proxy_free_variables( var_list_t *args ) LIST_FOR_EACH_ENTRY( arg, args, const var_t, entry ) if (is_attr(arg->attrs, ATTR_OUT)) { - free_variable( arg ); + free_variable( arg, local_var_prefix ); fprintf(proxy, "\n"); } } @@ -358,12 +277,23 @@ static void gen_proxy(type_t *iface, const func_t *cur, int idx, if (!callconv) callconv = ""; indent = 0; + print_proxy( "static void __finally_%s_%s_Proxy( struct __proxy_frame *__frame )\n", + iface->name, get_name(def) ); + print_proxy( "{\n"); + indent++; + if (has_full_pointer) write_full_pointer_free(proxy, indent, cur); + print_proxy( "NdrProxyFreeBuffer( __frame->This, &__frame->_StubMsg );\n" ); + indent--; + print_proxy( "}\n"); + print_proxy( "\n"); + write_type_decl_left(proxy, get_func_return_type(cur)); print_proxy( " %s %s_%s_Proxy(\n", callconv, iface->name, get_name(def)); write_args(proxy, cur->args, iface->name, 1, TRUE); print_proxy( ")\n"); print_proxy( "{\n"); indent ++; + print_proxy( "struct __proxy_frame __f, * const __frame = &__f;\n" ); /* local variables */ if (has_ret) { print_proxy( "" ); @@ -371,7 +301,6 @@ static void gen_proxy(type_t *iface, const func_t *cur, int idx, print_proxy( " _RetVal;\n"); } print_proxy( "RPC_MESSAGE _RpcMessage;\n" ); - print_proxy( "MIDL_STUB_MESSAGE _StubMsg;\n" ); if (has_ret) { if (decl_indirect(get_func_return_type(cur))) print_proxy("void *_p_%s = &%s;\n", @@ -379,6 +308,9 @@ static void gen_proxy(type_t *iface, const func_t *cur, int idx, } print_proxy( "\n"); + print_proxy( "RpcExceptionInit( __proxy_filter, __finally_%s_%s_Proxy );\n", iface->name, get_name(def) ); + print_proxy( "__frame->This = This;\n" ); + if (has_full_pointer) write_full_pointer_init(proxy, indent, cur, FALSE); @@ -388,31 +320,31 @@ static void gen_proxy(type_t *iface, const func_t *cur, int idx, print_proxy( "RpcTryExcept\n" ); print_proxy( "{\n" ); indent++; - print_proxy( "NdrProxyInitialize(This, &_RpcMessage, &_StubMsg, &Object_StubDesc, %d);\n", idx); + print_proxy( "NdrProxyInitialize(This, &_RpcMessage, &__frame->_StubMsg, &Object_StubDesc, %d);\n", idx); proxy_check_pointers( cur->args ); - print_proxy( "RpcTryFinallyProxy(%d)\n", has_full_pointer ); + print_proxy( "RpcTryFinally\n" ); print_proxy( "{\n" ); indent++; - write_remoting_arguments(proxy, indent, cur, PASS_IN, PHASE_BUFFERSIZE); + write_remoting_arguments(proxy, indent, cur, "", PASS_IN, PHASE_BUFFERSIZE); - print_proxy( "NdrProxyGetBuffer(This, &_StubMsg);\n" ); + print_proxy( "NdrProxyGetBuffer(This, &__frame->_StubMsg);\n" ); - write_remoting_arguments(proxy, indent, cur, PASS_IN, PHASE_MARSHAL); + write_remoting_arguments(proxy, indent, cur, "", PASS_IN, PHASE_MARSHAL); - print_proxy( "NdrProxySendReceive(This, &_StubMsg);\n" ); + print_proxy( "NdrProxySendReceive(This, &__frame->_StubMsg);\n" ); fprintf(proxy, "\n"); - print_proxy( "_StubMsg.BufferStart = _RpcMessage.Buffer;\n" ); - print_proxy( "_StubMsg.BufferEnd = _StubMsg.BufferStart + _RpcMessage.BufferLength;\n\n" ); + print_proxy( "__frame->_StubMsg.BufferStart = _RpcMessage.Buffer;\n" ); + print_proxy( "__frame->_StubMsg.BufferEnd = __frame->_StubMsg.BufferStart + _RpcMessage.BufferLength;\n\n" ); print_proxy("if ((_RpcMessage.DataRepresentation & 0xffff) != NDR_LOCAL_DATA_REPRESENTATION)\n"); indent++; - print_proxy("NdrConvert( &_StubMsg, &__MIDL_ProcFormatString.Format[%u]);\n", proc_offset ); + print_proxy("NdrConvert( &__frame->_StubMsg, &__MIDL_ProcFormatString.Format[%u]);\n", proc_offset ); indent--; fprintf(proxy, "\n"); - write_remoting_arguments(proxy, indent, cur, PASS_OUT, PHASE_UNMARSHAL); + write_remoting_arguments(proxy, indent, cur, "", PASS_OUT, PHASE_UNMARSHAL); if (has_ret) { @@ -420,27 +352,25 @@ static void gen_proxy(type_t *iface, const func_t *cur, int idx, print_proxy("MIDL_memset(&%s, 0, sizeof(%s));\n", "_RetVal", "_RetVal"); else if (is_ptr(get_func_return_type(cur)) || is_array(get_func_return_type(cur))) print_proxy("%s = 0;\n", "_RetVal"); - write_remoting_arguments(proxy, indent, cur, PASS_RETURN, PHASE_UNMARSHAL); + write_remoting_arguments(proxy, indent, cur, "", PASS_RETURN, PHASE_UNMARSHAL); } indent--; print_proxy( "}\n"); - print_proxy( "RpcFinallyProxy\n" ); + print_proxy( "RpcFinally\n" ); print_proxy( "{\n" ); indent++; - if (has_full_pointer) - write_full_pointer_free(proxy, indent, cur); - print_proxy( "NdrProxyFreeBuffer(This, &_StubMsg);\n" ); + print_proxy( "__finally_%s_%s_Proxy( __frame );\n", iface->name, get_name(def) ); indent--; print_proxy( "}\n"); print_proxy( "RpcEndFinally\n" ); indent--; print_proxy( "}\n" ); - print_proxy( "RpcExcept(_StubMsg.dwStubPhase != PROXY_SENDRECEIVE)\n" ); + print_proxy( "RpcExcept(__frame->_StubMsg.dwStubPhase != PROXY_SENDRECEIVE)\n" ); print_proxy( "{\n" ); if (has_ret) { indent++; - proxy_free_variables( cur->args ); + proxy_free_variables( cur->args, "" ); print_proxy( "_RetVal = NdrProxyErrorHandler(RpcExceptionCode());\n" ); indent--; } @@ -464,6 +394,24 @@ static void gen_stub(type_t *iface, const func_t *cur, const char *cas, int has_full_pointer = is_full_pointer_function(cur); indent = 0; + print_proxy( "struct __frame_%s_%s_Stub\n{\n", iface->name, get_name(def)); + indent++; + print_proxy( "__DECL_EXCEPTION_FRAME\n" ); + print_proxy( "MIDL_STUB_MESSAGE _StubMsg;\n"); + print_proxy( "%s * _This;\n", iface->name ); + declare_stub_args( proxy, indent, cur ); + indent--; + print_proxy( "};\n\n" ); + + print_proxy( "static void __finally_%s_%s_Stub(", iface->name, get_name(def) ); + print_proxy( " struct __frame_%s_%s_Stub *__frame )\n{\n", iface->name, get_name(def) ); + indent++; + write_remoting_arguments(proxy, indent, cur, "__frame->", PASS_OUT, PHASE_FREE); + if (has_full_pointer) + write_full_pointer_free(proxy, indent, cur); + indent--; + print_proxy( "}\n\n" ); + print_proxy( "void __RPC_STUB %s_%s_Stub(\n", iface->name, get_name(def)); indent++; print_proxy( "IRpcStubBuffer* This,\n"); @@ -473,80 +421,78 @@ static void gen_stub(type_t *iface, const func_t *cur, const char *cas, indent--; print_proxy( "{\n"); indent++; - print_proxy("%s * _This = (%s*)((CStdStubBuffer*)This)->pvServerObject;\n", iface->name, iface->name); - print_proxy("MIDL_STUB_MESSAGE _StubMsg;\n"); - declare_stub_args( proxy, indent, cur ); - fprintf(proxy, "\n"); + print_proxy( "struct __frame_%s_%s_Stub __f, * const __frame = &__f;\n\n", + iface->name, get_name(def) ); + + print_proxy("__frame->_This = (%s*)((CStdStubBuffer*)This)->pvServerObject;\n\n", iface->name); /* FIXME: trace */ - print_proxy("NdrStubInitialize(_pRpcMessage, &_StubMsg, &Object_StubDesc, _pRpcChannelBuffer);\n"); + print_proxy("NdrStubInitialize(_pRpcMessage, &__frame->_StubMsg, &Object_StubDesc, _pRpcChannelBuffer);\n"); fprintf(proxy, "\n"); + print_proxy( "RpcExceptionInit( 0, __finally_%s_%s_Stub );\n", iface->name, get_name(def) ); - write_parameters_init(proxy, indent, cur); + write_parameters_init(proxy, indent, cur, "__frame->"); - print_proxy("RpcTryFinallyStub\n"); + print_proxy("RpcTryFinally\n"); print_proxy("{\n"); indent++; if (has_full_pointer) write_full_pointer_init(proxy, indent, cur, TRUE); print_proxy("if ((_pRpcMessage->DataRepresentation & 0xffff) != NDR_LOCAL_DATA_REPRESENTATION)\n"); indent++; - print_proxy("NdrConvert( &_StubMsg, &__MIDL_ProcFormatString.Format[%u]);\n", proc_offset ); + print_proxy("NdrConvert( &__frame->_StubMsg, &__MIDL_ProcFormatString.Format[%u]);\n", proc_offset ); indent--; fprintf(proxy, "\n"); - write_remoting_arguments(proxy, indent, cur, PASS_IN, PHASE_UNMARSHAL); + write_remoting_arguments(proxy, indent, cur, "__frame->", PASS_IN, PHASE_UNMARSHAL); fprintf(proxy, "\n"); - assign_stub_out_args( proxy, indent, cur ); + assign_stub_out_args( proxy, indent, cur, "__frame->" ); print_proxy("*_pdwStubPhase = STUB_CALL_SERVER;\n"); fprintf(proxy, "\n"); print_proxy(""); - if (has_ret) fprintf(proxy, "_RetVal = "); + if (has_ret) fprintf(proxy, "__frame->_RetVal = "); if (cas) fprintf(proxy, "%s_%s_Stub", iface->name, cas); - else fprintf(proxy, "_This->lpVtbl->%s", get_name(def)); - fprintf(proxy, "(_This"); + else fprintf(proxy, "__frame->_This->lpVtbl->%s", get_name(def)); + fprintf(proxy, "(__frame->_This"); if (cur->args) { LIST_FOR_EACH_ENTRY( arg, cur->args, const var_t, entry ) - fprintf(proxy, ", %s%s", arg->type->declarray ? "*" : "", get_name(arg)); + fprintf(proxy, ", %s__frame->%s", arg->type->declarray ? "*" : "", arg->name); } fprintf(proxy, ");\n"); fprintf(proxy, "\n"); print_proxy("*_pdwStubPhase = STUB_MARSHAL;\n"); fprintf(proxy, "\n"); - write_remoting_arguments(proxy, indent, cur, PASS_OUT, PHASE_BUFFERSIZE); + write_remoting_arguments(proxy, indent, cur, "__frame->", PASS_OUT, PHASE_BUFFERSIZE); if (!is_void(get_func_return_type(cur))) - write_remoting_arguments(proxy, indent, cur, PASS_RETURN, PHASE_BUFFERSIZE); + write_remoting_arguments(proxy, indent, cur, "__frame->", PASS_RETURN, PHASE_BUFFERSIZE); - print_proxy("NdrStubGetBuffer(This, _pRpcChannelBuffer, &_StubMsg);\n"); + print_proxy("NdrStubGetBuffer(This, _pRpcChannelBuffer, &__frame->_StubMsg);\n"); - write_remoting_arguments(proxy, indent, cur, PASS_OUT, PHASE_MARSHAL); + write_remoting_arguments(proxy, indent, cur, "__frame->", PASS_OUT, PHASE_MARSHAL); fprintf(proxy, "\n"); /* marshall the return value */ if (!is_void(get_func_return_type(cur))) - write_remoting_arguments(proxy, indent, cur, PASS_RETURN, PHASE_MARSHAL); + write_remoting_arguments(proxy, indent, cur, "__frame->", PASS_RETURN, PHASE_MARSHAL); indent--; print_proxy("}\n"); - print_proxy("RpcFinallyStub\n"); + print_proxy("RpcFinally\n"); print_proxy("{\n"); - - write_remoting_arguments(proxy, indent+1, cur, PASS_OUT, PHASE_FREE); - - if (has_full_pointer) - write_full_pointer_free(proxy, indent, cur); - + indent++; + print_proxy( "__finally_%s_%s_Stub( __frame );\n", iface->name, get_name(def) ); + indent--; print_proxy("}\n"); print_proxy("RpcEndFinally\n"); - print_proxy("_pRpcMessage->BufferLength = _StubMsg.Buffer - (unsigned char *)_pRpcMessage->Buffer;\n"); + print_proxy("_pRpcMessage->BufferLength = __frame->_StubMsg.Buffer - (unsigned char *)_pRpcMessage->Buffer;\n"); indent--; print_proxy("}\n"); diff --git a/tools/widl/server.c b/tools/widl/server.c index c2271998719..db2d6c28880 100644 --- a/tools/widl/server.c +++ b/tools/widl/server.c @@ -63,34 +63,52 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) /* check for a defined binding handle */ explicit_handle_var = get_explicit_handle_var(func); - print_server("void __RPC_STUB %s_%s( PRPC_MESSAGE _pRpcMessage )\n", iface->name, get_name(def)); - - /* write the functions body */ - fprintf(server, "{\n"); + print_server("struct __frame_%s_%s\n{\n", iface->name, get_name(def)); indent++; + print_server("__DECL_EXCEPTION_FRAME\n"); + print_server("MIDL_STUB_MESSAGE _StubMsg;\n"); /* Declare arguments */ declare_stub_args(server, indent, func); - print_server("MIDL_STUB_MESSAGE _StubMsg;\n"); - print_server("RPC_STATUS _Status;\n"); - fprintf(server, "\n"); + indent--; + print_server("};\n\n"); + print_server("static void __finally_%s_%s(", iface->name, get_name(def)); + fprintf(server," struct __frame_%s_%s *__frame )\n{\n", iface->name, get_name(def)); + + indent++; + write_remoting_arguments(server, indent, func, "__frame->", PASS_OUT, PHASE_FREE); + + if (has_full_pointer) + write_full_pointer_free(server, indent, func); + + indent--; + print_server("}\n\n"); + + print_server("void __RPC_STUB %s_%s( PRPC_MESSAGE _pRpcMessage )\n", iface->name, get_name(def)); + + /* write the functions body */ + fprintf(server, "{\n"); + indent++; + print_server("struct __frame_%s_%s __f, * const __frame = &__f;\n", iface->name, get_name(def)); + if (has_out_arg_or_return(func)) print_server("RPC_STATUS _Status;\n"); + fprintf(server, "\n"); - print_server("((void)(_Status));\n"); print_server("NdrServerInitializeNew(\n"); indent++; print_server("_pRpcMessage,\n"); - print_server("&_StubMsg,\n"); + print_server("&__frame->_StubMsg,\n"); print_server("&%s_StubDesc);\n", iface->name); indent--; fprintf(server, "\n"); + print_server( "RpcExceptionInit( __server_filter, __finally_%s_%s );\n", iface->name, get_name(def)); - write_parameters_init(server, indent, func); + write_parameters_init(server, indent, func, "__frame->"); if (explicit_handle_var) { - print_server("%s = _pRpcMessage->Handle;\n", explicit_handle_var->name); + print_server("__frame->%s = _pRpcMessage->Handle;\n", explicit_handle_var->name); fprintf(server, "\n"); } @@ -108,18 +126,16 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) { print_server("if ((_pRpcMessage->DataRepresentation & 0x0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION)\n"); indent++; - print_server("NdrConvert(\n"); - indent++; - print_server("(PMIDL_STUB_MESSAGE)&_StubMsg,\n"); - print_server("(PFORMAT_STRING)&__MIDL_ProcFormatString.Format[%u]);\n", *proc_offset); - indent -= 2; + print_server("NdrConvert(&__frame->_StubMsg, (PFORMAT_STRING)&__MIDL_ProcFormatString.Format[%u]);\n", + *proc_offset); + indent--; fprintf(server, "\n"); /* unmarshall arguments */ - write_remoting_arguments(server, indent, func, PASS_IN, PHASE_UNMARSHAL); + write_remoting_arguments(server, indent, func, "__frame->", PASS_IN, PHASE_UNMARSHAL); } - print_server("if (_StubMsg.Buffer > _StubMsg.BufferEnd)\n"); + print_server("if (__frame->_StubMsg.Buffer > __frame->_StubMsg.BufferEnd)\n"); print_server("{\n"); indent++; print_server("RpcRaiseException(RPC_X_BAD_STUB_DATA);\n"); @@ -137,11 +153,11 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) fprintf(server, "\n"); /* Assign 'out' arguments */ - assign_stub_out_args(server, indent, func); + assign_stub_out_args(server, indent, func, "__frame->"); /* Call the real server function */ if (!is_void(get_func_return_type(func))) - print_server("_RetVal = "); + print_server("__frame->_RetVal = "); else print_server(""); fprintf(server, "%s%s", prefix_server, get_name(def)); @@ -166,11 +182,12 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) int is_ch_ptr = is_aliaschain_attr(var->type, ATTR_CONTEXTHANDLE) ? FALSE : TRUE; print_server("("); write_type_decl_left(server, var->type); - fprintf(server, ")%sNDRSContextValue(%s)", is_ch_ptr ? "" : "*", var->name); + fprintf(server, ")%sNDRSContextValue(__frame->%s)", + is_ch_ptr ? "" : "*", var->name); } else { - print_server("%s%s", var->type->declarray ? "*" : "", get_name(var)); + print_server("%s__frame->%s", var->type->declarray ? "*" : "", var->name); } } fprintf(server, ");\n"); @@ -183,12 +200,12 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) if (has_out_arg_or_return(func)) { - write_remoting_arguments(server, indent, func, PASS_OUT, PHASE_BUFFERSIZE); + write_remoting_arguments(server, indent, func, "__frame->", PASS_OUT, PHASE_BUFFERSIZE); if (!is_void(get_func_return_type(func))) - write_remoting_arguments(server, indent, func, PASS_RETURN, PHASE_BUFFERSIZE); + write_remoting_arguments(server, indent, func, "__frame->", PASS_RETURN, PHASE_BUFFERSIZE); - print_server("_pRpcMessage->BufferLength = _StubMsg.BufferLength;\n"); + print_server("_pRpcMessage->BufferLength = __frame->_StubMsg.BufferLength;\n"); fprintf(server, "\n"); print_server("_Status = I_RpcGetBuffer(_pRpcMessage);\n"); print_server("if (_Status)\n"); @@ -196,38 +213,30 @@ static void write_function_stubs(type_t *iface, unsigned int *proc_offset) print_server("RpcRaiseException(_Status);\n"); indent--; fprintf(server, "\n"); - print_server("_StubMsg.Buffer = (unsigned char *)_pRpcMessage->Buffer;\n"); + print_server("__frame->_StubMsg.Buffer = _pRpcMessage->Buffer;\n"); fprintf(server, "\n"); } /* marshall arguments */ - write_remoting_arguments(server, indent, func, PASS_OUT, PHASE_MARSHAL); + write_remoting_arguments(server, indent, func, "__frame->", PASS_OUT, PHASE_MARSHAL); /* marshall the return value */ if (!is_void(get_func_return_type(func))) - write_remoting_arguments(server, indent, func, PASS_RETURN, PHASE_MARSHAL); + write_remoting_arguments(server, indent, func, "__frame->", PASS_RETURN, PHASE_MARSHAL); indent--; print_server("}\n"); print_server("RpcFinally\n"); print_server("{\n"); indent++; - - write_remoting_arguments(server, indent, func, PASS_OUT, PHASE_FREE); - - if (has_full_pointer) - write_full_pointer_free(server, indent, func); - + print_server("__finally_%s_%s( __frame );\n", iface->name, get_name(def)); indent--; print_server("}\n"); print_server("RpcEndFinally\n"); /* calculate buffer length */ fprintf(server, "\n"); - print_server("_pRpcMessage->BufferLength =\n"); - indent++; - print_server("(unsigned int)(_StubMsg.Buffer - (unsigned char *)_pRpcMessage->Buffer);\n"); - indent--; + print_server("_pRpcMessage->BufferLength = __frame->_StubMsg.Buffer - (unsigned char *)_pRpcMessage->Buffer;\n"); indent--; fprintf(server, "}\n"); fprintf(server, "\n"); @@ -370,62 +379,19 @@ static void init_server(void) fprintf(server, "\n"); print_server("#include \"%s\"\n", header_name); print_server("\n"); - print_server("#ifndef USE_COMPILER_EXCEPTIONS\n"); + write_exceptions( server ); print_server("\n"); - print_server("#include \"wine/exception.h\"\n"); - print_server("#undef RpcTryExcept\n"); - print_server("#undef RpcExcept\n"); - print_server("#undef RpcEndExcept\n"); - print_server("#undef RpcExceptionCode\n"); + print_server("struct __server_frame\n"); + print_server("{\n"); + print_server(" __DECL_EXCEPTION_FRAME;\n"); + print_server(" MIDL_STUB_MESSAGE _StubMsg;\n"); + print_server("};\n"); print_server("\n"); - print_server( "struct __server_frame\n"); + print_server("static int __server_filter( struct __server_frame *__frame )\n"); print_server( "{\n"); - print_server( " EXCEPTION_REGISTRATION_RECORD frame;\n"); - print_server( " sigjmp_buf jmp;\n"); - print_server( "};\n"); + print_server( " return RPC_BAD_STUB_DATA_EXCEPTION_FILTER;\n"); + print_server( "}\n"); print_server( "\n"); - print_server("static DWORD __server_exception_handler( EXCEPTION_RECORD *record,\n"); - print_server(" EXCEPTION_REGISTRATION_RECORD *frame,\n"); - print_server(" CONTEXT *context,\n"); - print_server(" EXCEPTION_REGISTRATION_RECORD **pdispatcher )\n"); - print_server("{\n"); - print_server(" struct __server_frame *server_frame = (struct __server_frame *)frame;\n"); - print_server("\n"); - print_server(" if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))\n"); - print_server(" return ExceptionContinueSearch;\n"); - print_server("\n"); - print_server(" if (record->ExceptionCode != STATUS_ACCESS_VIOLATION &&\n"); - print_server(" record->ExceptionCode != STATUS_DATATYPE_MISALIGNMENT &&\n"); - print_server(" record->ExceptionCode != RPC_X_BAD_STUB_DATA &&\n"); - print_server(" record->ExceptionCode != RPC_S_INVALID_BOUND)\n"); - print_server(" return ExceptionContinueSearch;\n"); - print_server("\n"); - print_server(" __wine_rtl_unwind( frame, record );\n"); - print_server(" __wine_pop_frame( frame );\n"); - print_server(" siglongjmp( server_frame->jmp, 1 );\n"); - print_server("}\n"); - print_server("#define RpcTryExcept \\\n"); - print_server(" do { \\\n"); - print_server(" struct __server_frame __server_frame; \\\n"); - print_server(" __server_frame.frame.Handler = __server_exception_handler; \\\n"); - print_server(" if (!sigsetjmp( __server_frame.jmp, 0 )) \\\n"); - print_server(" { \\\n"); - print_server(" __wine_push_frame( &__server_frame.frame ); \\\n"); - print_server(" {\n"); - print_server("\n"); - print_server("#define RpcExcept(expr) \\\n"); - print_server(" } \\\n"); - print_server(" __wine_pop_frame( &__server_frame.frame ); \\\n"); - print_server(" } \\\n"); - print_server(" else \\\n"); - print_server(" {\n"); - print_server("\n"); - print_server("#define RpcEndExcept \\\n"); - print_server(" } \\\n"); - print_server(" } while(0);\n"); - print_server("\n"); - print_server("#endif /* USE_COMPILER_EXCEPTIONS */\n"); - print_server("\n"); } diff --git a/tools/widl/typegen.c b/tools/widl/typegen.c index 3cc2fb998bc..5a6e59264b9 100644 --- a/tools/widl/typegen.c +++ b/tools/widl/typegen.c @@ -341,26 +341,30 @@ void print(FILE *file, int indent, const char *format, va_list va) } -static void write_var_init(FILE *file, int indent, const type_t *t, const char *n) +static void write_var_init(FILE *file, int indent, const type_t *t, const char *n, const char *local_var_prefix) { if (decl_indirect(t)) - print_file(file, indent, "MIDL_memset(&%s, 0, sizeof(%s));\n", n, n); + { + print_file(file, indent, "MIDL_memset(&%s%s, 0, sizeof(%s%s));\n", + local_var_prefix, n, local_var_prefix, n); + print_file(file, indent, "%s_p_%s = &%s%s;\n", local_var_prefix, n, local_var_prefix, n); + } else if (is_ptr(t) || is_array(t)) - print_file(file, indent, "%s = 0;\n", n); + print_file(file, indent, "%s%s = 0;\n", local_var_prefix, n); } -void write_parameters_init(FILE *file, int indent, const func_t *func) +void write_parameters_init(FILE *file, int indent, const func_t *func, const char *local_var_prefix) { const var_t *var; if (!is_void(get_func_return_type(func))) - write_var_init(file, indent, get_func_return_type(func), "_RetVal"); + write_var_init(file, indent, get_func_return_type(func), "_RetVal", local_var_prefix); if (!func->args) return; LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) - write_var_init(file, indent, var->type, var->name); + write_var_init(file, indent, var->type, var->name, local_var_prefix); fprintf(file, "\n"); } @@ -897,14 +901,14 @@ int is_full_pointer_function(const func_t *func) void write_full_pointer_init(FILE *file, int indent, const func_t *func, int is_server) { - print_file(file, indent, "_StubMsg.FullPtrXlatTables = NdrFullPointerXlatInit(0,%s);\n", + print_file(file, indent, "__frame->_StubMsg.FullPtrXlatTables = NdrFullPointerXlatInit(0,%s);\n", is_server ? "XLAT_SERVER" : "XLAT_CLIENT"); fprintf(file, "\n"); } void write_full_pointer_free(FILE *file, int indent, const func_t *func) { - print_file(file, indent, "NdrFullPointerXlatFree(_StubMsg.FullPtrXlatTables);\n"); + print_file(file, indent, "NdrFullPointerXlatFree(__frame->_StubMsg.FullPtrXlatTables);\n"); fprintf(file, "\n"); } @@ -2631,7 +2635,7 @@ static unsigned int get_function_buffer_size( const func_t *func, enum pass pass } static void print_phase_function(FILE *file, int indent, const char *type, - enum remoting_phase phase, + const char *local_var_prefix, enum remoting_phase phase, const var_t *var, unsigned int type_offset) { const char *function; @@ -2656,10 +2660,11 @@ static void print_phase_function(FILE *file, int indent, const char *type, print_file(file, indent, "Ndr%s%s(\n", type, function); indent++; - print_file(file, indent, "&_StubMsg,\n"); - print_file(file, indent, "%s%s%s%s,\n", + print_file(file, indent, "&__frame->_StubMsg,\n"); + print_file(file, indent, "%s%s%s%s%s,\n", (phase == PHASE_UNMARSHAL) ? "(unsigned char **)" : "(unsigned char *)", (phase == PHASE_UNMARSHAL || decl_indirect(var->type)) ? "&" : "", + local_var_prefix, (phase == PHASE_UNMARSHAL && decl_indirect(var->type)) ? "_p_" : "", var->name); print_file(file, indent, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]%s\n", @@ -2669,8 +2674,8 @@ static void print_phase_function(FILE *file, int indent, const char *type, indent--; } -void print_phase_basetype(FILE *file, int indent, enum remoting_phase phase, - enum pass pass, const var_t *var, +void print_phase_basetype(FILE *file, int indent, const char *local_var_prefix, + enum remoting_phase phase, enum pass pass, const var_t *var, const char *varname) { type_t *type = var->type; @@ -2728,8 +2733,8 @@ void print_phase_basetype(FILE *file, int indent, enum remoting_phase phase, } if (phase == PHASE_MARSHAL) - print_file(file, indent, "MIDL_memset(_StubMsg.Buffer, 0, (0x%x - (long)_StubMsg.Buffer) & 0x%x);\n", alignment, alignment - 1); - print_file(file, indent, "_StubMsg.Buffer = (unsigned char *)(((long)_StubMsg.Buffer + %u) & ~0x%x);\n", + print_file(file, indent, "MIDL_memset(__frame->_StubMsg.Buffer, 0, (0x%x - (long)__frame->_StubMsg.Buffer) & 0x%x);\n", alignment, alignment - 1); + print_file(file, indent, "__frame->_StubMsg.Buffer = (unsigned char *)(((long)__frame->_StubMsg.Buffer + %u) & ~0x%x);\n", alignment - 1, alignment - 1); if (phase == PHASE_MARSHAL) @@ -2737,17 +2742,17 @@ void print_phase_basetype(FILE *file, int indent, enum remoting_phase phase, print_file(file, indent, "*("); write_type_decl(file, is_ptr(type) ? type->ref : type, NULL); if (is_ptr(type)) - fprintf(file, " *)_StubMsg.Buffer = *"); + fprintf(file, " *)__frame->_StubMsg.Buffer = *"); else - fprintf(file, " *)_StubMsg.Buffer = "); - fprintf(file, "%s", varname); + fprintf(file, " *)__frame->_StubMsg.Buffer = "); + fprintf(file, "%s%s", local_var_prefix, varname); fprintf(file, ";\n"); } else if (phase == PHASE_UNMARSHAL) { - print_file(file, indent, "if (_StubMsg.Buffer + sizeof("); + print_file(file, indent, "if (__frame->_StubMsg.Buffer + sizeof("); write_type_decl(file, is_ptr(type) ? type->ref : type, NULL); - fprintf(file, ") > _StubMsg.BufferEnd)\n"); + fprintf(file, ") > __frame->_StubMsg.BufferEnd)\n"); print_file(file, indent, "{\n"); print_file(file, indent + 1, "RpcRaiseException(RPC_X_BAD_STUB_DATA);\n"); print_file(file, indent, "}\n"); @@ -2755,16 +2760,16 @@ void print_phase_basetype(FILE *file, int indent, enum remoting_phase phase, print_file(file, indent, ""); else print_file(file, indent, "*"); - fprintf(file, "%s", varname); + fprintf(file, "%s%s", local_var_prefix, varname); if (pass == PASS_IN && is_ptr(type)) fprintf(file, " = ("); else fprintf(file, " = *("); write_type_decl(file, is_ptr(type) ? type->ref : type, NULL); - fprintf(file, " *)_StubMsg.Buffer;\n"); + fprintf(file, " *)__frame->_StubMsg.Buffer;\n"); } - print_file(file, indent, "_StubMsg.Buffer += sizeof("); + print_file(file, indent, "__frame->_StubMsg.Buffer += sizeof("); write_type_decl(file, var->type, NULL); fprintf(file, ");\n"); } @@ -2794,9 +2799,8 @@ expr_t *get_size_is_expr(const type_t *t, const char *name) return x; } -static void write_parameter_conf_or_var_exprs(FILE *file, int indent, - enum remoting_phase phase, - const var_t *var) +static void write_parameter_conf_or_var_exprs(FILE *file, int indent, const char *local_var_prefix, + enum remoting_phase phase, const var_t *var) { const type_t *type = var->type; /* get fundamental type for the argument */ @@ -2812,16 +2816,16 @@ static void write_parameter_conf_or_var_exprs(FILE *file, int indent, { if (type->size_is) { - print_file(file, indent, "_StubMsg.MaxCount = (unsigned long)"); - write_expr(file, type->size_is, 1, 1, NULL, NULL); + print_file(file, indent, "__frame->_StubMsg.MaxCount = (unsigned long)"); + write_expr(file, type->size_is, 1, 1, NULL, NULL, local_var_prefix); fprintf(file, ";\n\n"); } if (type->length_is) { - print_file(file, indent, "_StubMsg.Offset = (unsigned long)0;\n"); /* FIXME */ - print_file(file, indent, "_StubMsg.ActualCount = (unsigned long)"); - write_expr(file, type->length_is, 1, 1, NULL, NULL); - fprintf(file, ";\n\n"); + print_file(file, indent, "__frame->_StubMsg.Offset = (unsigned long)0;\n"); /* FIXME */ + print_file(file, indent, "__frame->_StubMsg.ActualCount = (unsigned long)"); + write_expr(file, type->length_is, 1, 1, NULL, NULL, local_var_prefix); + fprintf(file, ";\n\n"); } } break; @@ -2830,8 +2834,8 @@ static void write_parameter_conf_or_var_exprs(FILE *file, int indent, { if (is_conformance_needed_for_phase(phase)) { - print_file(file, indent, "_StubMsg.MaxCount = (unsigned long)"); - write_expr(file, get_attrp(var->attrs, ATTR_SWITCHIS), 1, 1, NULL, NULL); + print_file(file, indent, "__frame->_StubMsg.MaxCount = (unsigned long)"); + write_expr(file, get_attrp(var->attrs, ATTR_SWITCHIS), 1, 1, NULL, NULL, local_var_prefix); fprintf(file, ";\n\n"); } break; @@ -2842,8 +2846,8 @@ static void write_parameter_conf_or_var_exprs(FILE *file, int indent, if (is_conformance_needed_for_phase(phase) && (iid = get_attrp( var->attrs, ATTR_IIDIS ))) { - print_file( file, indent, "_StubMsg.MaxCount = (unsigned long) " ); - write_expr( file, iid, 1, 1, NULL, NULL ); + print_file( file, indent, "__frame->_StubMsg.MaxCount = (unsigned long) " ); + write_expr( file, iid, 1, 1, NULL, NULL, local_var_prefix ); fprintf( file, ";\n\n" ); } break; @@ -2855,9 +2859,8 @@ static void write_parameter_conf_or_var_exprs(FILE *file, int indent, } } -static void write_remoting_arg(FILE *file, int indent, const func_t *func, - enum pass pass, enum remoting_phase phase, - const var_t *var) +static void write_remoting_arg(FILE *file, int indent, const func_t *func, const char *local_var_prefix, + enum pass pass, enum remoting_phase phase, const var_t *var) { int in_attr, out_attr, pointer_type; const type_t *type = var->type; @@ -2886,7 +2889,7 @@ static void write_remoting_arg(FILE *file, int indent, const func_t *func, break; } - write_parameter_conf_or_var_exprs(file, indent, phase, var); + write_parameter_conf_or_var_exprs(file, indent, local_var_prefix, phase, var); rtype = type->type; if (is_context_handle(type)) @@ -2900,15 +2903,15 @@ static void write_remoting_arg(FILE *file, int indent, const func_t *func, * be direct, otherwise it is a pointer */ int is_ch_ptr = is_aliaschain_attr(type, ATTR_CONTEXTHANDLE) ? FALSE : TRUE; print_file(file, indent, "NdrClientContextMarshall(\n"); - print_file(file, indent + 1, "&_StubMsg,\n"); - print_file(file, indent + 1, "(NDR_CCONTEXT)%s%s,\n", is_ch_ptr ? "*" : "", var->name); + print_file(file, indent + 1, "&__frame->_StubMsg,\n"); + print_file(file, indent + 1, "(NDR_CCONTEXT)%s%s%s,\n", is_ch_ptr ? "*" : "", local_var_prefix, var->name); print_file(file, indent + 1, "%s);\n", in_attr && out_attr ? "1" : "0"); } else { print_file(file, indent, "NdrServerContextNewMarshall(\n"); - print_file(file, indent + 1, "&_StubMsg,\n"); - print_file(file, indent + 1, "(NDR_SCONTEXT)%s,\n", var->name); + print_file(file, indent + 1, "&__frame->_StubMsg,\n"); + print_file(file, indent + 1, "(NDR_SCONTEXT)%s%s,\n", local_var_prefix, var->name); print_file(file, indent + 1, "(NDR_RUNDOWN)%s_rundown,\n", get_context_handle_type_name(var->type)); print_file(file, indent + 1, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]);\n", start_offset); } @@ -2918,36 +2921,37 @@ static void write_remoting_arg(FILE *file, int indent, const func_t *func, if (pass == PASS_OUT) { if (!in_attr) - print_file(file, indent, "*%s = 0;\n", var->name); + print_file(file, indent, "*%s%s = 0;\n", local_var_prefix, var->name); print_file(file, indent, "NdrClientContextUnmarshall(\n"); - print_file(file, indent + 1, "&_StubMsg,\n"); - print_file(file, indent + 1, "(NDR_CCONTEXT *)%s,\n", var->name); - print_file(file, indent + 1, "_Handle);\n"); + print_file(file, indent + 1, "&__frame->_StubMsg,\n"); + print_file(file, indent + 1, "(NDR_CCONTEXT *)%s%s,\n", local_var_prefix, var->name); + print_file(file, indent + 1, "__frame->_Handle);\n"); } else { - print_file(file, indent, "%s = NdrServerContextNewUnmarshall(\n", var->name); - print_file(file, indent + 1, "&_StubMsg,\n"); + print_file(file, indent, "%s%s = NdrServerContextNewUnmarshall(\n", local_var_prefix, var->name); + print_file(file, indent + 1, "&__frame->_StubMsg,\n"); print_file(file, indent + 1, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]);\n", start_offset); } } } else if (is_user_type(var->type)) { - print_phase_function(file, indent, "UserMarshal", phase, var, start_offset); + print_phase_function(file, indent, "UserMarshal", local_var_prefix, phase, var, start_offset); } else if (is_string_type(var->attrs, var->type)) { if (is_array(type) && !is_conformant_array(type)) - print_phase_function(file, indent, "NonConformantString", phase, var, start_offset); + print_phase_function(file, indent, "NonConformantString", local_var_prefix, + phase, var, start_offset); else { if (phase == PHASE_FREE || pass == PASS_RETURN || pointer_type == RPC_FC_UP) - print_phase_function(file, indent, "Pointer", phase, var, + print_phase_function(file, indent, "Pointer", local_var_prefix, phase, var, start_offset - (type->size_is ? 4 : 2)); else - print_phase_function(file, indent, "ConformantString", phase, var, - start_offset); + print_phase_function(file, indent, "ConformantString", local_var_prefix, + phase, var, start_offset); } } else if (is_array(type)) @@ -2978,7 +2982,7 @@ static void write_remoting_arg(FILE *file, int indent, const func_t *func, } if (pointer_type != RPC_FC_RP) array_type = "Pointer"; - print_phase_function(file, indent, array_type, phase, var, start_offset); + print_phase_function(file, indent, array_type, local_var_prefix, phase, var, start_offset); if (phase == PHASE_FREE && pointer_type == RPC_FC_RP) { /* these are all unmarshalled by allocating memory */ @@ -2987,16 +2991,16 @@ static void write_remoting_arg(FILE *file, int indent, const func_t *func, ((type->type == RPC_FC_SMVARRAY || type->type == RPC_FC_LGVARRAY) && in_attr) || (type->type == RPC_FC_CARRAY && !in_attr)) { - print_file(file, indent, "if (%s)\n", var->name); + print_file(file, indent, "if (%s%s)\n", local_var_prefix, var->name); indent++; - print_file(file, indent, "_StubMsg.pfnFree(%s);\n", var->name); + print_file(file, indent, "__frame->_StubMsg.pfnFree(%s%s);\n", local_var_prefix, var->name); } } } else if (!is_ptr(var->type) && is_base_type(rtype)) { if (phase != PHASE_FREE) - print_phase_basetype(file, indent, phase, pass, var, var->name); + print_phase_basetype(file, indent, local_var_prefix, phase, pass, var, var->name); } else if (!is_ptr(var->type)) { @@ -3004,38 +3008,38 @@ static void write_remoting_arg(FILE *file, int indent, const func_t *func, { case RPC_FC_STRUCT: case RPC_FC_PSTRUCT: - print_phase_function(file, indent, "SimpleStruct", phase, var, start_offset); + print_phase_function(file, indent, "SimpleStruct", local_var_prefix, phase, var, start_offset); break; case RPC_FC_CSTRUCT: case RPC_FC_CPSTRUCT: - print_phase_function(file, indent, "ConformantStruct", phase, var, start_offset); + print_phase_function(file, indent, "ConformantStruct", local_var_prefix, phase, var, start_offset); break; case RPC_FC_CVSTRUCT: - print_phase_function(file, indent, "ConformantVaryingStruct", phase, var, start_offset); + print_phase_function(file, indent, "ConformantVaryingStruct", local_var_prefix, phase, var, start_offset); break; case RPC_FC_BOGUS_STRUCT: - print_phase_function(file, indent, "ComplexStruct", phase, var, start_offset); + print_phase_function(file, indent, "ComplexStruct", local_var_prefix, phase, var, start_offset); break; case RPC_FC_RP: if (is_base_type( var->type->ref->type )) { - print_phase_basetype(file, indent, phase, pass, var, var->name); + print_phase_basetype(file, indent, local_var_prefix, phase, pass, var, var->name); } else if (var->type->ref->type == RPC_FC_STRUCT) { if (phase != PHASE_BUFFERSIZE && phase != PHASE_FREE) - print_phase_function(file, indent, "SimpleStruct", phase, var, start_offset + 4); + print_phase_function(file, indent, local_var_prefix, "SimpleStruct", phase, var, start_offset + 4); } else { expr_t *iid; if ((iid = get_attrp( var->attrs, ATTR_IIDIS ))) { - print_file( file, indent, "_StubMsg.MaxCount = (unsigned long) " ); - write_expr( file, iid, 1, 1, NULL, NULL ); + print_file( file, indent, "__frame->_StubMsg.MaxCount = (unsigned long) " ); + write_expr( file, iid, 1, 1, NULL, NULL, local_var_prefix ); fprintf( file, ";\n\n" ); } - print_phase_function(file, indent, "Pointer", phase, var, start_offset); + print_phase_function(file, indent, "Pointer", local_var_prefix, phase, var, start_offset); } break; default: @@ -3047,31 +3051,31 @@ static void write_remoting_arg(FILE *file, int indent, const func_t *func, if (last_ptr(var->type) && (pointer_type == RPC_FC_RP) && is_base_type(rtype)) { if (phase != PHASE_FREE) - print_phase_basetype(file, indent, phase, pass, var, var->name); + print_phase_basetype(file, indent, local_var_prefix, phase, pass, var, var->name); } else if (last_ptr(var->type) && (pointer_type == RPC_FC_RP) && (rtype == RPC_FC_STRUCT)) { if (phase != PHASE_BUFFERSIZE && phase != PHASE_FREE) - print_phase_function(file, indent, "SimpleStruct", phase, var, start_offset + 4); + print_phase_function(file, indent, "SimpleStruct", local_var_prefix, phase, var, start_offset + 4); } else { if (var->type->ref->type == RPC_FC_IP) - print_phase_function(file, indent, "InterfacePointer", phase, var, start_offset); + print_phase_function(file, indent, "InterfacePointer", local_var_prefix, phase, var, start_offset); else - print_phase_function(file, indent, "Pointer", phase, var, start_offset); + print_phase_function(file, indent, "Pointer", local_var_prefix, phase, var, start_offset); } } fprintf(file, "\n"); } -void write_remoting_arguments(FILE *file, int indent, const func_t *func, +void write_remoting_arguments(FILE *file, int indent, const func_t *func, const char *local_var_prefix, enum pass pass, enum remoting_phase phase) { if (phase == PHASE_BUFFERSIZE && pass != PASS_RETURN) { unsigned int size = get_function_buffer_size( func, pass ); - print_file(file, indent, "_StubMsg.BufferLength = %u;\n", size); + print_file(file, indent, "__frame->_StubMsg.BufferLength = %u;\n", size); } if (pass == PASS_RETURN) @@ -3080,7 +3084,7 @@ void write_remoting_arguments(FILE *file, int indent, const func_t *func, var = *func->def; var.type = get_func_return_type(func); var.name = xstrdup( "_RetVal" ); - write_remoting_arg( file, indent, func, pass, phase, &var ); + write_remoting_arg( file, indent, func, local_var_prefix, pass, phase, &var ); free( var.name ); } else @@ -3089,7 +3093,7 @@ void write_remoting_arguments(FILE *file, int indent, const func_t *func, if (!func->args) return; LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry ) - write_remoting_arg( file, indent, func, pass, phase, var ); + write_remoting_arg( file, indent, func, local_var_prefix, pass, phase, var ); } } @@ -3196,21 +3200,20 @@ void declare_stub_args( FILE *file, int indent, const func_t *func ) write_type_decl_left(file, var->type); fprintf(file, " "); if (var->type->declarray) { - fprintf(file, "(*%s)", get_name(var)); + fprintf(file, "(*%s)", var->name); } else - fprintf(file, "%s", get_name(var)); + fprintf(file, "%s", var->name); write_type_right(file, var->type, FALSE); fprintf(file, ";\n"); if (decl_indirect(var->type)) - print_file(file, indent, "void *_p_%s = &%s;\n", - var->name, var->name); + print_file(file, indent, "void *_p_%s;\n", var->name); } } } -void assign_stub_out_args( FILE *file, int indent, const func_t *func ) +void assign_stub_out_args( FILE *file, int indent, const func_t *func, const char *local_var_prefix ) { int in_attr, out_attr; int i = 0, sep = 0; @@ -3229,12 +3232,12 @@ void assign_stub_out_args( FILE *file, int indent, const func_t *func ) if (!in_attr) { - print_file(file, indent, "%s", get_name(var)); + print_file(file, indent, "%s%s", local_var_prefix, var->name); if (is_context_handle(var->type)) { fprintf(file, " = NdrContextHandleInitialize(\n"); - print_file(file, indent + 1, "&_StubMsg,\n"); + print_file(file, indent + 1, "&__frame->_StubMsg,\n"); print_file(file, indent + 1, "(PFORMAT_STRING)&__MIDL_TypeFormatString.Format[%d]);\n", var->type->typestring_offset); } @@ -3243,10 +3246,10 @@ void assign_stub_out_args( FILE *file, int indent, const func_t *func ) unsigned int size, align = 0; type_t *type = var->type; - fprintf(file, " = NdrAllocate(&_StubMsg, "); + fprintf(file, " = NdrAllocate(&__frame->_StubMsg, "); for ( ; type->size_is ; type = type->ref) { - write_expr(file, type->size_is, TRUE, TRUE, NULL, NULL); + write_expr(file, type->size_is, TRUE, TRUE, NULL, NULL, local_var_prefix); fprintf(file, " * "); } size = type_memsize(type, &align); @@ -3254,9 +3257,9 @@ void assign_stub_out_args( FILE *file, int indent, const func_t *func ) } else if (!is_string) { - fprintf(file, " = &_W%u;\n", i); + fprintf(file, " = &%s_W%u;\n", local_var_prefix, i); if (is_ptr(var->type) && !last_ptr(var->type)) - print_file(file, indent, "_W%u = 0;\n", i); + print_file(file, indent, "%s_W%u = 0;\n", local_var_prefix, i); i++; } @@ -3288,7 +3291,7 @@ int write_expr_eval_routines(FILE *file, const char *iface) name, var_name, name, eval->baseoff); print_file(file, 1, "pStubMsg->Offset = 0;\n"); /* FIXME */ print_file(file, 1, "pStubMsg->MaxCount = (unsigned long)"); - write_expr(file, eval->expr, 1, 1, var_name_expr, eval->structure); + write_expr(file, eval->expr, 1, 1, var_name_expr, eval->structure, ""); fprintf(file, ";\n"); print_file(file, 0, "}\n\n"); callback_offset++; @@ -3372,3 +3375,117 @@ void write_endpoints( FILE *f, const char *prefix, const str_list_t *list ) error: error("Invalid endpoint syntax '%s'\n", endpoint->str); } + +void write_exceptions( FILE *file ) +{ + fprintf( file, "#ifndef USE_COMPILER_EXCEPTIONS\n"); + fprintf( file, "\n"); + fprintf( file, "#include \"wine/exception.h\"\n"); + fprintf( file, "#undef RpcTryExcept\n"); + fprintf( file, "#undef RpcExcept\n"); + fprintf( file, "#undef RpcEndExcept\n"); + fprintf( file, "#undef RpcTryFinally\n"); + fprintf( file, "#undef RpcFinally\n"); + fprintf( file, "#undef RpcEndFinally\n"); + fprintf( file, "#undef RpcExceptionCode\n"); + fprintf( file, "#undef RpcAbnormalTermination\n"); + fprintf( file, "\n"); + fprintf( file, "struct __exception_frame;\n"); + fprintf( file, "typedef int (*__filter_func)(EXCEPTION_RECORD *, struct __exception_frame *);\n"); + fprintf( file, "typedef void (*__finally_func)(struct __exception_frame *);\n"); + fprintf( file, "\n"); + fprintf( file, "#define __DECL_EXCEPTION_FRAME \\\n"); + fprintf( file, " EXCEPTION_REGISTRATION_RECORD frame; \\\n"); + fprintf( file, " __filter_func filter; \\\n"); + fprintf( file, " __finally_func finally; \\\n"); + fprintf( file, " sigjmp_buf jmp; \\\n"); + fprintf( file, " DWORD code; \\\n"); + fprintf( file, " unsigned char abnormal_termination; \\\n"); + fprintf( file, " unsigned char filter_level; \\\n"); + fprintf( file, " unsigned char finally_level;\n"); + fprintf( file, "\n"); + fprintf( file, "struct __exception_frame\n{\n"); + fprintf( file, " __DECL_EXCEPTION_FRAME;\n"); + fprintf( file, "};\n"); + fprintf( file, "\n"); + fprintf( file, "static DWORD __widl_exception_handler( EXCEPTION_RECORD *record,\n"); + fprintf( file, " EXCEPTION_REGISTRATION_RECORD *frame,\n"); + fprintf( file, " CONTEXT *context,\n"); + fprintf( file, " EXCEPTION_REGISTRATION_RECORD **pdispatcher )\n"); + fprintf( file, "{\n"); + fprintf( file, " struct __exception_frame *exc_frame = (struct __exception_frame *)frame;\n"); + fprintf( file, "\n"); + fprintf( file, " if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))\n"); + fprintf( file, " {\n" ); + fprintf( file, " if (exc_frame->finally_level && (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))\n"); + fprintf( file, " {\n" ); + fprintf( file, " exc_frame->abnormal_termination = 1;\n"); + fprintf( file, " exc_frame->finally( exc_frame );\n"); + fprintf( file, " }\n" ); + fprintf( file, " return ExceptionContinueSearch;\n"); + fprintf( file, " }\n" ); + fprintf( file, " exc_frame->code = record->ExceptionCode;\n"); + fprintf( file, " if (exc_frame->filter_level && exc_frame->filter( record, exc_frame ) == EXCEPTION_EXECUTE_HANDLER)\n" ); + fprintf( file, " {\n"); + fprintf( file, " __wine_rtl_unwind( frame, record );\n"); + fprintf( file, " if (exc_frame->finally_level > exc_frame->filter_level)\n" ); + fprintf( file, " {\n"); + fprintf( file, " exc_frame->abnormal_termination = 1;\n"); + fprintf( file, " exc_frame->finally( exc_frame );\n"); + fprintf( file, " __wine_pop_frame( frame );\n"); + fprintf( file, " }\n"); + fprintf( file, " exc_frame->filter_level = 0;\n"); + fprintf( file, " siglongjmp( exc_frame->jmp, 1 );\n"); + fprintf( file, " }\n"); + fprintf( file, " return ExceptionContinueSearch;\n"); + fprintf( file, "}\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcTryExcept \\\n"); + fprintf( file, " if (!sigsetjmp( __frame->jmp, 0 )) \\\n"); + fprintf( file, " { \\\n"); + fprintf( file, " if (!__frame->finally_level) \\\n" ); + fprintf( file, " __wine_push_frame( &__frame->frame ); \\\n"); + fprintf( file, " __frame->filter_level = __frame->finally_level + 1;\n" ); + fprintf( file, "\n"); + fprintf( file, "#define RpcExcept(expr) \\\n"); + fprintf( file, " if (!__frame->finally_level) \\\n" ); + fprintf( file, " __wine_pop_frame( &__frame->frame ); \\\n"); + fprintf( file, " __frame->filter_level = 0; \\\n" ); + fprintf( file, " } \\\n"); + fprintf( file, " else \\\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcEndExcept\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcExceptionCode() (__frame->code)\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcTryFinally \\\n"); + fprintf( file, " if (!__frame->filter_level) \\\n"); + fprintf( file, " __wine_push_frame( &__frame->frame ); \\\n"); + fprintf( file, " __frame->finally_level = __frame->filter_level + 1;\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcFinally \\\n"); + fprintf( file, " if (!__frame->filter_level) \\\n"); + fprintf( file, " __wine_pop_frame( &__frame->frame ); \\\n"); + fprintf( file, " __frame->finally_level = 0;\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcEndFinally\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcAbnormalTermination() (__frame->abnormal_termination)\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcExceptionInit(filter_func,finally_func) \\\n"); + fprintf( file, " do { \\\n"); + fprintf( file, " __frame->frame.Handler = __widl_exception_handler; \\\n"); + fprintf( file, " __frame->filter = (__filter_func)(filter_func); \\\n" ); + fprintf( file, " __frame->finally = (__finally_func)(finally_func); \\\n"); + fprintf( file, " __frame->abnormal_termination = 0; \\\n"); + fprintf( file, " __frame->filter_level = 0; \\\n"); + fprintf( file, " __frame->finally_level = 0; \\\n"); + fprintf( file, " } while (0)\n"); + fprintf( file, "\n"); + fprintf( file, "#else /* USE_COMPILER_EXCEPTIONS */\n"); + fprintf( file, "\n"); + fprintf( file, "#define RpcExceptionInit(filter_func,finally_func) do {} while(0)\n"); + fprintf( file, "#define __DECL_EXCEPTION_FRAME\n"); + fprintf( file, "\n"); + fprintf( file, "#endif /* USE_COMPILER_EXCEPTIONS */\n"); +} diff --git a/tools/widl/typegen.h b/tools/widl/typegen.h index a1013cb37f8..0276b7fa433 100644 --- a/tools/widl/typegen.h +++ b/tools/widl/typegen.h @@ -41,21 +41,24 @@ typedef int (*type_pred_t)(const type_t *); void write_formatstringsdecl(FILE *f, int indent, const statement_list_t *stmts, type_pred_t pred); void write_procformatstring(FILE *file, const statement_list_t *stmts, type_pred_t pred); void write_typeformatstring(FILE *file, const statement_list_t *stmts, type_pred_t pred); -void print_phase_basetype(FILE *file, int indent, enum remoting_phase phase, enum pass pass, const var_t *var, const char *varname); -void write_remoting_arguments(FILE *file, int indent, const func_t *func, enum pass pass, enum remoting_phase phase); +void print_phase_basetype(FILE *file, int indent, const char *local_var_prefix, enum remoting_phase phase, + enum pass pass, const var_t *var, const char *varname); +void write_remoting_arguments(FILE *file, int indent, const func_t *func, const char *local_var_prefix, + enum pass pass, enum remoting_phase phase); size_t get_size_procformatstring_type(const char *name, const type_t *type, const attr_list_t *attrs); size_t get_size_procformatstring_func(const func_t *func); size_t get_size_procformatstring(const statement_list_t *stmts, type_pred_t pred); size_t get_size_typeformatstring(const statement_list_t *stmts, type_pred_t pred); -void assign_stub_out_args( FILE *file, int indent, const func_t *func ); +void assign_stub_out_args( FILE *file, int indent, const func_t *func, const char *local_var_prefix ); void declare_stub_args( FILE *file, int indent, const func_t *func ); int write_expr_eval_routines(FILE *file, const char *iface); void write_expr_eval_routine_list(FILE *file, const char *iface); void write_user_quad_list(FILE *file); void write_endpoints( FILE *f, const char *prefix, const str_list_t *list ); +void write_exceptions( FILE *file ); size_t type_memsize(const type_t *t, unsigned int *align); int decl_indirect(const type_t *t); -void write_parameters_init(FILE *file, int indent, const func_t *func); +void write_parameters_init(FILE *file, int indent, const func_t *func, const char *local_var_prefix); void print(FILE *file, int indent, const char *format, va_list ap); int get_padding(const var_list_t *fields); int is_user_type(const type_t *t); -- 2.11.4.GIT