Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This patch series will implement some of the dynamic TypeInfo for the script's dispatch, that inherits from IDispatch. It's necessary to do it this way since the members are retrieved from the script itself. Note that even the member IDs on Windows have the upper 16 bits zero, unlike normal typelibs (i.e. they start from 1, so this patch series will do something similar to Windows)
Implementing the TypeInfo is necessary even for Wine's own msscript.ocx (for example, to implement the Procedures property with tests) or to have native msscript work properly.
More patches to implement the remaining methods will come after this patch set (I didn't want to flood the mailing list).
dlls/vbscript/vbdisp.c | 270 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 268 insertions(+), 2 deletions(-)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index fb646e7..f3385ae 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -700,6 +700,267 @@ static ident_map_t *add_ident(ScriptDisp *This, const WCHAR *name) return ret; }
+typedef struct { + ITypeInfo ITypeInfo_iface; + LONG ref; +} ScriptTypeInfo; + +static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface) +{ + return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeInfo_iface); +} + +static HRESULT WINAPI ScriptTypeInfo_QueryInterface(ITypeInfo *iface, REFIID riid, void **ppv) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ITypeInfo, riid)) + *ppv = &This->ITypeInfo_iface; + else + { + WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI ScriptTypeInfo_AddRef(ITypeInfo *iface) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if (!ref) + { + heap_free(This); + } + return ref; +} + +static HRESULT WINAPI ScriptTypeInfo_GetTypeAttr(ITypeInfo *iface, TYPEATTR **ppTypeAttr) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, ppTypeAttr); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **ppTComp) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, ppTComp); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, FUNCDESC **ppFuncDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, ppFuncDesc); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetVarDesc(ITypeInfo *iface, UINT index, VARDESC **ppVarDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, ppVarDesc); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetNames(ITypeInfo *iface, MEMBERID memid, BSTR *rgBstrNames, + UINT cMaxNames, UINT *pcNames) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %p %u %p)\n", This, memid, rgBstrNames, cMaxNames, pcNames); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetRefTypeOfImplType(ITypeInfo *iface, UINT index, HREFTYPE *pRefType) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, pRefType); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetImplTypeFlags(ITypeInfo *iface, UINT index, INT *pImplTypeFlags) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, pImplTypeFlags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetIDsOfNames(ITypeInfo *iface, LPOLESTR *rgszNames, UINT cNames, + MEMBERID *pMemId) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %u %p)\n", This, rgszNames, cNames, pMemId); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_Invoke(ITypeInfo *iface, PVOID pvInstance, MEMBERID memid, WORD wFlags, + DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %d %d %p %p %p %p)\n", This, pvInstance, memid, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetDocumentation(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrName, + BSTR *pBstrDocString, DWORD *pdwHelpContext, BSTR *pBstrHelpFile) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %p %p %p %p)\n", This, memid, pBstrName, pBstrDocString, pdwHelpContext, pBstrHelpFile); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetDllEntry(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind, + BSTR *pBstrDllName, BSTR *pBstrName, WORD *pwOrdinal) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %d %p %p %p)\n", This, memid, invKind, pBstrDllName, pBstrName, pwOrdinal); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetRefTypeInfo(ITypeInfo *iface, HREFTYPE hRefType, ITypeInfo **ppTInfo) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%x %p)\n", This, hRefType, ppTInfo); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_AddressOfMember(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind, PVOID *ppv) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %d %p)\n", This, memid, invKind, ppv); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_CreateInstance(ITypeInfo *iface, IUnknown *pUnkOuter, REFIID riid, PVOID *ppvObj) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %s %p)\n", This, pUnkOuter, debugstr_guid(riid), ppvObj); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetMops(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrMops) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %p)\n", This, memid, pBstrMops); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetContainingTypeLib(ITypeInfo *iface, ITypeLib **ppTLib, UINT *pIndex) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %p)\n", This, ppTLib, pIndex); + + return E_NOTIMPL; +} + +static void WINAPI ScriptTypeInfo_ReleaseTypeAttr(ITypeInfo *iface, TYPEATTR *pTypeAttr) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, pTypeAttr); +} + +static void WINAPI ScriptTypeInfo_ReleaseFuncDesc(ITypeInfo *iface, FUNCDESC *pFuncDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, pFuncDesc); +} + +static void WINAPI ScriptTypeInfo_ReleaseVarDesc(ITypeInfo *iface, VARDESC *pVarDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, pVarDesc); +} + +static const ITypeInfoVtbl ScriptTypeInfoVtbl = { + ScriptTypeInfo_QueryInterface, + ScriptTypeInfo_AddRef, + ScriptTypeInfo_Release, + ScriptTypeInfo_GetTypeAttr, + ScriptTypeInfo_GetTypeComp, + ScriptTypeInfo_GetFuncDesc, + ScriptTypeInfo_GetVarDesc, + ScriptTypeInfo_GetNames, + ScriptTypeInfo_GetRefTypeOfImplType, + ScriptTypeInfo_GetImplTypeFlags, + ScriptTypeInfo_GetIDsOfNames, + ScriptTypeInfo_Invoke, + ScriptTypeInfo_GetDocumentation, + ScriptTypeInfo_GetDllEntry, + ScriptTypeInfo_GetRefTypeInfo, + ScriptTypeInfo_AddressOfMember, + ScriptTypeInfo_CreateInstance, + ScriptTypeInfo_GetMops, + ScriptTypeInfo_GetContainingTypeLib, + ScriptTypeInfo_ReleaseTypeAttr, + ScriptTypeInfo_ReleaseFuncDesc, + ScriptTypeInfo_ReleaseVarDesc +}; + +static ITypeInfo *create_script_typeinfo(ScriptDisp *disp) +{ + ScriptTypeInfo *p; + + if (!(p = heap_alloc(sizeof(*p)))) + return NULL; + + p->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl; + p->ref = 1; + + return &p->ITypeInfo_iface; +} + static inline ScriptDisp *ScriptDisp_from_IDispatchEx(IDispatchEx *iface) { return CONTAINING_RECORD(iface, ScriptDisp, IDispatchEx_iface); @@ -768,8 +1029,13 @@ static HRESULT WINAPI ScriptDisp_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LC ITypeInfo **ppTInfo) { ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); - FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); - return E_NOTIMPL; + + TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + + if (iTInfo != 0) return DISP_E_BADINDEX; + + *ppTInfo = create_script_typeinfo(This); + return *ppTInfo ? S_OK : E_OUTOFMEMORY; }
static HRESULT WINAPI ScriptDisp_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This will be needed for next patch to build the TypeInfo properly, as the DISPIDs have to match with MEMBERIDs.
dlls/vbscript/compile.c | 2 ++ dlls/vbscript/interp.c | 1 + dlls/vbscript/vbdisp.c | 38 ++++++++++++++++++++++++++++---------- dlls/vbscript/vbscript.h | 2 ++ 4 files changed, 33 insertions(+), 10 deletions(-)
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index b2be38d..06d293e 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -1424,6 +1424,7 @@ static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *f
V_VT(&new_var->v) = VT_EMPTY; new_var->is_const = FALSE; + new_var->dispid = 0;
new_var->next = ctx->global_vars; ctx->global_vars = new_var; @@ -1497,6 +1498,7 @@ static HRESULT create_function(compile_ctx_t *ctx, function_decl_t *decl, functi if(!func->name) return E_OUTOFMEMORY;
+ func->dispid = 0; func->vars = NULL; func->var_cnt = 0; func->array_cnt = 0; diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 8649164..0fe0fb9 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -233,6 +233,7 @@ static HRESULT add_dynamic_var(exec_ctx_t *ctx, const WCHAR *name, memcpy(str, name, size); new_var->name = str; new_var->is_const = is_const; + new_var->dispid = 0; V_VT(&new_var->v) = VT_EMPTY;
if(ctx->func->type == FUNC_GLOBAL) { diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index f3385ae..c0535f1 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -700,6 +700,30 @@ static ident_map_t *add_ident(ScriptDisp *This, const WCHAR *name) return ret; }
+static ident_map_t *add_var_ident(ScriptDisp *This, dynamic_var_t *var) +{ + ident_map_t *ident = add_ident(This, var->name); + if (ident) + { + ident->is_var = TRUE; + ident->u.var = var; + var->dispid = ident_to_id(This, ident); + } + return ident; +} + +static ident_map_t *add_func_ident(ScriptDisp *This, function_t *func) +{ + ident_map_t *ident = add_ident(This, func->name); + if (ident) + { + ident->is_var = FALSE; + ident->u.func = func; + func->dispid = ident_to_id(This, ident); + } + return ident; +} + typedef struct { ITypeInfo ITypeInfo_iface; LONG ref; @@ -1090,26 +1114,20 @@ static HRESULT WINAPI ScriptDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DW
for(var = This->ctx->global_vars; var; var = var->next) { if(!wcsicmp(var->name, bstrName)) { - ident = add_ident(This, var->name); - if(!ident) + if(!add_var_ident(This, var)) return E_OUTOFMEMORY;
- ident->is_var = TRUE; - ident->u.var = var; - *pid = ident_to_id(This, ident); + *pid = var->dispid; return S_OK; } }
for(func = This->ctx->global_funcs; func; func = func->next) { if(!wcsicmp(func->name, bstrName)) { - ident = add_ident(This, func->name); - if(!ident) + if(!add_func_ident(This, func)) return E_OUTOFMEMORY;
- ident->is_var = FALSE; - ident->u.func = func; - *pid = ident_to_id(This, ident); + *pid = func->dispid; return S_OK; } } diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 1d9353a..654e827 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -174,6 +174,7 @@ typedef struct _dynamic_var_t { VARIANT v; const WCHAR *name; BOOL is_const; + DISPID dispid; } dynamic_var_t;
struct _script_ctx_t { @@ -322,6 +323,7 @@ typedef struct { } var_desc_t;
struct _function_t { + DISPID dispid; function_type_t type; const WCHAR *name; BOOL is_public;
Hi Gabriel,
On 10/15/19 4:54 PM, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescugabrielopcode@gmail.com
This will be needed for next patch to build the TypeInfo properly, as the DISPIDs have to match with MEMBERIDs.
Storing it like that indeed seems to make sense. However, if we're going to do that, we should probably calculate those DISPIDs in the compiler and get rid of ident_map_t.
Thanks,
Jacek
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
The TypeInfo is built when it is retrieved, even if the script changes after that and more stuff is added to it (tests at the end of the patch series will show this). This builds most of the necessary information for subsequent patches.
dlls/vbscript/vbdisp.c | 106 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index c0535f1..fea82c1 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -26,6 +26,16 @@ WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
#define FDEX_VERSION_MASK 0xf0000000
+static WCHAR *copy_string_to_heap_pool(const WCHAR *str, heap_pool_t *heap) +{ + size_t size = (lstrlenW(str) + 1) * sizeof(WCHAR); + WCHAR *ret; + + if ((ret = heap_pool_alloc(heap, size))) + memcpy(ret, str, size); + return ret; +} + static inline BOOL is_func_id(vbdisp_t *This, DISPID id) { return id < This->desc->func_cnt; @@ -724,9 +734,29 @@ static ident_map_t *add_func_ident(ScriptDisp *This, function_t *func) return ident; }
+struct script_typeinfo_var { + const WCHAR *name; + MEMBERID memid; +}; + +struct script_typeinfo_func { + const WCHAR *name; + const WCHAR **arg_names; + MEMBERID memid; + VARTYPE ret_type; + WORD num_args; +}; + typedef struct { ITypeInfo ITypeInfo_iface; LONG ref; + + UINT num_funcs; + UINT num_vars; + struct script_typeinfo_func *funcs; + struct script_typeinfo_var *vars; + + heap_pool_t heap; } ScriptTypeInfo;
static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface) @@ -771,6 +801,7 @@ static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface)
if (!ref) { + heap_pool_free(&This->heap); heap_free(This); } return ref; @@ -974,15 +1005,90 @@ static const ITypeInfoVtbl ScriptTypeInfoVtbl = {
static ITypeInfo *create_script_typeinfo(ScriptDisp *disp) { + struct script_typeinfo_func *typefunc; + struct script_typeinfo_var *typevar; + UINT num_funcs = 0, num_vars = 0; + dynamic_var_t *var; + function_t *func; ScriptTypeInfo *p; + BOOL add_const;
+ /* Populate the identifiers to get the DISPIDs which have to match the MEMBERIDs */ + for (var = disp->ctx->global_vars; var; var = var->next) + { + if (!var->dispid && !add_var_ident(disp, var)) + return NULL; + num_vars++; + } + + for (func = disp->ctx->global_funcs; func; func = func->next) + { + if (!func->is_public) continue; + if (!func->dispid && !add_func_ident(disp, func)) + return NULL; + num_funcs++; + } + + /* Now we can allocate the typeinfo */ if (!(p = heap_alloc(sizeof(*p)))) return NULL;
p->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl; p->ref = 1; + p->num_vars = num_vars; + p->num_funcs = num_funcs; + heap_pool_init(&p->heap); + + p->funcs = heap_pool_alloc(&p->heap, sizeof(*p->funcs) * num_funcs); + if (!p->funcs) goto fail; + p->vars = heap_pool_alloc(&p->heap, sizeof(*p->vars) * num_vars); + if (!p->vars) goto fail; + + /* Const variables must be placed after the other variables, + and the order for all reversed compared to the original. */ + typevar = p->vars + num_vars - 1; + add_const = TRUE; + for (;;) + { + for (var = disp->ctx->global_vars; var; var = var->next) + { + if (var->is_const != add_const) continue; + if (!(typevar->name = copy_string_to_heap_pool(var->name, &p->heap))) + goto fail; + typevar->memid = var->dispid; + typevar--; + } + if (!add_const) break; + add_const = FALSE; + } + + for (typefunc = p->funcs, func = disp->ctx->global_funcs; func; func = func->next) + { + UINT i; + + if (!func->is_public) continue; + if (!(typefunc->name = copy_string_to_heap_pool(func->name, &p->heap))) + goto fail; + + typefunc->arg_names = heap_pool_alloc(&p->heap, func->arg_cnt * sizeof(WCHAR*)); + if (!typefunc->arg_names) + goto fail; + for (i = 0; i < func->arg_cnt; i++) + if (!(typefunc->arg_names[i] = copy_string_to_heap_pool(func->args[i].name, &p->heap))) + goto fail; + + typefunc->memid = func->dispid; + typefunc->num_args = func->arg_cnt; + typefunc->ret_type = (func->type == FUNC_SUB) ? VT_VOID : VT_VARIANT; + typefunc++; + }
return &p->ITypeInfo_iface; + +fail: + heap_pool_free(&p->heap); + heap_free(p); + return NULL; }
static inline ScriptDisp *ScriptDisp_from_IDispatchEx(IDispatchEx *iface)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/vbdisp.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index fea82c1..297524d 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -19,11 +19,14 @@ #include <assert.h>
#include "vbscript.h" +#include "initguid.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
+DEFINE_GUID(IID_IScriptTypeInfo, 0xc59c6b12, 0xf6c1, 0x11cf, 0x88,0x35, 0x00,0xa0,0xc9,0x11,0xe8,0xb2); + #define FDEX_VERSION_MASK 0xf0000000
static WCHAR *copy_string_to_heap_pool(const WCHAR *str, heap_pool_t *heap) @@ -810,10 +813,32 @@ static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface) static HRESULT WINAPI ScriptTypeInfo_GetTypeAttr(ITypeInfo *iface, TYPEATTR **ppTypeAttr) { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); - - FIXME("(%p)->(%p)\n", This, ppTypeAttr); - - return E_NOTIMPL; + TYPEATTR *p; + + TRACE("(%p)->(%p)\n", This, ppTypeAttr); + + if (!ppTypeAttr) return E_INVALIDARG; + + p = heap_alloc_zero(sizeof(*p)); + if (!p) return E_OUTOFMEMORY; + + p->guid = IID_IScriptTypeInfo; + p->lcid = LOCALE_USER_DEFAULT; + p->memidConstructor = MEMBERID_NIL; + p->memidDestructor = MEMBERID_NIL; + p->cbSizeInstance = 4; + p->typekind = TKIND_DISPATCH; + p->cFuncs = This->num_funcs; + p->cVars = This->num_vars; + p->cImplTypes = 1; + p->cbSizeVft = 7 * sizeof(void*); + p->cbAlignment = 4; + p->wTypeFlags = TYPEFLAG_FDISPATCHABLE; + p->wMajorVerNum = VBSCRIPT_MAJOR_VERSION; + p->wMinorVerNum = VBSCRIPT_MINOR_VERSION; + + *ppTypeAttr = p; + return S_OK; }
static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **ppTComp) @@ -961,7 +986,9 @@ static void WINAPI ScriptTypeInfo_ReleaseTypeAttr(ITypeInfo *iface, TYPEATTR *pT { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
- FIXME("(%p)->(%p)\n", This, pTypeAttr); + TRACE("(%p)->(%p)\n", This, pTypeAttr); + + heap_free(pTypeAttr); }
static void WINAPI ScriptTypeInfo_ReleaseFuncDesc(ITypeInfo *iface, FUNCDESC *pFuncDesc)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/vbdisp.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index 297524d..f1649b6 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -853,10 +853,33 @@ static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **p static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, FUNCDESC **ppFuncDesc) { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + struct script_typeinfo_func *func; + FUNCDESC *p; + UINT i;
- FIXME("(%p)->(%u %p)\n", This, index, ppFuncDesc); + TRACE("(%p)->(%u %p)\n", This, index, ppFuncDesc);
- return E_NOTIMPL; + if (!ppFuncDesc) return E_INVALIDARG; + if (index >= This->num_funcs) return TYPE_E_ELEMENTNOTFOUND; + func = &This->funcs[index]; + + /* Store the parameter array after the FUNCDESC structure */ + p = heap_alloc_zero(sizeof(*p) + sizeof(ELEMDESC) * func->num_args); + if (!p) return E_OUTOFMEMORY; + + p->memid = func->memid; + p->funckind = FUNC_DISPATCH; + p->invkind = INVOKE_FUNC; + p->callconv = CC_STDCALL; + p->cParams = func->num_args; + p->elemdescFunc.tdesc.vt = func->ret_type; + + if (func->num_args) p->lprgelemdescParam = (ELEMDESC*)(p + 1); + for (i = 0; i < func->num_args; i++) + p->lprgelemdescParam[i].tdesc.vt = VT_VARIANT; + + *ppFuncDesc = p; + return S_OK; }
static HRESULT WINAPI ScriptTypeInfo_GetVarDesc(ITypeInfo *iface, UINT index, VARDESC **ppVarDesc) @@ -995,7 +1018,9 @@ static void WINAPI ScriptTypeInfo_ReleaseFuncDesc(ITypeInfo *iface, FUNCDESC *pF { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
- FIXME("(%p)->(%p)\n", This, pFuncDesc); + TRACE("(%p)->(%p)\n", This, pFuncDesc); + + heap_free(pFuncDesc); }
static void WINAPI ScriptTypeInfo_ReleaseVarDesc(ITypeInfo *iface, VARDESC *pVarDesc)
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/vbdisp.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index f1649b6..783662a 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -885,10 +885,24 @@ static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, F static HRESULT WINAPI ScriptTypeInfo_GetVarDesc(ITypeInfo *iface, UINT index, VARDESC **ppVarDesc) { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + struct script_typeinfo_var *var; + VARDESC *p;
- FIXME("(%p)->(%u %p)\n", This, index, ppVarDesc); + TRACE("(%p)->(%u %p)\n", This, index, ppVarDesc);
- return E_NOTIMPL; + if (!ppVarDesc) return E_INVALIDARG; + if (index >= This->num_vars) return TYPE_E_ELEMENTNOTFOUND; + var = &This->vars[index]; + + p = heap_alloc_zero(sizeof(*p)); + if (!p) return E_OUTOFMEMORY; + + p->memid = var->memid; + p->varkind = VAR_DISPATCH; + p->elemdescVar.tdesc.vt = VT_VARIANT; + + *ppVarDesc = p; + return S_OK; }
static HRESULT WINAPI ScriptTypeInfo_GetNames(ITypeInfo *iface, MEMBERID memid, BSTR *rgBstrNames, @@ -1027,7 +1041,9 @@ static void WINAPI ScriptTypeInfo_ReleaseVarDesc(ITypeInfo *iface, VARDESC *pVar { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
- FIXME("(%p)->(%p)\n", This, pVarDesc); + TRACE("(%p)->(%p)\n", This, pVarDesc); + + heap_free(pVarDesc); }
static const ITypeInfoVtbl ScriptTypeInfoVtbl = {
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/vbdisp.c | 45 +++++++++++++++++++++++++++++++++-- dlls/vbscript/vbscript.h | 1 + dlls/vbscript/vbscript_main.c | 25 +++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index 783662a..cb04bf9 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -937,10 +937,51 @@ static HRESULT WINAPI ScriptTypeInfo_GetIDsOfNames(ITypeInfo *iface, LPOLESTR *r MEMBERID *pMemId) { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + struct script_typeinfo_func *func, *endf; + struct script_typeinfo_var *var, *endv; + UINT i, j, num_args; + const WCHAR *name; + HRESULT hr = S_OK;
- FIXME("(%p)->(%p %u %p)\n", This, rgszNames, cNames, pMemId); + TRACE("(%p)->(%p %u %p)\n", This, rgszNames, cNames, pMemId);
- return E_NOTIMPL; + if (!rgszNames || !cNames || !pMemId) return E_INVALIDARG; + + for (i = 0; i < cNames; i++) pMemId[i] = MEMBERID_NIL; + name = rgszNames[0]; + + for (func = This->funcs, endf = func + This->num_funcs; func != endf; func++) + { + if (wcsicmp(name, func->name)) continue; + pMemId[0] = func->memid; + num_args = func->num_args; + + for(i = 1; i < cNames; i++) + { + name = rgszNames[i]; + for (j = 0; j < num_args; j++) + if (!wcsicmp(name, func->arg_names[j])) + break; + if (j < num_args) + pMemId[i] = j; + else + hr = DISP_E_UNKNOWNNAME; + } + return hr; + } + + for (var = This->vars, endv = var + This->num_vars; var != endv; var++) + { + if (wcsicmp(name, var->name)) continue; + pMemId[0] = var->memid; + return S_OK; + } + + /* Look into the inherited IDispatch */ + hr = get_IDispatch_typeinfo(&iface); + if (FAILED(hr)) return hr; + + return ITypeInfo_GetIDsOfNames(iface, rgszNames, cNames, pMemId); }
static HRESULT WINAPI ScriptTypeInfo_Invoke(ITypeInfo *iface, PVOID pvInstance, MEMBERID memid, WORD wFlags, diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 654e827..24ba40a 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -382,6 +382,7 @@ TID_LIST
HRESULT get_typeinfo(tid_t,ITypeInfo**) DECLSPEC_HIDDEN; void release_regexp_typelib(void) DECLSPEC_HIDDEN; +HRESULT get_IDispatch_typeinfo(ITypeInfo**) DECLSPEC_HIDDEN;
static inline BOOL is_int32(double d) { diff --git a/dlls/vbscript/vbscript_main.c b/dlls/vbscript/vbscript_main.c index 3a8019a..caa39b4 100644 --- a/dlls/vbscript/vbscript_main.c +++ b/dlls/vbscript/vbscript_main.c @@ -37,6 +37,7 @@ static HINSTANCE vbscript_hinstance;
static ITypeLib *typelib; static ITypeInfo *typeinfos[LAST_tid]; +static ITypeInfo *IDispatch_typeinfo;
static REFIID tid_ids[] = { #define XDIID(iface) &DIID_ ## iface, @@ -95,6 +96,29 @@ static void release_typelib(void) ITypeLib_Release(typelib); }
+HRESULT get_IDispatch_typeinfo(ITypeInfo **out) +{ + ITypeInfo *typeinfo; + ITypeLib *typelib; + HRESULT hr; + + if (!IDispatch_typeinfo) + { + hr = LoadRegTypeLib(&IID_StdOle, STDOLE_MAJORVERNUM, STDOLE_MINORVERNUM, STDOLE_LCID, &typelib); + if (FAILED(hr)) return hr; + + hr = ITypeLib_GetTypeInfoOfGuid(typelib, &IID_IDispatch, &typeinfo); + ITypeLib_Release(typelib); + if (FAILED(hr)) return hr; + + if (InterlockedCompareExchangePointer((void**)(&IDispatch_typeinfo), typeinfo, NULL)) + ITypeInfo_Release(typeinfo); + } + + *out = IDispatch_typeinfo; + return S_OK; +} + BSTR get_vbscript_string(int id) { WCHAR buf[512]; @@ -316,6 +340,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) break; case DLL_PROCESS_DETACH: if (lpv) break; + if (IDispatch_typeinfo) ITypeInfo_Release(IDispatch_typeinfo); release_typelib(); release_regexp_typelib(); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/vbdisp.c | 125 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index cb04bf9..d3e4a09 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -752,6 +752,7 @@ struct script_typeinfo_func {
typedef struct { ITypeInfo ITypeInfo_iface; + ITypeComp ITypeComp_iface; LONG ref;
UINT num_funcs; @@ -767,12 +768,19 @@ static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface) return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeInfo_iface); }
+static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeComp(ITypeComp *iface) +{ + return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeComp_iface); +} + static HRESULT WINAPI ScriptTypeInfo_QueryInterface(ITypeInfo *iface, REFIID riid, void **ppv) { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ITypeInfo, riid)) *ppv = &This->ITypeInfo_iface; + else if (IsEqualGUID(&IID_ITypeComp, riid)) + *ppv = &This->ITypeComp_iface; else { WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); @@ -845,9 +853,13 @@ static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **p { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
- FIXME("(%p)->(%p)\n", This, ppTComp); + TRACE("(%p)->(%p)\n", This, ppTComp);
- return E_NOTIMPL; + if (!ppTComp) return E_INVALIDARG; + + *ppTComp = &This->ITypeComp_iface; + ITypeInfo_AddRef(iface); + return S_OK; }
static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, FUNCDESC **ppFuncDesc) @@ -1112,6 +1124,114 @@ static const ITypeInfoVtbl ScriptTypeInfoVtbl = { ScriptTypeInfo_ReleaseVarDesc };
+static HRESULT WINAPI ScriptTypeComp_QueryInterface(ITypeComp *iface, REFIID riid, void **ppv) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface); + return ITypeInfo_QueryInterface(&This->ITypeInfo_iface, riid, ppv); +} + +static ULONG WINAPI ScriptTypeComp_AddRef(ITypeComp *iface) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface); + return ITypeInfo_AddRef(&This->ITypeInfo_iface); +} + +static ULONG WINAPI ScriptTypeComp_Release(ITypeComp *iface) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface); + return ITypeInfo_Release(&This->ITypeInfo_iface); +} + +static HRESULT WINAPI ScriptTypeComp_Bind(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal, WORD wFlags, + ITypeInfo **ppTInfo, DESCKIND *pDescKind, BINDPTR *pBindPtr) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface); + struct script_typeinfo_func *func, *endf; + struct script_typeinfo_var *var, *endv; + UINT flags = wFlags ? wFlags : ~0; + ITypeInfo *typeinfo; + ITypeComp *typecomp; + HRESULT hr; + + TRACE("(%p)->(%s %08x %d %p %p %p)\n", This, debugstr_w(szName), lHashVal, + wFlags, ppTInfo, pDescKind, pBindPtr); + + if (!szName || !ppTInfo || !pDescKind || !pBindPtr) + return E_INVALIDARG; + + for (func = This->funcs, endf = func + This->num_funcs; func != endf; func++) + { + if (wcsicmp(szName, func->name)) continue; + if (!(flags & INVOKE_FUNC)) return TYPE_E_TYPEMISMATCH; + + hr = ITypeInfo_GetFuncDesc(&This->ITypeInfo_iface, func - This->funcs, &pBindPtr->lpfuncdesc); + if (FAILED(hr)) return hr; + + *pDescKind = DESCKIND_FUNCDESC; + *ppTInfo = &This->ITypeInfo_iface; + ITypeInfo_AddRef(*ppTInfo); + return S_OK; + } + + for (var = This->vars, endv = var + This->num_vars; var != endv; var++) + { + if (wcsicmp(szName, var->name)) continue; + if (!(flags & INVOKE_PROPERTYGET)) return TYPE_E_TYPEMISMATCH; + + hr = ITypeInfo_GetVarDesc(&This->ITypeInfo_iface, var - This->vars, &pBindPtr->lpvardesc); + if (FAILED(hr)) return hr; + + *pDescKind = DESCKIND_VARDESC; + *ppTInfo = &This->ITypeInfo_iface; + ITypeInfo_AddRef(*ppTInfo); + return S_OK; + } + + /* Look into the inherited IDispatch */ + hr = get_IDispatch_typeinfo(&typeinfo); + if (FAILED(hr)) return hr; + + hr = ITypeInfo_GetTypeComp(typeinfo, &typecomp); + if (FAILED(hr)) return hr; + + hr = ITypeComp_Bind(typecomp, szName, lHashVal, wFlags, ppTInfo, pDescKind, pBindPtr); + ITypeComp_Release(typecomp); + return hr; +} + +static HRESULT WINAPI ScriptTypeComp_BindType(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal, + ITypeInfo **ppTInfo, ITypeComp **ppTComp) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface); + ITypeInfo *typeinfo; + ITypeComp *typecomp; + HRESULT hr; + + TRACE("(%p)->(%s %08x %p %p)\n", This, debugstr_w(szName), lHashVal, ppTInfo, ppTComp); + + if (!szName || !ppTInfo || !ppTComp) + return E_INVALIDARG; + + /* Look into the inherited IDispatch */ + hr = get_IDispatch_typeinfo(&typeinfo); + if (FAILED(hr)) return hr; + + hr = ITypeInfo_GetTypeComp(typeinfo, &typecomp); + if (FAILED(hr)) return hr; + + hr = ITypeComp_BindType(typecomp, szName, lHashVal, ppTInfo, ppTComp); + ITypeComp_Release(typecomp); + return hr; +} + +static const ITypeCompVtbl ScriptTypeCompVtbl = { + ScriptTypeComp_QueryInterface, + ScriptTypeComp_AddRef, + ScriptTypeComp_Release, + ScriptTypeComp_Bind, + ScriptTypeComp_BindType +}; + static ITypeInfo *create_script_typeinfo(ScriptDisp *disp) { struct script_typeinfo_func *typefunc; @@ -1143,6 +1263,7 @@ static ITypeInfo *create_script_typeinfo(ScriptDisp *disp) return NULL;
p->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl; + p->ITypeComp_iface.lpVtbl = &ScriptTypeCompVtbl; p->ref = 1; p->num_vars = num_vars; p->num_funcs = num_funcs;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/tests/vbscript.c | 327 +++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+)
diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index 9e86a5f..f6dde0b 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -50,6 +50,7 @@ #endif
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); +DEFINE_GUID(IID_IScriptTypeInfo, 0xc59c6b12, 0xf6c1, 0x11cf, 0x88,0x35, 0x00,0xa0,0xc9,0x11,0xe8,0xb2);
#define DEFINE_EXPECT(func) \ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE @@ -514,6 +515,331 @@ static void test_scriptdisp(void) ok(!ref, "ref = %d\n", ref); }
+static void test_script_typeinfo(void) +{ + static struct + { + const WCHAR *name; + VARTYPE ret_type; + UINT num_args; + } func[] = + { + { L"test", VT_VOID, 0 }, + { L"subtract", VT_VARIANT, 2 }, + { L"emptyfn", VT_VARIANT, 0 }, + { L"foobar", VT_VARIANT, 0 } + }; + static struct + { + const WCHAR *name; + VARTYPE vt; + } var[] = + { + { L"global_var", VT_VARIANT }, + { L"obj", VT_VARIANT }, + { L"const_var", VT_VARIANT } + }; + ITypeInfo *typeinfo, *typeinfo2; + ITypeComp *typecomp, *typecomp2; + IActiveScriptParse *parser; + IDispatchEx *script_disp; + IActiveScript *vbscript; + FUNCDESC *funcdesc; + VARDESC *vardesc; + DESCKIND desckind; + BINDPTR bindptr; + MEMBERID memid; + TYPEATTR *attr; + UINT index; + HRESULT hr; + BSTR str; + int i; + + vbscript = create_vbscript(); + + hr = IActiveScript_QueryInterface(vbscript, &IID_IActiveScriptParse, (void**)&parser); + ok(hr == S_OK, "Could not get IActiveScriptParse iface: %08x\n", hr); + + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(vbscript, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScriptParse_InitNew(parser); + ok(hr == S_OK, "InitNew failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + + parse_script(parser, + "dim global_var\nglobal_var = 42\n" + "const const_var = 1337\n" + + "sub test\nend sub\n" + "private sub private_sub\nend sub\n" + + "function subtract(byref x, byval y)\n" + " subtract = x - y\n" + "end function\n" + + "function emptyfn\nend function\n" + + "function foobar\n" + " foobar = "foobar"\n" + "end function\n" + + "class C\n" + " dim x\n" + " public sub method\nend sub\n" + " private function strret\n" + " strret = "ret"\n" + " end function\n" + "end class\n" + + "dim obj\nset obj = new C\n"); + + script_disp = get_script_dispatch(vbscript); + hr = IDispatchEx_QueryInterface(script_disp, &IID_ITypeInfo, (void**)&typeinfo); + ok(hr == E_NOINTERFACE, "QueryInterface(IID_ITypeInfo) returned: %08x\n", hr); + hr = IDispatchEx_GetTypeInfo(script_disp, 1, LOCALE_USER_DEFAULT, &typeinfo); + ok(hr == DISP_E_BADINDEX, "GetTypeInfo returned: %08x\n", hr); + hr = IDispatchEx_GetTypeInfo(script_disp, 0, LOCALE_USER_DEFAULT, &typeinfo); + ok(hr == S_OK, "GetTypeInfo failed: %08x\n", hr); + hr = IDispatchEx_GetTypeInfo(script_disp, 0, LOCALE_USER_DEFAULT, &typeinfo2); + ok(hr == S_OK, "GetTypeInfo failed: %08x\n", hr); + ok(typeinfo != typeinfo2, "TypeInfo was not supposed to be shared.\n"); + ITypeInfo_Release(typeinfo2); + + hr = ITypeInfo_GetTypeAttr(typeinfo, &attr); + ok(hr == S_OK, "GetTypeAttr failed: %08x\n", hr); + ok(IsEqualGUID(&attr->guid, &IID_IScriptTypeInfo), "Unexpected GUID %s\n", wine_dbgstr_guid(&attr->guid)); + ok(attr->lcid == LOCALE_USER_DEFAULT, "Unexpected LCID %u\n", attr->lcid); + ok(attr->memidConstructor == MEMBERID_NIL, "Unexpected constructor memid %u\n", attr->memidConstructor); + ok(attr->memidDestructor == MEMBERID_NIL, "Unexpected destructor memid %u\n", attr->memidDestructor); + ok(attr->cbSizeInstance == 4, "Unexpected cbSizeInstance %u\n", attr->cbSizeInstance); + ok(attr->typekind == TKIND_DISPATCH, "Unexpected typekind %u\n", attr->typekind); + ok(attr->cFuncs == ARRAY_SIZE(func), "Unexpected cFuncs %u\n", attr->cFuncs); + ok(attr->cVars == ARRAY_SIZE(var), "Unexpected cVars %u\n", attr->cVars); + ok(attr->cImplTypes == 1, "Unexpected cImplTypes %u\n", attr->cImplTypes); + ok(attr->cbSizeVft == 7 * sizeof(void*), "Unexpected cbSizeVft %u\n", attr->cbSizeVft); + ok(attr->cbAlignment == 4, "Unexpected cbAlignment %u\n", attr->cbAlignment); + ok(attr->wTypeFlags == TYPEFLAG_FDISPATCHABLE, "Unexpected wTypeFlags 0x%x\n", attr->wTypeFlags); + ok(attr->tdescAlias.vt == VT_EMPTY, "Unexpected tdescAlias.vt %d\n", attr->tdescAlias.vt); + ok(attr->idldescType.wIDLFlags == IDLFLAG_NONE, "Unexpected idldescType.wIDLFlags 0x%x\n", attr->idldescType.wIDLFlags); + ITypeInfo_ReleaseTypeAttr(typeinfo, attr); + + /* GetIDsOfNames looks into the inherited types as well */ + str = a2bstr("queryinterface"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, NULL, 1, &memid); + ok(hr == E_INVALIDARG, "GetIDsOfNames returned: %08x\n", hr); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &str, 1, NULL); + ok(hr == E_INVALIDARG, "GetIDsOfNames returned: %08x\n", hr); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &str, 0, &memid); + ok(hr == E_INVALIDARG, "GetIDsOfNames returned: %08x\n", hr); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &str, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames failed: %08x\n", hr); + ok(!lstrcmpW(str, L"queryinterface"), "Unexpected string %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + str = a2bstr("C"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &str, 1, &memid); + ok(hr == DISP_E_UNKNOWNNAME, "GetIDsOfNames returned: %08x\n", hr); + SysFreeString(str); + str = a2bstr("SUBtract"); + hr = ITypeInfo_GetIDsOfNames(typeinfo, &str, 1, &memid); + ok(hr == S_OK, "GetIDsOfNames failed: %08x\n", hr); + ok(!lstrcmpW(str, L"SUBtract"), "Unexpected string %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* Check variable descriptions */ + hr = ITypeInfo_GetVarDesc(typeinfo, 0, NULL); + ok(hr == E_INVALIDARG, "GetVarDesc returned: %08x\n", hr); + hr = ITypeInfo_GetVarDesc(typeinfo, 1337, &vardesc); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "GetVarDesc returned: %08x\n", hr); + for (i = 0; i < ARRAY_SIZE(var); i++) + { + hr = ITypeInfo_GetVarDesc(typeinfo, i, &vardesc); + ok(hr == S_OK, "GetVarDesc(%u) failed: %08x\n", i, hr); + ok(vardesc->memid <= 0xFFFF, "[%u] Unexpected memid 0x%x\n", i, vardesc->memid); + ok(vardesc->lpstrSchema == NULL, "[%u] Unexpected lpstrSchema %p\n", i, vardesc->lpstrSchema); + ok(vardesc->oInst == 0, "[%u] Unexpected oInst %u\n", i, vardesc->oInst); + ok(vardesc->varkind == VAR_DISPATCH, "[%u] Unexpected varkind %d\n", i, vardesc->varkind); + ok(vardesc->wVarFlags == 0, "[%u] Unexpected wVarFlags 0x%x\n", i, vardesc->wVarFlags); + ok(vardesc->elemdescVar.tdesc.vt == var[i].vt, + "[%u] Unexpected variable type vt %d (expected %d)\n", i, vardesc->elemdescVar.tdesc.vt, 0); + ok(vardesc->elemdescVar.paramdesc.pparamdescex == NULL, + "[%u] Unexpected variable type pparamdescex %p\n", i, vardesc->elemdescVar.paramdesc.pparamdescex); + ok(vardesc->elemdescVar.paramdesc.wParamFlags == PARAMFLAG_NONE, + "[%u] Unexpected variable type wParamFlags 0x%x\n", i, vardesc->elemdescVar.paramdesc.wParamFlags); + ITypeInfo_ReleaseVarDesc(typeinfo, vardesc); + } + + /* Check function descriptions */ + hr = ITypeInfo_GetFuncDesc(typeinfo, 0, NULL); + ok(hr == E_INVALIDARG, "GetFuncDesc returned: %08x\n", hr); + hr = ITypeInfo_GetFuncDesc(typeinfo, 1337, &funcdesc); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "GetFuncDesc returned: %08x\n", hr); + for (i = 0; i < ARRAY_SIZE(func); i++) + { + hr = ITypeInfo_GetFuncDesc(typeinfo, i, &funcdesc); + ok(hr == S_OK, "GetFuncDesc(%u) failed: %08x\n", i, hr); + ok(funcdesc->memid <= 0xFFFF, "[%u] Unexpected memid 0x%x\n", i, funcdesc->memid); + ok(funcdesc->lprgscode == NULL, "[%u] Unexpected lprgscode %p\n", i, funcdesc->lprgscode); + ok(func[i].num_args ? (funcdesc->lprgelemdescParam != NULL) : (funcdesc->lprgelemdescParam == NULL), + "[%u] Unexpected lprgelemdescParam %p\n", i, funcdesc->lprgelemdescParam); + ok(funcdesc->funckind == FUNC_DISPATCH, "[%u] Unexpected funckind %u\n", i, funcdesc->funckind); + ok(funcdesc->invkind == INVOKE_FUNC, "[%u] Unexpected invkind %u\n", i, funcdesc->invkind); + ok(funcdesc->callconv == CC_STDCALL, "[%u] Unexpected callconv %u\n", i, funcdesc->callconv); + ok(funcdesc->cParams == func[i].num_args, "[%u] Unexpected cParams %d (expected %d)\n", + i, funcdesc->cParams, func[i].num_args); + ok(funcdesc->cParamsOpt == 0, "[%u] Unexpected cParamsOpt %d\n", i, funcdesc->cParamsOpt); + ok(funcdesc->cScodes == 0, "[%u] Unexpected cScodes %d\n", i, funcdesc->cScodes); + ok(funcdesc->wFuncFlags == 0, "[%u] Unexpected wFuncFlags 0x%x\n", i, funcdesc->wFuncFlags); + ok(funcdesc->elemdescFunc.tdesc.vt == func[i].ret_type, + "[%u] Unexpected return type vt %d (expected %d)\n", i, funcdesc->elemdescFunc.tdesc.vt, func[i].ret_type); + ok(funcdesc->elemdescFunc.paramdesc.pparamdescex == NULL, + "[%u] Unexpected return type pparamdescex %p\n", i, funcdesc->elemdescFunc.paramdesc.pparamdescex); + ok(funcdesc->elemdescFunc.paramdesc.wParamFlags == PARAMFLAG_NONE, + "[%u] Unexpected return type wParamFlags 0x%x\n", i, funcdesc->elemdescFunc.paramdesc.wParamFlags); + if (funcdesc->lprgelemdescParam) + for (index = 0; index < funcdesc->cParams; index++) + { + ok(funcdesc->lprgelemdescParam[index].tdesc.vt == VT_VARIANT, + "[%u] Unexpected parameter %u vt %d\n", i, index, funcdesc->lprgelemdescParam[index].tdesc.vt); + ok(funcdesc->lprgelemdescParam[index].paramdesc.pparamdescex == NULL, + "[%u] Unexpected parameter %u pparamdescex %p\n", i, index, funcdesc->lprgelemdescParam[index].paramdesc.pparamdescex); + ok(funcdesc->lprgelemdescParam[index].paramdesc.wParamFlags == PARAMFLAG_NONE, + "[%u] Unexpected parameter %u wParamFlags 0x%x\n", i, index, funcdesc->lprgelemdescParam[index].paramdesc.wParamFlags); + } + ITypeInfo_ReleaseFuncDesc(typeinfo, funcdesc); + } + + /* Test TypeComp Binds */ + hr = ITypeInfo_QueryInterface(typeinfo, &IID_ITypeComp, (void**)&typecomp); + ok(hr == S_OK, "QueryInterface(IID_ITypeComp) failed: %08x\n", hr); + hr = ITypeInfo_GetTypeComp(typeinfo, NULL); + ok(hr == E_INVALIDARG, "GetTypeComp returned: %08x\n", hr); + hr = ITypeInfo_GetTypeComp(typeinfo, &typecomp2); + ok(hr == S_OK, "GetTypeComp failed: %08x\n", hr); + ok(typecomp == typecomp2, "QueryInterface(IID_ITypeComp) and GetTypeComp returned different TypeComps\n"); + ITypeComp_Release(typecomp2); + str = a2bstr("not_found"); + hr = ITypeComp_Bind(typecomp, NULL, 0, 0, &typeinfo2, &desckind, &bindptr); + ok(hr == E_INVALIDARG, "Bind returned: %08x\n", hr); + hr = ITypeComp_Bind(typecomp, str, 0, 0, NULL, &desckind, &bindptr); + ok(hr == E_INVALIDARG, "Bind returned: %08x\n", hr); + hr = ITypeComp_Bind(typecomp, str, 0, 0, &typeinfo2, NULL, &bindptr); + ok(hr == E_INVALIDARG, "Bind returned: %08x\n", hr); + hr = ITypeComp_Bind(typecomp, str, 0, 0, &typeinfo2, &desckind, NULL); + ok(hr == E_INVALIDARG, "Bind returned: %08x\n", hr); + hr = ITypeComp_Bind(typecomp, str, 0, 0, &typeinfo2, &desckind, &bindptr); + ok(hr == S_OK, "Bind failed: %08x\n", hr); + ok(desckind == DESCKIND_NONE, "Unexpected desckind %u\n", desckind); + SysFreeString(str); + str = a2bstr("GLOBAL_VAR"); + hr = ITypeComp_Bind(typecomp, str, 0, INVOKE_FUNC, &typeinfo2, &desckind, &bindptr); + ok(hr == TYPE_E_TYPEMISMATCH, "Bind returned: %08x\n", hr); + ok(!lstrcmpW(str, L"GLOBAL_VAR"), "Unexpected string %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + str = a2bstr("C"); + hr = ITypeComp_Bind(typecomp, str, 0, 0, &typeinfo2, &desckind, &bindptr); + ok(hr == S_OK, "Bind failed: %08x\n", hr); + ok(desckind == DESCKIND_NONE, "Unexpected desckind %u\n", desckind); + SysFreeString(str); + str = a2bstr("addRef"); + hr = ITypeComp_Bind(typecomp, str, 0, 0, &typeinfo2, &desckind, &bindptr); + ok(hr == S_OK, "Bind failed: %08x\n", hr); + ok(desckind == DESCKIND_FUNCDESC, "Unexpected desckind %u\n", desckind); + ok(!lstrcmpW(str, L"addRef"), "Unexpected string %s\n", wine_dbgstr_w(str)); + ITypeInfo_ReleaseFuncDesc(typeinfo2, bindptr.lpfuncdesc); + ITypeInfo_Release(typeinfo2); + SysFreeString(str); + for (i = 0; i < ARRAY_SIZE(var); i++) + { + str = SysAllocString(var[i].name); + hr = ITypeComp_Bind(typecomp, str, 0, INVOKE_PROPERTYGET, &typeinfo2, &desckind, &bindptr); + ok(hr == S_OK, "Bind failed: %08x\n", hr); + ok(desckind == DESCKIND_VARDESC, "Unexpected desckind %u\n", desckind); + ITypeInfo_ReleaseVarDesc(typeinfo2, bindptr.lpvardesc); + ITypeInfo_Release(typeinfo2); + SysFreeString(str); + } + for (i = 0; i < ARRAY_SIZE(func); i++) + { + str = SysAllocString(func[i].name); + hr = ITypeComp_Bind(typecomp, str, 0, INVOKE_FUNC, &typeinfo2, &desckind, &bindptr); + ok(hr == S_OK, "Bind failed: %08x\n", hr); + ok(desckind == DESCKIND_FUNCDESC, "Unexpected desckind %u\n", desckind); + ITypeInfo_ReleaseFuncDesc(typeinfo2, bindptr.lpfuncdesc); + ITypeInfo_Release(typeinfo2); + SysFreeString(str); + } + str = a2bstr("VBScriptTypeInfo"); + hr = ITypeComp_BindType(typecomp, NULL, 0, &typeinfo2, &typecomp2); + ok(hr == E_INVALIDARG, "BindType returned: %08x\n", hr); + hr = ITypeComp_BindType(typecomp, str, 0, NULL, &typecomp2); + ok(hr == E_INVALIDARG, "BindType returned: %08x\n", hr); + hr = ITypeComp_BindType(typecomp, str, 0, &typeinfo2, NULL); + ok(hr == E_INVALIDARG, "BindType returned: %08x\n", hr); + hr = ITypeComp_BindType(typecomp, str, 0, &typeinfo2, &typecomp2); + ok(hr == S_OK, "BindType failed: %08x\n", hr); + ok(!typeinfo2, "Unexpected TypeInfo %p (expected null)\n", typeinfo2); + ok(!typecomp2, "Unexpected TypeComp %p (expected null)\n", typecomp2); + SysFreeString(str); + str = a2bstr("C"); + hr = ITypeComp_BindType(typecomp, str, 0, &typeinfo2, &typecomp2); + ok(hr == S_OK, "BindType failed: %08x\n", hr); + ok(!typeinfo2, "Unexpected TypeInfo %p (expected null)\n", typeinfo2); + ok(!typecomp2, "Unexpected TypeComp %p (expected null)\n", typecomp2); + SysFreeString(str); + str = a2bstr("IDispatch"); + hr = ITypeComp_BindType(typecomp, str, 0, &typeinfo2, &typecomp2); + ok(hr == S_OK, "BindType failed: %08x\n", hr); + ok(!typeinfo2, "Unexpected TypeInfo %p (expected null)\n", typeinfo2); + ok(!typecomp2, "Unexpected TypeComp %p (expected null)\n", typecomp2); + SysFreeString(str); + ITypeComp_Release(typecomp); + + /* Updating the script won't update the typeinfo obtained before, + but it will be reflected in any typeinfo obtained afterwards. */ + parse_script(parser, + "dim new_var\nnew_var = 10\n" + "sub new_sub\nend sub\n" + "function new_fn\nend function\n"); + + hr = IDispatchEx_GetTypeInfo(script_disp, 0, LOCALE_USER_DEFAULT, &typeinfo2); + ok(hr == S_OK, "GetTypeInfo failed: %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(typeinfo, &attr); + ok(hr == S_OK, "GetTypeAttr failed: %08x\n", hr); + ok(attr->cFuncs == ARRAY_SIZE(func), "Unexpected cFuncs %u\n", attr->cFuncs); + ok(attr->cVars == ARRAY_SIZE(var), "Unexpected cVars %u\n", attr->cVars); + ITypeInfo_ReleaseTypeAttr(typeinfo, attr); + hr = ITypeInfo_GetTypeAttr(typeinfo2, &attr); + ok(hr == S_OK, "GetTypeAttr failed: %08x\n", hr); + ok(attr->cFuncs == ARRAY_SIZE(func) + 2, "Unexpected cFuncs %u\n", attr->cFuncs); + ok(attr->cVars == ARRAY_SIZE(var) + 1, "Unexpected cVars %u\n", attr->cVars); + ITypeInfo_ReleaseTypeAttr(typeinfo2, attr); + ITypeInfo_Release(typeinfo2); + + ITypeInfo_Release(typeinfo); + IDispatchEx_Release(script_disp); + IActiveScriptParse_Release(parser); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hr = IActiveScript_Close(vbscript); + ok(hr == S_OK, "Close failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScript_Release(vbscript); +} + static void test_vbscript(void) { IActiveScriptParseProcedure2 *parse_proc; @@ -958,6 +1284,7 @@ START_TEST(vbscript) test_vbscript_simplecreate(); test_vbscript_initializing(); test_scriptdisp(); + test_script_typeinfo(); test_RegExp(); }else { win_skip("VBScript engine not available or too old\n");
Does it have to be dynamically allocated? It seems that you could've added it to ScriptDisp directly.Directly related to that, if context changes are reflected by earlier created typeinfo's, then you don't need to duplicate things in patch 3/9.
On 10/16/19 8:25 AM, Nikolay Sivov wrote:
Does it have to be dynamically allocated? It seems that you could've added it to ScriptDisp directly.Directly related to that, if context changes are reflected by earlier created typeinfo's, then you don't need to duplicate things in patch 3/9.
Yeah, it has to be dynamic. It also can't be added to ScriptDisp, because one could theoretically release the IDispatch and close the script while still having access to the TypeInfo.
And the context changes are not reflected by earlier typeinfos, so the duplication is necessary, each of them is "frozen" at the moment it is retrieved (again, since the dispatch may also be released in the meantime).
And yes, even retrieving it twice in a row results in different typeinfos (I did add tests for this, though) with duplicated information.
On 10/16/19 2:52 PM, Gabriel Ivăncescu wrote:
On 10/16/19 8:25 AM, Nikolay Sivov wrote:
Does it have to be dynamically allocated? It seems that you could've added it to ScriptDisp directly.Directly related to that, if context changes are reflected by earlier created typeinfo's, then you don't need to duplicate things in patch 3/9.
Yeah, it has to be dynamic. It also can't be added to ScriptDisp, because one could theoretically release the IDispatch and close the script while still having access to the TypeInfo.
And the context changes are not reflected by earlier typeinfos, so the duplication is necessary, each of them is "frozen" at the moment it is retrieved (again, since the dispatch may also be released in the meantime).
And yes, even retrieving it twice in a row results in different typeinfos (I did add tests for this, though) with duplicated information.
Ok, thanks for the explanation.
Hi Gabriel,
On 10/16/19 1:52 PM, Gabriel Ivăncescu wrote:
And the context changes are not reflected by earlier typeinfos, so the duplication is necessary, each of them is "frozen" at the moment it is retrieved
I'm not convinced. Note that we currently store functions and variables in lists and always add new members to the beginning of the list. It means that you may treat each element of the list (head is the most interesting element) as an immutable (or "frozen") snapshot of current context. Storing current head of variables and functions lists in TypeInfo should be enough, AFAICS.
If you need something more sophisticated, it should be possible to change how we store the context in a way that we could retrieve such a snapshot easily. I can imagine compiler storing global functions and variables in a separate collection that is immutable. It would probably have to be recreated whenever something changes (which is rare), but it could be efficiently use across vbscript.
Thanks,
Jacek
On 10/16/19 4:16 PM, Jacek Caban wrote:
Hi Gabriel,
On 10/16/19 1:52 PM, Gabriel Ivăncescu wrote:
And the context changes are not reflected by earlier typeinfos, so the duplication is necessary, each of them is "frozen" at the moment it is retrieved
I'm not convinced. Note that we currently store functions and variables in lists and always add new members to the beginning of the list. It means that you may treat each element of the list (head is the most interesting element) as an immutable (or "frozen") snapshot of current context. Storing current head of variables and functions lists in TypeInfo should be enough, AFAICS.
There's several issues with this. First, the script engine itself can be closed and released, and in this case, the lists will get released as well (the code has asserts about this behavior when the IDispatch ScriptDisp gets released). This is why I copy the strings to the TypeInfo's pool.
Also, the variables need to be stored in reverse order compared to how we compile them, and separated by const/non-const to match Windows in terms of their indices. I think this may be important because you can use GetVarDesc to retrieve it by index.
BTW, we cannot store a pointer to the head of the list, because we need to random-access them in GetFuncDesc/GetVarDesc by index. I also think this index must match Windows. Some applications loop through them with indices (from 0 up to cVars from the GetTypeAttr). At least creating an array is necessary so this process doesn't have non-linear complexity...
If you need something more sophisticated, it should be possible to change how we store the context in a way that we could retrieve such a snapshot easily. I can imagine compiler storing global functions and variables in a separate collection that is immutable. It would probably have to be recreated whenever something changes (which is rare), but it could be efficiently use across vbscript.
So one giant pool for everything? The problem in this case is, what would be the best way to release it properly? We'd need some sort of reference counting at the very least, so that releasing the ScriptDisp doesn't also release the list if there are any TypeInfos holding it. Which would complicate things a bit. Is that something like what you're thinking of?
Thanks, Gabriel