vbscript: Use separated IDispatch implementation for objects exposing builtin functions.

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2019-10-17 22:46:45 +02:00 committed by Alexandre Julliard
parent 4c3e2f2b4f
commit d061ba0e35
4 changed files with 279 additions and 163 deletions

View File

@ -39,9 +39,253 @@ const GUID GUID_CUSTOM_CONFIRMOBJECTSAFETY =
static const WCHAR emptyW[] = {0};
static const WCHAR vbscriptW[] = {'V','B','S','c','r','i','p','t',0};
#define BP_GET 1
#define BP_GETPUT 2
typedef struct {
UINT16 len;
WCHAR buf[7];
} string_constant_t;
struct _builtin_prop_t {
DISPID id;
HRESULT (*proc)(BuiltinDisp*,VARIANT*,unsigned,VARIANT*);
DWORD flags;
unsigned min_args;
UINT_PTR max_args;
};
static inline BuiltinDisp *impl_from_IDispatch(IDispatch *iface)
{
return CONTAINING_RECORD(iface, BuiltinDisp, IDispatch_iface);
}
static HRESULT WINAPI Builtin_QueryInterface(IDispatch *iface, REFIID riid, void **ppv)
{
BuiltinDisp *This = impl_from_IDispatch(iface);
if(IsEqualGUID(&IID_IUnknown, riid)) {
TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
*ppv = &This->IDispatch_iface;
}else if(IsEqualGUID(&IID_IDispatch, riid)) {
TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
*ppv = &This->IDispatch_iface;
}else {
WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
*ppv = NULL;
return E_NOINTERFACE;
}
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
static ULONG WINAPI Builtin_AddRef(IDispatch *iface)
{
BuiltinDisp *This = impl_from_IDispatch(iface);
LONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) ref=%d\n", This, ref);
return ref;
}
static ULONG WINAPI Builtin_Release(IDispatch *iface)
{
BuiltinDisp *This = impl_from_IDispatch(iface);
LONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) ref=%d\n", This, ref);
if(!ref) {
assert(!This->ctx);
heap_free(This);
}
return ref;
}
static HRESULT WINAPI Builtin_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo)
{
BuiltinDisp *This = impl_from_IDispatch(iface);
TRACE("(%p)->(%p)\n", This, pctinfo);
*pctinfo = 0;
return S_OK;
}
static HRESULT WINAPI Builtin_GetTypeInfo(IDispatch *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
BuiltinDisp *This = impl_from_IDispatch(iface);
TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
return DISP_E_BADINDEX;
}
HRESULT get_builtin_id(BuiltinDisp *disp, const WCHAR *name, DISPID *id)
{
return ITypeInfo_GetIDsOfNames(disp->desc->typeinfo, (WCHAR**)&name, 1, id);
return ITypeInfo_GetIDsOfNames(disp->typeinfo, (WCHAR**)&name, 1, id);
}
static HRESULT WINAPI Builtin_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *names, UINT name_cnt,
LCID lcid, DISPID *ids)
{
BuiltinDisp *This = impl_from_IDispatch(iface);
unsigned i;
HRESULT hres;
TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), names, name_cnt, lcid, ids);
if(!This->ctx) {
FIXME("NULL context\n");
return E_UNEXPECTED;
}
for(i = 0; i < name_cnt; i++) {
hres = get_builtin_id(This, names[i], &ids[i]);
if(FAILED(hres))
return hres;
}
return S_OK;
}
static HRESULT WINAPI Builtin_Invoke(IDispatch *iface, DISPID id, REFIID riid, LCID lcid, WORD flags,
DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, UINT *err)
{
BuiltinDisp *This = impl_from_IDispatch(iface);
const builtin_prop_t *prop;
VARIANT args[8];
unsigned argn, i;
TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, id, debugstr_guid(riid), lcid, flags, dp, res, ei, err);
if(!This->ctx) {
FIXME("NULL context\n");
return E_UNEXPECTED;
}
if(This->member_cnt) {
unsigned min = 0, max = This->member_cnt-1, i;
while(min <= max) {
i = (min+max)/2;
if(This->members[i].id == id) {
id = i;
break;
}
if(This->members[i].id < id)
min = i+1;
else
max = i-1;
}
if(min > max)
return DISP_E_MEMBERNOTFOUND;
}
if(id >= This->member_cnt || (!This->members[id].proc && !This->members[id].flags))
return DISP_E_MEMBERNOTFOUND;
prop = This->members + id;
switch(flags) {
case DISPATCH_PROPERTYGET:
if(!(prop->flags & (BP_GET|BP_GETPUT))) {
FIXME("property does not support DISPATCH_PROPERTYGET\n");
return E_FAIL;
}
break;
case DISPATCH_PROPERTYGET|DISPATCH_METHOD:
if(!prop->proc && prop->flags == BP_GET) {
const int vt = prop->min_args, val = prop->max_args;
switch(vt) {
case VT_I2:
V_VT(res) = VT_I2;
V_I2(res) = val;
break;
case VT_I4:
V_VT(res) = VT_I4;
V_I4(res) = val;
break;
case VT_BSTR: {
const string_constant_t *str = (const string_constant_t*)prop->max_args;
BSTR ret;
ret = SysAllocStringLen(str->buf, str->len);
if(!ret)
return E_OUTOFMEMORY;
V_VT(res) = VT_BSTR;
V_BSTR(res) = ret;
break;
}
DEFAULT_UNREACHABLE;
}
return S_OK;
}
break;
case DISPATCH_METHOD:
if(prop->flags & (BP_GET|BP_GETPUT)) {
FIXME("Call on property\n");
return E_FAIL;
}
break;
case DISPATCH_PROPERTYPUT:
if(!(prop->flags & BP_GETPUT)) {
FIXME("property does not support DISPATCH_PROPERTYPUT\n");
return E_FAIL;
}
FIXME("call put\n");
return E_NOTIMPL;
default:
FIXME("unsupported flags %x\n", flags);
return E_NOTIMPL;
}
argn = arg_cnt(dp);
if(argn < prop->min_args || argn > (prop->max_args ? prop->max_args : prop->min_args)) {
WARN("invalid number of arguments\n");
return MAKE_VBSERROR(VBSE_FUNC_ARITY_MISMATCH);
}
assert(argn < ARRAY_SIZE(args));
for(i=0; i < argn; i++) {
if(V_VT(dp->rgvarg+dp->cArgs-i-1) == (VT_BYREF|VT_VARIANT))
args[i] = *V_VARIANTREF(dp->rgvarg+dp->cArgs-i-1);
else
args[i] = dp->rgvarg[dp->cArgs-i-1];
}
return prop->proc(This, args, dp->cArgs, res);
}
static const IDispatchVtbl BuiltinDispVtbl = {
Builtin_QueryInterface,
Builtin_AddRef,
Builtin_Release,
Builtin_GetTypeInfoCount,
Builtin_GetTypeInfo,
Builtin_GetIDsOfNames,
Builtin_Invoke
};
static HRESULT create_builtin_dispatch(script_ctx_t *ctx, const builtin_prop_t *members, size_t member_cnt,
ITypeInfo *typeinfo, BuiltinDisp **ret)
{
BuiltinDisp *disp;
if(!(disp = heap_alloc(sizeof(*disp))))
return E_OUTOFMEMORY;
disp->IDispatch_iface.lpVtbl = &BuiltinDispVtbl;
disp->ref = 1;
disp->members = members;
disp->member_cnt = member_cnt;
disp->ctx = ctx;
disp->typeinfo = typeinfo;
*ret = disp;
return S_OK;
}
static IInternetHostSecurityManager *get_sec_mgr(script_ctx_t *ctx)
@ -1740,7 +1984,7 @@ static HRESULT Global_MsgBox(BuiltinDisp *This, VARIANT *args, unsigned args_cnt
}
if(SUCCEEDED(hres))
hres = show_msgbox(This->desc->ctx, prompt, type, title, res);
hres = show_msgbox(This->ctx, prompt, type, title, res);
SysFreeString(prompt);
SysFreeString(title);
@ -1759,7 +2003,7 @@ static HRESULT Global_CreateObject(BuiltinDisp *This, VARIANT *arg, unsigned arg
return E_INVALIDARG;
}
obj = create_object(This->desc->ctx, V_BSTR(arg));
obj = create_object(This->ctx, V_BSTR(arg));
if(!obj)
return VB_E_CANNOT_CREATE_OBJ;
@ -1791,7 +2035,7 @@ static HRESULT Global_GetObject(BuiltinDisp *This, VARIANT *args, unsigned args_
return E_NOTIMPL;
}
if(This->desc->ctx->safeopt & (INTERFACE_USES_SECURITY_MANAGER|INTERFACESAFE_FOR_UNTRUSTED_DATA)) {
if(This->ctx->safeopt & (INTERFACE_USES_SECURITY_MANAGER|INTERFACESAFE_FOR_UNTRUSTED_DATA)) {
WARN("blocked in current safety mode\n");
return VB_E_CANNOT_CREATE_OBJ;
}
@ -1811,7 +2055,7 @@ static HRESULT Global_GetObject(BuiltinDisp *This, VARIANT *args, unsigned args_
if(FAILED(hres))
return hres;
hres = set_object_site(This->desc->ctx, obj_unk);
hres = set_object_site(This->ctx, obj_unk);
if(FAILED(hres)) {
IUnknown_Release(obj_unk);
return hres;
@ -2470,28 +2714,25 @@ static HRESULT err_string_prop(BSTR *prop, VARIANT *args, unsigned args_cnt, VAR
static HRESULT Err_Description(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
{
TRACE("\n");
return !This->desc ? E_UNEXPECTED : err_string_prop(&This->desc->ctx->ei.bstrDescription, args, args_cnt, res);
return err_string_prop(&This->ctx->ei.bstrDescription, args, args_cnt, res);
}
static HRESULT Err_HelpContext(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
{
TRACE("\n");
if(!This->desc)
return E_UNEXPECTED;
if(args_cnt) {
FIXME("setter not implemented\n");
return E_NOTIMPL;
}
return return_int(res, This->desc->ctx->ei.dwHelpContext);
return return_int(res, This->ctx->ei.dwHelpContext);
}
static HRESULT Err_HelpFile(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
{
TRACE("\n");
return !This->desc ? E_UNEXPECTED : err_string_prop(&This->desc->ctx->ei.bstrHelpFile, args, args_cnt, res);
return err_string_prop(&This->ctx->ei.bstrHelpFile, args, args_cnt, res);
}
static HRESULT Err_Number(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
@ -2500,32 +2741,26 @@ static HRESULT Err_Number(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, V
TRACE("\n");
if(!This->desc)
return E_UNEXPECTED;
if(args_cnt) {
FIXME("setter not implemented\n");
return E_NOTIMPL;
}
hres = This->desc->ctx->ei.scode;
hres = This->ctx->ei.scode;
return return_int(res, HRESULT_FACILITY(hres) == FACILITY_VBS ? HRESULT_CODE(hres) : hres);
}
static HRESULT Err_Source(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
{
TRACE("\n");
return !This->desc ? E_UNEXPECTED : err_string_prop(&This->desc->ctx->ei.bstrSource, args, args_cnt, res);
return err_string_prop(&This->ctx->ei.bstrSource, args, args_cnt, res);
}
static HRESULT Err_Clear(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
{
TRACE("\n");
if(!This->desc)
return E_UNEXPECTED;
clear_ei(&This->desc->ctx->ei);
clear_ei(&This->ctx->ei);
return S_OK;
}
@ -2552,8 +2787,8 @@ static HRESULT Err_Raise(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VA
if(args_cnt >= 5 && SUCCEEDED(hres))
hres = to_int(args + 4, &helpcontext);
if(SUCCEEDED(hres) && This->desc) {
script_ctx_t *ctx = This->desc->ctx;
if(SUCCEEDED(hres)) {
script_ctx_t *ctx = This->ctx;
error = (code & ~0xffff) ? map_hres(code) : MAKE_VBSERROR(code);
@ -2600,12 +2835,14 @@ static const builtin_prop_t err_props[] = {
void detach_global_objects(script_ctx_t *ctx)
{
if(ctx->err_obj) {
IDispatchEx_Release(&ctx->err_obj->IDispatchEx_iface);
ctx->err_obj->ctx = NULL;
IDispatch_Release(&ctx->err_obj->IDispatch_iface);
ctx->err_obj = NULL;
}
if(ctx->global_obj) {
IDispatchEx_Release(&ctx->global_obj->IDispatchEx_iface);
ctx->global_obj->ctx = NULL;
IDispatch_Release(&ctx->global_obj->IDispatch_iface);
ctx->global_obj = NULL;
}
@ -2620,17 +2857,14 @@ void detach_global_objects(script_ctx_t *ctx)
HRESULT init_global(script_ctx_t *ctx)
{
ITypeInfo *typeinfo;
HRESULT hres;
ctx->global_desc.ctx = ctx;
ctx->global_desc.builtin_prop_cnt = ARRAY_SIZE(global_props);
ctx->global_desc.builtin_props = global_props;
hres = get_typeinfo(GlobalObj_tid, &ctx->global_desc.typeinfo);
hres = get_typeinfo(GlobalObj_tid, &typeinfo);
if(FAILED(hres))
return hres;
hres = create_vbdisp(&ctx->global_desc, (vbdisp_t**)&ctx->global_obj);
hres = create_builtin_dispatch(ctx, global_props, ARRAY_SIZE(global_props), typeinfo, &ctx->global_obj);
if(FAILED(hres))
return hres;
@ -2638,13 +2872,9 @@ HRESULT init_global(script_ctx_t *ctx)
if(FAILED(hres))
return hres;
ctx->err_desc.ctx = ctx;
ctx->err_desc.builtin_prop_cnt = ARRAY_SIZE(err_props);
ctx->err_desc.builtin_props = err_props;
hres = get_typeinfo(ErrObj_tid, &ctx->err_desc.typeinfo);
hres = get_typeinfo(ErrObj_tid, &typeinfo);
if(FAILED(hres))
return hres;
return create_vbdisp(&ctx->err_desc, (vbdisp_t**)&ctx->err_obj);
return create_builtin_dispatch(ctx, err_props, ARRAY_SIZE(err_props), typeinfo, &ctx->err_obj);
}

View File

@ -177,14 +177,14 @@ static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, vbdisp_invoke_type_
if(!wcsicmp(name, errW)) {
ref->type = REF_OBJ;
ref->u.obj = (IDispatch*)&ctx->script->err_obj->IDispatchEx_iface;
ref->u.obj = &ctx->script->err_obj->IDispatch_iface;
return S_OK;
}
hres = get_builtin_id(ctx->script->global_obj, name, &id);
if(SUCCEEDED(hres)) {
ref->type = REF_DISP;
ref->u.d.disp = (IDispatch*)&ctx->script->global_obj->IDispatchEx_iface;
ref->u.d.disp = &ctx->script->global_obj->IDispatch_iface;
ref->u.d.id = id;
return S_OK;
}

View File

@ -73,14 +73,6 @@ HRESULT vbdisp_get_id(vbdisp_t *This, BSTR name, vbdisp_invoke_type_t invoke_typ
}
}
if(This->desc->typeinfo) {
HRESULT hres;
hres = ITypeInfo_GetIDsOfNames(This->desc->typeinfo, &name, 1, id);
if(SUCCEEDED(hres))
return S_OK;
}
*id = -1;
return DISP_E_UNKNOWNNAME;
}
@ -169,85 +161,6 @@ static HRESULT invoke_variant_prop(script_ctx_t *ctx, VARIANT *v, WORD flags, DI
return hres;
}
static HRESULT invoke_builtin(vbdisp_t *This, const builtin_prop_t *prop, WORD flags, DISPPARAMS *dp, VARIANT *res)
{
VARIANT args[8];
unsigned argn, i;
switch(flags) {
case DISPATCH_PROPERTYGET:
if(!(prop->flags & (BP_GET|BP_GETPUT))) {
FIXME("property does not support DISPATCH_PROPERTYGET\n");
return E_FAIL;
}
break;
case DISPATCH_PROPERTYGET|DISPATCH_METHOD:
if(!prop->proc && prop->flags == BP_GET) {
const int vt = prop->min_args, val = prop->max_args;
switch(vt) {
case VT_I2:
V_VT(res) = VT_I2;
V_I2(res) = val;
break;
case VT_I4:
V_VT(res) = VT_I4;
V_I4(res) = val;
break;
case VT_BSTR: {
const string_constant_t *str = (const string_constant_t*)prop->max_args;
BSTR ret;
ret = SysAllocStringLen(str->buf, str->len);
if(!ret)
return E_OUTOFMEMORY;
V_VT(res) = VT_BSTR;
V_BSTR(res) = ret;
break;
}
DEFAULT_UNREACHABLE;
}
return S_OK;
}
break;
case DISPATCH_METHOD:
if(prop->flags & (BP_GET|BP_GETPUT)) {
FIXME("Call on property\n");
return E_FAIL;
}
break;
case DISPATCH_PROPERTYPUT:
if(!(prop->flags & BP_GETPUT)) {
FIXME("property does not support DISPATCH_PROPERTYPUT\n");
return E_FAIL;
}
FIXME("call put\n");
return E_NOTIMPL;
default:
FIXME("unsupported flags %x\n", flags);
return E_NOTIMPL;
}
argn = arg_cnt(dp);
if(argn < prop->min_args || argn > (prop->max_args ? prop->max_args : prop->min_args)) {
WARN("invalid number of arguments\n");
return MAKE_VBSERROR(VBSE_FUNC_ARITY_MISMATCH);
}
assert(argn < ARRAY_SIZE(args));
for(i=0; i < argn; i++) {
if(V_VT(dp->rgvarg+dp->cArgs-i-1) == (VT_BYREF|VT_VARIANT))
args[i] = *V_VARIANTREF(dp->rgvarg+dp->cArgs-i-1);
else
args[i] = dp->rgvarg[dp->cArgs-i-1];
}
return prop->proc(This, args, dp->cArgs, res);
}
static HRESULT invoke_vbdisp(vbdisp_t *This, DISPID id, DWORD flags, BOOL extern_caller, DISPPARAMS *params, VARIANT *res)
{
if(id < 0)
@ -313,22 +226,8 @@ static HRESULT invoke_vbdisp(vbdisp_t *This, DISPID id, DWORD flags, BOOL extern
}
}
if(id >= This->desc->prop_cnt + This->desc->func_cnt) {
if(This->desc->builtin_prop_cnt) {
unsigned min = 0, max = This->desc->builtin_prop_cnt-1, i;
while(min <= max) {
i = (min+max)/2;
if(This->desc->builtin_props[i].id == id)
return invoke_builtin(This, This->desc->builtin_props+i, flags, params, res);
if(This->desc->builtin_props[i].id < id)
min = i+1;
else
max = i-1;
}
}
if(id >= This->desc->prop_cnt + This->desc->func_cnt)
return DISP_E_MEMBERNOTFOUND;
}
TRACE("%p->%s\n", This, debugstr_w(This->desc->props[id - This->desc->func_cnt].name));
return invoke_variant_prop(This->desc->ctx, This->props+(id-This->desc->func_cnt), flags, params, res);

View File

@ -88,17 +88,6 @@ typedef struct {
function_t *entries[VBDISP_ANY];
} vbdisp_funcprop_desc_t;
#define BP_GET 1
#define BP_GETPUT 2
typedef struct {
DISPID id;
HRESULT (*proc)(vbdisp_t*,VARIANT*,unsigned,VARIANT*);
DWORD flags;
unsigned min_args;
UINT_PTR max_args;
} builtin_prop_t;
typedef struct _class_desc_t {
const WCHAR *name;
script_ctx_t *ctx;
@ -114,9 +103,6 @@ typedef struct _class_desc_t {
unsigned array_cnt;
array_desc_t *array_descs;
unsigned builtin_prop_cnt;
const builtin_prop_t *builtin_props;
ITypeInfo *typeinfo;
function_t *value_func;
struct _class_desc_t *next;
@ -147,7 +133,16 @@ typedef struct {
script_ctx_t *ctx;
} ScriptDisp;
typedef vbdisp_t BuiltinDisp;
typedef struct _builtin_prop_t builtin_prop_t;
typedef struct {
IDispatch IDispatch_iface;
LONG ref;
size_t member_cnt;
const builtin_prop_t *members;
script_ctx_t *ctx;
ITypeInfo *typeinfo;
} BuiltinDisp;
HRESULT create_vbdisp(const class_desc_t*,vbdisp_t**) DECLSPEC_HIDDEN;
HRESULT disp_get_id(IDispatch*,BSTR,vbdisp_invoke_type_t,BOOL,DISPID*) DECLSPEC_HIDDEN;
@ -188,10 +183,7 @@ struct _script_ctx_t {
ScriptDisp *script_obj;
class_desc_t global_desc;
BuiltinDisp *global_obj;
class_desc_t err_desc;
BuiltinDisp *err_obj;
EXCEPINFO ei;
@ -366,11 +358,6 @@ HRESULT report_script_error(script_ctx_t*) DECLSPEC_HIDDEN;
void detach_global_objects(script_ctx_t*) DECLSPEC_HIDDEN;
HRESULT get_builtin_id(BuiltinDisp*,const WCHAR*,DISPID*) DECLSPEC_HIDDEN;
typedef struct {
UINT16 len;
WCHAR buf[7];
} string_constant_t;
#define TID_LIST \
XDIID(ErrObj) \
XDIID(GlobalObj)