xref: /reactos/dll/win32/jscript/dispex.c (revision 19b18ce2)
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 #include <assert.h>
20 
21 #include "jscript.h"
22 
23 #include "wine/unicode.h"
24 #include "wine/debug.h"
25 
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
27 
28 #define FDEX_VERSION_MASK 0xf0000000
29 #define GOLDEN_RATIO 0x9E3779B9U
30 
31 typedef enum {
32     PROP_JSVAL,
33     PROP_BUILTIN,
34     PROP_PROTREF,
35     PROP_DELETED,
36     PROP_IDX
37 } prop_type_t;
38 
39 struct _dispex_prop_t {
40     WCHAR *name;
41     unsigned hash;
42     prop_type_t type;
43     DWORD flags;
44 
45     union {
46         jsval_t val;
47         const builtin_prop_t *p;
48         DWORD ref;
49         unsigned idx;
50     } u;
51 
52     int bucket_head;
53     int bucket_next;
54 };
55 
56 static inline DISPID prop_to_id(jsdisp_t *This, dispex_prop_t *prop)
57 {
58     return prop - This->props;
59 }
60 
61 static inline dispex_prop_t *get_prop(jsdisp_t *This, DISPID id)
62 {
63     if(id < 0 || id >= This->prop_cnt || This->props[id].type == PROP_DELETED)
64         return NULL;
65 
66     return This->props+id;
67 }
68 
69 static DWORD get_flags(jsdisp_t *This, dispex_prop_t *prop)
70 {
71     if(prop->type == PROP_PROTREF) {
72         dispex_prop_t *parent = get_prop(This->prototype, prop->u.ref);
73         if(!parent) {
74             prop->type = PROP_DELETED;
75             return 0;
76         }
77 
78         return get_flags(This->prototype, parent);
79     }
80 
81     return prop->flags;
82 }
83 
84 static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name)
85 {
86     int min = 0, max, i, r;
87 
88     max = This->builtin_info->props_cnt-1;
89     while(min <= max) {
90         i = (min+max)/2;
91 
92         r = strcmpW(name, This->builtin_info->props[i].name);
93         if(!r) {
94             /* Skip prop if it's available only in higher compatibility mode. */
95             unsigned version = (This->builtin_info->props[i].flags & PROPF_VERSION_MASK)
96                 >> PROPF_VERSION_SHIFT;
97             if(version && version > This->ctx->version)
98                 return NULL;
99 
100             /* Skip prop if it's available only in HTML mode and we're not running in HTML mode. */
101             if((This->builtin_info->props[i].flags & PROPF_HTML) && !This->ctx->html_mode)
102                 return NULL;
103 
104             return This->builtin_info->props + i;
105         }
106 
107         if(r < 0)
108             max = i-1;
109         else
110             min = i+1;
111     }
112 
113     return NULL;
114 }
115 
116 static inline unsigned string_hash(const WCHAR *name)
117 {
118     unsigned h = 0;
119     for(; *name; name++)
120         h = (h>>(sizeof(unsigned)*8-4)) ^ (h<<4) ^ tolowerW(*name);
121     return h;
122 }
123 
124 static inline unsigned get_props_idx(jsdisp_t *This, unsigned hash)
125 {
126     return (hash*GOLDEN_RATIO) & (This->buf_size-1);
127 }
128 
129 static inline HRESULT resize_props(jsdisp_t *This)
130 {
131     dispex_prop_t *props;
132     int i, bucket;
133 
134     if(This->buf_size != This->prop_cnt)
135         return S_FALSE;
136 
137     props = heap_realloc(This->props, sizeof(dispex_prop_t)*This->buf_size*2);
138     if(!props)
139         return E_OUTOFMEMORY;
140     This->buf_size *= 2;
141     This->props = props;
142 
143     for(i=0; i<This->buf_size; i++) {
144         This->props[i].bucket_head = 0;
145         This->props[i].bucket_next = 0;
146     }
147 
148     for(i=1; i<This->prop_cnt; i++) {
149         props = This->props+i;
150 
151         bucket = get_props_idx(This, props->hash);
152         props->bucket_next = This->props[bucket].bucket_head;
153         This->props[bucket].bucket_head = i;
154     }
155 
156     return S_OK;
157 }
158 
159 static inline dispex_prop_t* alloc_prop(jsdisp_t *This, const WCHAR *name, prop_type_t type, DWORD flags)
160 {
161     dispex_prop_t *prop;
162     unsigned bucket;
163 
164     if(FAILED(resize_props(This)))
165         return NULL;
166 
167     prop = &This->props[This->prop_cnt];
168     prop->name = heap_strdupW(name);
169     if(!prop->name)
170         return NULL;
171     prop->type = type;
172     prop->flags = flags;
173     prop->hash = string_hash(name);
174 
175     bucket = get_props_idx(This, prop->hash);
176     prop->bucket_next = This->props[bucket].bucket_head;
177     This->props[bucket].bucket_head = This->prop_cnt++;
178     return prop;
179 }
180 
181 static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref)
182 {
183     dispex_prop_t *ret;
184 
185     ret = alloc_prop(This, name, PROP_PROTREF, 0);
186     if(!ret)
187         return NULL;
188 
189     ret->u.ref = ref;
190     return ret;
191 }
192 
193 static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret)
194 {
195     const builtin_prop_t *builtin;
196     unsigned bucket, pos, prev = 0;
197     dispex_prop_t *prop;
198 
199     bucket = get_props_idx(This, hash);
200     pos = This->props[bucket].bucket_head;
201     while(pos != 0) {
202         if(!strcmpW(name, This->props[pos].name)) {
203             if(prev != 0) {
204                 This->props[prev].bucket_next = This->props[pos].bucket_next;
205                 This->props[pos].bucket_next = This->props[bucket].bucket_head;
206                 This->props[bucket].bucket_head = pos;
207             }
208 
209             *ret = &This->props[pos];
210             return S_OK;
211         }
212 
213         prev = pos;
214         pos = This->props[pos].bucket_next;
215     }
216 
217     builtin = find_builtin_prop(This, name);
218     if(builtin) {
219         prop = alloc_prop(This, name, PROP_BUILTIN, builtin->flags);
220         if(!prop)
221             return E_OUTOFMEMORY;
222 
223         prop->u.p = builtin;
224         *ret = prop;
225         return S_OK;
226     }
227 
228     if(This->builtin_info->idx_length) {
229         const WCHAR *ptr;
230         unsigned idx = 0;
231 
232         for(ptr = name; isdigitW(*ptr) && idx < 0x10000; ptr++)
233             idx = idx*10 + (*ptr-'0');
234         if(!*ptr && idx < This->builtin_info->idx_length(This)) {
235             prop = alloc_prop(This, name, PROP_IDX, This->builtin_info->idx_put ? 0 : PROPF_CONST);
236             if(!prop)
237                 return E_OUTOFMEMORY;
238 
239             prop->u.idx = idx;
240             *ret = prop;
241             return S_OK;
242         }
243     }
244 
245     *ret = NULL;
246     return S_OK;
247 }
248 
249 static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret)
250 {
251     dispex_prop_t *prop, *del=NULL;
252     HRESULT hres;
253 
254     hres = find_prop_name(This, hash, name, &prop);
255     if(FAILED(hres))
256         return hres;
257     if(prop && prop->type==PROP_DELETED) {
258         del = prop;
259     } else if(prop) {
260         *ret = prop;
261         return S_OK;
262     }
263 
264     if(This->prototype) {
265         hres = find_prop_name_prot(This->prototype, hash, name, &prop);
266         if(FAILED(hres))
267             return hres;
268         if(prop) {
269             if(del) {
270                 del->type = PROP_PROTREF;
271                 del->flags = 0;
272                 del->u.ref = prop - This->prototype->props;
273                 prop = del;
274             }else {
275                 prop = alloc_protref(This, prop->name, prop - This->prototype->props);
276                 if(!prop)
277                     return E_OUTOFMEMORY;
278             }
279 
280             *ret = prop;
281             return S_OK;
282         }
283     }
284 
285     *ret = del;
286     return S_OK;
287 }
288 
289 static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, BOOL search_prot, DWORD create_flags, dispex_prop_t **ret)
290 {
291     dispex_prop_t *prop;
292     HRESULT hres;
293 
294     if(search_prot)
295         hres = find_prop_name_prot(This, string_hash(name), name, &prop);
296     else
297         hres = find_prop_name(This, string_hash(name), name, &prop);
298     if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) {
299         TRACE("creating prop %s flags %x\n", debugstr_w(name), create_flags);
300 
301         if(prop) {
302             prop->type = PROP_JSVAL;
303             prop->flags = create_flags;
304             prop->u.val = jsval_undefined();
305         }else {
306             prop = alloc_prop(This, name, PROP_JSVAL, create_flags);
307             if(!prop)
308                 return E_OUTOFMEMORY;
309         }
310 
311         prop->u.val = jsval_undefined();
312     }
313 
314     *ret = prop;
315     return hres;
316 }
317 
318 static IDispatch *get_this(DISPPARAMS *dp)
319 {
320     DWORD i;
321 
322     for(i=0; i < dp->cNamedArgs; i++) {
323         if(dp->rgdispidNamedArgs[i] == DISPID_THIS) {
324             if(V_VT(dp->rgvarg+i) == VT_DISPATCH)
325                 return V_DISPATCH(dp->rgvarg+i);
326 
327             WARN("This is not VT_DISPATCH\n");
328             return NULL;
329         }
330     }
331 
332     TRACE("no this passed\n");
333     return NULL;
334 }
335 
336 static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret)
337 {
338     jsval_t *argv;
339     unsigned cnt;
340     unsigned i;
341     HRESULT hres;
342 
343     cnt = dp->cArgs - dp->cNamedArgs;
344 
345     if(cnt > 6) {
346         argv = heap_alloc(cnt * sizeof(*argv));
347         if(!argv)
348             return E_OUTOFMEMORY;
349     }else {
350         argv = buf;
351     }
352 
353     for(i = 0; i < cnt; i++) {
354         hres = variant_to_jsval(dp->rgvarg+dp->cArgs-i-1, argv+i);
355         if(FAILED(hres)) {
356             while(i--)
357                 jsval_release(argv[i]);
358             if(argv != buf)
359                 heap_free(argv);
360             return hres;
361         }
362     }
363 
364     *argc = cnt;
365     *ret = argv;
366     return S_OK;
367 }
368 
369 static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t *prop, WORD flags,
370         unsigned argc, jsval_t *argv, jsval_t *r, IServiceProvider *caller)
371 {
372     HRESULT hres;
373 
374     switch(prop->type) {
375     case PROP_BUILTIN: {
376         if(flags == DISPATCH_CONSTRUCT && (prop->flags & PROPF_METHOD)) {
377             WARN("%s is not a constructor\n", debugstr_w(prop->name));
378             return E_INVALIDARG;
379         }
380 
381         if(prop->name || This->builtin_info->class != JSCLASS_FUNCTION) {
382             vdisp_t vthis;
383 
384             if(This->builtin_info->class != JSCLASS_FUNCTION && prop->u.p->invoke != JSGlobal_eval)
385                 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
386             if(jsthis)
387                 set_disp(&vthis, jsthis);
388             else
389                 set_jsdisp(&vthis, This);
390             hres = prop->u.p->invoke(This->ctx, &vthis, flags, argc, argv, r);
391             vdisp_release(&vthis);
392         }else {
393             /* Function object calls are special case */
394             hres = Function_invoke(This, jsthis, flags, argc, argv, r);
395         }
396         return hres;
397     }
398     case PROP_PROTREF:
399         return invoke_prop_func(This->prototype, jsthis, This->prototype->props+prop->u.ref,
400                 flags, argc, argv, r, caller);
401     case PROP_JSVAL: {
402         if(!is_object_instance(prop->u.val)) {
403             FIXME("invoke %s\n", debugstr_jsval(prop->u.val));
404             return E_FAIL;
405         }
406 
407         TRACE("call %s %p\n", debugstr_w(prop->name), get_object(prop->u.val));
408 
409         return disp_call_value(This->ctx, get_object(prop->u.val), jsthis, flags, argc, argv, r);
410     }
411     case PROP_IDX:
412         FIXME("Invoking PROP_IDX not yet supported\n");
413         return E_NOTIMPL;
414     case PROP_DELETED:
415         assert(0);
416     }
417 
418     assert(0);
419     return E_FAIL;
420 }
421 
422 static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, DISPPARAMS *dp,
423         jsval_t *r, IServiceProvider *caller)
424 {
425     HRESULT hres;
426 
427     switch(prop->type) {
428     case PROP_BUILTIN:
429         if(prop->u.p->getter) {
430             hres = prop->u.p->getter(This->ctx, This, r);
431         }else {
432             jsdisp_t *obj;
433 
434             assert(prop->u.p->invoke != NULL);
435             hres = create_builtin_function(This->ctx, prop->u.p->invoke, prop->u.p->name, NULL,
436                     prop->u.p->flags, NULL, &obj);
437             if(FAILED(hres))
438                 break;
439 
440             prop->type = PROP_JSVAL;
441             prop->u.val = jsval_obj(obj);
442 
443             jsdisp_addref(obj);
444             *r = jsval_obj(obj);
445         }
446         break;
447     case PROP_PROTREF:
448         hres = prop_get(This->prototype, This->prototype->props+prop->u.ref, dp, r, caller);
449         break;
450     case PROP_JSVAL:
451         hres = jsval_copy(prop->u.val, r);
452         break;
453     case PROP_IDX:
454         hres = This->builtin_info->idx_get(This, prop->u.idx, r);
455         break;
456     default:
457         ERR("type %d\n", prop->type);
458         return E_FAIL;
459     }
460 
461     if(FAILED(hres)) {
462         TRACE("fail %08x\n", hres);
463         return hres;
464     }
465 
466     TRACE("%s ret %s\n", debugstr_w(prop->name), debugstr_jsval(*r));
467     return hres;
468 }
469 
470 static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val, IServiceProvider *caller)
471 {
472     HRESULT hres;
473 
474     if(prop->flags & PROPF_CONST)
475         return S_OK;
476 
477     switch(prop->type) {
478     case PROP_BUILTIN:
479         if(prop->u.p->setter)
480             return prop->u.p->setter(This->ctx, This, val);
481 
482         if(prop->u.p->setter) {
483             FIXME("getter with no setter\n");
484             return E_FAIL;
485         }
486         /* fall through */
487     case PROP_PROTREF:
488         prop->type = PROP_JSVAL;
489         prop->flags = PROPF_ENUM;
490         prop->u.val = jsval_undefined();
491         break;
492     case PROP_JSVAL:
493         jsval_release(prop->u.val);
494         break;
495     case PROP_IDX:
496         return This->builtin_info->idx_put(This, prop->u.idx, val);
497     default:
498         ERR("type %d\n", prop->type);
499         return E_FAIL;
500     }
501 
502     TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_jsval(val));
503 
504     hres = jsval_copy(val, &prop->u.val);
505     if(FAILED(hres))
506         return hres;
507 
508     if(This->builtin_info->on_put)
509         This->builtin_info->on_put(This, prop->name);
510 
511     return S_OK;
512 }
513 
514 HRESULT builtin_set_const(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
515 {
516     TRACE("%p %s\n", jsthis, debugstr_jsval(value));
517     return S_OK;
518 }
519 
520 static HRESULT fill_protrefs(jsdisp_t *This)
521 {
522     dispex_prop_t *iter, *prop;
523     HRESULT hres;
524 
525     if(!This->prototype)
526         return S_OK;
527 
528     fill_protrefs(This->prototype);
529 
530     for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) {
531         if(!iter->name)
532             continue;
533         hres = find_prop_name(This, iter->hash, iter->name, &prop);
534         if(FAILED(hres))
535             return hres;
536         if(!prop || prop->type==PROP_DELETED) {
537             if(prop) {
538                 prop->type = PROP_PROTREF;
539                 prop->flags = 0;
540                 prop->u.ref = iter - This->prototype->props;
541             }else {
542                 prop = alloc_protref(This, iter->name, iter - This->prototype->props);
543                 if(!prop)
544                     return E_OUTOFMEMORY;
545             }
546         }
547     }
548 
549     return S_OK;
550 }
551 
552 static inline jsdisp_t *impl_from_IDispatchEx(IDispatchEx *iface)
553 {
554     return CONTAINING_RECORD(iface, jsdisp_t, IDispatchEx_iface);
555 }
556 
557 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
558 {
559     jsdisp_t *This = impl_from_IDispatchEx(iface);
560 
561     if(IsEqualGUID(&IID_IUnknown, riid)) {
562         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
563         *ppv = &This->IDispatchEx_iface;
564     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
565         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
566         *ppv = &This->IDispatchEx_iface;
567     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
568         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
569         *ppv = &This->IDispatchEx_iface;
570     }else {
571         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
572         *ppv = NULL;
573         return E_NOINTERFACE;
574     }
575 
576     jsdisp_addref(This);
577     return S_OK;
578 }
579 
580 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
581 {
582     jsdisp_t *This = impl_from_IDispatchEx(iface);
583     jsdisp_addref(This);
584     return This->ref;
585 }
586 
587 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
588 {
589     jsdisp_t *This = impl_from_IDispatchEx(iface);
590     ULONG ref = --This->ref;
591     TRACE("(%p) ref=%d\n", This, ref);
592     if(!ref)
593         jsdisp_free(This);
594     return ref;
595 }
596 
597 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
598 {
599     jsdisp_t *This = impl_from_IDispatchEx(iface);
600 
601     TRACE("(%p)->(%p)\n", This, pctinfo);
602 
603     *pctinfo = 1;
604     return S_OK;
605 }
606 
607 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
608                                               ITypeInfo **ppTInfo)
609 {
610     jsdisp_t *This = impl_from_IDispatchEx(iface);
611     FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
612     return E_NOTIMPL;
613 }
614 
615 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
616                                                 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
617                                                 DISPID *rgDispId)
618 {
619     jsdisp_t *This = impl_from_IDispatchEx(iface);
620     UINT i;
621     HRESULT hres;
622 
623     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
624           lcid, rgDispId);
625 
626     for(i=0; i < cNames; i++) {
627         hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
628         if(FAILED(hres))
629             return hres;
630     }
631 
632     return S_OK;
633 }
634 
635 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
636                                         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
637                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
638 {
639     jsdisp_t *This = impl_from_IDispatchEx(iface);
640 
641     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
642           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
643 
644     return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags,
645             pDispParams, pVarResult, pExcepInfo, NULL);
646 }
647 
648 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
649 {
650     jsdisp_t *This = impl_from_IDispatchEx(iface);
651 
652     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
653 
654     if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) {
655         FIXME("Unsupported grfdex %x\n", grfdex);
656         return E_NOTIMPL;
657     }
658 
659     return jsdisp_get_id(This, bstrName, grfdex, pid);
660 }
661 
662 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
663         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
664 {
665     jsdisp_t *This = impl_from_IDispatchEx(iface);
666     dispex_prop_t *prop;
667     HRESULT hres;
668 
669     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
670 
671     if(pvarRes)
672         V_VT(pvarRes) = VT_EMPTY;
673 
674     prop = get_prop(This, id);
675     if(!prop || prop->type == PROP_DELETED) {
676         TRACE("invalid id\n");
677         return DISP_E_MEMBERNOTFOUND;
678     }
679 
680     clear_ei(This->ctx);
681 
682     switch(wFlags) {
683     case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
684         wFlags = DISPATCH_METHOD;
685         /* fall through */
686     case DISPATCH_METHOD:
687     case DISPATCH_CONSTRUCT: {
688         jsval_t *argv, buf[6], r;
689         unsigned argc;
690 
691         hres = convert_params(pdp, buf, &argc, &argv);
692         if(FAILED(hres))
693             return hres;
694 
695         hres = invoke_prop_func(This, get_this(pdp), prop, wFlags, argc, argv, pvarRes ? &r : NULL, pspCaller);
696         if(argv != buf)
697             heap_free(argv);
698         if(SUCCEEDED(hres) && pvarRes) {
699             hres = jsval_to_variant(r, pvarRes);
700             jsval_release(r);
701         }
702         break;
703     }
704     case DISPATCH_PROPERTYGET: {
705         jsval_t r;
706 
707         hres = prop_get(This, prop, pdp, &r, pspCaller);
708         if(SUCCEEDED(hres)) {
709             hres = jsval_to_variant(r, pvarRes);
710             jsval_release(r);
711         }
712         break;
713     }
714     case DISPATCH_PROPERTYPUT: {
715         jsval_t val;
716         DWORD i;
717 
718         for(i=0; i < pdp->cNamedArgs; i++) {
719             if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
720                 break;
721         }
722 
723         if(i == pdp->cNamedArgs) {
724             TRACE("no value to set\n");
725             return DISP_E_PARAMNOTOPTIONAL;
726         }
727 
728         hres = variant_to_jsval(pdp->rgvarg+i, &val);
729         if(FAILED(hres))
730             return hres;
731 
732         hres = prop_put(This, prop, val, pspCaller);
733         jsval_release(val);
734         break;
735     }
736     default:
737         FIXME("Unimplemented flags %x\n", wFlags);
738         return E_INVALIDARG;
739     }
740 
741     if(pei)
742         *pei = This->ctx->ei.ei;
743     return hres;
744 }
745 
746 static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret)
747 {
748     if(prop->flags & PROPF_DONTDELETE) {
749         *ret = FALSE;
750         return S_OK;
751     }
752 
753     *ret = TRUE; /* FIXME: not exactly right */
754 
755     if(prop->type == PROP_JSVAL) {
756         jsval_release(prop->u.val);
757         prop->type = PROP_DELETED;
758     }
759     return S_OK;
760 }
761 
762 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
763 {
764     jsdisp_t *This = impl_from_IDispatchEx(iface);
765     dispex_prop_t *prop;
766     BOOL b;
767     HRESULT hres;
768 
769     TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
770 
771     if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
772         FIXME("Unsupported grfdex %x\n", grfdex);
773 
774     hres = find_prop_name(This, string_hash(bstrName), bstrName, &prop);
775     if(FAILED(hres))
776         return hres;
777     if(!prop) {
778         TRACE("not found\n");
779         return S_OK;
780     }
781 
782     return delete_prop(prop, &b);
783 }
784 
785 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
786 {
787     jsdisp_t *This = impl_from_IDispatchEx(iface);
788     dispex_prop_t *prop;
789     BOOL b;
790 
791     TRACE("(%p)->(%x)\n", This, id);
792 
793     prop = get_prop(This, id);
794     if(!prop) {
795         WARN("invalid id\n");
796         return DISP_E_MEMBERNOTFOUND;
797     }
798 
799     return delete_prop(prop, &b);
800 }
801 
802 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
803 {
804     jsdisp_t *This = impl_from_IDispatchEx(iface);
805     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
806     return E_NOTIMPL;
807 }
808 
809 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
810 {
811     jsdisp_t *This = impl_from_IDispatchEx(iface);
812     dispex_prop_t *prop;
813 
814     TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
815 
816     prop = get_prop(This, id);
817     if(!prop || !prop->name || prop->type == PROP_DELETED)
818         return DISP_E_MEMBERNOTFOUND;
819 
820     *pbstrName = SysAllocString(prop->name);
821     if(!*pbstrName)
822         return E_OUTOFMEMORY;
823 
824     return S_OK;
825 }
826 
827 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
828 {
829     jsdisp_t *This = impl_from_IDispatchEx(iface);
830     dispex_prop_t *iter;
831     HRESULT hres;
832 
833     TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
834 
835     if(id == DISPID_STARTENUM) {
836         hres = fill_protrefs(This);
837         if(FAILED(hres))
838             return hres;
839     }
840 
841     if(id+1>=0 && id+1<This->prop_cnt) {
842         iter = &This->props[id+1];
843     }else {
844         *pid = DISPID_STARTENUM;
845         return S_FALSE;
846     }
847 
848     while(iter < This->props + This->prop_cnt) {
849         if(iter->name && (get_flags(This, iter) & PROPF_ENUM) && iter->type!=PROP_DELETED) {
850             *pid = prop_to_id(This, iter);
851             return S_OK;
852         }
853         iter++;
854     }
855 
856     *pid = DISPID_STARTENUM;
857     return S_FALSE;
858 }
859 
860 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
861 {
862     jsdisp_t *This = impl_from_IDispatchEx(iface);
863     FIXME("(%p)->(%p)\n", This, ppunk);
864     return E_NOTIMPL;
865 }
866 
867 static IDispatchExVtbl DispatchExVtbl = {
868     DispatchEx_QueryInterface,
869     DispatchEx_AddRef,
870     DispatchEx_Release,
871     DispatchEx_GetTypeInfoCount,
872     DispatchEx_GetTypeInfo,
873     DispatchEx_GetIDsOfNames,
874     DispatchEx_Invoke,
875     DispatchEx_GetDispID,
876     DispatchEx_InvokeEx,
877     DispatchEx_DeleteMemberByName,
878     DispatchEx_DeleteMemberByDispID,
879     DispatchEx_GetMemberProperties,
880     DispatchEx_GetMemberName,
881     DispatchEx_GetNextDispID,
882     DispatchEx_GetNameSpaceParent
883 };
884 
885 jsdisp_t *as_jsdisp(IDispatch *disp)
886 {
887     assert(disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl);
888     return impl_from_IDispatchEx((IDispatchEx*)disp);
889 }
890 
891 jsdisp_t *to_jsdisp(IDispatch *disp)
892 {
893     return disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl ? impl_from_IDispatchEx((IDispatchEx*)disp) : NULL;
894 }
895 
896 HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype)
897 {
898     TRACE("%p (%p)\n", dispex, prototype);
899 
900     dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
901     dispex->ref = 1;
902     dispex->builtin_info = builtin_info;
903 
904     dispex->props = heap_alloc_zero(sizeof(dispex_prop_t)*(dispex->buf_size=4));
905     if(!dispex->props)
906         return E_OUTOFMEMORY;
907 
908     dispex->prototype = prototype;
909     if(prototype)
910         jsdisp_addref(prototype);
911 
912     dispex->prop_cnt = 1;
913     if(builtin_info->value_prop.invoke || builtin_info->value_prop.getter) {
914         dispex->props[0].type = PROP_BUILTIN;
915         dispex->props[0].u.p = &builtin_info->value_prop;
916     }else {
917         dispex->props[0].type = PROP_DELETED;
918     }
919 
920     script_addref(ctx);
921     dispex->ctx = ctx;
922 
923     return S_OK;
924 }
925 
926 static const builtin_info_t dispex_info = {
927     JSCLASS_NONE,
928     {NULL, NULL, 0},
929     0, NULL,
930     NULL,
931     NULL
932 };
933 
934 HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype, jsdisp_t **dispex)
935 {
936     jsdisp_t *ret;
937     HRESULT hres;
938 
939     ret = heap_alloc_zero(sizeof(jsdisp_t));
940     if(!ret)
941         return E_OUTOFMEMORY;
942 
943     hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype);
944     if(FAILED(hres)) {
945         heap_free(ret);
946         return hres;
947     }
948 
949     *dispex = ret;
950     return S_OK;
951 }
952 
953 void jsdisp_free(jsdisp_t *obj)
954 {
955     dispex_prop_t *prop;
956 
957     TRACE("(%p)\n", obj);
958 
959     for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) {
960         if(prop->type == PROP_JSVAL)
961             jsval_release(prop->u.val);
962         heap_free(prop->name);
963     }
964     heap_free(obj->props);
965     script_release(obj->ctx);
966     if(obj->prototype)
967         jsdisp_release(obj->prototype);
968 
969     if(obj->builtin_info->destructor)
970         obj->builtin_info->destructor(obj);
971     else
972         heap_free(obj);
973 }
974 
975 #ifdef TRACE_REFCNT
976 
977 jsdisp_t *jsdisp_addref(jsdisp_t *jsdisp)
978 {
979     ULONG ref = ++jsdisp->ref;
980     TRACE("(%p) ref=%d\n", jsdisp, ref);
981     return jsdisp;
982 }
983 
984 void jsdisp_release(jsdisp_t *jsdisp)
985 {
986     ULONG ref = --jsdisp->ref;
987 
988     TRACE("(%p) ref=%d\n", jsdisp, ref);
989 
990     if(!ref)
991         jsdisp_free(jsdisp);
992 }
993 
994 #endif
995 
996 HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *constr)
997 {
998     jsdisp_t *prot = NULL;
999     dispex_prop_t *prop;
1000     HRESULT hres;
1001 
1002     static const WCHAR prototypeW[] = {'p','r','o','t','o','t','y','p','e',0};
1003 
1004     hres = find_prop_name_prot(constr, string_hash(prototypeW), prototypeW, &prop);
1005     if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) {
1006         jsval_t val;
1007 
1008         hres = prop_get(constr, prop, NULL, &val, NULL);
1009         if(FAILED(hres)) {
1010             ERR("Could not get prototype\n");
1011             return hres;
1012         }
1013 
1014         if(is_object_instance(val))
1015             prot = iface_to_jsdisp(get_object(val));
1016         jsval_release(val);
1017     }
1018 
1019     hres = init_dispex(dispex, ctx, builtin_info, prot);
1020 
1021     if(prot)
1022         jsdisp_release(prot);
1023     return hres;
1024 }
1025 
1026 jsdisp_t *iface_to_jsdisp(IDispatch *iface)
1027 {
1028     return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl
1029         ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface))
1030         : NULL;
1031 }
1032 
1033 HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id)
1034 {
1035     dispex_prop_t *prop;
1036     HRESULT hres;
1037 
1038     if(flags & fdexNameEnsure)
1039         hres = ensure_prop_name(jsdisp, name, TRUE, PROPF_ENUM, &prop);
1040     else
1041         hres = find_prop_name_prot(jsdisp, string_hash(name), name, &prop);
1042     if(FAILED(hres))
1043         return hres;
1044 
1045     if(prop && prop->type!=PROP_DELETED) {
1046         *id = prop_to_id(jsdisp, prop);
1047         return S_OK;
1048     }
1049 
1050     TRACE("not found %s\n", debugstr_w(name));
1051     return DISP_E_UNKNOWNNAME;
1052 }
1053 
1054 HRESULT jsdisp_call_value(jsdisp_t *jsfunc, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1055 {
1056     HRESULT hres;
1057 
1058     assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
1059 
1060     if(is_class(jsfunc, JSCLASS_FUNCTION)) {
1061         hres = Function_invoke(jsfunc, jsthis, flags, argc, argv, r);
1062     }else {
1063         vdisp_t vdisp;
1064 
1065         if(!jsfunc->builtin_info->value_prop.invoke) {
1066             WARN("Not a function\n");
1067             return throw_type_error(jsfunc->ctx, JS_E_FUNCTION_EXPECTED, NULL);
1068         }
1069 
1070         set_disp(&vdisp, jsthis);
1071         flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1072         hres = jsfunc->builtin_info->value_prop.invoke(jsfunc->ctx, &vdisp, flags, argc, argv, r);
1073         vdisp_release(&vdisp);
1074     }
1075     return hres;
1076 }
1077 
1078 HRESULT jsdisp_call(jsdisp_t *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1079 {
1080     dispex_prop_t *prop;
1081 
1082     prop = get_prop(disp, id);
1083     if(!prop)
1084         return DISP_E_MEMBERNOTFOUND;
1085 
1086     return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL);
1087 }
1088 
1089 HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1090 {
1091     dispex_prop_t *prop;
1092     HRESULT hres;
1093 
1094     hres = find_prop_name_prot(disp, string_hash(name), name, &prop);
1095     if(FAILED(hres))
1096         return hres;
1097 
1098     return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL);
1099 }
1100 
1101 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret)
1102 {
1103     IDispatchEx *dispex;
1104     jsdisp_t *jsdisp;
1105     VARIANT buf[6], retv;
1106     DISPPARAMS dp;
1107     unsigned i;
1108     HRESULT hres;
1109 
1110     jsdisp = iface_to_jsdisp(disp);
1111     if(jsdisp) {
1112         if(flags & DISPATCH_PROPERTYPUT) {
1113             FIXME("disp_call(propput) on builtin object\n");
1114             return E_FAIL;
1115         }
1116 
1117         if(ctx != jsdisp->ctx)
1118             flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1119         hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret);
1120         jsdisp_release(jsdisp);
1121         return hres;
1122     }
1123 
1124     flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1125     if(ret && argc)
1126         flags |= DISPATCH_PROPERTYGET;
1127 
1128     dp.cArgs = argc;
1129 
1130     if(flags & DISPATCH_PROPERTYPUT) {
1131         static DISPID propput_dispid = DISPID_PROPERTYPUT;
1132 
1133         dp.cNamedArgs = 1;
1134         dp.rgdispidNamedArgs = &propput_dispid;
1135     }else {
1136         dp.cNamedArgs = 0;
1137         dp.rgdispidNamedArgs = NULL;
1138     }
1139 
1140     if(argc > 6) {
1141         dp.rgvarg = heap_alloc(argc*sizeof(VARIANT));
1142         if(!dp.rgvarg)
1143             return E_OUTOFMEMORY;
1144     }else {
1145         dp.rgvarg = buf;
1146     }
1147 
1148     for(i=0; i<argc; i++) {
1149         hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1);
1150         if(FAILED(hres)) {
1151             while(i--)
1152                 VariantClear(dp.rgvarg+argc-i-1);
1153             if(dp.rgvarg != buf)
1154                 heap_free(dp.rgvarg);
1155             return hres;
1156         }
1157     }
1158 
1159     V_VT(&retv) = VT_EMPTY;
1160     clear_ei(ctx);
1161     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1162     if(SUCCEEDED(hres)) {
1163         hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei,
1164                 &ctx->jscaller->IServiceProvider_iface);
1165         IDispatchEx_Release(dispex);
1166     }else {
1167         UINT err = 0;
1168 
1169         if(flags == DISPATCH_CONSTRUCT) {
1170             WARN("IDispatch cannot be constructor\n");
1171             return DISP_E_MEMBERNOTFOUND;
1172         }
1173 
1174         TRACE("using IDispatch\n");
1175         hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei, &err);
1176     }
1177 
1178     for(i=0; i<argc; i++)
1179         VariantClear(dp.rgvarg+argc-i-1);
1180     if(dp.rgvarg != buf)
1181         heap_free(dp.rgvarg);
1182     if(FAILED(hres))
1183         return hres;
1184 
1185     if(ret) {
1186         hres = variant_to_jsval(&retv, ret);
1187         VariantClear(&retv);
1188     }
1189 
1190     return hres;
1191 }
1192 
1193 HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1194         jsval_t *r)
1195 {
1196     jsdisp_t *jsdisp;
1197     IDispatchEx *dispex;
1198     VARIANT buf[6], retv;
1199     DISPPARAMS dp;
1200     unsigned i;
1201     HRESULT hres;
1202 
1203     assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
1204 
1205     jsdisp = iface_to_jsdisp(disp);
1206     if(jsdisp) {
1207         if(ctx != jsdisp->ctx)
1208             flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1209         hres = jsdisp_call_value(jsdisp, jsthis, flags, argc, argv, r);
1210         jsdisp_release(jsdisp);
1211         return hres;
1212     }
1213 
1214     flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1215     if(r && argc && flags == DISPATCH_METHOD)
1216         flags |= DISPATCH_PROPERTYGET;
1217 
1218     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1219     if(FAILED(hres)) {
1220         TRACE("using IDispatch\n");
1221         dispex = NULL;
1222         jsthis = NULL;
1223     }
1224 
1225     if(jsthis) {
1226         static DISPID this_id = DISPID_THIS;
1227 
1228         dp.cArgs = argc+1;
1229         dp.cNamedArgs = 1;
1230         dp.rgdispidNamedArgs = &this_id;
1231     }else {
1232         dp.cArgs = argc;
1233         dp.cNamedArgs = 0;
1234         dp.rgdispidNamedArgs = NULL;
1235     }
1236 
1237     if(dp.cArgs > sizeof(buf)/sizeof(*buf)) {
1238         dp.rgvarg = heap_alloc(dp.cArgs*sizeof(VARIANT));
1239         if(!dp.rgvarg) {
1240             if(dispex)
1241                 IDispatchEx_Release(dispex);
1242             return E_OUTOFMEMORY;
1243         }
1244     }else {
1245         dp.rgvarg = buf;
1246     }
1247 
1248     for(i=0; i<argc; i++) {
1249         hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1);
1250         if(FAILED(hres)) {
1251             while(i--)
1252                 VariantClear(dp.rgvarg+dp.cArgs-i-1);
1253             if(dp.rgvarg != buf)
1254                 heap_free(dp.rgvarg);
1255             if(dispex)
1256                 IDispatchEx_Release(dispex);
1257             return hres;
1258         }
1259     }
1260     if(jsthis) {
1261         V_VT(dp.rgvarg) = VT_DISPATCH;
1262         V_DISPATCH(dp.rgvarg) = jsthis;
1263     }
1264 
1265     V_VT(&retv) = VT_EMPTY;
1266     clear_ei(ctx);
1267     if(dispex) {
1268         hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei,
1269                 &ctx->jscaller->IServiceProvider_iface);
1270         IDispatchEx_Release(dispex);
1271     }else {
1272         UINT err = 0;
1273 
1274         if(flags == DISPATCH_CONSTRUCT) {
1275             WARN("IDispatch cannot be constructor\n");
1276             return DISP_E_MEMBERNOTFOUND;
1277         }
1278 
1279         hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei, &err);
1280     }
1281 
1282     for(i=0; i<argc; i++)
1283         VariantClear(dp.rgvarg+dp.cArgs-i-1);
1284     if(dp.rgvarg != buf)
1285         heap_free(dp.rgvarg);
1286     if(FAILED(hres))
1287         return hres;
1288 
1289     if(!r)
1290         return S_OK;
1291 
1292     hres = variant_to_jsval(&retv, r);
1293     VariantClear(&retv);
1294     return hres;
1295 }
1296 
1297 HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, jsval_t val)
1298 {
1299     dispex_prop_t *prop;
1300     HRESULT hres;
1301 
1302     hres = ensure_prop_name(obj, name, FALSE, flags, &prop);
1303     if(FAILED(hres))
1304         return hres;
1305 
1306     return prop_put(obj, prop, val, NULL);
1307 }
1308 
1309 HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val)
1310 {
1311     return jsdisp_propput(obj, name, PROPF_ENUM, val);
1312 }
1313 
1314 HRESULT jsdisp_propput_const(jsdisp_t *obj, const WCHAR *name, jsval_t val)
1315 {
1316     dispex_prop_t *prop;
1317     HRESULT hres;
1318 
1319     hres = ensure_prop_name(obj, name, FALSE, PROPF_CONST, &prop);
1320     if(FAILED(hres))
1321         return hres;
1322 
1323     return jsval_copy(val, &prop->u.val);
1324 }
1325 
1326 HRESULT jsdisp_propput_dontenum(jsdisp_t *obj, const WCHAR *name, jsval_t val)
1327 {
1328     return jsdisp_propput(obj, name, 0, val);
1329 }
1330 
1331 HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val)
1332 {
1333     WCHAR buf[12];
1334 
1335     static const WCHAR formatW[] = {'%','d',0};
1336 
1337     sprintfW(buf, formatW, idx);
1338     return jsdisp_propput_name(obj, buf, val);
1339 }
1340 
1341 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val)
1342 {
1343     jsdisp_t *jsdisp;
1344     HRESULT hres;
1345 
1346     jsdisp = iface_to_jsdisp(disp);
1347     if(jsdisp) {
1348         dispex_prop_t *prop;
1349 
1350         prop = get_prop(jsdisp, id);
1351         if(prop)
1352             hres = prop_put(jsdisp, prop, val, NULL);
1353         else
1354             hres = DISP_E_MEMBERNOTFOUND;
1355 
1356         jsdisp_release(jsdisp);
1357     }else {
1358         DISPID dispid = DISPID_PROPERTYPUT;
1359         DWORD flags = DISPATCH_PROPERTYPUT;
1360         VARIANT var;
1361         DISPPARAMS dp  = {&var, &dispid, 1, 1};
1362         IDispatchEx *dispex;
1363 
1364         hres = jsval_to_variant(val, &var);
1365         if(FAILED(hres))
1366             return hres;
1367 
1368         if(V_VT(&var) == VT_DISPATCH)
1369             flags |= DISPATCH_PROPERTYPUTREF;
1370 
1371         clear_ei(ctx);
1372         hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1373         if(SUCCEEDED(hres)) {
1374             hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei,
1375                     &ctx->jscaller->IServiceProvider_iface);
1376             IDispatchEx_Release(dispex);
1377         }else {
1378             ULONG err = 0;
1379 
1380             TRACE("using IDispatch\n");
1381             hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei, &err);
1382         }
1383 
1384         VariantClear(&var);
1385     }
1386 
1387     return hres;
1388 }
1389 
1390 HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val)
1391 {
1392     DISPPARAMS dp = {NULL, NULL, 0, 0};
1393     dispex_prop_t *prop;
1394     HRESULT hres;
1395 
1396     hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
1397     if(FAILED(hres))
1398         return hres;
1399 
1400     if(!prop || prop->type==PROP_DELETED) {
1401         *val = jsval_undefined();
1402         return S_OK;
1403     }
1404 
1405     return prop_get(obj, prop, &dp, val, NULL);
1406 }
1407 
1408 HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r)
1409 {
1410     WCHAR name[12];
1411     DISPPARAMS dp = {NULL, NULL, 0, 0};
1412     dispex_prop_t *prop;
1413     HRESULT hres;
1414 
1415     static const WCHAR formatW[] = {'%','d',0};
1416 
1417     sprintfW(name, formatW, idx);
1418 
1419     hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
1420     if(FAILED(hres))
1421         return hres;
1422 
1423     if(!prop || prop->type==PROP_DELETED) {
1424         *r = jsval_undefined();
1425         return DISP_E_UNKNOWNNAME;
1426     }
1427 
1428     return prop_get(obj, prop, &dp, r, NULL);
1429 }
1430 
1431 HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val)
1432 {
1433     DISPPARAMS dp  = {NULL,NULL,0,0};
1434     dispex_prop_t *prop;
1435 
1436     prop = get_prop(jsdisp, id);
1437     if(!prop)
1438         return DISP_E_MEMBERNOTFOUND;
1439 
1440     return prop_get(jsdisp, prop, &dp, val, NULL);
1441 }
1442 
1443 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val)
1444 {
1445     DISPPARAMS dp  = {NULL,NULL,0,0};
1446     IDispatchEx *dispex;
1447     jsdisp_t *jsdisp;
1448     VARIANT var;
1449     HRESULT hres;
1450 
1451     jsdisp = iface_to_jsdisp(disp);
1452     if(jsdisp) {
1453         hres = jsdisp_propget(jsdisp, id, val);
1454         jsdisp_release(jsdisp);
1455         return hres;
1456     }
1457 
1458     V_VT(&var) = VT_EMPTY;
1459     clear_ei(ctx);
1460     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1461     if(SUCCEEDED(hres)) {
1462         hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei,
1463                 &ctx->jscaller->IServiceProvider_iface);
1464         IDispatchEx_Release(dispex);
1465     }else {
1466         ULONG err = 0;
1467 
1468         TRACE("using IDispatch\n");
1469         hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei, &err);
1470     }
1471     if(FAILED(hres))
1472         return hres;
1473 
1474     hres = variant_to_jsval(&var, val);
1475     VariantClear(&var);
1476     return hres;
1477 }
1478 
1479 HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
1480 {
1481     static const WCHAR formatW[] = {'%','d',0};
1482     WCHAR buf[12];
1483     dispex_prop_t *prop;
1484     BOOL b;
1485     HRESULT hres;
1486 
1487     sprintfW(buf, formatW, idx);
1488 
1489     hres = find_prop_name(obj, string_hash(buf), buf, &prop);
1490     if(FAILED(hres) || !prop)
1491         return hres;
1492 
1493     return delete_prop(prop, &b);
1494 }
1495 
1496 HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret)
1497 {
1498     IDispatchEx *dispex;
1499     jsdisp_t *jsdisp;
1500     HRESULT hres;
1501 
1502     jsdisp = iface_to_jsdisp(disp);
1503     if(jsdisp) {
1504         dispex_prop_t *prop;
1505 
1506         prop = get_prop(jsdisp, id);
1507         if(prop)
1508             hres = delete_prop(prop, ret);
1509         else
1510             hres = DISP_E_MEMBERNOTFOUND;
1511 
1512         jsdisp_release(jsdisp);
1513         return hres;
1514     }
1515 
1516     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1517     if(FAILED(hres)) {
1518         *ret = FALSE;
1519         return S_OK;
1520     }
1521 
1522     hres = IDispatchEx_DeleteMemberByDispID(dispex, id);
1523     IDispatchEx_Release(dispex);
1524     if(FAILED(hres))
1525         return hres;
1526 
1527     *ret = hres == S_OK;
1528     return S_OK;
1529 }
1530 
1531 HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret)
1532 {
1533     IDispatchEx *dispex;
1534     jsdisp_t *jsdisp;
1535     BSTR bstr;
1536     HRESULT hres;
1537 
1538     jsdisp = iface_to_jsdisp(disp);
1539     if(jsdisp) {
1540         dispex_prop_t *prop;
1541         const WCHAR *ptr;
1542 
1543         ptr = jsstr_flatten(name);
1544         if(!ptr) {
1545             jsdisp_release(jsdisp);
1546             return E_OUTOFMEMORY;
1547         }
1548 
1549         hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop);
1550         if(prop) {
1551             hres = delete_prop(prop, ret);
1552         }else {
1553             *ret = TRUE;
1554             hres = S_OK;
1555         }
1556 
1557         jsdisp_release(jsdisp);
1558         return hres;
1559     }
1560 
1561     bstr = SysAllocStringLen(NULL, jsstr_length(name));
1562     if(!bstr)
1563         return E_OUTOFMEMORY;
1564     jsstr_flush(name, bstr);
1565 
1566     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1567     if(SUCCEEDED(hres)) {
1568         hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive));
1569         if(SUCCEEDED(hres))
1570             *ret = hres == S_OK;
1571         IDispatchEx_Release(dispex);
1572     }else {
1573         DISPID id;
1574 
1575         hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
1576         if(SUCCEEDED(hres)) {
1577             /* Property exists and we can't delete it from pure IDispatch interface, so return false. */
1578             *ret = FALSE;
1579         }else if(hres == DISP_E_UNKNOWNNAME) {
1580             /* Property doesn't exist, so nothing to delete */
1581             *ret = TRUE;
1582             hres = S_OK;
1583         }
1584     }
1585 
1586     SysFreeString(bstr);
1587     return hres;
1588 }
1589 
1590 HRESULT jsdisp_is_own_prop(jsdisp_t *obj, const WCHAR *name, BOOL *ret)
1591 {
1592     dispex_prop_t *prop;
1593     HRESULT hres;
1594 
1595     hres = find_prop_name(obj, string_hash(name), name, &prop);
1596     if(FAILED(hres))
1597         return hres;
1598 
1599     *ret = prop && (prop->type == PROP_JSVAL || prop->type == PROP_BUILTIN);
1600     return S_OK;
1601 }
1602 
1603 HRESULT jsdisp_is_enumerable(jsdisp_t *obj, const WCHAR *name, BOOL *ret)
1604 {
1605     dispex_prop_t *prop;
1606     HRESULT hres;
1607 
1608     hres = find_prop_name(obj, string_hash(name), name, &prop);
1609     if(FAILED(hres))
1610         return hres;
1611 
1612     *ret = prop && (prop->flags & PROPF_ENUM) && prop->type != PROP_PROTREF;
1613     return S_OK;
1614 }
1615