| /* | |
| * Copyright 2008 Jacek Caban for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "jscript.h" | |
| #include "wine/unicode.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(jscript); | |
| /* | |
| * This IID is used to get DispatchEx objecto from interface. | |
| * We might consider using private insteface instead. | |
| */ | |
| static const IID IID_IDispatchJS = | |
| {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}}; | |
| #define FDEX_VERSION_MASK 0xf0000000 | |
| typedef enum { | |
| PROP_VARIANT, | |
| PROP_BUILTIN, | |
| PROP_PROTREF, | |
| PROP_DELETED | |
| } prop_type_t; | |
| struct _dispex_prop_t { | |
| WCHAR *name; | |
| prop_type_t type; | |
| DWORD flags; | |
| union { | |
| VARIANT var; | |
| const builtin_prop_t *p; | |
| DWORD ref; | |
| } u; | |
| }; | |
| static inline DISPID prop_to_id(DispatchEx *This, dispex_prop_t *prop) | |
| { | |
| return prop - This->props; | |
| } | |
| static inline dispex_prop_t *get_prop(DispatchEx *This, DISPID id) | |
| { | |
| if(id < 0 || id >= This->prop_cnt || This->props[id].type == PROP_DELETED) | |
| return NULL; | |
| return This->props+id; | |
| } | |
| static DWORD get_flags(DispatchEx *This, dispex_prop_t *prop) | |
| { | |
| if(prop->type == PROP_PROTREF) { | |
| dispex_prop_t *parent = get_prop(This->prototype, prop->u.ref); | |
| if(!parent) { | |
| prop->type = PROP_DELETED; | |
| return 0; | |
| } | |
| return get_flags(This->prototype, parent); | |
| } | |
| return prop->flags; | |
| } | |
| static const builtin_prop_t *find_builtin_prop(DispatchEx *This, const WCHAR *name) | |
| { | |
| int min = 0, max, i, r; | |
| max = This->builtin_info->props_cnt-1; | |
| while(min <= max) { | |
| i = (min+max)/2; | |
| r = strcmpW(name, This->builtin_info->props[i].name); | |
| if(!r) | |
| return This->builtin_info->props + i; | |
| if(r < 0) | |
| max = i-1; | |
| else | |
| min = i+1; | |
| } | |
| return NULL; | |
| } | |
| static dispex_prop_t *alloc_prop(DispatchEx *This, const WCHAR *name, prop_type_t type, DWORD flags) | |
| { | |
| dispex_prop_t *ret; | |
| if(This->buf_size == This->prop_cnt) { | |
| dispex_prop_t *tmp = heap_realloc(This->props, (This->buf_size<<=1)*sizeof(*This->props)); | |
| if(!tmp) | |
| return NULL; | |
| This->props = tmp; | |
| } | |
| ret = This->props + This->prop_cnt++; | |
| ret->type = type; | |
| ret->flags = flags; | |
| ret->name = heap_strdupW(name); | |
| if(!ret->name) | |
| return NULL; | |
| return ret; | |
| } | |
| static dispex_prop_t *alloc_protref(DispatchEx *This, const WCHAR *name, DWORD ref) | |
| { | |
| dispex_prop_t *ret; | |
| ret = alloc_prop(This, name, PROP_PROTREF, 0); | |
| if(!ret) | |
| return NULL; | |
| ret->u.ref = ref; | |
| return ret; | |
| } | |
| static HRESULT find_prop_name(DispatchEx *This, const WCHAR *name, dispex_prop_t **ret) | |
| { | |
| const builtin_prop_t *builtin; | |
| dispex_prop_t *prop; | |
| for(prop = This->props; prop < This->props+This->prop_cnt; prop++) { | |
| if(prop->name && !strcmpW(prop->name, name)) { | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| } | |
| builtin = find_builtin_prop(This, name); | |
| if(builtin) { | |
| prop = alloc_prop(This, name, PROP_BUILTIN, builtin->flags); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| prop->u.p = builtin; | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| *ret = NULL; | |
| return S_OK; | |
| } | |
| static HRESULT find_prop_name_prot(DispatchEx *This, const WCHAR *name, dispex_prop_t **ret) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = find_prop_name(This, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(prop) { | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| if(This->prototype) { | |
| hres = find_prop_name_prot(This->prototype, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(prop) { | |
| prop = alloc_protref(This, prop->name, prop - This->prototype->props); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| } | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| static HRESULT ensure_prop_name(DispatchEx *This, const WCHAR *name, BOOL search_prot, DWORD create_flags, dispex_prop_t **ret) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| if(search_prot) | |
| hres = find_prop_name_prot(This, name, &prop); | |
| else | |
| hres = find_prop_name(This, name, &prop); | |
| if(SUCCEEDED(hres) && !prop) { | |
| TRACE("creating prop %s\n", debugstr_w(name)); | |
| prop = alloc_prop(This, name, PROP_VARIANT, create_flags); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| VariantInit(&prop->u.var); | |
| } | |
| *ret = prop; | |
| return hres; | |
| } | |
| static HRESULT set_this(DISPPARAMS *dp, DISPPARAMS *olddp, IDispatch *jsthis) | |
| { | |
| VARIANTARG *oldargs; | |
| int i; | |
| static DISPID this_id = DISPID_THIS; | |
| *dp = *olddp; | |
| for(i = 0; i < dp->cNamedArgs; i++) { | |
| if(dp->rgdispidNamedArgs[i] == DISPID_THIS) | |
| return S_OK; | |
| } | |
| oldargs = dp->rgvarg; | |
| dp->rgvarg = heap_alloc((dp->cArgs+1) * sizeof(VARIANTARG)); | |
| if(!dp->rgvarg) | |
| return E_OUTOFMEMORY; | |
| memcpy(dp->rgvarg+1, oldargs, dp->cArgs*sizeof(VARIANTARG)); | |
| V_VT(dp->rgvarg) = VT_DISPATCH; | |
| V_DISPATCH(dp->rgvarg) = jsthis; | |
| dp->cArgs++; | |
| if(dp->cNamedArgs) { | |
| DISPID *old = dp->rgdispidNamedArgs; | |
| dp->rgdispidNamedArgs = heap_alloc((dp->cNamedArgs+1)*sizeof(DISPID)); | |
| if(!dp->rgdispidNamedArgs) { | |
| heap_free(dp->rgvarg); | |
| return E_OUTOFMEMORY; | |
| } | |
| memcpy(dp->rgdispidNamedArgs+1, old, dp->cNamedArgs*sizeof(DISPID)); | |
| dp->rgdispidNamedArgs[0] = DISPID_THIS; | |
| dp->cNamedArgs++; | |
| }else { | |
| dp->rgdispidNamedArgs = &this_id; | |
| dp->cNamedArgs = 1; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT invoke_prop_func(DispatchEx *This, DispatchEx *jsthis, dispex_prop_t *prop, WORD flags, | |
| DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| HRESULT hres; | |
| switch(prop->type) { | |
| case PROP_BUILTIN: { | |
| vdisp_t vthis; | |
| if(flags == DISPATCH_CONSTRUCT && (prop->flags & DISPATCH_METHOD)) { | |
| WARN("%s is not a constructor\n", debugstr_w(prop->name)); | |
| return E_INVALIDARG; | |
| } | |
| set_jsdisp(&vthis, jsthis); | |
| hres = prop->u.p->invoke(This->ctx, &vthis, flags, dp, retv, ei, caller); | |
| vdisp_release(&vthis); | |
| return hres; | |
| } | |
| case PROP_PROTREF: | |
| return invoke_prop_func(This->prototype, jsthis, This->prototype->props+prop->u.ref, flags, dp, retv, ei, caller); | |
| case PROP_VARIANT: { | |
| DISPPARAMS new_dp; | |
| if(V_VT(&prop->u.var) != VT_DISPATCH) { | |
| FIXME("invoke vt %d\n", V_VT(&prop->u.var)); | |
| return E_FAIL; | |
| } | |
| TRACE("call %s %p\n", debugstr_w(prop->name), V_DISPATCH(&prop->u.var)); | |
| hres = set_this(&new_dp, dp, (IDispatch*)_IDispatchEx_(jsthis)); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = disp_call(This->ctx, V_DISPATCH(&prop->u.var), DISPID_VALUE, flags, &new_dp, retv, ei, caller); | |
| if(new_dp.rgvarg != dp->rgvarg) { | |
| heap_free(new_dp.rgvarg); | |
| if(new_dp.cNamedArgs > 1) | |
| heap_free(new_dp.rgdispidNamedArgs); | |
| } | |
| return hres; | |
| } | |
| default: | |
| ERR("type %d\n", prop->type); | |
| } | |
| return E_FAIL; | |
| } | |
| static HRESULT prop_get(DispatchEx *This, dispex_prop_t *prop, DISPPARAMS *dp, | |
| VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| HRESULT hres; | |
| switch(prop->type) { | |
| case PROP_BUILTIN: | |
| if(prop->u.p->flags & PROPF_METHOD) { | |
| DispatchEx *obj; | |
| hres = create_builtin_function(This->ctx, prop->u.p->invoke, prop->u.p->name, NULL, | |
| prop->u.p->flags, NULL, &obj); | |
| if(FAILED(hres)) | |
| break; | |
| prop->type = PROP_VARIANT; | |
| V_VT(&prop->u.var) = VT_DISPATCH; | |
| V_DISPATCH(&prop->u.var) = (IDispatch*)_IDispatchEx_(obj); | |
| hres = VariantCopy(retv, &prop->u.var); | |
| }else { | |
| vdisp_t vthis; | |
| set_jsdisp(&vthis, This); | |
| hres = prop->u.p->invoke(This->ctx, &vthis, DISPATCH_PROPERTYGET, dp, retv, ei, caller); | |
| vdisp_release(&vthis); | |
| } | |
| break; | |
| case PROP_PROTREF: | |
| hres = prop_get(This->prototype, This->prototype->props+prop->u.ref, dp, retv, ei, caller); | |
| break; | |
| case PROP_VARIANT: | |
| hres = VariantCopy(retv, &prop->u.var); | |
| break; | |
| default: | |
| ERR("type %d\n", prop->type); | |
| return E_FAIL; | |
| } | |
| if(FAILED(hres)) { | |
| TRACE("fail %08x\n", hres); | |
| return hres; | |
| } | |
| TRACE("%s ret %s\n", debugstr_w(prop->name), debugstr_variant(retv)); | |
| return hres; | |
| } | |
| static HRESULT prop_put(DispatchEx *This, dispex_prop_t *prop, VARIANT *val, | |
| jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| HRESULT hres; | |
| switch(prop->type) { | |
| case PROP_BUILTIN: | |
| if(!(prop->flags & PROPF_METHOD)) { | |
| DISPPARAMS dp = {val, NULL, 1, 0}; | |
| vdisp_t vthis; | |
| set_jsdisp(&vthis, This); | |
| hres = prop->u.p->invoke(This->ctx, &vthis, DISPATCH_PROPERTYPUT, &dp, NULL, ei, caller); | |
| vdisp_release(&vthis); | |
| return hres; | |
| } | |
| case PROP_PROTREF: | |
| prop->type = PROP_VARIANT; | |
| prop->flags = PROPF_ENUM; | |
| V_VT(&prop->u.var) = VT_EMPTY; | |
| break; | |
| case PROP_VARIANT: | |
| VariantClear(&prop->u.var); | |
| break; | |
| default: | |
| ERR("type %d\n", prop->type); | |
| return E_FAIL; | |
| } | |
| hres = VariantCopy(&prop->u.var, val); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(This->builtin_info->on_put) | |
| This->builtin_info->on_put(This, prop->name); | |
| TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_variant(val)); | |
| return S_OK; | |
| } | |
| static HRESULT fill_protrefs(DispatchEx *This) | |
| { | |
| dispex_prop_t *iter, *prop; | |
| HRESULT hres; | |
| if(!This->prototype) | |
| return S_OK; | |
| fill_protrefs(This->prototype); | |
| for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) { | |
| if(!iter->name) | |
| continue; | |
| hres = find_prop_name(This, iter->name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!prop) { | |
| prop = alloc_protref(This, iter->name, iter - This->prototype->props); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| } | |
| } | |
| return S_OK; | |
| } | |
| #define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface) | |
| static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| if(IsEqualGUID(&IID_IUnknown, riid)) { | |
| TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); | |
| *ppv = _IDispatchEx_(This); | |
| }else if(IsEqualGUID(&IID_IDispatch, riid)) { | |
| TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); | |
| *ppv = _IDispatchEx_(This); | |
| }else if(IsEqualGUID(&IID_IDispatchEx, riid)) { | |
| TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); | |
| *ppv = _IDispatchEx_(This); | |
| }else if(IsEqualGUID(&IID_IDispatchJS, riid)) { | |
| TRACE("(%p)->(IID_IDispatchJS %p)\n", This, ppv); | |
| IUnknown_AddRef(_IDispatchEx_(This)); | |
| *ppv = This; | |
| return S_OK; | |
| }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 DispatchEx_AddRef(IDispatchEx *iface) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| LONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p) ref=%d\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| LONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p) ref=%d\n", This, ref); | |
| if(!ref) { | |
| dispex_prop_t *prop; | |
| for(prop = This->props; prop < This->props+This->prop_cnt; prop++) { | |
| if(prop->type == PROP_VARIANT) | |
| VariantClear(&prop->u.var); | |
| heap_free(prop->name); | |
| } | |
| heap_free(This->props); | |
| script_release(This->ctx); | |
| if(This->prototype) | |
| jsdisp_release(This->prototype); | |
| if(This->builtin_info->destructor) | |
| This->builtin_info->destructor(This); | |
| else | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| TRACE("(%p)->(%p)\n", This, pctinfo); | |
| *pctinfo = 1; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid, | |
| ITypeInfo **ppTInfo) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid, | |
| LPOLESTR *rgszNames, UINT cNames, LCID lcid, | |
| DISPID *rgDispId) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| UINT i; | |
| HRESULT hres; | |
| TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, | |
| lcid, rgDispId); | |
| for(i=0; i < cNames; i++) { | |
| hres = IDispatchEx_GetDispID(_IDispatchEx_(This), rgszNames[i], 0, rgDispId+i); | |
| if(FAILED(hres)) | |
| return hres; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember, | |
| REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, | |
| VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), | |
| lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); | |
| return IDispatchEx_InvokeEx(_IDispatchEx_(This), dispIdMember, lcid, wFlags, | |
| pDispParams, pVarResult, pExcepInfo, NULL); | |
| } | |
| static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); | |
| if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) { | |
| FIXME("Unsupported grfdex %x\n", grfdex); | |
| return E_NOTIMPL; | |
| } | |
| return jsdisp_get_id(This, bstrName, grfdex, pid); | |
| } | |
| static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, | |
| VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| dispex_prop_t *prop; | |
| jsexcept_t jsexcept; | |
| HRESULT hres; | |
| TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); | |
| if(pvarRes) | |
| V_VT(pvarRes) = VT_EMPTY; | |
| prop = get_prop(This, id); | |
| if(!prop || prop->type == PROP_DELETED) { | |
| TRACE("invalid id\n"); | |
| return DISP_E_MEMBERNOTFOUND; | |
| } | |
| memset(&jsexcept, 0, sizeof(jsexcept)); | |
| switch(wFlags) { | |
| case DISPATCH_METHOD: | |
| case DISPATCH_CONSTRUCT: | |
| hres = invoke_prop_func(This, This, prop, wFlags, pdp, pvarRes, &jsexcept, pspCaller); | |
| break; | |
| case DISPATCH_PROPERTYGET: | |
| hres = prop_get(This, prop, pdp, pvarRes, &jsexcept, pspCaller); | |
| break; | |
| case DISPATCH_PROPERTYPUT: { | |
| DWORD i; | |
| for(i=0; i < pdp->cNamedArgs; i++) { | |
| if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT) | |
| break; | |
| } | |
| if(i == pdp->cNamedArgs) { | |
| TRACE("no value to set\n"); | |
| return DISP_E_PARAMNOTOPTIONAL; | |
| } | |
| hres = prop_put(This, prop, pdp->rgvarg+i, &jsexcept, pspCaller); | |
| break; | |
| } | |
| default: | |
| FIXME("Unimplemented flags %x\n", wFlags); | |
| return E_INVALIDARG; | |
| } | |
| if(pei) | |
| *pei = jsexcept.ei; | |
| return hres; | |
| } | |
| static HRESULT delete_prop(dispex_prop_t *prop) | |
| { | |
| heap_free(prop->name); | |
| prop->name = NULL; | |
| prop->type = PROP_DELETED; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex); | |
| if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) | |
| FIXME("Unsupported grfdex %x\n", grfdex); | |
| hres = find_prop_name(This, bstrName, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!prop) { | |
| TRACE("not found\n"); | |
| return S_OK; | |
| } | |
| return delete_prop(prop); | |
| } | |
| static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| dispex_prop_t *prop; | |
| TRACE("(%p)->(%x)\n", This, id); | |
| prop = get_prop(This, id); | |
| if(!prop) { | |
| WARN("invalid id\n"); | |
| return DISP_E_MEMBERNOTFOUND; | |
| } | |
| return delete_prop(prop); | |
| } | |
| static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| dispex_prop_t *prop; | |
| TRACE("(%p)->(%x %p)\n", This, id, pbstrName); | |
| prop = get_prop(This, id); | |
| if(!prop || !prop->name || prop->type == PROP_DELETED) | |
| return DISP_E_MEMBERNOTFOUND; | |
| *pbstrName = SysAllocString(prop->name); | |
| if(!*pbstrName) | |
| return E_OUTOFMEMORY; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| dispex_prop_t *iter; | |
| HRESULT hres; | |
| TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid); | |
| if(id == DISPID_STARTENUM) { | |
| hres = fill_protrefs(This); | |
| if(FAILED(hres)) | |
| return hres; | |
| } | |
| iter = get_prop(This, id+1); | |
| if(!iter) { | |
| *pid = DISPID_STARTENUM; | |
| return S_FALSE; | |
| } | |
| while(iter < This->props + This->prop_cnt) { | |
| if(iter->name && (get_flags(This, iter) & PROPF_ENUM)) { | |
| *pid = prop_to_id(This, iter); | |
| return S_OK; | |
| } | |
| iter++; | |
| } | |
| *pid = DISPID_STARTENUM; | |
| return S_FALSE; | |
| } | |
| static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk) | |
| { | |
| DispatchEx *This = DISPATCHEX_THIS(iface); | |
| FIXME("(%p)->(%p)\n", This, ppunk); | |
| return E_NOTIMPL; | |
| } | |
| #undef DISPATCHEX_THIS | |
| static IDispatchExVtbl DispatchExVtbl = { | |
| DispatchEx_QueryInterface, | |
| DispatchEx_AddRef, | |
| DispatchEx_Release, | |
| DispatchEx_GetTypeInfoCount, | |
| DispatchEx_GetTypeInfo, | |
| DispatchEx_GetIDsOfNames, | |
| DispatchEx_Invoke, | |
| DispatchEx_GetDispID, | |
| DispatchEx_InvokeEx, | |
| DispatchEx_DeleteMemberByName, | |
| DispatchEx_DeleteMemberByDispID, | |
| DispatchEx_GetMemberProperties, | |
| DispatchEx_GetMemberName, | |
| DispatchEx_GetNextDispID, | |
| DispatchEx_GetNameSpaceParent | |
| }; | |
| HRESULT init_dispex(DispatchEx *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *prototype) | |
| { | |
| TRACE("%p (%p)\n", dispex, prototype); | |
| dispex->lpIDispatchExVtbl = &DispatchExVtbl; | |
| dispex->ref = 1; | |
| dispex->builtin_info = builtin_info; | |
| dispex->props = heap_alloc((dispex->buf_size=4) * sizeof(dispex_prop_t)); | |
| if(!dispex->props) | |
| return E_OUTOFMEMORY; | |
| dispex->prototype = prototype; | |
| if(prototype) | |
| IDispatchEx_AddRef(_IDispatchEx_(prototype)); | |
| dispex->prop_cnt = 1; | |
| dispex->props[0].name = NULL; | |
| dispex->props[0].flags = 0; | |
| if(builtin_info->value_prop.invoke) { | |
| dispex->props[0].type = PROP_BUILTIN; | |
| dispex->props[0].u.p = &builtin_info->value_prop; | |
| }else { | |
| dispex->props[0].type = PROP_DELETED; | |
| } | |
| script_addref(ctx); | |
| dispex->ctx = ctx; | |
| return S_OK; | |
| } | |
| static const builtin_info_t dispex_info = { | |
| JSCLASS_NONE, | |
| {NULL, NULL, 0}, | |
| 0, NULL, | |
| NULL, | |
| NULL | |
| }; | |
| HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *prototype, DispatchEx **dispex) | |
| { | |
| DispatchEx *ret; | |
| HRESULT hres; | |
| ret = heap_alloc_zero(sizeof(DispatchEx)); | |
| if(!ret) | |
| return E_OUTOFMEMORY; | |
| hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype); | |
| if(FAILED(hres)) | |
| return hres; | |
| *dispex = ret; | |
| return S_OK; | |
| } | |
| HRESULT init_dispex_from_constr(DispatchEx *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *constr) | |
| { | |
| DispatchEx *prot = NULL; | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| static const WCHAR constructorW[] = {'c','o','n','s','t','r','u','c','t','o','r'}; | |
| static const WCHAR prototypeW[] = {'p','r','o','t','o','t','y','p','e',0}; | |
| hres = find_prop_name_prot(constr, prototypeW, &prop); | |
| if(SUCCEEDED(hres) && prop) { | |
| jsexcept_t jsexcept; | |
| VARIANT var; | |
| V_VT(&var) = VT_EMPTY; | |
| memset(&jsexcept, 0, sizeof(jsexcept)); | |
| hres = prop_get(constr, prop, NULL, &var, &jsexcept, NULL/*FIXME*/); | |
| if(FAILED(hres)) { | |
| ERR("Could not get prototype\n"); | |
| return hres; | |
| } | |
| if(V_VT(&var) == VT_DISPATCH) | |
| prot = iface_to_jsdisp((IUnknown*)V_DISPATCH(&var)); | |
| VariantClear(&var); | |
| } | |
| hres = init_dispex(dispex, ctx, builtin_info, prot); | |
| if(prot) | |
| jsdisp_release(prot); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = ensure_prop_name(dispex, constructorW, FALSE, 0, &prop); | |
| if(SUCCEEDED(hres)) { | |
| jsexcept_t jsexcept; | |
| VARIANT var; | |
| V_VT(&var) = VT_DISPATCH; | |
| V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(constr); | |
| memset(&jsexcept, 0, sizeof(jsexcept)); | |
| hres = prop_put(dispex, prop, &var, &jsexcept, NULL/*FIXME*/); | |
| } | |
| if(FAILED(hres)) | |
| jsdisp_release(dispex); | |
| return hres; | |
| } | |
| DispatchEx *iface_to_jsdisp(IUnknown *iface) | |
| { | |
| DispatchEx *ret; | |
| HRESULT hres; | |
| hres = IUnknown_QueryInterface(iface, &IID_IDispatchJS, (void**)&ret); | |
| if(FAILED(hres)) | |
| return NULL; | |
| return ret; | |
| } | |
| HRESULT jsdisp_get_id(DispatchEx *jsdisp, const WCHAR *name, DWORD flags, DISPID *id) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| if(flags & fdexNameEnsure) | |
| hres = ensure_prop_name(jsdisp, name, TRUE, PROPF_ENUM, &prop); | |
| else | |
| hres = find_prop_name_prot(jsdisp, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(prop) { | |
| *id = prop_to_id(jsdisp, prop); | |
| return S_OK; | |
| } | |
| TRACE("not found %s\n", debugstr_w(name)); | |
| return DISP_E_UNKNOWNNAME; | |
| } | |
| HRESULT jsdisp_call_value(DispatchEx *jsthis, WORD flags, DISPPARAMS *dp, VARIANT *retv, | |
| jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| vdisp_t vdisp; | |
| HRESULT hres; | |
| set_jsdisp(&vdisp, jsthis); | |
| hres = jsthis->builtin_info->value_prop.invoke(jsthis->ctx, &vdisp, flags, dp, retv, ei, caller); | |
| vdisp_release(&vdisp); | |
| return hres; | |
| } | |
| HRESULT jsdisp_call(DispatchEx *disp, DISPID id, WORD flags, DISPPARAMS *dp, VARIANT *retv, | |
| jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| dispex_prop_t *prop; | |
| memset(ei, 0, sizeof(*ei)); | |
| if(retv) | |
| V_VT(retv) = VT_EMPTY; | |
| prop = get_prop(disp, id); | |
| if(!prop) | |
| return DISP_E_MEMBERNOTFOUND; | |
| return invoke_prop_func(disp, disp, prop, flags, dp, retv, ei, caller); | |
| } | |
| HRESULT jsdisp_call_name(DispatchEx *disp, const WCHAR *name, WORD flags, DISPPARAMS *dp, VARIANT *retv, | |
| jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = find_prop_name_prot(disp, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| memset(ei, 0, sizeof(*ei)); | |
| if(retv) | |
| V_VT(retv) = VT_EMPTY; | |
| return invoke_prop_func(disp, disp, prop, flags, dp, retv, ei, caller); | |
| } | |
| HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, DISPPARAMS *dp, VARIANT *retv, | |
| jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| DispatchEx *jsdisp; | |
| IDispatchEx *dispex; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp((IUnknown*)disp); | |
| if(jsdisp) { | |
| hres = jsdisp_call(jsdisp, id, flags, dp, retv, ei, caller); | |
| jsdisp_release(jsdisp); | |
| return hres; | |
| } | |
| memset(ei, 0, sizeof(*ei)); | |
| if(retv) | |
| V_VT(retv) = VT_EMPTY; | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(FAILED(hres)) { | |
| UINT err = 0; | |
| if(flags == DISPATCH_CONSTRUCT) { | |
| WARN("IDispatch cannot be constructor\n"); | |
| return DISP_E_MEMBERNOTFOUND; | |
| } | |
| TRACE("using IDispatch\n"); | |
| return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, dp, retv, &ei->ei, &err); | |
| } | |
| hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, dp, retv, &ei->ei, caller); | |
| IDispatchEx_Release(dispex); | |
| return hres; | |
| } | |
| HRESULT jsdisp_propput_name(DispatchEx *obj, const WCHAR *name, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = ensure_prop_name(obj, name, FALSE, PROPF_ENUM, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| return prop_put(obj, prop, val, ei, caller); | |
| } | |
| HRESULT jsdisp_propput_idx(DispatchEx *obj, DWORD idx, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| WCHAR buf[12]; | |
| static const WCHAR formatW[] = {'%','d',0}; | |
| sprintfW(buf, formatW, idx); | |
| return jsdisp_propput_name(obj, buf, val, ei, caller); | |
| } | |
| HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| DispatchEx *jsdisp; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp((IUnknown*)disp); | |
| if(jsdisp) { | |
| dispex_prop_t *prop; | |
| prop = get_prop(jsdisp, id); | |
| if(prop) | |
| hres = prop_put(jsdisp, prop, val, ei, caller); | |
| else | |
| hres = DISP_E_MEMBERNOTFOUND; | |
| jsdisp_release(jsdisp); | |
| }else { | |
| DISPID dispid = DISPID_PROPERTYPUT; | |
| DISPPARAMS dp = {val, &dispid, 1, 1}; | |
| IDispatchEx *dispex; | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(SUCCEEDED(hres)) { | |
| hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_PROPERTYPUT, &dp, NULL, &ei->ei, caller); | |
| IDispatchEx_Release(dispex); | |
| }else { | |
| ULONG err = 0; | |
| TRACE("using IDispatch\n"); | |
| hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, DISPATCH_PROPERTYPUT, &dp, NULL, &ei->ei, &err); | |
| } | |
| } | |
| return hres; | |
| } | |
| HRESULT jsdisp_propget_name(DispatchEx *obj, const WCHAR *name, VARIANT *var, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| DISPPARAMS dp = {NULL, NULL, 0, 0}; | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = find_prop_name_prot(obj, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| V_VT(var) = VT_EMPTY; | |
| if(!prop) | |
| return S_OK; | |
| return prop_get(obj, prop, &dp, var, ei, caller); | |
| } | |
| HRESULT jsdisp_get_idx(DispatchEx *obj, DWORD idx, VARIANT *var, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| WCHAR name[12]; | |
| DISPPARAMS dp = {NULL, NULL, 0, 0}; | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| static const WCHAR formatW[] = {'%','d',0}; | |
| sprintfW(name, formatW, idx); | |
| hres = find_prop_name_prot(obj, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| V_VT(var) = VT_EMPTY; | |
| if(!prop) | |
| return DISP_E_UNKNOWNNAME; | |
| return prop_get(obj, prop, &dp, var, ei, caller); | |
| } | |
| HRESULT jsdisp_propget(DispatchEx *jsdisp, DISPID id, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| DISPPARAMS dp = {NULL,NULL,0,0}; | |
| dispex_prop_t *prop; | |
| prop = get_prop(jsdisp, id); | |
| if(!prop) | |
| return DISP_E_MEMBERNOTFOUND; | |
| V_VT(val) = VT_EMPTY; | |
| return prop_get(jsdisp, prop, &dp, val, ei, caller); | |
| } | |
| HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) | |
| { | |
| DISPPARAMS dp = {NULL,NULL,0,0}; | |
| IDispatchEx *dispex; | |
| DispatchEx *jsdisp; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp((IUnknown*)disp); | |
| if(jsdisp) { | |
| hres = jsdisp_propget(jsdisp, id, val, ei, caller); | |
| jsdisp_release(jsdisp); | |
| return hres; | |
| } | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(FAILED(hres)) { | |
| ULONG err = 0; | |
| TRACE("using IDispatch\n"); | |
| return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, INVOKE_PROPERTYGET, &dp, val, &ei->ei, &err); | |
| } | |
| hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, INVOKE_PROPERTYGET, &dp, val, &ei->ei, caller); | |
| IDispatchEx_Release(dispex); | |
| return hres; | |
| } | |
| HRESULT jsdisp_delete_idx(DispatchEx *obj, DWORD idx) | |
| { | |
| static const WCHAR formatW[] = {'%','d',0}; | |
| WCHAR buf[12]; | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| sprintfW(buf, formatW, idx); | |
| hres = find_prop_name(obj, buf, &prop); | |
| if(FAILED(hres) || !prop) | |
| return hres; | |
| return delete_prop(prop); | |
| } |