2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define FDEX_VERSION_MASK 0xf0000000
22 #define GOLDEN_RATIO 0x9E3779B9U
32 struct _dispex_prop_t
{
40 const builtin_prop_t
*p
;
49 static inline DISPID
prop_to_id(jsdisp_t
*This
, dispex_prop_t
*prop
)
51 return prop
- This
->props
;
54 static inline dispex_prop_t
*get_prop(jsdisp_t
*This
, DISPID id
)
56 if(id
< 0 || id
>= This
->prop_cnt
|| This
->props
[id
].type
== PROP_DELETED
)
59 return This
->props
+id
;
62 static DWORD
get_flags(jsdisp_t
*This
, dispex_prop_t
*prop
)
64 if(prop
->type
== PROP_PROTREF
) {
65 dispex_prop_t
*parent
= get_prop(This
->prototype
, prop
->u
.ref
);
67 prop
->type
= PROP_DELETED
;
71 return get_flags(This
->prototype
, parent
);
77 static const builtin_prop_t
*find_builtin_prop(jsdisp_t
*This
, const WCHAR
*name
)
79 int min
= 0, max
, i
, r
;
81 max
= This
->builtin_info
->props_cnt
-1;
85 r
= strcmpW(name
, This
->builtin_info
->props
[i
].name
);
87 return This
->builtin_info
->props
+ i
;
98 static inline unsigned string_hash(const WCHAR
*name
)
102 h
= (h
>>(sizeof(unsigned)*8-4)) ^ (h
<<4) ^ tolowerW(*name
);
106 static inline unsigned get_props_idx(jsdisp_t
*This
, unsigned hash
)
108 return (hash
*GOLDEN_RATIO
) & (This
->buf_size
-1);
111 static inline HRESULT
resize_props(jsdisp_t
*This
)
113 dispex_prop_t
*props
;
116 if(This
->buf_size
!= This
->prop_cnt
)
119 props
= heap_realloc(This
->props
, sizeof(dispex_prop_t
)*This
->buf_size
*2);
121 return E_OUTOFMEMORY
;
125 for(i
=0; i
<This
->buf_size
; i
++) {
126 This
->props
[i
].bucket_head
= 0;
127 This
->props
[i
].bucket_next
= 0;
130 for(i
=1; i
<This
->prop_cnt
; i
++) {
131 props
= This
->props
+i
;
133 bucket
= get_props_idx(This
, props
->hash
);
134 props
->bucket_next
= This
->props
[bucket
].bucket_head
;
135 This
->props
[bucket
].bucket_head
= i
;
141 static inline dispex_prop_t
* alloc_prop(jsdisp_t
*This
, const WCHAR
*name
, prop_type_t type
, DWORD flags
)
146 if(FAILED(resize_props(This
)))
149 prop
= &This
->props
[This
->prop_cnt
];
150 prop
->name
= heap_strdupW(name
);
155 prop
->hash
= string_hash(name
);
157 bucket
= get_props_idx(This
, prop
->hash
);
158 prop
->bucket_next
= This
->props
[bucket
].bucket_head
;
159 This
->props
[bucket
].bucket_head
= This
->prop_cnt
++;
163 static dispex_prop_t
*alloc_protref(jsdisp_t
*This
, const WCHAR
*name
, DWORD ref
)
167 ret
= alloc_prop(This
, name
, PROP_PROTREF
, 0);
175 static HRESULT
find_prop_name(jsdisp_t
*This
, unsigned hash
, const WCHAR
*name
, dispex_prop_t
**ret
)
177 const builtin_prop_t
*builtin
;
178 unsigned bucket
, pos
, prev
= 0;
181 bucket
= get_props_idx(This
, hash
);
182 pos
= This
->props
[bucket
].bucket_head
;
184 if(!strcmpW(name
, This
->props
[pos
].name
)) {
186 This
->props
[prev
].bucket_next
= This
->props
[pos
].bucket_next
;
187 This
->props
[pos
].bucket_next
= This
->props
[bucket
].bucket_head
;
188 This
->props
[bucket
].bucket_head
= pos
;
191 *ret
= &This
->props
[pos
];
196 pos
= This
->props
[pos
].bucket_next
;
199 builtin
= find_builtin_prop(This
, name
);
201 prop
= alloc_prop(This
, name
, PROP_BUILTIN
, builtin
->flags
);
203 return E_OUTOFMEMORY
;
210 if(This
->builtin_info
->idx_length
) {
214 for(ptr
= name
; isdigitW(*ptr
) && idx
< 0x10000; ptr
++)
215 idx
= idx
*10 + (*ptr
-'0');
216 if(!*ptr
&& idx
< This
->builtin_info
->idx_length(This
)) {
217 prop
= alloc_prop(This
, name
, PROP_IDX
, This
->builtin_info
->idx_put
? 0 : PROPF_CONST
);
219 return E_OUTOFMEMORY
;
231 static HRESULT
find_prop_name_prot(jsdisp_t
*This
, unsigned hash
, const WCHAR
*name
, dispex_prop_t
**ret
)
233 dispex_prop_t
*prop
, *del
=NULL
;
236 hres
= find_prop_name(This
, hash
, name
, &prop
);
239 if(prop
&& prop
->type
==PROP_DELETED
) {
246 if(This
->prototype
) {
247 hres
= find_prop_name_prot(This
->prototype
, hash
, name
, &prop
);
252 del
->type
= PROP_PROTREF
;
254 del
->u
.ref
= prop
- This
->prototype
->props
;
257 prop
= alloc_protref(This
, prop
->name
, prop
- This
->prototype
->props
);
259 return E_OUTOFMEMORY
;
271 static HRESULT
ensure_prop_name(jsdisp_t
*This
, const WCHAR
*name
, BOOL search_prot
, DWORD create_flags
, dispex_prop_t
**ret
)
277 hres
= find_prop_name_prot(This
, string_hash(name
), name
, &prop
);
279 hres
= find_prop_name(This
, string_hash(name
), name
, &prop
);
280 if(SUCCEEDED(hres
) && (!prop
|| prop
->type
== PROP_DELETED
)) {
281 TRACE("creating prop %s flags %x\n", debugstr_w(name
), create_flags
);
284 prop
->type
= PROP_JSVAL
;
285 prop
->flags
= create_flags
;
286 prop
->u
.val
= jsval_undefined();
288 prop
= alloc_prop(This
, name
, PROP_JSVAL
, create_flags
);
290 return E_OUTOFMEMORY
;
293 prop
->u
.val
= jsval_undefined();
300 static IDispatch
*get_this(DISPPARAMS
*dp
)
304 for(i
=0; i
< dp
->cNamedArgs
; i
++) {
305 if(dp
->rgdispidNamedArgs
[i
] == DISPID_THIS
) {
306 if(V_VT(dp
->rgvarg
+i
) == VT_DISPATCH
)
307 return V_DISPATCH(dp
->rgvarg
+i
);
309 WARN("This is not VT_DISPATCH\n");
314 TRACE("no this passed\n");
318 static HRESULT
convert_params(const DISPPARAMS
*dp
, jsval_t
*buf
, unsigned *argc
, jsval_t
**ret
)
325 cnt
= dp
->cArgs
- dp
->cNamedArgs
;
328 argv
= heap_alloc(cnt
* sizeof(*argv
));
330 return E_OUTOFMEMORY
;
335 for(i
= 0; i
< cnt
; i
++) {
336 hres
= variant_to_jsval(dp
->rgvarg
+dp
->cArgs
-i
-1, argv
+i
);
339 jsval_release(argv
[i
]);
351 static HRESULT
invoke_prop_func(jsdisp_t
*This
, IDispatch
*jsthis
, dispex_prop_t
*prop
, WORD flags
,
352 unsigned argc
, jsval_t
*argv
, jsval_t
*r
, IServiceProvider
*caller
)
358 if(flags
== DISPATCH_CONSTRUCT
&& (prop
->flags
& PROPF_METHOD
)) {
359 WARN("%s is not a constructor\n", debugstr_w(prop
->name
));
363 if(prop
->name
|| This
->builtin_info
->class != JSCLASS_FUNCTION
) {
366 if(This
->builtin_info
->class != JSCLASS_FUNCTION
&& prop
->u
.p
->invoke
!= JSGlobal_eval
)
367 flags
&= ~DISPATCH_JSCRIPT_INTERNAL_MASK
;
369 set_disp(&vthis
, jsthis
);
371 set_jsdisp(&vthis
, This
);
372 hres
= prop
->u
.p
->invoke(This
->ctx
, &vthis
, flags
, argc
, argv
, r
);
373 vdisp_release(&vthis
);
375 /* Function object calls are special case */
376 hres
= Function_invoke(This
, jsthis
, flags
, argc
, argv
, r
);
381 return invoke_prop_func(This
->prototype
, jsthis
, This
->prototype
->props
+prop
->u
.ref
,
382 flags
, argc
, argv
, r
, caller
);
384 if(!is_object_instance(prop
->u
.val
)) {
385 FIXME("invoke %s\n", debugstr_jsval(prop
->u
.val
));
389 TRACE("call %s %p\n", debugstr_w(prop
->name
), get_object(prop
->u
.val
));
391 return disp_call_value(This
->ctx
, get_object(prop
->u
.val
), jsthis
, flags
, argc
, argv
, r
);
394 FIXME("Invoking PROP_IDX not yet supported\n");
404 static HRESULT
prop_get(jsdisp_t
*This
, dispex_prop_t
*prop
, DISPPARAMS
*dp
,
405 jsval_t
*r
, IServiceProvider
*caller
)
411 if(prop
->u
.p
->getter
) {
412 hres
= prop
->u
.p
->getter(This
->ctx
, This
, r
);
416 assert(prop
->u
.p
->invoke
!= NULL
);
417 hres
= create_builtin_function(This
->ctx
, prop
->u
.p
->invoke
, prop
->u
.p
->name
, NULL
,
418 prop
->u
.p
->flags
, NULL
, &obj
);
422 prop
->type
= PROP_JSVAL
;
423 prop
->u
.val
= jsval_obj(obj
);
430 hres
= prop_get(This
->prototype
, This
->prototype
->props
+prop
->u
.ref
, dp
, r
, caller
);
433 hres
= jsval_copy(prop
->u
.val
, r
);
436 hres
= This
->builtin_info
->idx_get(This
, prop
->u
.idx
, r
);
439 ERR("type %d\n", prop
->type
);
444 TRACE("fail %08x\n", hres
);
448 TRACE("%s ret %s\n", debugstr_w(prop
->name
), debugstr_jsval(*r
));
452 static HRESULT
prop_put(jsdisp_t
*This
, dispex_prop_t
*prop
, jsval_t val
, IServiceProvider
*caller
)
456 if(prop
->flags
& PROPF_CONST
)
461 if(prop
->u
.p
->setter
)
462 return prop
->u
.p
->setter(This
->ctx
, This
, val
);
464 if(prop
->u
.p
->setter
) {
465 FIXME("getter with no setter\n");
470 prop
->type
= PROP_JSVAL
;
471 prop
->flags
= PROPF_ENUM
;
472 prop
->u
.val
= jsval_undefined();
475 jsval_release(prop
->u
.val
);
478 return This
->builtin_info
->idx_put(This
, prop
->u
.idx
, val
);
480 ERR("type %d\n", prop
->type
);
484 TRACE("%s = %s\n", debugstr_w(prop
->name
), debugstr_jsval(val
));
486 hres
= jsval_copy(val
, &prop
->u
.val
);
490 if(This
->builtin_info
->on_put
)
491 This
->builtin_info
->on_put(This
, prop
->name
);
496 HRESULT
builtin_set_const(script_ctx_t
*ctx
, jsdisp_t
*jsthis
, jsval_t value
)
498 TRACE("%p %s\n", jsthis
, debugstr_jsval(value
));
502 static HRESULT
fill_protrefs(jsdisp_t
*This
)
504 dispex_prop_t
*iter
, *prop
;
510 fill_protrefs(This
->prototype
);
512 for(iter
= This
->prototype
->props
; iter
< This
->prototype
->props
+This
->prototype
->prop_cnt
; iter
++) {
515 hres
= find_prop_name(This
, iter
->hash
, iter
->name
, &prop
);
518 if(!prop
|| prop
->type
==PROP_DELETED
) {
520 prop
->type
= PROP_PROTREF
;
522 prop
->u
.ref
= iter
- This
->prototype
->props
;
524 prop
= alloc_protref(This
, iter
->name
, iter
- This
->prototype
->props
);
526 return E_OUTOFMEMORY
;
534 static inline jsdisp_t
*impl_from_IDispatchEx(IDispatchEx
*iface
)
536 return CONTAINING_RECORD(iface
, jsdisp_t
, IDispatchEx_iface
);
539 static HRESULT WINAPI
DispatchEx_QueryInterface(IDispatchEx
*iface
, REFIID riid
, void **ppv
)
541 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
543 if(IsEqualGUID(&IID_IUnknown
, riid
)) {
544 TRACE("(%p)->(IID_IUnknown %p)\n", This
, ppv
);
545 *ppv
= &This
->IDispatchEx_iface
;
546 }else if(IsEqualGUID(&IID_IDispatch
, riid
)) {
547 TRACE("(%p)->(IID_IDispatch %p)\n", This
, ppv
);
548 *ppv
= &This
->IDispatchEx_iface
;
549 }else if(IsEqualGUID(&IID_IDispatchEx
, riid
)) {
550 TRACE("(%p)->(IID_IDispatchEx %p)\n", This
, ppv
);
551 *ppv
= &This
->IDispatchEx_iface
;
553 WARN("(%p)->(%s %p)\n", This
, debugstr_guid(riid
), ppv
);
555 return E_NOINTERFACE
;
562 static ULONG WINAPI
DispatchEx_AddRef(IDispatchEx
*iface
)
564 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
569 static ULONG WINAPI
DispatchEx_Release(IDispatchEx
*iface
)
571 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
572 ULONG ref
= --This
->ref
;
573 TRACE("(%p) ref=%d\n", This
, ref
);
579 static HRESULT WINAPI
DispatchEx_GetTypeInfoCount(IDispatchEx
*iface
, UINT
*pctinfo
)
581 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
583 TRACE("(%p)->(%p)\n", This
, pctinfo
);
589 static HRESULT WINAPI
DispatchEx_GetTypeInfo(IDispatchEx
*iface
, UINT iTInfo
, LCID lcid
,
592 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
593 FIXME("(%p)->(%u %u %p)\n", This
, iTInfo
, lcid
, ppTInfo
);
597 static HRESULT WINAPI
DispatchEx_GetIDsOfNames(IDispatchEx
*iface
, REFIID riid
,
598 LPOLESTR
*rgszNames
, UINT cNames
, LCID lcid
,
601 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
605 TRACE("(%p)->(%s %p %u %u %p)\n", This
, debugstr_guid(riid
), rgszNames
, cNames
,
608 for(i
=0; i
< cNames
; i
++) {
609 hres
= IDispatchEx_GetDispID(&This
->IDispatchEx_iface
, rgszNames
[i
], 0, rgDispId
+i
);
617 static HRESULT WINAPI
DispatchEx_Invoke(IDispatchEx
*iface
, DISPID dispIdMember
,
618 REFIID riid
, LCID lcid
, WORD wFlags
, DISPPARAMS
*pDispParams
,
619 VARIANT
*pVarResult
, EXCEPINFO
*pExcepInfo
, UINT
*puArgErr
)
621 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
623 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This
, dispIdMember
, debugstr_guid(riid
),
624 lcid
, wFlags
, pDispParams
, pVarResult
, pExcepInfo
, puArgErr
);
626 return IDispatchEx_InvokeEx(&This
->IDispatchEx_iface
, dispIdMember
, lcid
, wFlags
,
627 pDispParams
, pVarResult
, pExcepInfo
, NULL
);
630 static HRESULT WINAPI
DispatchEx_GetDispID(IDispatchEx
*iface
, BSTR bstrName
, DWORD grfdex
, DISPID
*pid
)
632 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
634 TRACE("(%p)->(%s %x %p)\n", This
, debugstr_w(bstrName
), grfdex
, pid
);
636 if(grfdex
& ~(fdexNameCaseSensitive
|fdexNameEnsure
|fdexNameImplicit
|FDEX_VERSION_MASK
)) {
637 FIXME("Unsupported grfdex %x\n", grfdex
);
641 return jsdisp_get_id(This
, bstrName
, grfdex
, pid
);
644 static HRESULT WINAPI
DispatchEx_InvokeEx(IDispatchEx
*iface
, DISPID id
, LCID lcid
, WORD wFlags
, DISPPARAMS
*pdp
,
645 VARIANT
*pvarRes
, EXCEPINFO
*pei
, IServiceProvider
*pspCaller
)
647 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
651 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This
, id
, lcid
, wFlags
, pdp
, pvarRes
, pei
, pspCaller
);
654 V_VT(pvarRes
) = VT_EMPTY
;
656 prop
= get_prop(This
, id
);
657 if(!prop
|| prop
->type
== PROP_DELETED
) {
658 TRACE("invalid id\n");
659 return DISP_E_MEMBERNOTFOUND
;
665 case DISPATCH_METHOD
|DISPATCH_PROPERTYGET
:
666 wFlags
= DISPATCH_METHOD
;
668 case DISPATCH_METHOD
:
669 case DISPATCH_CONSTRUCT
: {
670 jsval_t
*argv
, buf
[6], r
;
673 hres
= convert_params(pdp
, buf
, &argc
, &argv
);
677 hres
= invoke_prop_func(This
, get_this(pdp
), prop
, wFlags
, argc
, argv
, pvarRes
? &r
: NULL
, pspCaller
);
680 if(SUCCEEDED(hres
) && pvarRes
) {
681 hres
= jsval_to_variant(r
, pvarRes
);
686 case DISPATCH_PROPERTYGET
: {
689 hres
= prop_get(This
, prop
, pdp
, &r
, pspCaller
);
690 if(SUCCEEDED(hres
)) {
691 hres
= jsval_to_variant(r
, pvarRes
);
696 case DISPATCH_PROPERTYPUT
: {
700 for(i
=0; i
< pdp
->cNamedArgs
; i
++) {
701 if(pdp
->rgdispidNamedArgs
[i
] == DISPID_PROPERTYPUT
)
705 if(i
== pdp
->cNamedArgs
) {
706 TRACE("no value to set\n");
707 return DISP_E_PARAMNOTOPTIONAL
;
710 hres
= variant_to_jsval(pdp
->rgvarg
+i
, &val
);
714 hres
= prop_put(This
, prop
, val
, pspCaller
);
719 FIXME("Unimplemented flags %x\n", wFlags
);
724 *pei
= This
->ctx
->ei
.ei
;
728 static HRESULT
delete_prop(dispex_prop_t
*prop
, BOOL
*ret
)
730 if(prop
->flags
& PROPF_DONTDELETE
) {
735 *ret
= TRUE
; /* FIXME: not exactly right */
737 if(prop
->type
== PROP_JSVAL
) {
738 jsval_release(prop
->u
.val
);
739 prop
->type
= PROP_DELETED
;
744 static HRESULT WINAPI
DispatchEx_DeleteMemberByName(IDispatchEx
*iface
, BSTR bstrName
, DWORD grfdex
)
746 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
751 TRACE("(%p)->(%s %x)\n", This
, debugstr_w(bstrName
), grfdex
);
753 if(grfdex
& ~(fdexNameCaseSensitive
|fdexNameEnsure
|fdexNameImplicit
|FDEX_VERSION_MASK
))
754 FIXME("Unsupported grfdex %x\n", grfdex
);
756 hres
= find_prop_name(This
, string_hash(bstrName
), bstrName
, &prop
);
760 TRACE("not found\n");
764 return delete_prop(prop
, &b
);
767 static HRESULT WINAPI
DispatchEx_DeleteMemberByDispID(IDispatchEx
*iface
, DISPID id
)
769 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
773 TRACE("(%p)->(%x)\n", This
, id
);
775 prop
= get_prop(This
, id
);
777 WARN("invalid id\n");
778 return DISP_E_MEMBERNOTFOUND
;
781 return delete_prop(prop
, &b
);
784 static HRESULT WINAPI
DispatchEx_GetMemberProperties(IDispatchEx
*iface
, DISPID id
, DWORD grfdexFetch
, DWORD
*pgrfdex
)
786 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
787 FIXME("(%p)->(%x %x %p)\n", This
, id
, grfdexFetch
, pgrfdex
);
791 static HRESULT WINAPI
DispatchEx_GetMemberName(IDispatchEx
*iface
, DISPID id
, BSTR
*pbstrName
)
793 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
796 TRACE("(%p)->(%x %p)\n", This
, id
, pbstrName
);
798 prop
= get_prop(This
, id
);
799 if(!prop
|| !prop
->name
|| prop
->type
== PROP_DELETED
)
800 return DISP_E_MEMBERNOTFOUND
;
802 *pbstrName
= SysAllocString(prop
->name
);
804 return E_OUTOFMEMORY
;
809 static HRESULT WINAPI
DispatchEx_GetNextDispID(IDispatchEx
*iface
, DWORD grfdex
, DISPID id
, DISPID
*pid
)
811 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
815 TRACE("(%p)->(%x %x %p)\n", This
, grfdex
, id
, pid
);
817 if(id
== DISPID_STARTENUM
) {
818 hres
= fill_protrefs(This
);
823 if(id
+1>=0 && id
+1<This
->prop_cnt
) {
824 iter
= &This
->props
[id
+1];
826 *pid
= DISPID_STARTENUM
;
830 while(iter
< This
->props
+ This
->prop_cnt
) {
831 if(iter
->name
&& (get_flags(This
, iter
) & PROPF_ENUM
) && iter
->type
!=PROP_DELETED
) {
832 *pid
= prop_to_id(This
, iter
);
838 *pid
= DISPID_STARTENUM
;
842 static HRESULT WINAPI
DispatchEx_GetNameSpaceParent(IDispatchEx
*iface
, IUnknown
**ppunk
)
844 jsdisp_t
*This
= impl_from_IDispatchEx(iface
);
845 FIXME("(%p)->(%p)\n", This
, ppunk
);
849 static IDispatchExVtbl DispatchExVtbl
= {
850 DispatchEx_QueryInterface
,
853 DispatchEx_GetTypeInfoCount
,
854 DispatchEx_GetTypeInfo
,
855 DispatchEx_GetIDsOfNames
,
857 DispatchEx_GetDispID
,
859 DispatchEx_DeleteMemberByName
,
860 DispatchEx_DeleteMemberByDispID
,
861 DispatchEx_GetMemberProperties
,
862 DispatchEx_GetMemberName
,
863 DispatchEx_GetNextDispID
,
864 DispatchEx_GetNameSpaceParent
867 jsdisp_t
*as_jsdisp(IDispatch
*disp
)
869 assert(disp
->lpVtbl
== (IDispatchVtbl
*)&DispatchExVtbl
);
870 return impl_from_IDispatchEx((IDispatchEx
*)disp
);
873 jsdisp_t
*to_jsdisp(IDispatch
*disp
)
875 return disp
->lpVtbl
== (IDispatchVtbl
*)&DispatchExVtbl
? impl_from_IDispatchEx((IDispatchEx
*)disp
) : NULL
;
878 HRESULT
init_dispex(jsdisp_t
*dispex
, script_ctx_t
*ctx
, const builtin_info_t
*builtin_info
, jsdisp_t
*prototype
)
880 TRACE("%p (%p)\n", dispex
, prototype
);
882 dispex
->IDispatchEx_iface
.lpVtbl
= &DispatchExVtbl
;
884 dispex
->builtin_info
= builtin_info
;
886 dispex
->props
= heap_alloc_zero(sizeof(dispex_prop_t
)*(dispex
->buf_size
=4));
888 return E_OUTOFMEMORY
;
890 dispex
->prototype
= prototype
;
892 jsdisp_addref(prototype
);
894 dispex
->prop_cnt
= 1;
895 if(builtin_info
->value_prop
.invoke
|| builtin_info
->value_prop
.getter
) {
896 dispex
->props
[0].type
= PROP_BUILTIN
;
897 dispex
->props
[0].u
.p
= &builtin_info
->value_prop
;
899 dispex
->props
[0].type
= PROP_DELETED
;
908 static const builtin_info_t dispex_info
= {
916 HRESULT
create_dispex(script_ctx_t
*ctx
, const builtin_info_t
*builtin_info
, jsdisp_t
*prototype
, jsdisp_t
**dispex
)
921 ret
= heap_alloc_zero(sizeof(jsdisp_t
));
923 return E_OUTOFMEMORY
;
925 hres
= init_dispex(ret
, ctx
, builtin_info
? builtin_info
: &dispex_info
, prototype
);
935 void jsdisp_free(jsdisp_t
*obj
)
939 TRACE("(%p)\n", obj
);
941 for(prop
= obj
->props
; prop
< obj
->props
+obj
->prop_cnt
; prop
++) {
942 if(prop
->type
== PROP_JSVAL
)
943 jsval_release(prop
->u
.val
);
944 heap_free(prop
->name
);
946 heap_free(obj
->props
);
947 script_release(obj
->ctx
);
949 jsdisp_release(obj
->prototype
);
951 if(obj
->builtin_info
->destructor
)
952 obj
->builtin_info
->destructor(obj
);
959 jsdisp_t
*jsdisp_addref(jsdisp_t
*jsdisp
)
961 ULONG ref
= ++jsdisp
->ref
;
962 TRACE("(%p) ref=%d\n", jsdisp
, ref
);
966 void jsdisp_release(jsdisp_t
*jsdisp
)
968 ULONG ref
= --jsdisp
->ref
;
970 TRACE("(%p) ref=%d\n", jsdisp
, ref
);
978 HRESULT
init_dispex_from_constr(jsdisp_t
*dispex
, script_ctx_t
*ctx
, const builtin_info_t
*builtin_info
, jsdisp_t
*constr
)
980 jsdisp_t
*prot
= NULL
;
984 static const WCHAR prototypeW
[] = {'p','r','o','t','o','t','y','p','e',0};
986 hres
= find_prop_name_prot(constr
, string_hash(prototypeW
), prototypeW
, &prop
);
987 if(SUCCEEDED(hres
) && prop
&& prop
->type
!=PROP_DELETED
) {
990 hres
= prop_get(constr
, prop
, NULL
, &val
, NULL
);
992 ERR("Could not get prototype\n");
996 if(is_object_instance(val
))
997 prot
= iface_to_jsdisp(get_object(val
));
1001 hres
= init_dispex(dispex
, ctx
, builtin_info
, prot
);
1004 jsdisp_release(prot
);
1008 jsdisp_t
*iface_to_jsdisp(IDispatch
*iface
)
1010 return iface
->lpVtbl
== (const IDispatchVtbl
*)&DispatchExVtbl
1011 ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx
*)iface
))
1015 HRESULT
jsdisp_get_id(jsdisp_t
*jsdisp
, const WCHAR
*name
, DWORD flags
, DISPID
*id
)
1017 dispex_prop_t
*prop
;
1020 if(flags
& fdexNameEnsure
)
1021 hres
= ensure_prop_name(jsdisp
, name
, TRUE
, PROPF_ENUM
, &prop
);
1023 hres
= find_prop_name_prot(jsdisp
, string_hash(name
), name
, &prop
);
1027 if(prop
&& prop
->type
!=PROP_DELETED
) {
1028 *id
= prop_to_id(jsdisp
, prop
);
1032 TRACE("not found %s\n", debugstr_w(name
));
1033 return DISP_E_UNKNOWNNAME
;
1036 HRESULT
jsdisp_call_value(jsdisp_t
*jsfunc
, IDispatch
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1040 assert(!(flags
& ~(DISPATCH_METHOD
|DISPATCH_CONSTRUCT
|DISPATCH_JSCRIPT_INTERNAL_MASK
)));
1042 if(is_class(jsfunc
, JSCLASS_FUNCTION
)) {
1043 hres
= Function_invoke(jsfunc
, jsthis
, flags
, argc
, argv
, r
);
1047 if(!jsfunc
->builtin_info
->value_prop
.invoke
) {
1048 WARN("Not a function\n");
1049 return throw_type_error(jsfunc
->ctx
, JS_E_FUNCTION_EXPECTED
, NULL
);
1052 set_disp(&vdisp
, jsthis
);
1053 flags
&= ~DISPATCH_JSCRIPT_INTERNAL_MASK
;
1054 hres
= jsfunc
->builtin_info
->value_prop
.invoke(jsfunc
->ctx
, &vdisp
, flags
, argc
, argv
, r
);
1055 vdisp_release(&vdisp
);
1060 HRESULT
jsdisp_call(jsdisp_t
*disp
, DISPID id
, WORD flags
, unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1062 dispex_prop_t
*prop
;
1064 prop
= get_prop(disp
, id
);
1066 return DISP_E_MEMBERNOTFOUND
;
1068 return invoke_prop_func(disp
, to_disp(disp
), prop
, flags
, argc
, argv
, r
, NULL
);
1071 HRESULT
jsdisp_call_name(jsdisp_t
*disp
, const WCHAR
*name
, WORD flags
, unsigned argc
, jsval_t
*argv
, jsval_t
*r
)
1073 dispex_prop_t
*prop
;
1076 hres
= find_prop_name_prot(disp
, string_hash(name
), name
, &prop
);
1080 return invoke_prop_func(disp
, to_disp(disp
), prop
, flags
, argc
, argv
, r
, NULL
);
1083 HRESULT
disp_call(script_ctx_t
*ctx
, IDispatch
*disp
, DISPID id
, WORD flags
, unsigned argc
, jsval_t
*argv
, jsval_t
*ret
)
1085 IDispatchEx
*dispex
;
1087 VARIANT buf
[6], retv
;
1092 jsdisp
= iface_to_jsdisp(disp
);
1094 if(flags
& DISPATCH_PROPERTYPUT
) {
1095 FIXME("disp_call(propput) on builtin object\n");
1099 if(ctx
!= jsdisp
->ctx
)
1100 flags
&= ~DISPATCH_JSCRIPT_INTERNAL_MASK
;
1101 hres
= jsdisp_call(jsdisp
, id
, flags
, argc
, argv
, ret
);
1102 jsdisp_release(jsdisp
);
1106 flags
&= ~DISPATCH_JSCRIPT_INTERNAL_MASK
;
1108 flags
|= DISPATCH_PROPERTYGET
;
1112 if(flags
& DISPATCH_PROPERTYPUT
) {
1113 static DISPID propput_dispid
= DISPID_PROPERTYPUT
;
1116 dp
.rgdispidNamedArgs
= &propput_dispid
;
1119 dp
.rgdispidNamedArgs
= NULL
;
1123 dp
.rgvarg
= heap_alloc(argc
*sizeof(VARIANT
));
1125 return E_OUTOFMEMORY
;
1130 for(i
=0; i
<argc
; i
++) {
1131 hres
= jsval_to_variant(argv
[i
], dp
.rgvarg
+argc
-i
-1);
1134 VariantClear(dp
.rgvarg
+argc
-i
-1);
1135 if(dp
.rgvarg
!= buf
)
1136 heap_free(dp
.rgvarg
);
1141 V_VT(&retv
) = VT_EMPTY
;
1143 hres
= IDispatch_QueryInterface(disp
, &IID_IDispatchEx
, (void**)&dispex
);
1144 if(SUCCEEDED(hres
)) {
1145 hres
= IDispatchEx_InvokeEx(dispex
, id
, ctx
->lcid
, flags
, &dp
, ret
? &retv
: NULL
, &ctx
->ei
.ei
,
1146 &ctx
->jscaller
->IServiceProvider_iface
);
1147 IDispatchEx_Release(dispex
);
1151 if(flags
== DISPATCH_CONSTRUCT
) {
1152 WARN("IDispatch cannot be constructor\n");
1153 return DISP_E_MEMBERNOTFOUND
;
1156 TRACE("using IDispatch\n");
1157 hres
= IDispatch_Invoke(disp
, id
, &IID_NULL
, ctx
->lcid
, flags
, &dp
, ret
? &retv
: NULL
, &ctx
->ei
.ei
, &err
);
1160 for(i
=0; i
<argc
; i
++)
1161 VariantClear(dp
.rgvarg
+argc
-i
-1);
1162 if(dp
.rgvarg
!= buf
)
1163 heap_free(dp
.rgvarg
);
1168 hres
= variant_to_jsval(&retv
, ret
);
1169 VariantClear(&retv
);
1175 HRESULT
disp_call_value(script_ctx_t
*ctx
, IDispatch
*disp
, IDispatch
*jsthis
, WORD flags
, unsigned argc
, jsval_t
*argv
,
1179 IDispatchEx
*dispex
;
1180 VARIANT buf
[6], retv
;
1185 assert(!(flags
& ~(DISPATCH_METHOD
|DISPATCH_CONSTRUCT
|DISPATCH_JSCRIPT_INTERNAL_MASK
)));
1187 jsdisp
= iface_to_jsdisp(disp
);
1189 if(ctx
!= jsdisp
->ctx
)
1190 flags
&= ~DISPATCH_JSCRIPT_INTERNAL_MASK
;
1191 hres
= jsdisp_call_value(jsdisp
, jsthis
, flags
, argc
, argv
, r
);
1192 jsdisp_release(jsdisp
);
1196 flags
&= ~DISPATCH_JSCRIPT_INTERNAL_MASK
;
1197 if(r
&& argc
&& flags
== DISPATCH_METHOD
)
1198 flags
|= DISPATCH_PROPERTYGET
;
1200 hres
= IDispatch_QueryInterface(disp
, &IID_IDispatchEx
, (void**)&dispex
);
1202 TRACE("using IDispatch\n");
1208 static DISPID this_id
= DISPID_THIS
;
1212 dp
.rgdispidNamedArgs
= &this_id
;
1216 dp
.rgdispidNamedArgs
= NULL
;
1219 if(dp
.cArgs
> sizeof(buf
)/sizeof(*buf
)) {
1220 dp
.rgvarg
= heap_alloc(dp
.cArgs
*sizeof(VARIANT
));
1223 IDispatchEx_Release(dispex
);
1224 return E_OUTOFMEMORY
;
1230 for(i
=0; i
<argc
; i
++) {
1231 hres
= jsval_to_variant(argv
[i
], dp
.rgvarg
+dp
.cArgs
-i
-1);
1234 VariantClear(dp
.rgvarg
+dp
.cArgs
-i
-1);
1235 if(dp
.rgvarg
!= buf
)
1236 heap_free(dp
.rgvarg
);
1238 IDispatchEx_Release(dispex
);
1243 V_VT(dp
.rgvarg
) = VT_DISPATCH
;
1244 V_DISPATCH(dp
.rgvarg
) = jsthis
;
1247 V_VT(&retv
) = VT_EMPTY
;
1250 hres
= IDispatchEx_InvokeEx(dispex
, DISPID_VALUE
, ctx
->lcid
, flags
, &dp
, r
? &retv
: NULL
, &ctx
->ei
.ei
,
1251 &ctx
->jscaller
->IServiceProvider_iface
);
1252 IDispatchEx_Release(dispex
);
1256 if(flags
== DISPATCH_CONSTRUCT
) {
1257 WARN("IDispatch cannot be constructor\n");
1258 return DISP_E_MEMBERNOTFOUND
;
1261 hres
= IDispatch_Invoke(disp
, DISPID_VALUE
, &IID_NULL
, ctx
->lcid
, flags
, &dp
, r
? &retv
: NULL
, &ctx
->ei
.ei
, &err
);
1264 for(i
=0; i
<argc
; i
++)
1265 VariantClear(dp
.rgvarg
+dp
.cArgs
-i
-1);
1266 if(dp
.rgvarg
!= buf
)
1267 heap_free(dp
.rgvarg
);
1274 hres
= variant_to_jsval(&retv
, r
);
1275 VariantClear(&retv
);
1279 HRESULT
jsdisp_propput(jsdisp_t
*obj
, const WCHAR
*name
, DWORD flags
, jsval_t val
)
1281 dispex_prop_t
*prop
;
1284 hres
= ensure_prop_name(obj
, name
, FALSE
, flags
, &prop
);
1288 return prop_put(obj
, prop
, val
, NULL
);
1291 HRESULT
jsdisp_propput_name(jsdisp_t
*obj
, const WCHAR
*name
, jsval_t val
)
1293 return jsdisp_propput(obj
, name
, PROPF_ENUM
, val
);
1296 HRESULT
jsdisp_propput_const(jsdisp_t
*obj
, const WCHAR
*name
, jsval_t val
)
1298 dispex_prop_t
*prop
;
1301 hres
= ensure_prop_name(obj
, name
, FALSE
, PROPF_CONST
, &prop
);
1305 return jsval_copy(val
, &prop
->u
.val
);
1308 HRESULT
jsdisp_propput_dontenum(jsdisp_t
*obj
, const WCHAR
*name
, jsval_t val
)
1310 return jsdisp_propput(obj
, name
, 0, val
);
1313 HRESULT
jsdisp_propput_idx(jsdisp_t
*obj
, DWORD idx
, jsval_t val
)
1317 static const WCHAR formatW
[] = {'%','d',0};
1319 sprintfW(buf
, formatW
, idx
);
1320 return jsdisp_propput_name(obj
, buf
, val
);
1323 HRESULT
disp_propput(script_ctx_t
*ctx
, IDispatch
*disp
, DISPID id
, jsval_t val
)
1328 jsdisp
= iface_to_jsdisp(disp
);
1330 dispex_prop_t
*prop
;
1332 prop
= get_prop(jsdisp
, id
);
1334 hres
= prop_put(jsdisp
, prop
, val
, NULL
);
1336 hres
= DISP_E_MEMBERNOTFOUND
;
1338 jsdisp_release(jsdisp
);
1340 DISPID dispid
= DISPID_PROPERTYPUT
;
1341 DWORD flags
= DISPATCH_PROPERTYPUT
;
1343 DISPPARAMS dp
= {&var
, &dispid
, 1, 1};
1344 IDispatchEx
*dispex
;
1346 hres
= jsval_to_variant(val
, &var
);
1350 if(V_VT(&var
) == VT_DISPATCH
)
1351 flags
|= DISPATCH_PROPERTYPUTREF
;
1354 hres
= IDispatch_QueryInterface(disp
, &IID_IDispatchEx
, (void**)&dispex
);
1355 if(SUCCEEDED(hres
)) {
1356 hres
= IDispatchEx_InvokeEx(dispex
, id
, ctx
->lcid
, flags
, &dp
, NULL
, &ctx
->ei
.ei
,
1357 &ctx
->jscaller
->IServiceProvider_iface
);
1358 IDispatchEx_Release(dispex
);
1362 TRACE("using IDispatch\n");
1363 hres
= IDispatch_Invoke(disp
, id
, &IID_NULL
, ctx
->lcid
, flags
, &dp
, NULL
, &ctx
->ei
.ei
, &err
);
1372 HRESULT
jsdisp_propget_name(jsdisp_t
*obj
, const WCHAR
*name
, jsval_t
*val
)
1374 DISPPARAMS dp
= {NULL
, NULL
, 0, 0};
1375 dispex_prop_t
*prop
;
1378 hres
= find_prop_name_prot(obj
, string_hash(name
), name
, &prop
);
1382 if(!prop
|| prop
->type
==PROP_DELETED
) {
1383 *val
= jsval_undefined();
1387 return prop_get(obj
, prop
, &dp
, val
, NULL
);
1390 HRESULT
jsdisp_get_idx(jsdisp_t
*obj
, DWORD idx
, jsval_t
*r
)
1393 DISPPARAMS dp
= {NULL
, NULL
, 0, 0};
1394 dispex_prop_t
*prop
;
1397 static const WCHAR formatW
[] = {'%','d',0};
1399 sprintfW(name
, formatW
, idx
);
1401 hres
= find_prop_name_prot(obj
, string_hash(name
), name
, &prop
);
1405 if(!prop
|| prop
->type
==PROP_DELETED
) {
1406 *r
= jsval_undefined();
1407 return DISP_E_UNKNOWNNAME
;
1410 return prop_get(obj
, prop
, &dp
, r
, NULL
);
1413 HRESULT
jsdisp_propget(jsdisp_t
*jsdisp
, DISPID id
, jsval_t
*val
)
1415 DISPPARAMS dp
= {NULL
,NULL
,0,0};
1416 dispex_prop_t
*prop
;
1418 prop
= get_prop(jsdisp
, id
);
1420 return DISP_E_MEMBERNOTFOUND
;
1422 return prop_get(jsdisp
, prop
, &dp
, val
, NULL
);
1425 HRESULT
disp_propget(script_ctx_t
*ctx
, IDispatch
*disp
, DISPID id
, jsval_t
*val
)
1427 DISPPARAMS dp
= {NULL
,NULL
,0,0};
1428 IDispatchEx
*dispex
;
1433 jsdisp
= iface_to_jsdisp(disp
);
1435 hres
= jsdisp_propget(jsdisp
, id
, val
);
1436 jsdisp_release(jsdisp
);
1440 V_VT(&var
) = VT_EMPTY
;
1442 hres
= IDispatch_QueryInterface(disp
, &IID_IDispatchEx
, (void**)&dispex
);
1443 if(SUCCEEDED(hres
)) {
1444 hres
= IDispatchEx_InvokeEx(dispex
, id
, ctx
->lcid
, INVOKE_PROPERTYGET
, &dp
, &var
, &ctx
->ei
.ei
,
1445 &ctx
->jscaller
->IServiceProvider_iface
);
1446 IDispatchEx_Release(dispex
);
1450 TRACE("using IDispatch\n");
1451 hres
= IDispatch_Invoke(disp
, id
, &IID_NULL
, ctx
->lcid
, INVOKE_PROPERTYGET
, &dp
, &var
, &ctx
->ei
.ei
, &err
);
1456 hres
= variant_to_jsval(&var
, val
);
1461 HRESULT
jsdisp_delete_idx(jsdisp_t
*obj
, DWORD idx
)
1463 static const WCHAR formatW
[] = {'%','d',0};
1465 dispex_prop_t
*prop
;
1469 sprintfW(buf
, formatW
, idx
);
1471 hres
= find_prop_name(obj
, string_hash(buf
), buf
, &prop
);
1472 if(FAILED(hres
) || !prop
)
1475 return delete_prop(prop
, &b
);
1478 HRESULT
disp_delete(IDispatch
*disp
, DISPID id
, BOOL
*ret
)
1480 IDispatchEx
*dispex
;
1484 jsdisp
= iface_to_jsdisp(disp
);
1486 dispex_prop_t
*prop
;
1488 prop
= get_prop(jsdisp
, id
);
1490 hres
= delete_prop(prop
, ret
);
1492 hres
= DISP_E_MEMBERNOTFOUND
;
1494 jsdisp_release(jsdisp
);
1498 hres
= IDispatch_QueryInterface(disp
, &IID_IDispatchEx
, (void**)&dispex
);
1504 hres
= IDispatchEx_DeleteMemberByDispID(dispex
, id
);
1505 IDispatchEx_Release(dispex
);
1509 *ret
= hres
== S_OK
;
1513 HRESULT
disp_delete_name(script_ctx_t
*ctx
, IDispatch
*disp
, jsstr_t
*name
, BOOL
*ret
)
1515 IDispatchEx
*dispex
;
1520 jsdisp
= iface_to_jsdisp(disp
);
1522 dispex_prop_t
*prop
;
1525 ptr
= jsstr_flatten(name
);
1527 jsdisp_release(jsdisp
);
1528 return E_OUTOFMEMORY
;
1531 hres
= find_prop_name(jsdisp
, string_hash(ptr
), ptr
, &prop
);
1533 hres
= delete_prop(prop
, ret
);
1539 jsdisp_release(jsdisp
);
1543 bstr
= SysAllocStringLen(NULL
, jsstr_length(name
));
1545 return E_OUTOFMEMORY
;
1546 jsstr_flush(name
, bstr
);
1548 hres
= IDispatch_QueryInterface(disp
, &IID_IDispatchEx
, (void**)&dispex
);
1549 if(SUCCEEDED(hres
)) {
1550 hres
= IDispatchEx_DeleteMemberByName(dispex
, bstr
, make_grfdex(ctx
, fdexNameCaseSensitive
));
1552 *ret
= hres
== S_OK
;
1553 IDispatchEx_Release(dispex
);
1557 hres
= IDispatch_GetIDsOfNames(disp
, &IID_NULL
, &bstr
, 1, 0, &id
);
1558 if(SUCCEEDED(hres
)) {
1559 /* Property exists and we can't delete it from pure IDispatch interface, so return false. */
1561 }else if(hres
== DISP_E_UNKNOWNNAME
) {
1562 /* Property doesn't exist, so nothing to delete */
1568 SysFreeString(bstr
);
1572 HRESULT
jsdisp_is_own_prop(jsdisp_t
*obj
, const WCHAR
*name
, BOOL
*ret
)
1574 dispex_prop_t
*prop
;
1577 hres
= find_prop_name(obj
, string_hash(name
), name
, &prop
);
1581 *ret
= prop
&& (prop
->type
== PROP_JSVAL
|| prop
->type
== PROP_BUILTIN
);
1585 HRESULT
jsdisp_is_enumerable(jsdisp_t
*obj
, const WCHAR
*name
, BOOL
*ret
)
1587 dispex_prop_t
*prop
;
1590 hres
= find_prop_name(obj
, string_hash(name
), name
, &prop
);
1594 *ret
= prop
&& (prop
->flags
& PROPF_ENUM
) && prop
->type
!= PROP_PROTREF
;