| /* | |
| * 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 <assert.h> | |
| #include "jscript.h" | |
| #include "wine/unicode.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(jscript); | |
| #define FDEX_VERSION_MASK 0xf0000000 | |
| #define GOLDEN_RATIO 0x9E3779B9U | |
| typedef enum { | |
| PROP_JSVAL, | |
| PROP_BUILTIN, | |
| PROP_PROTREF, | |
| PROP_DELETED, | |
| PROP_IDX | |
| } prop_type_t; | |
| struct _dispex_prop_t { | |
| WCHAR *name; | |
| unsigned hash; | |
| prop_type_t type; | |
| DWORD flags; | |
| union { | |
| jsval_t val; | |
| const builtin_prop_t *p; | |
| DWORD ref; | |
| unsigned idx; | |
| } u; | |
| int bucket_head; | |
| int bucket_next; | |
| }; | |
| static inline DISPID prop_to_id(jsdisp_t *This, dispex_prop_t *prop) | |
| { | |
| return prop - This->props; | |
| } | |
| static inline dispex_prop_t *get_prop(jsdisp_t *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(jsdisp_t *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(jsdisp_t *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 inline unsigned string_hash(const WCHAR *name) | |
| { | |
| unsigned h = 0; | |
| for(; *name; name++) | |
| h = (h>>(sizeof(unsigned)*8-4)) ^ (h<<4) ^ tolowerW(*name); | |
| return h; | |
| } | |
| static inline unsigned get_props_idx(jsdisp_t *This, unsigned hash) | |
| { | |
| return (hash*GOLDEN_RATIO) & (This->buf_size-1); | |
| } | |
| static inline HRESULT resize_props(jsdisp_t *This) | |
| { | |
| dispex_prop_t *props; | |
| int i, bucket; | |
| if(This->buf_size != This->prop_cnt) | |
| return S_FALSE; | |
| props = heap_realloc(This->props, sizeof(dispex_prop_t)*This->buf_size*2); | |
| if(!props) | |
| return E_OUTOFMEMORY; | |
| This->buf_size *= 2; | |
| This->props = props; | |
| for(i=0; i<This->buf_size; i++) { | |
| This->props[i].bucket_head = 0; | |
| This->props[i].bucket_next = 0; | |
| } | |
| for(i=1; i<This->prop_cnt; i++) { | |
| props = This->props+i; | |
| bucket = get_props_idx(This, props->hash); | |
| props->bucket_next = This->props[bucket].bucket_head; | |
| This->props[bucket].bucket_head = i; | |
| } | |
| return S_OK; | |
| } | |
| static inline dispex_prop_t* alloc_prop(jsdisp_t *This, const WCHAR *name, prop_type_t type, DWORD flags) | |
| { | |
| dispex_prop_t *prop; | |
| unsigned bucket; | |
| if(FAILED(resize_props(This))) | |
| return NULL; | |
| prop = &This->props[This->prop_cnt]; | |
| prop->name = heap_strdupW(name); | |
| if(!prop->name) | |
| return NULL; | |
| prop->type = type; | |
| prop->flags = flags; | |
| prop->hash = string_hash(name); | |
| bucket = get_props_idx(This, prop->hash); | |
| prop->bucket_next = This->props[bucket].bucket_head; | |
| This->props[bucket].bucket_head = This->prop_cnt++; | |
| return prop; | |
| } | |
| static dispex_prop_t *alloc_protref(jsdisp_t *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(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret) | |
| { | |
| const builtin_prop_t *builtin; | |
| unsigned bucket, pos, prev = 0; | |
| dispex_prop_t *prop; | |
| bucket = get_props_idx(This, hash); | |
| pos = This->props[bucket].bucket_head; | |
| while(pos != 0) { | |
| if(!strcmpW(name, This->props[pos].name)) { | |
| if(prev != 0) { | |
| This->props[prev].bucket_next = This->props[pos].bucket_next; | |
| This->props[pos].bucket_next = This->props[bucket].bucket_head; | |
| This->props[bucket].bucket_head = pos; | |
| } | |
| *ret = &This->props[pos]; | |
| return S_OK; | |
| } | |
| prev = pos; | |
| pos = This->props[pos].bucket_next; | |
| } | |
| 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; | |
| } | |
| if(This->builtin_info->idx_length) { | |
| const WCHAR *ptr; | |
| unsigned idx = 0; | |
| for(ptr = name; isdigitW(*ptr) && idx < 0x10000; ptr++) | |
| idx = idx*10 + (*ptr-'0'); | |
| if(!*ptr && idx < This->builtin_info->idx_length(This)) { | |
| prop = alloc_prop(This, name, PROP_IDX, This->builtin_info->idx_put ? 0 : PROPF_CONST); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| prop->u.idx = idx; | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| } | |
| *ret = NULL; | |
| return S_OK; | |
| } | |
| static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret) | |
| { | |
| dispex_prop_t *prop, *del=NULL; | |
| HRESULT hres; | |
| hres = find_prop_name(This, hash, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(prop && prop->type==PROP_DELETED) { | |
| del = prop; | |
| } else if(prop) { | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| if(This->prototype) { | |
| hres = find_prop_name_prot(This->prototype, hash, name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(prop) { | |
| if(del) { | |
| del->type = PROP_PROTREF; | |
| del->flags = 0; | |
| del->u.ref = prop - This->prototype->props; | |
| prop = del; | |
| }else { | |
| prop = alloc_protref(This, prop->name, prop - This->prototype->props); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| } | |
| *ret = prop; | |
| return S_OK; | |
| } | |
| } | |
| *ret = del; | |
| return S_OK; | |
| } | |
| static HRESULT ensure_prop_name(jsdisp_t *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, string_hash(name), name, &prop); | |
| else | |
| hres = find_prop_name(This, string_hash(name), name, &prop); | |
| if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) { | |
| TRACE("creating prop %s flags %x\n", debugstr_w(name), create_flags); | |
| if(prop) { | |
| prop->type = PROP_JSVAL; | |
| prop->flags = create_flags; | |
| prop->u.val = jsval_undefined(); | |
| }else { | |
| prop = alloc_prop(This, name, PROP_JSVAL, create_flags); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| } | |
| prop->u.val = jsval_undefined(); | |
| } | |
| *ret = prop; | |
| return hres; | |
| } | |
| static IDispatch *get_this(DISPPARAMS *dp) | |
| { | |
| DWORD i; | |
| for(i=0; i < dp->cNamedArgs; i++) { | |
| if(dp->rgdispidNamedArgs[i] == DISPID_THIS) { | |
| if(V_VT(dp->rgvarg+i) == VT_DISPATCH) | |
| return V_DISPATCH(dp->rgvarg+i); | |
| WARN("This is not VT_DISPATCH\n"); | |
| return NULL; | |
| } | |
| } | |
| TRACE("no this passed\n"); | |
| return NULL; | |
| } | |
| static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret) | |
| { | |
| jsval_t *argv; | |
| unsigned cnt; | |
| unsigned i; | |
| HRESULT hres; | |
| cnt = dp->cArgs - dp->cNamedArgs; | |
| if(cnt > 6) { | |
| argv = heap_alloc(cnt * sizeof(*argv)); | |
| if(!argv) | |
| return E_OUTOFMEMORY; | |
| }else { | |
| argv = buf; | |
| } | |
| for(i = 0; i < cnt; i++) { | |
| hres = variant_to_jsval(dp->rgvarg+dp->cArgs-i-1, argv+i); | |
| if(FAILED(hres)) { | |
| while(i--) | |
| jsval_release(argv[i]); | |
| if(argv != buf) | |
| heap_free(argv); | |
| return hres; | |
| } | |
| } | |
| *argc = cnt; | |
| *ret = argv; | |
| return S_OK; | |
| } | |
| static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t *prop, WORD flags, | |
| unsigned argc, jsval_t *argv, jsval_t *r, IServiceProvider *caller) | |
| { | |
| HRESULT hres; | |
| switch(prop->type) { | |
| case PROP_BUILTIN: { | |
| if(flags == DISPATCH_CONSTRUCT && (prop->flags & PROPF_METHOD)) { | |
| WARN("%s is not a constructor\n", debugstr_w(prop->name)); | |
| return E_INVALIDARG; | |
| } | |
| if(prop->name || This->builtin_info->class != JSCLASS_FUNCTION) { | |
| vdisp_t vthis; | |
| if(This->builtin_info->class != JSCLASS_FUNCTION && prop->u.p->invoke != JSGlobal_eval) | |
| flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; | |
| if(jsthis) | |
| set_disp(&vthis, jsthis); | |
| else | |
| set_jsdisp(&vthis, This); | |
| hres = prop->u.p->invoke(This->ctx, &vthis, flags, argc, argv, r); | |
| vdisp_release(&vthis); | |
| }else { | |
| /* Function object calls are special case */ | |
| hres = Function_invoke(This, jsthis, flags, argc, argv, r); | |
| } | |
| return hres; | |
| } | |
| case PROP_PROTREF: | |
| return invoke_prop_func(This->prototype, jsthis, This->prototype->props+prop->u.ref, | |
| flags, argc, argv, r, caller); | |
| case PROP_JSVAL: { | |
| if(!is_object_instance(prop->u.val)) { | |
| FIXME("invoke %s\n", debugstr_jsval(prop->u.val)); | |
| return E_FAIL; | |
| } | |
| TRACE("call %s %p\n", debugstr_w(prop->name), get_object(prop->u.val)); | |
| return disp_call_value(This->ctx, get_object(prop->u.val), jsthis, flags, argc, argv, r); | |
| } | |
| case PROP_IDX: | |
| FIXME("Invoking PROP_IDX not yet supported\n"); | |
| return E_NOTIMPL; | |
| case PROP_DELETED: | |
| assert(0); | |
| } | |
| assert(0); | |
| return E_FAIL; | |
| } | |
| static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, DISPPARAMS *dp, | |
| jsval_t *r, IServiceProvider *caller) | |
| { | |
| HRESULT hres; | |
| switch(prop->type) { | |
| case PROP_BUILTIN: | |
| if(prop->u.p->getter) { | |
| hres = prop->u.p->getter(This->ctx, This, r); | |
| }else { | |
| jsdisp_t *obj; | |
| assert(prop->u.p->invoke != NULL); | |
| 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_JSVAL; | |
| prop->u.val = jsval_obj(obj); | |
| jsdisp_addref(obj); | |
| *r = jsval_obj(obj); | |
| } | |
| break; | |
| case PROP_PROTREF: | |
| hres = prop_get(This->prototype, This->prototype->props+prop->u.ref, dp, r, caller); | |
| break; | |
| case PROP_JSVAL: | |
| hres = jsval_copy(prop->u.val, r); | |
| break; | |
| case PROP_IDX: | |
| hres = This->builtin_info->idx_get(This, prop->u.idx, r); | |
| 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_jsval(*r)); | |
| return hres; | |
| } | |
| static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val, IServiceProvider *caller) | |
| { | |
| HRESULT hres; | |
| if(prop->flags & PROPF_CONST) | |
| return S_OK; | |
| switch(prop->type) { | |
| case PROP_BUILTIN: | |
| if(prop->u.p->setter) | |
| return prop->u.p->setter(This->ctx, This, val); | |
| if(prop->u.p->setter) { | |
| FIXME("getter with no setter\n"); | |
| return E_FAIL; | |
| } | |
| /* fall through */ | |
| case PROP_PROTREF: | |
| prop->type = PROP_JSVAL; | |
| prop->flags = PROPF_ENUM; | |
| prop->u.val = jsval_undefined(); | |
| break; | |
| case PROP_JSVAL: | |
| jsval_release(prop->u.val); | |
| break; | |
| case PROP_IDX: | |
| return This->builtin_info->idx_put(This, prop->u.idx, val); | |
| default: | |
| ERR("type %d\n", prop->type); | |
| return E_FAIL; | |
| } | |
| TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_jsval(val)); | |
| hres = jsval_copy(val, &prop->u.val); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(This->builtin_info->on_put) | |
| This->builtin_info->on_put(This, prop->name); | |
| return S_OK; | |
| } | |
| HRESULT builtin_set_const(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) | |
| { | |
| TRACE("%p %s\n", jsthis, debugstr_jsval(value)); | |
| return S_OK; | |
| } | |
| static HRESULT fill_protrefs(jsdisp_t *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->hash, iter->name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!prop || prop->type==PROP_DELETED) { | |
| if(prop) { | |
| prop->type = PROP_PROTREF; | |
| prop->flags = 0; | |
| prop->u.ref = iter - This->prototype->props; | |
| }else { | |
| prop = alloc_protref(This, iter->name, iter - This->prototype->props); | |
| if(!prop) | |
| return E_OUTOFMEMORY; | |
| } | |
| } | |
| } | |
| return S_OK; | |
| } | |
| static inline jsdisp_t *impl_from_IDispatchEx(IDispatchEx *iface) | |
| { | |
| return CONTAINING_RECORD(iface, jsdisp_t, IDispatchEx_iface); | |
| } | |
| static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(iface); | |
| if(IsEqualGUID(&IID_IUnknown, riid)) { | |
| TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); | |
| *ppv = &This->IDispatchEx_iface; | |
| }else if(IsEqualGUID(&IID_IDispatch, riid)) { | |
| TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); | |
| *ppv = &This->IDispatchEx_iface; | |
| }else if(IsEqualGUID(&IID_IDispatchEx, riid)) { | |
| TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); | |
| *ppv = &This->IDispatchEx_iface; | |
| }else { | |
| WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); | |
| *ppv = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| jsdisp_addref(This); | |
| return S_OK; | |
| } | |
| static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(iface); | |
| jsdisp_addref(This); | |
| return This->ref; | |
| } | |
| static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(iface); | |
| ULONG ref = --This->ref; | |
| TRACE("(%p) ref=%d\n", This, ref); | |
| if(!ref) | |
| jsdisp_free(This); | |
| return ref; | |
| } | |
| static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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(&This->IDispatchEx_iface, 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) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, | |
| pDispParams, pVarResult, pExcepInfo, NULL); | |
| } | |
| static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(iface); | |
| dispex_prop_t *prop; | |
| 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; | |
| } | |
| clear_ei(This->ctx); | |
| switch(wFlags) { | |
| case DISPATCH_METHOD|DISPATCH_PROPERTYGET: | |
| wFlags = DISPATCH_METHOD; | |
| /* fall through */ | |
| case DISPATCH_METHOD: | |
| case DISPATCH_CONSTRUCT: { | |
| jsval_t *argv, buf[6], r; | |
| unsigned argc; | |
| hres = convert_params(pdp, buf, &argc, &argv); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = invoke_prop_func(This, get_this(pdp), prop, wFlags, argc, argv, pvarRes ? &r : NULL, pspCaller); | |
| if(argv != buf) | |
| heap_free(argv); | |
| if(SUCCEEDED(hres) && pvarRes) { | |
| hres = jsval_to_variant(r, pvarRes); | |
| jsval_release(r); | |
| } | |
| break; | |
| } | |
| case DISPATCH_PROPERTYGET: { | |
| jsval_t r; | |
| hres = prop_get(This, prop, pdp, &r, pspCaller); | |
| if(SUCCEEDED(hres)) { | |
| hres = jsval_to_variant(r, pvarRes); | |
| jsval_release(r); | |
| } | |
| break; | |
| } | |
| case DISPATCH_PROPERTYPUT: { | |
| jsval_t val; | |
| 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 = variant_to_jsval(pdp->rgvarg+i, &val); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = prop_put(This, prop, val, pspCaller); | |
| jsval_release(val); | |
| break; | |
| } | |
| default: | |
| FIXME("Unimplemented flags %x\n", wFlags); | |
| return E_INVALIDARG; | |
| } | |
| if(pei) | |
| *pei = This->ctx->ei.ei; | |
| return hres; | |
| } | |
| static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret) | |
| { | |
| if(prop->flags & PROPF_DONTDELETE) { | |
| *ret = FALSE; | |
| return S_OK; | |
| } | |
| *ret = TRUE; /* FIXME: not exactly right */ | |
| if(prop->type == PROP_JSVAL) { | |
| jsval_release(prop->u.val); | |
| prop->type = PROP_DELETED; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(iface); | |
| dispex_prop_t *prop; | |
| BOOL b; | |
| 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, string_hash(bstrName), bstrName, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!prop) { | |
| TRACE("not found\n"); | |
| return S_OK; | |
| } | |
| return delete_prop(prop, &b); | |
| } | |
| static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(iface); | |
| dispex_prop_t *prop; | |
| BOOL b; | |
| 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, &b); | |
| } | |
| static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(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; | |
| } | |
| if(id+1>=0 && id+1<This->prop_cnt) { | |
| iter = &This->props[id+1]; | |
| }else { | |
| *pid = DISPID_STARTENUM; | |
| return S_FALSE; | |
| } | |
| while(iter < This->props + This->prop_cnt) { | |
| if(iter->name && (get_flags(This, iter) & PROPF_ENUM) && iter->type!=PROP_DELETED) { | |
| *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) | |
| { | |
| jsdisp_t *This = impl_from_IDispatchEx(iface); | |
| FIXME("(%p)->(%p)\n", This, ppunk); | |
| return E_NOTIMPL; | |
| } | |
| 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 | |
| }; | |
| jsdisp_t *as_jsdisp(IDispatch *disp) | |
| { | |
| assert(disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl); | |
| return impl_from_IDispatchEx((IDispatchEx*)disp); | |
| } | |
| jsdisp_t *to_jsdisp(IDispatch *disp) | |
| { | |
| return disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl ? impl_from_IDispatchEx((IDispatchEx*)disp) : NULL; | |
| } | |
| HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype) | |
| { | |
| TRACE("%p (%p)\n", dispex, prototype); | |
| dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl; | |
| dispex->ref = 1; | |
| dispex->builtin_info = builtin_info; | |
| dispex->props = heap_alloc_zero(sizeof(dispex_prop_t)*(dispex->buf_size=4)); | |
| if(!dispex->props) | |
| return E_OUTOFMEMORY; | |
| dispex->prototype = prototype; | |
| if(prototype) | |
| jsdisp_addref(prototype); | |
| dispex->prop_cnt = 1; | |
| if(builtin_info->value_prop.invoke || builtin_info->value_prop.getter) { | |
| 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, jsdisp_t *prototype, jsdisp_t **dispex) | |
| { | |
| jsdisp_t *ret; | |
| HRESULT hres; | |
| ret = heap_alloc_zero(sizeof(jsdisp_t)); | |
| if(!ret) | |
| return E_OUTOFMEMORY; | |
| hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype); | |
| if(FAILED(hres)) { | |
| heap_free(ret); | |
| return hres; | |
| } | |
| *dispex = ret; | |
| return S_OK; | |
| } | |
| void jsdisp_free(jsdisp_t *obj) | |
| { | |
| dispex_prop_t *prop; | |
| TRACE("(%p)\n", obj); | |
| for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) { | |
| if(prop->type == PROP_JSVAL) | |
| jsval_release(prop->u.val); | |
| heap_free(prop->name); | |
| } | |
| heap_free(obj->props); | |
| script_release(obj->ctx); | |
| if(obj->prototype) | |
| jsdisp_release(obj->prototype); | |
| if(obj->builtin_info->destructor) | |
| obj->builtin_info->destructor(obj); | |
| else | |
| heap_free(obj); | |
| } | |
| #ifdef TRACE_REFCNT | |
| jsdisp_t *jsdisp_addref(jsdisp_t *jsdisp) | |
| { | |
| ULONG ref = ++jsdisp->ref; | |
| TRACE("(%p) ref=%d\n", jsdisp, ref); | |
| return jsdisp; | |
| } | |
| void jsdisp_release(jsdisp_t *jsdisp) | |
| { | |
| ULONG ref = --jsdisp->ref; | |
| TRACE("(%p) ref=%d\n", jsdisp, ref); | |
| if(!ref) | |
| jsdisp_free(jsdisp); | |
| } | |
| #endif | |
| HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *constr) | |
| { | |
| jsdisp_t *prot = NULL; | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| static const WCHAR prototypeW[] = {'p','r','o','t','o','t','y','p','e',0}; | |
| hres = find_prop_name_prot(constr, string_hash(prototypeW), prototypeW, &prop); | |
| if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) { | |
| jsval_t val; | |
| hres = prop_get(constr, prop, NULL, &val, NULL); | |
| if(FAILED(hres)) { | |
| ERR("Could not get prototype\n"); | |
| return hres; | |
| } | |
| if(is_object_instance(val)) | |
| prot = iface_to_jsdisp(get_object(val)); | |
| jsval_release(val); | |
| } | |
| hres = init_dispex(dispex, ctx, builtin_info, prot); | |
| if(prot) | |
| jsdisp_release(prot); | |
| return hres; | |
| } | |
| jsdisp_t *iface_to_jsdisp(IDispatch *iface) | |
| { | |
| return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl | |
| ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface)) | |
| : NULL; | |
| } | |
| HRESULT jsdisp_get_id(jsdisp_t *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, string_hash(name), name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(prop && prop->type!=PROP_DELETED) { | |
| *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(jsdisp_t *jsfunc, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) | |
| { | |
| HRESULT hres; | |
| assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK))); | |
| if(is_class(jsfunc, JSCLASS_FUNCTION)) { | |
| hres = Function_invoke(jsfunc, jsthis, flags, argc, argv, r); | |
| }else { | |
| vdisp_t vdisp; | |
| if(!jsfunc->builtin_info->value_prop.invoke) { | |
| WARN("Not a function\n"); | |
| return throw_type_error(jsfunc->ctx, JS_E_FUNCTION_EXPECTED, NULL); | |
| } | |
| set_disp(&vdisp, jsthis); | |
| flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; | |
| hres = jsfunc->builtin_info->value_prop.invoke(jsfunc->ctx, &vdisp, flags, argc, argv, r); | |
| vdisp_release(&vdisp); | |
| } | |
| return hres; | |
| } | |
| HRESULT jsdisp_call(jsdisp_t *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) | |
| { | |
| dispex_prop_t *prop; | |
| prop = get_prop(disp, id); | |
| if(!prop) | |
| return DISP_E_MEMBERNOTFOUND; | |
| return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL); | |
| } | |
| HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = find_prop_name_prot(disp, string_hash(name), name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL); | |
| } | |
| HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret) | |
| { | |
| IDispatchEx *dispex; | |
| jsdisp_t *jsdisp; | |
| VARIANT buf[6], retv; | |
| DISPPARAMS dp; | |
| unsigned i; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp(disp); | |
| if(jsdisp) { | |
| if(flags & DISPATCH_PROPERTYPUT) { | |
| FIXME("disp_call(propput) on builtin object\n"); | |
| return E_FAIL; | |
| } | |
| hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret); | |
| jsdisp_release(jsdisp); | |
| return hres; | |
| } | |
| flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; | |
| if(ret && argc) | |
| flags |= DISPATCH_PROPERTYGET; | |
| dp.cArgs = argc; | |
| if(flags & DISPATCH_PROPERTYPUT) { | |
| static DISPID propput_dispid = DISPID_PROPERTYPUT; | |
| dp.cNamedArgs = 1; | |
| dp.rgdispidNamedArgs = &propput_dispid; | |
| }else { | |
| dp.cNamedArgs = 0; | |
| dp.rgdispidNamedArgs = NULL; | |
| } | |
| if(argc > 6) { | |
| dp.rgvarg = heap_alloc(argc*sizeof(VARIANT)); | |
| if(!dp.rgvarg) | |
| return E_OUTOFMEMORY; | |
| }else { | |
| dp.rgvarg = buf; | |
| } | |
| for(i=0; i<argc; i++) { | |
| hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1); | |
| if(FAILED(hres)) { | |
| while(i--) | |
| VariantClear(dp.rgvarg+argc-i-1); | |
| if(dp.rgvarg != buf) | |
| heap_free(dp.rgvarg); | |
| return hres; | |
| } | |
| } | |
| V_VT(&retv) = VT_EMPTY; | |
| clear_ei(ctx); | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(SUCCEEDED(hres)) { | |
| hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei, | |
| &ctx->jscaller->IServiceProvider_iface); | |
| IDispatchEx_Release(dispex); | |
| }else { | |
| UINT err = 0; | |
| if(flags == DISPATCH_CONSTRUCT) { | |
| WARN("IDispatch cannot be constructor\n"); | |
| return DISP_E_MEMBERNOTFOUND; | |
| } | |
| TRACE("using IDispatch\n"); | |
| hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei, &err); | |
| } | |
| for(i=0; i<argc; i++) | |
| VariantClear(dp.rgvarg+argc-i-1); | |
| if(dp.rgvarg != buf) | |
| heap_free(dp.rgvarg); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(ret) { | |
| hres = variant_to_jsval(&retv, ret); | |
| VariantClear(&retv); | |
| } | |
| return hres; | |
| } | |
| HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, | |
| jsval_t *r) | |
| { | |
| jsdisp_t *jsdisp; | |
| IDispatchEx *dispex; | |
| VARIANT buf[6], retv; | |
| DISPPARAMS dp; | |
| unsigned i; | |
| HRESULT hres; | |
| assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK))); | |
| jsdisp = iface_to_jsdisp(disp); | |
| if(jsdisp) { | |
| hres = jsdisp_call_value(jsdisp, jsthis, flags, argc, argv, r); | |
| jsdisp_release(jsdisp); | |
| return hres; | |
| } | |
| flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK; | |
| if(r && argc && flags == DISPATCH_METHOD) | |
| flags |= DISPATCH_PROPERTYGET; | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(FAILED(hres)) { | |
| TRACE("using IDispatch\n"); | |
| dispex = NULL; | |
| jsthis = NULL; | |
| } | |
| if(jsthis) { | |
| static DISPID this_id = DISPID_THIS; | |
| dp.cArgs = argc+1; | |
| dp.cNamedArgs = 1; | |
| dp.rgdispidNamedArgs = &this_id; | |
| }else { | |
| dp.cArgs = argc; | |
| dp.cNamedArgs = 0; | |
| dp.rgdispidNamedArgs = NULL; | |
| } | |
| if(dp.cArgs > sizeof(buf)/sizeof(*buf)) { | |
| dp.rgvarg = heap_alloc(dp.cArgs*sizeof(VARIANT)); | |
| if(!dp.rgvarg) { | |
| if(dispex) | |
| IDispatchEx_Release(dispex); | |
| return E_OUTOFMEMORY; | |
| } | |
| }else { | |
| dp.rgvarg = buf; | |
| } | |
| for(i=0; i<argc; i++) { | |
| hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1); | |
| if(FAILED(hres)) { | |
| while(i--) | |
| VariantClear(dp.rgvarg+dp.cArgs-i-1); | |
| if(dp.rgvarg != buf) | |
| heap_free(dp.rgvarg); | |
| if(dispex) | |
| IDispatchEx_Release(dispex); | |
| return hres; | |
| } | |
| } | |
| if(jsthis) { | |
| V_VT(dp.rgvarg) = VT_DISPATCH; | |
| V_DISPATCH(dp.rgvarg) = jsthis; | |
| } | |
| V_VT(&retv) = VT_EMPTY; | |
| clear_ei(ctx); | |
| if(dispex) { | |
| hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei, | |
| &ctx->jscaller->IServiceProvider_iface); | |
| IDispatchEx_Release(dispex); | |
| }else { | |
| UINT err = 0; | |
| if(flags == DISPATCH_CONSTRUCT) { | |
| WARN("IDispatch cannot be constructor\n"); | |
| return DISP_E_MEMBERNOTFOUND; | |
| } | |
| hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei, &err); | |
| } | |
| for(i=0; i<argc; i++) | |
| VariantClear(dp.rgvarg+dp.cArgs-i-1); | |
| if(dp.rgvarg != buf) | |
| heap_free(dp.rgvarg); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!r) | |
| return S_OK; | |
| hres = variant_to_jsval(&retv, r); | |
| VariantClear(&retv); | |
| return hres; | |
| } | |
| HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, jsval_t val) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = ensure_prop_name(obj, name, FALSE, flags, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| return prop_put(obj, prop, val, NULL); | |
| } | |
| HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val) | |
| { | |
| return jsdisp_propput(obj, name, PROPF_ENUM, val); | |
| } | |
| HRESULT jsdisp_propput_const(jsdisp_t *obj, const WCHAR *name, jsval_t val) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = ensure_prop_name(obj, name, FALSE, PROPF_CONST, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| return jsval_copy(val, &prop->u.val); | |
| } | |
| HRESULT jsdisp_propput_dontenum(jsdisp_t *obj, const WCHAR *name, jsval_t val) | |
| { | |
| return jsdisp_propput(obj, name, 0, val); | |
| } | |
| HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val) | |
| { | |
| WCHAR buf[12]; | |
| static const WCHAR formatW[] = {'%','d',0}; | |
| sprintfW(buf, formatW, idx); | |
| return jsdisp_propput_name(obj, buf, val); | |
| } | |
| HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val) | |
| { | |
| jsdisp_t *jsdisp; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp(disp); | |
| if(jsdisp) { | |
| dispex_prop_t *prop; | |
| prop = get_prop(jsdisp, id); | |
| if(prop) | |
| hres = prop_put(jsdisp, prop, val, NULL); | |
| else | |
| hres = DISP_E_MEMBERNOTFOUND; | |
| jsdisp_release(jsdisp); | |
| }else { | |
| DISPID dispid = DISPID_PROPERTYPUT; | |
| DWORD flags = DISPATCH_PROPERTYPUT; | |
| VARIANT var; | |
| DISPPARAMS dp = {&var, &dispid, 1, 1}; | |
| IDispatchEx *dispex; | |
| hres = jsval_to_variant(val, &var); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(V_VT(&var) == VT_DISPATCH) | |
| flags |= DISPATCH_PROPERTYPUTREF; | |
| clear_ei(ctx); | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(SUCCEEDED(hres)) { | |
| hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei, | |
| &ctx->jscaller->IServiceProvider_iface); | |
| IDispatchEx_Release(dispex); | |
| }else { | |
| ULONG err = 0; | |
| TRACE("using IDispatch\n"); | |
| hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei, &err); | |
| } | |
| VariantClear(&var); | |
| } | |
| return hres; | |
| } | |
| HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val) | |
| { | |
| DISPPARAMS dp = {NULL, NULL, 0, 0}; | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = find_prop_name_prot(obj, string_hash(name), name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!prop || prop->type==PROP_DELETED) { | |
| *val = jsval_undefined(); | |
| return S_OK; | |
| } | |
| return prop_get(obj, prop, &dp, val, NULL); | |
| } | |
| HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r) | |
| { | |
| 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, string_hash(name), name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| if(!prop || prop->type==PROP_DELETED) { | |
| *r = jsval_undefined(); | |
| return DISP_E_UNKNOWNNAME; | |
| } | |
| return prop_get(obj, prop, &dp, r, NULL); | |
| } | |
| HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val) | |
| { | |
| DISPPARAMS dp = {NULL,NULL,0,0}; | |
| dispex_prop_t *prop; | |
| prop = get_prop(jsdisp, id); | |
| if(!prop) | |
| return DISP_E_MEMBERNOTFOUND; | |
| return prop_get(jsdisp, prop, &dp, val, NULL); | |
| } | |
| HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val) | |
| { | |
| DISPPARAMS dp = {NULL,NULL,0,0}; | |
| IDispatchEx *dispex; | |
| jsdisp_t *jsdisp; | |
| VARIANT var; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp(disp); | |
| if(jsdisp) { | |
| hres = jsdisp_propget(jsdisp, id, val); | |
| jsdisp_release(jsdisp); | |
| return hres; | |
| } | |
| V_VT(&var) = VT_EMPTY; | |
| clear_ei(ctx); | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(SUCCEEDED(hres)) { | |
| hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei, | |
| &ctx->jscaller->IServiceProvider_iface); | |
| IDispatchEx_Release(dispex); | |
| }else { | |
| ULONG err = 0; | |
| TRACE("using IDispatch\n"); | |
| hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei, &err); | |
| } | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = variant_to_jsval(&var, val); | |
| VariantClear(&var); | |
| return hres; | |
| } | |
| HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx) | |
| { | |
| static const WCHAR formatW[] = {'%','d',0}; | |
| WCHAR buf[12]; | |
| dispex_prop_t *prop; | |
| BOOL b; | |
| HRESULT hres; | |
| sprintfW(buf, formatW, idx); | |
| hres = find_prop_name(obj, string_hash(buf), buf, &prop); | |
| if(FAILED(hres) || !prop) | |
| return hres; | |
| return delete_prop(prop, &b); | |
| } | |
| HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret) | |
| { | |
| IDispatchEx *dispex; | |
| jsdisp_t *jsdisp; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp(disp); | |
| if(jsdisp) { | |
| dispex_prop_t *prop; | |
| prop = get_prop(jsdisp, id); | |
| if(prop) | |
| hres = delete_prop(prop, ret); | |
| else | |
| hres = DISP_E_MEMBERNOTFOUND; | |
| jsdisp_release(jsdisp); | |
| return hres; | |
| } | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(FAILED(hres)) { | |
| *ret = FALSE; | |
| return S_OK; | |
| } | |
| hres = IDispatchEx_DeleteMemberByDispID(dispex, id); | |
| IDispatchEx_Release(dispex); | |
| if(FAILED(hres)) | |
| return hres; | |
| *ret = hres == S_OK; | |
| return S_OK; | |
| } | |
| HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret) | |
| { | |
| IDispatchEx *dispex; | |
| jsdisp_t *jsdisp; | |
| BSTR bstr; | |
| HRESULT hres; | |
| jsdisp = iface_to_jsdisp(disp); | |
| if(jsdisp) { | |
| dispex_prop_t *prop; | |
| const WCHAR *ptr; | |
| ptr = jsstr_flatten(name); | |
| if(!ptr) { | |
| jsdisp_release(jsdisp); | |
| return E_OUTOFMEMORY; | |
| } | |
| hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop); | |
| if(prop) { | |
| hres = delete_prop(prop, ret); | |
| }else { | |
| *ret = TRUE; | |
| hres = S_OK; | |
| } | |
| jsdisp_release(jsdisp); | |
| return hres; | |
| } | |
| bstr = SysAllocStringLen(NULL, jsstr_length(name)); | |
| if(!bstr) | |
| return E_OUTOFMEMORY; | |
| jsstr_flush(name, bstr); | |
| hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); | |
| if(SUCCEEDED(hres)) { | |
| hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive)); | |
| if(SUCCEEDED(hres)) | |
| *ret = hres == S_OK; | |
| IDispatchEx_Release(dispex); | |
| }else { | |
| DISPID id; | |
| hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id); | |
| if(SUCCEEDED(hres)) { | |
| /* Property exists and we can't delete it from pure IDispatch interface, so return false. */ | |
| *ret = FALSE; | |
| }else if(hres == DISP_E_UNKNOWNNAME) { | |
| /* Property doesn't exist, so nothing to delete */ | |
| *ret = TRUE; | |
| hres = S_OK; | |
| } | |
| } | |
| SysFreeString(bstr); | |
| return hres; | |
| } | |
| HRESULT jsdisp_is_own_prop(jsdisp_t *obj, const WCHAR *name, BOOL *ret) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = find_prop_name(obj, string_hash(name), name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| *ret = prop && (prop->type == PROP_JSVAL || prop->type == PROP_BUILTIN); | |
| return S_OK; | |
| } | |
| HRESULT jsdisp_is_enumerable(jsdisp_t *obj, const WCHAR *name, BOOL *ret) | |
| { | |
| dispex_prop_t *prop; | |
| HRESULT hres; | |
| hres = find_prop_name(obj, string_hash(name), name, &prop); | |
| if(FAILED(hres)) | |
| return hres; | |
| *ret = prop && (prop->flags & PROPF_ENUM) && prop->type != PROP_PROTREF; | |
| return S_OK; | |
| } |