dbghelp: Fix an invalid pointer cast.
[wine] / dlls / jscript / dispex.c
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 "jscript.h"
20
21 #include "wine/unicode.h"
22 #include "wine/debug.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
25
26 /*
27  * This IID is used to get DispatchEx objecto from interface.
28  * We might consider using private insteface instead.
29  */
30 static const IID IID_IDispatchJS =
31         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}};
32
33 #define FDEX_VERSION_MASK 0xf0000000
34
35 typedef enum {
36     PROP_VARIANT,
37     PROP_BUILTIN,
38     PROP_PROTREF,
39     PROP_DELETED
40 } prop_type_t;
41
42 struct _dispex_prop_t {
43     WCHAR *name;
44     prop_type_t type;
45     DWORD flags;
46
47     union {
48         VARIANT var;
49         const builtin_prop_t *p;
50         DWORD ref;
51     } u;
52 };
53
54 static inline DISPID prop_to_id(DispatchEx *This, dispex_prop_t *prop)
55 {
56     return prop - This->props;
57 }
58
59 static inline dispex_prop_t *get_prop(DispatchEx *This, DISPID id)
60 {
61     if(id < 0 || id >= This->prop_cnt || This->props[id].type == PROP_DELETED)
62         return NULL;
63
64     return This->props+id;
65 }
66
67 static DWORD get_flags(DispatchEx *This, dispex_prop_t *prop)
68 {
69     if(prop->type == PROP_PROTREF) {
70         dispex_prop_t *parent = get_prop(This->prototype, prop->u.ref);
71         if(!parent) {
72             prop->type = PROP_DELETED;
73             return 0;
74         }
75
76         return get_flags(This->prototype, parent);
77     }
78
79     return prop->flags;
80 }
81
82 static const builtin_prop_t *find_builtin_prop(DispatchEx *This, const WCHAR *name)
83 {
84     int min = 0, max, i, r;
85
86     max = This->builtin_info->props_cnt-1;
87     while(min <= max) {
88         i = (min+max)/2;
89
90         r = strcmpW(name, This->builtin_info->props[i].name);
91         if(!r)
92             return This->builtin_info->props + i;
93
94         if(r < 0)
95             max = i-1;
96         else
97             min = i+1;
98     }
99
100     return NULL;
101 }
102
103 static dispex_prop_t *alloc_prop(DispatchEx *This, const WCHAR *name, prop_type_t type, DWORD flags)
104 {
105     dispex_prop_t *ret;
106
107     if(This->buf_size == This->prop_cnt) {
108         dispex_prop_t *tmp = heap_realloc(This->props, (This->buf_size<<=1)*sizeof(*This->props));
109         if(!tmp)
110             return NULL;
111         This->props = tmp;
112     }
113
114     ret = This->props + This->prop_cnt++;
115     ret->type = type;
116     ret->flags = flags;
117     ret->name = heap_strdupW(name);
118     if(!ret->name)
119         return NULL;
120
121     return ret;
122 }
123
124 static dispex_prop_t *alloc_protref(DispatchEx *This, const WCHAR *name, DWORD ref)
125 {
126     dispex_prop_t *ret;
127
128     ret = alloc_prop(This, name, PROP_PROTREF, 0);
129     if(!ret)
130         return NULL;
131
132     ret->u.ref = ref;
133     return ret;
134 }
135
136 static HRESULT find_prop_name(DispatchEx *This, const WCHAR *name, dispex_prop_t **ret)
137 {
138     const builtin_prop_t *builtin;
139     dispex_prop_t *prop;
140
141     for(prop = This->props; prop < This->props+This->prop_cnt; prop++) {
142         if(prop->name && !strcmpW(prop->name, name)) {
143             *ret = prop;
144             return S_OK;
145         }
146     }
147
148     builtin = find_builtin_prop(This, name);
149     if(builtin) {
150         prop = alloc_prop(This, name, PROP_BUILTIN, builtin->flags);
151         if(!prop)
152             return E_OUTOFMEMORY;
153
154         prop->u.p = builtin;
155         *ret = prop;
156         return S_OK;
157     }
158
159     *ret = NULL;
160     return S_OK;
161 }
162
163 static HRESULT find_prop_name_prot(DispatchEx *This, const WCHAR *name, BOOL alloc, dispex_prop_t **ret)
164 {
165     dispex_prop_t *prop;
166     HRESULT hres;
167
168     hres = find_prop_name(This, name, &prop);
169     if(FAILED(hres))
170         return hres;
171     if(prop) {
172         *ret = prop;
173         return S_OK;
174     }
175
176     if(This->prototype) {
177         hres = find_prop_name_prot(This->prototype, name, FALSE, &prop);
178         if(FAILED(hres))
179             return hres;
180         if(prop) {
181             prop = alloc_protref(This, prop->name, prop - This->prototype->props);
182             if(!prop)
183                 return E_OUTOFMEMORY;
184             *ret = prop;
185             return S_OK;
186         }
187     }
188
189     if(alloc) {
190         TRACE("creating prop %s\n", debugstr_w(name));
191
192         prop = alloc_prop(This, name, PROP_VARIANT, PROPF_ENUM);
193         if(!prop)
194             return E_OUTOFMEMORY;
195         VariantInit(&prop->u.var);
196     }
197
198     *ret = prop;
199     return S_OK;
200 }
201
202 static HRESULT set_this(DISPPARAMS *dp, DISPPARAMS *olddp, IDispatch *jsthis)
203 {
204     VARIANTARG *oldargs;
205     int i;
206
207     static DISPID this_id = DISPID_THIS;
208
209     *dp = *olddp;
210
211     for(i = 0; i < dp->cNamedArgs; i++) {
212         if(dp->rgdispidNamedArgs[i] == DISPID_THIS)
213             return S_OK;
214     }
215
216     oldargs = dp->rgvarg;
217     dp->rgvarg = heap_alloc((dp->cArgs+1) * sizeof(VARIANTARG));
218     if(!dp->rgvarg)
219         return E_OUTOFMEMORY;
220     memcpy(dp->rgvarg+1, oldargs, dp->cArgs*sizeof(VARIANTARG));
221     V_VT(dp->rgvarg) = VT_DISPATCH;
222     V_DISPATCH(dp->rgvarg) = jsthis;
223     dp->cArgs++;
224
225     if(dp->cNamedArgs) {
226         DISPID *old = dp->rgdispidNamedArgs;
227         dp->rgdispidNamedArgs = heap_alloc((dp->cNamedArgs+1)*sizeof(DISPID));
228         if(!dp->rgdispidNamedArgs) {
229             heap_free(dp->rgvarg);
230             return E_OUTOFMEMORY;
231         }
232
233         memcpy(dp->rgdispidNamedArgs+1, old, dp->cNamedArgs*sizeof(DISPID));
234         dp->rgdispidNamedArgs[0] = DISPID_THIS;
235         dp->cNamedArgs++;
236     }else {
237         dp->rgdispidNamedArgs = &this_id;
238         dp->cNamedArgs = 1;
239     }
240
241     return S_OK;
242 }
243
244 static HRESULT invoke_prop_func(DispatchEx *This, DispatchEx *jsthis, dispex_prop_t *prop, WORD flags,
245         DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
246 {
247     HRESULT hres;
248
249     switch(prop->type) {
250     case PROP_BUILTIN: {
251         vdisp_t vthis;
252
253         if(flags == DISPATCH_CONSTRUCT && (prop->flags & DISPATCH_METHOD)) {
254             WARN("%s is not a constructor\n", debugstr_w(prop->name));
255             return E_INVALIDARG;
256         }
257
258         set_jsdisp(&vthis, jsthis);
259         hres = prop->u.p->invoke(This->ctx, &vthis, flags, dp, retv, ei, caller);
260         vdisp_release(&vthis);
261         return hres;
262     }
263     case PROP_PROTREF:
264         return invoke_prop_func(This->prototype, jsthis, This->prototype->props+prop->u.ref, flags, dp, retv, ei, caller);
265     case PROP_VARIANT: {
266         DISPPARAMS new_dp;
267
268         if(V_VT(&prop->u.var) != VT_DISPATCH) {
269             FIXME("invoke vt %d\n", V_VT(&prop->u.var));
270             return E_FAIL;
271         }
272
273         TRACE("call %s %p\n", debugstr_w(prop->name), V_DISPATCH(&prop->u.var));
274
275         hres = set_this(&new_dp, dp, (IDispatch*)_IDispatchEx_(jsthis));
276         if(FAILED(hres))
277             return hres;
278
279         hres = disp_call(This->ctx, V_DISPATCH(&prop->u.var), DISPID_VALUE, flags, &new_dp, retv, ei, caller);
280
281         if(new_dp.rgvarg != dp->rgvarg) {
282             heap_free(new_dp.rgvarg);
283             if(new_dp.cNamedArgs > 1)
284                 heap_free(new_dp.rgdispidNamedArgs);
285         }
286
287         return hres;
288     }
289     default:
290         ERR("type %d\n", prop->type);
291     }
292
293     return E_FAIL;
294 }
295
296 static HRESULT prop_get(DispatchEx *This, dispex_prop_t *prop, DISPPARAMS *dp,
297         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
298 {
299     HRESULT hres;
300
301     switch(prop->type) {
302     case PROP_BUILTIN:
303         if(prop->u.p->flags & PROPF_METHOD) {
304             DispatchEx *obj;
305             hres = create_builtin_function(This->ctx, prop->u.p->invoke, prop->u.p->name, NULL,
306                     prop->u.p->flags, NULL, &obj);
307             if(FAILED(hres))
308                 break;
309
310             prop->type = PROP_VARIANT;
311             V_VT(&prop->u.var) = VT_DISPATCH;
312             V_DISPATCH(&prop->u.var) = (IDispatch*)_IDispatchEx_(obj);
313
314             hres = VariantCopy(retv, &prop->u.var);
315         }else {
316             vdisp_t vthis;
317
318             set_jsdisp(&vthis, This);
319             hres = prop->u.p->invoke(This->ctx, &vthis, DISPATCH_PROPERTYGET, dp, retv, ei, caller);
320             vdisp_release(&vthis);
321         }
322         break;
323     case PROP_PROTREF:
324         hres = prop_get(This->prototype, This->prototype->props+prop->u.ref, dp, retv, ei, caller);
325         break;
326     case PROP_VARIANT:
327         hres = VariantCopy(retv, &prop->u.var);
328         break;
329     default:
330         ERR("type %d\n", prop->type);
331         return E_FAIL;
332     }
333
334     if(FAILED(hres)) {
335         TRACE("fail %08x\n", hres);
336         return hres;
337     }
338
339     TRACE("%s ret %s\n", debugstr_w(prop->name), debugstr_variant(retv));
340     return hres;
341 }
342
343 static HRESULT prop_put(DispatchEx *This, dispex_prop_t *prop, DISPPARAMS *dp,
344         jsexcept_t *ei, IServiceProvider *caller)
345 {
346     DWORD i;
347     HRESULT hres;
348
349     switch(prop->type) {
350     case PROP_BUILTIN:
351         if(!(prop->flags & PROPF_METHOD)) {
352             vdisp_t vthis;
353
354             set_jsdisp(&vthis, This);
355             hres = prop->u.p->invoke(This->ctx, &vthis, DISPATCH_PROPERTYPUT, dp, NULL, ei, caller);
356             vdisp_release(&vthis);
357             return hres;
358         }
359     case PROP_PROTREF:
360         prop->type = PROP_VARIANT;
361         prop->flags = PROPF_ENUM;
362         V_VT(&prop->u.var) = VT_EMPTY;
363         break;
364     case PROP_VARIANT:
365         VariantClear(&prop->u.var);
366         break;
367     default:
368         ERR("type %d\n", prop->type);
369         return E_FAIL;
370     }
371
372     for(i=0; i < dp->cNamedArgs; i++) {
373         if(dp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
374             break;
375     }
376
377     if(i == dp->cNamedArgs) {
378         TRACE("no value to set\n");
379         return DISP_E_PARAMNOTOPTIONAL;
380     }
381
382     hres = VariantCopy(&prop->u.var, dp->rgvarg+i);
383     if(FAILED(hres))
384         return hres;
385
386     if(This->builtin_info->on_put)
387         This->builtin_info->on_put(This, prop->name);
388
389     TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_variant(dp->rgvarg+i));
390     return S_OK;
391 }
392
393 static HRESULT fill_protrefs(DispatchEx *This)
394 {
395     dispex_prop_t *iter, *prop;
396     HRESULT hres;
397
398     if(!This->prototype)
399         return S_OK;
400
401     fill_protrefs(This->prototype);
402
403     for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) {
404         if(!iter->name)
405             continue;
406         hres = find_prop_name(This, iter->name, &prop);
407         if(FAILED(hres))
408             return hres;
409         if(!prop) {
410             prop = alloc_protref(This, iter->name, iter - This->prototype->props);
411             if(!prop)
412                 return E_OUTOFMEMORY;
413         }
414     }
415
416     return S_OK;
417 }
418
419 #define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface)
420
421 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
422 {
423     DispatchEx *This = DISPATCHEX_THIS(iface);
424
425     if(IsEqualGUID(&IID_IUnknown, riid)) {
426         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
427         *ppv = _IDispatchEx_(This);
428     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
429         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
430         *ppv = _IDispatchEx_(This);
431     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
432         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
433         *ppv = _IDispatchEx_(This);
434     }else if(IsEqualGUID(&IID_IDispatchJS, riid)) {
435         TRACE("(%p)->(IID_IDispatchJS %p)\n", This, ppv);
436         IUnknown_AddRef(_IDispatchEx_(This));
437         *ppv = This;
438         return S_OK;
439     }else {
440         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
441         *ppv = NULL;
442         return E_NOINTERFACE;
443     }
444
445     IUnknown_AddRef((IUnknown*)*ppv);
446     return S_OK;
447 }
448
449 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
450 {
451     DispatchEx *This = DISPATCHEX_THIS(iface);
452     LONG ref = InterlockedIncrement(&This->ref);
453
454     TRACE("(%p) ref=%d\n", This, ref);
455
456     return ref;
457 }
458
459 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
460 {
461     DispatchEx *This = DISPATCHEX_THIS(iface);
462     LONG ref = InterlockedDecrement(&This->ref);
463
464     TRACE("(%p) ref=%d\n", This, ref);
465
466     if(!ref) {
467         dispex_prop_t *prop;
468
469         for(prop = This->props; prop < This->props+This->prop_cnt; prop++) {
470             if(prop->type == PROP_VARIANT)
471                 VariantClear(&prop->u.var);
472             heap_free(prop->name);
473         }
474         heap_free(This->props);
475         script_release(This->ctx);
476
477         if(This->builtin_info->destructor)
478             This->builtin_info->destructor(This);
479         else
480             heap_free(This);
481     }
482
483     return ref;
484 }
485
486 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
487 {
488     DispatchEx *This = DISPATCHEX_THIS(iface);
489
490     TRACE("(%p)->(%p)\n", This, pctinfo);
491
492     *pctinfo = 1;
493     return S_OK;
494 }
495
496 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
497                                               ITypeInfo **ppTInfo)
498 {
499     DispatchEx *This = DISPATCHEX_THIS(iface);
500     FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
501     return E_NOTIMPL;
502 }
503
504 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
505                                                 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
506                                                 DISPID *rgDispId)
507 {
508     DispatchEx *This = DISPATCHEX_THIS(iface);
509     UINT i;
510     HRESULT hres;
511
512     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
513           lcid, rgDispId);
514
515     for(i=0; i < cNames; i++) {
516         hres = IDispatchEx_GetDispID(_IDispatchEx_(This), rgszNames[i], 0, rgDispId+i);
517         if(FAILED(hres))
518             return hres;
519     }
520
521     return S_OK;
522 }
523
524 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
525                                         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
526                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
527 {
528     DispatchEx *This = DISPATCHEX_THIS(iface);
529
530     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
531           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
532
533     return IDispatchEx_InvokeEx(_IDispatchEx_(This), dispIdMember, lcid, wFlags,
534             pDispParams, pVarResult, pExcepInfo, NULL);
535 }
536
537 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
538 {
539     DispatchEx *This = DISPATCHEX_THIS(iface);
540
541     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
542
543     if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) {
544         FIXME("Unsupported grfdex %x\n", grfdex);
545         return E_NOTIMPL;
546     }
547
548     return jsdisp_get_id(This, bstrName, grfdex, pid);
549 }
550
551 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
552         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
553 {
554     DispatchEx *This = DISPATCHEX_THIS(iface);
555     dispex_prop_t *prop;
556     jsexcept_t jsexcept;
557     HRESULT hres;
558
559     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
560
561     if(pvarRes)
562         V_VT(pvarRes) = VT_EMPTY;
563
564     prop = get_prop(This, id);
565     if(!prop || prop->type == PROP_DELETED) {
566         TRACE("invalid id\n");
567         return DISP_E_MEMBERNOTFOUND;
568     }
569
570     memset(&jsexcept, 0, sizeof(jsexcept));
571
572     switch(wFlags) {
573     case DISPATCH_METHOD:
574     case DISPATCH_CONSTRUCT:
575         hres = invoke_prop_func(This, This, prop, wFlags, pdp, pvarRes, &jsexcept, pspCaller);
576         break;
577     case DISPATCH_PROPERTYGET:
578         hres = prop_get(This, prop, pdp, pvarRes, &jsexcept, pspCaller);
579         break;
580     case DISPATCH_PROPERTYPUT:
581         hres = prop_put(This, prop, pdp, &jsexcept, pspCaller);
582         break;
583     default:
584         FIXME("Unimplemented flags %x\n", wFlags);
585         return E_INVALIDARG;
586     }
587
588     if(pei)
589         *pei = jsexcept.ei;
590
591     return hres;
592 }
593
594 static HRESULT delete_prop(dispex_prop_t *prop)
595 {
596     heap_free(prop->name);
597     prop->name = NULL;
598     prop->type = PROP_DELETED;
599
600     return S_OK;
601 }
602
603 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
604 {
605     DispatchEx *This = DISPATCHEX_THIS(iface);
606     dispex_prop_t *prop;
607     HRESULT hres;
608
609     TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
610
611     if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
612         FIXME("Unsupported grfdex %x\n", grfdex);
613
614     hres = find_prop_name(This, bstrName, &prop);
615     if(FAILED(hres))
616         return hres;
617     if(!prop) {
618         TRACE("not found\n");
619         return S_OK;
620     }
621
622     return delete_prop(prop);
623 }
624
625 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
626 {
627     DispatchEx *This = DISPATCHEX_THIS(iface);
628     dispex_prop_t *prop;
629
630     TRACE("(%p)->(%x)\n", This, id);
631
632     prop = get_prop(This, id);
633     if(!prop) {
634         WARN("invalid id\n");
635         return DISP_E_MEMBERNOTFOUND;
636     }
637
638     return delete_prop(prop);
639 }
640
641 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
642 {
643     DispatchEx *This = DISPATCHEX_THIS(iface);
644     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
645     return E_NOTIMPL;
646 }
647
648 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
649 {
650     DispatchEx *This = DISPATCHEX_THIS(iface);
651     dispex_prop_t *prop;
652
653     TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
654
655     prop = get_prop(This, id);
656     if(!prop || !prop->name || prop->type == PROP_DELETED)
657         return DISP_E_MEMBERNOTFOUND;
658
659     *pbstrName = SysAllocString(prop->name);
660     if(!*pbstrName)
661         return E_OUTOFMEMORY;
662
663     return S_OK;
664 }
665
666 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
667 {
668     DispatchEx *This = DISPATCHEX_THIS(iface);
669     dispex_prop_t *iter;
670     HRESULT hres;
671
672     TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
673
674     if(id == DISPID_STARTENUM) {
675         hres = fill_protrefs(This);
676         if(FAILED(hres))
677             return hres;
678     }
679
680     iter = get_prop(This, id+1);
681     if(!iter) {
682         *pid = DISPID_STARTENUM;
683         return S_FALSE;
684     }
685
686     while(iter < This->props + This->prop_cnt) {
687         if(iter->name && (get_flags(This, iter) & PROPF_ENUM)) {
688             *pid = prop_to_id(This, iter);
689             return S_OK;
690         }
691         iter++;
692     }
693
694     *pid = DISPID_STARTENUM;
695     return S_FALSE;
696 }
697
698 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
699 {
700     DispatchEx *This = DISPATCHEX_THIS(iface);
701     FIXME("(%p)->(%p)\n", This, ppunk);
702     return E_NOTIMPL;
703 }
704
705 #undef DISPATCHEX_THIS
706
707 static IDispatchExVtbl DispatchExVtbl = {
708     DispatchEx_QueryInterface,
709     DispatchEx_AddRef,
710     DispatchEx_Release,
711     DispatchEx_GetTypeInfoCount,
712     DispatchEx_GetTypeInfo,
713     DispatchEx_GetIDsOfNames,
714     DispatchEx_Invoke,
715     DispatchEx_GetDispID,
716     DispatchEx_InvokeEx,
717     DispatchEx_DeleteMemberByName,
718     DispatchEx_DeleteMemberByDispID,
719     DispatchEx_GetMemberProperties,
720     DispatchEx_GetMemberName,
721     DispatchEx_GetNextDispID,
722     DispatchEx_GetNameSpaceParent
723 };
724
725 HRESULT init_dispex(DispatchEx *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *prototype)
726 {
727     TRACE("%p (%p)\n", dispex, prototype);
728
729     dispex->lpIDispatchExVtbl = &DispatchExVtbl;
730     dispex->ref = 1;
731     dispex->builtin_info = builtin_info;
732
733     dispex->props = heap_alloc((dispex->buf_size=4) * sizeof(dispex_prop_t));
734     if(!dispex->props)
735         return E_OUTOFMEMORY;
736
737     dispex->prototype = prototype;
738     if(prototype)
739         IDispatchEx_AddRef(_IDispatchEx_(prototype));
740
741     dispex->prop_cnt = 1;
742     dispex->props[0].name = NULL;
743     dispex->props[0].flags = 0;
744     if(builtin_info->value_prop.invoke) {
745         dispex->props[0].type = PROP_BUILTIN;
746         dispex->props[0].u.p = &builtin_info->value_prop;
747     }else {
748         dispex->props[0].type = PROP_DELETED;
749     }
750
751     script_addref(ctx);
752     dispex->ctx = ctx;
753
754     return S_OK;
755 }
756
757 static const builtin_info_t dispex_info = {
758     JSCLASS_NONE,
759     {NULL, NULL, 0},
760     0, NULL,
761     NULL,
762     NULL
763 };
764
765 HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *prototype, DispatchEx **dispex)
766 {
767     DispatchEx *ret;
768     HRESULT hres;
769
770     ret = heap_alloc_zero(sizeof(DispatchEx));
771     if(!ret)
772         return E_OUTOFMEMORY;
773
774     hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype);
775     if(FAILED(hres))
776         return hres;
777
778     *dispex = ret;
779     return S_OK;
780 }
781
782 HRESULT init_dispex_from_constr(DispatchEx *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *constr)
783 {
784     DispatchEx *prot = NULL;
785     dispex_prop_t *prop;
786     HRESULT hres;
787
788     static const WCHAR prototypeW[] = {'p','r','o','t','o','t','y','p','e',0};
789
790     hres = find_prop_name_prot(constr, prototypeW, FALSE, &prop);
791     if(SUCCEEDED(hres) && prop) {
792         jsexcept_t jsexcept;
793         VARIANT var;
794
795         V_VT(&var) = VT_EMPTY;
796         memset(&jsexcept, 0, sizeof(jsexcept));
797         hres = prop_get(constr, prop, NULL, &var, &jsexcept, NULL/*FIXME*/);
798         if(FAILED(hres)) {
799             ERR("Could not get prototype\n");
800             return hres;
801         }
802
803         if(V_VT(&var) == VT_DISPATCH)
804             prot = iface_to_jsdisp((IUnknown*)V_DISPATCH(&var));
805         VariantClear(&var);
806     }
807
808     hres = init_dispex(dispex, ctx, builtin_info, prot);
809
810     if(prot)
811         jsdisp_release(prot);
812     return hres;
813 }
814
815 DispatchEx *iface_to_jsdisp(IUnknown *iface)
816 {
817     DispatchEx *ret;
818     HRESULT hres;
819
820     hres = IUnknown_QueryInterface(iface, &IID_IDispatchJS, (void**)&ret);
821     if(FAILED(hres))
822         return NULL;
823
824     return ret;
825 }
826
827 HRESULT jsdisp_get_id(DispatchEx *jsdisp, const WCHAR *name, DWORD flags, DISPID *id)
828 {
829     dispex_prop_t *prop;
830     HRESULT hres;
831
832     hres = find_prop_name_prot(jsdisp, name, (flags&fdexNameEnsure) != 0, &prop);
833     if(FAILED(hres))
834         return hres;
835
836     if(prop) {
837         *id = prop_to_id(jsdisp, prop);
838         return S_OK;
839     }
840
841     TRACE("not found %s\n", debugstr_w(name));
842     return DISP_E_UNKNOWNNAME;
843 }
844
845 HRESULT jsdisp_call_value(DispatchEx *jsthis, WORD flags, DISPPARAMS *dp, VARIANT *retv,
846         jsexcept_t *ei, IServiceProvider *caller)
847 {
848     vdisp_t vdisp;
849     HRESULT hres;
850
851     set_jsdisp(&vdisp, jsthis);
852     hres = jsthis->builtin_info->value_prop.invoke(jsthis->ctx, &vdisp, flags, dp, retv, ei, caller);
853     vdisp_release(&vdisp);
854     return hres;
855 }
856
857 HRESULT jsdisp_call(DispatchEx *disp, DISPID id, WORD flags, DISPPARAMS *dp, VARIANT *retv,
858         jsexcept_t *ei, IServiceProvider *caller)
859 {
860     dispex_prop_t *prop;
861
862     memset(ei, 0, sizeof(*ei));
863     if(retv)
864         V_VT(retv) = VT_EMPTY;
865
866     prop = get_prop(disp, id);
867     if(!prop)
868         return DISP_E_MEMBERNOTFOUND;
869
870     return invoke_prop_func(disp, disp, prop, flags, dp, retv, ei, caller);
871 }
872
873 HRESULT jsdisp_call_name(DispatchEx *disp, const WCHAR *name, WORD flags, DISPPARAMS *dp, VARIANT *retv,
874         jsexcept_t *ei, IServiceProvider *caller)
875 {
876     dispex_prop_t *prop;
877     HRESULT hres;
878
879     hres = find_prop_name_prot(disp, name, TRUE, &prop);
880     if(FAILED(hres))
881         return hres;
882
883     memset(ei, 0, sizeof(*ei));
884     if(retv)
885         V_VT(retv) = VT_EMPTY;
886
887     return invoke_prop_func(disp, disp, prop, flags, dp, retv, ei, caller);
888 }
889
890 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, DISPPARAMS *dp, VARIANT *retv,
891         jsexcept_t *ei, IServiceProvider *caller)
892 {
893     DispatchEx *jsdisp;
894     IDispatchEx *dispex;
895     HRESULT hres;
896
897     jsdisp = iface_to_jsdisp((IUnknown*)disp);
898     if(jsdisp) {
899         hres = jsdisp_call(jsdisp, id, flags, dp, retv, ei, caller);
900         jsdisp_release(jsdisp);
901         return hres;
902     }
903
904     memset(ei, 0, sizeof(*ei));
905
906     if(retv)
907         V_VT(retv) = VT_EMPTY;
908     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
909     if(FAILED(hres)) {
910         UINT err = 0;
911
912         if(flags == DISPATCH_CONSTRUCT) {
913             WARN("IDispatch cannot be constructor\n");
914             return DISP_E_MEMBERNOTFOUND;
915         }
916
917         TRACE("using IDispatch\n");
918         return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, dp, retv, &ei->ei, &err);
919     }
920
921     hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, dp, retv, &ei->ei, caller);
922     IDispatchEx_Release(dispex);
923
924     return hres;
925 }
926
927 HRESULT jsdisp_propput_name(DispatchEx *obj, const WCHAR *name, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller)
928 {
929     DISPID named_arg = DISPID_PROPERTYPUT;
930     DISPPARAMS dp = {val, &named_arg, 1, 1};
931     dispex_prop_t *prop;
932     HRESULT hres;
933
934     hres = find_prop_name_prot(obj, name, TRUE, &prop);
935     if(FAILED(hres))
936         return hres;
937
938     return prop_put(obj, prop, &dp, ei, caller);
939 }
940
941 HRESULT jsdisp_propput_idx(DispatchEx *obj, DWORD idx, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller)
942 {
943     WCHAR buf[12];
944
945     static const WCHAR formatW[] = {'%','d',0};
946
947     sprintfW(buf, formatW, idx);
948     return jsdisp_propput_name(obj, buf, val, ei, caller);
949 }
950
951 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller)
952 {
953     DISPID dispid = DISPID_PROPERTYPUT;
954     DISPPARAMS dp  = {val, &dispid, 1, 1};
955     IDispatchEx *dispex;
956     DispatchEx *jsdisp;
957     HRESULT hres;
958
959     jsdisp = iface_to_jsdisp((IUnknown*)disp);
960     if(jsdisp) {
961         dispex_prop_t *prop;
962
963         prop = get_prop(jsdisp, id);
964         if(prop)
965             hres = prop_put(jsdisp, prop, &dp, ei, caller);
966         else
967             hres = DISP_E_MEMBERNOTFOUND;
968
969         jsdisp_release(jsdisp);
970         return hres;
971     }
972
973     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
974     if(FAILED(hres)) {
975         ULONG err = 0;
976
977         TRACE("using IDispatch\n");
978         return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, DISPATCH_PROPERTYPUT, &dp, NULL, &ei->ei, &err);
979     }
980
981     hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_PROPERTYPUT, &dp, NULL, &ei->ei, caller);
982
983     IDispatchEx_Release(dispex);
984     return hres;
985 }
986
987 HRESULT jsdisp_propget_name(DispatchEx *obj, const WCHAR *name, VARIANT *var, jsexcept_t *ei, IServiceProvider *caller)
988 {
989     DISPPARAMS dp = {NULL, NULL, 0, 0};
990     dispex_prop_t *prop;
991     HRESULT hres;
992
993     hres = find_prop_name_prot(obj, name, FALSE, &prop);
994     if(FAILED(hres))
995         return hres;
996
997     V_VT(var) = VT_EMPTY;
998     if(!prop)
999         return S_OK;
1000
1001     return prop_get(obj, prop, &dp, var, ei, caller);
1002 }
1003
1004 HRESULT jsdisp_propget_idx(DispatchEx *obj, DWORD idx, VARIANT *var, jsexcept_t *ei, IServiceProvider *caller)
1005 {
1006     WCHAR buf[12];
1007
1008     static const WCHAR formatW[] = {'%','d',0};
1009
1010     sprintfW(buf, formatW, idx);
1011     return jsdisp_propget_name(obj, buf, var, ei, caller);
1012 }
1013
1014 HRESULT jsdisp_propget(DispatchEx *jsdisp, DISPID id, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller)
1015 {
1016     DISPPARAMS dp  = {NULL,NULL,0,0};
1017     dispex_prop_t *prop;
1018
1019     prop = get_prop(jsdisp, id);
1020     if(!prop)
1021         return DISP_E_MEMBERNOTFOUND;
1022
1023     V_VT(val) = VT_EMPTY;
1024     return prop_get(jsdisp, prop, &dp, val, ei, caller);
1025 }
1026
1027 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller)
1028 {
1029     DISPPARAMS dp  = {NULL,NULL,0,0};
1030     IDispatchEx *dispex;
1031     DispatchEx *jsdisp;
1032     HRESULT hres;
1033
1034     jsdisp = iface_to_jsdisp((IUnknown*)disp);
1035     if(jsdisp) {
1036         hres = jsdisp_propget(jsdisp, id, val, ei, caller);
1037         jsdisp_release(jsdisp);
1038         return hres;
1039     }
1040
1041     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1042     if(FAILED(hres)) {
1043         ULONG err = 0;
1044
1045         TRACE("using IDispatch\n");
1046         return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, INVOKE_PROPERTYGET, &dp, val, &ei->ei, &err);
1047     }
1048
1049     hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, INVOKE_PROPERTYGET, &dp, val, &ei->ei, caller);
1050     IDispatchEx_Release(dispex);
1051
1052     return hres;
1053 }
1054
1055 HRESULT jsdisp_delete_idx(DispatchEx *obj, DWORD idx)
1056 {
1057     static const WCHAR formatW[] = {'%','d',0};
1058     WCHAR buf[12];
1059     dispex_prop_t *prop;
1060     HRESULT hres;
1061
1062     sprintfW(buf, formatW, idx);
1063
1064     hres = find_prop_name(obj, buf, &prop);
1065     if(FAILED(hres) || !prop)
1066         return hres;
1067
1068     return delete_prop(prop);
1069 }