vbscript: Added support for a few more constants.
[wine] / dlls / vbscript / vbdisp.c
1 /*
2  * Copyright 2011 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 "vbscript.h"
22
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
26
27 #define FDEX_VERSION_MASK 0xf0000000
28
29 static inline BOOL is_func_id(vbdisp_t *This, DISPID id)
30 {
31     return id < This->desc->func_cnt;
32 }
33
34 static BOOL get_func_id(vbdisp_t *This, const WCHAR *name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id)
35 {
36     unsigned i;
37
38     for(i = invoke_type == VBDISP_ANY ? 0 : 1; i < This->desc->func_cnt; i++) {
39         if(invoke_type == VBDISP_ANY) {
40             if(!search_private && !This->desc->funcs[i].is_public)
41                 continue;
42             if(!i && !This->desc->funcs[0].name) /* default value may not exist */
43                 continue;
44         }else {
45             if(!This->desc->funcs[i].entries[invoke_type]
46                 || (!search_private && !This->desc->funcs[i].entries[invoke_type]->is_public))
47                 continue;
48         }
49
50         if(!strcmpiW(This->desc->funcs[i].name, name)) {
51             *id = i;
52             return TRUE;
53         }
54     }
55
56     return FALSE;
57 }
58
59 HRESULT vbdisp_get_id(vbdisp_t *This, BSTR name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id)
60 {
61     unsigned i;
62
63     if(get_func_id(This, name, invoke_type, search_private, id))
64         return S_OK;
65
66     for(i=0; i < This->desc->prop_cnt; i++) {
67         if(!search_private && !This->desc->props[i].is_public)
68             continue;
69
70         if(!strcmpiW(This->desc->props[i].name, name)) {
71             *id = i + This->desc->func_cnt;
72             return S_OK;
73         }
74     }
75
76     if(This->desc->typeinfo) {
77         HRESULT hres;
78
79         hres = ITypeInfo_GetIDsOfNames(This->desc->typeinfo, &name, 1, id);
80         if(SUCCEEDED(hres))
81             return S_OK;
82     }
83
84     *id = -1;
85     return DISP_E_UNKNOWNNAME;
86 }
87
88 static VARIANT *get_propput_arg(const DISPPARAMS *dp)
89 {
90     unsigned i;
91
92     for(i=0; i < dp->cNamedArgs; i++) {
93         if(dp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
94             return dp->rgvarg+i;
95     }
96
97     return NULL;
98 }
99
100 static HRESULT invoke_variant_prop(VARIANT *v, WORD flags, DISPPARAMS *dp, VARIANT *res)
101 {
102     HRESULT hres;
103
104     switch(flags) {
105     case DISPATCH_PROPERTYGET|DISPATCH_METHOD:
106     case DISPATCH_PROPERTYGET:
107         if(dp->cArgs) {
108             WARN("called with arguments\n");
109             return DISP_E_MEMBERNOTFOUND; /* That's what tests show */
110         }
111
112         hres = VariantCopy(res, v);
113         break;
114
115     case DISPATCH_PROPERTYPUT: {
116         VARIANT *put_val;
117
118         put_val = get_propput_arg(dp);
119         if(!put_val) {
120             WARN("no value to set\n");
121             return DISP_E_PARAMNOTOPTIONAL;
122         }
123
124         if(res)
125             V_VT(res) = VT_EMPTY;
126
127         hres = VariantCopy(v, put_val);
128         break;
129     }
130
131     default:
132         FIXME("unimplemented flags %x\n", flags);
133         return E_NOTIMPL;
134     }
135
136     return hres;
137 }
138
139 static HRESULT invoke_builtin(vbdisp_t *This, const builtin_prop_t *prop, WORD flags, DISPPARAMS *dp, VARIANT *res)
140 {
141     VARIANT args[8];
142     unsigned argn, i;
143
144     switch(flags) {
145     case DISPATCH_PROPERTYGET:
146         if(!(prop->flags & (BP_GET|BP_GETPUT))) {
147             FIXME("property does not support DISPATCH_PROPERTYGET\n");
148             return E_FAIL;
149         }
150         break;
151     case DISPATCH_PROPERTYGET|DISPATCH_METHOD:
152         if(!prop->proc && prop->flags == BP_GET) {
153             int val = prop->min_args;
154             if(val < 0x4000 && (INT16)val == val) {
155                 V_VT(res) = VT_I2;
156                 V_I2(res) = val;
157             }else {
158                 V_VT(res) = VT_I4;
159                 V_I4(res) = val;
160             }
161             return S_OK;
162         }
163         break;
164     case DISPATCH_METHOD:
165         if(prop->flags & (BP_GET|BP_GETPUT)) {
166             FIXME("Call on property\n");
167             return E_FAIL;
168         }
169         break;
170     case DISPATCH_PROPERTYPUT:
171         if(!(prop->flags & BP_GETPUT)) {
172             FIXME("property does not support DISPATCH_PROPERTYPUT\n");
173             return E_FAIL;
174         }
175
176         FIXME("call put\n");
177         return E_NOTIMPL;
178     default:
179         FIXME("unsupported flags %x\n", flags);
180         return E_NOTIMPL;
181     }
182
183     argn = arg_cnt(dp);
184
185     if(argn < prop->min_args || argn > (prop->max_args ? prop->max_args : prop->min_args)) {
186         FIXME("invalid number of arguments\n");
187         return E_FAIL;
188     }
189
190     assert(argn < sizeof(args)/sizeof(*args));
191
192     for(i=0; i < argn; i++) {
193         if(V_VT(dp->rgvarg+dp->cArgs-i-1) == (VT_BYREF|VT_VARIANT))
194             args[i] = *V_VARIANTREF(dp->rgvarg+dp->cArgs-i-1);
195         else
196             args[i] = dp->rgvarg[dp->cArgs-i-1];
197     }
198
199     return prop->proc(This, args, dp->cArgs, res);
200 }
201
202 static BOOL run_terminator(vbdisp_t *This)
203 {
204     DISPPARAMS dp = {0};
205
206     if(This->terminator_ran)
207         return TRUE;
208     This->terminator_ran = TRUE;
209
210     if(!This->desc->class_terminate_id)
211         return TRUE;
212
213     This->ref++;
214     exec_script(This->desc->ctx, This->desc->funcs[This->desc->class_terminate_id].entries[VBDISP_CALLGET],
215             (IDispatch*)&This->IDispatchEx_iface, &dp, NULL);
216     return !--This->ref;
217 }
218
219 static void clean_props(vbdisp_t *This)
220 {
221     unsigned i;
222
223     if(!This->desc)
224         return;
225
226     for(i=0; i < This->desc->prop_cnt; i++)
227         VariantClear(This->props+i);
228 }
229
230 static inline vbdisp_t *impl_from_IDispatchEx(IDispatchEx *iface)
231 {
232     return CONTAINING_RECORD(iface, vbdisp_t, IDispatchEx_iface);
233 }
234
235 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
236 {
237     vbdisp_t *This = impl_from_IDispatchEx(iface);
238
239     if(IsEqualGUID(&IID_IUnknown, riid)) {
240         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
241         *ppv = &This->IDispatchEx_iface;
242     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
243         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
244         *ppv = &This->IDispatchEx_iface;
245     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
246         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
247         *ppv = &This->IDispatchEx_iface;
248     }else {
249         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
250         *ppv = NULL;
251         return E_NOINTERFACE;
252     }
253
254     IUnknown_AddRef((IUnknown*)*ppv);
255     return S_OK;
256 }
257
258 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
259 {
260     vbdisp_t *This = impl_from_IDispatchEx(iface);
261     LONG ref = InterlockedIncrement(&This->ref);
262
263     TRACE("(%p) ref=%d\n", This, ref);
264
265     return ref;
266 }
267
268 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
269 {
270     vbdisp_t *This = impl_from_IDispatchEx(iface);
271     LONG ref = InterlockedDecrement(&This->ref);
272
273     TRACE("(%p) ref=%d\n", This, ref);
274
275     if(!ref && run_terminator(This)) {
276         clean_props(This);
277         list_remove(&This->entry);
278         heap_free(This);
279     }
280
281     return ref;
282 }
283
284 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
285 {
286     vbdisp_t *This = impl_from_IDispatchEx(iface);
287
288     TRACE("(%p)->(%p)\n", This, pctinfo);
289
290     *pctinfo = 1;
291     return S_OK;
292 }
293
294 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
295                                               ITypeInfo **ppTInfo)
296 {
297     vbdisp_t *This = impl_from_IDispatchEx(iface);
298     FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
299     return E_NOTIMPL;
300 }
301
302 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
303                                                 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
304                                                 DISPID *rgDispId)
305 {
306     vbdisp_t *This = impl_from_IDispatchEx(iface);
307     FIXME("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
308           lcid, rgDispId);
309     return E_NOTIMPL;
310 }
311
312 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
313                                         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
314                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
315 {
316     vbdisp_t *This = impl_from_IDispatchEx(iface);
317     FIXME("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
318           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
319     return E_NOTIMPL;
320 }
321
322 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
323 {
324     vbdisp_t *This = impl_from_IDispatchEx(iface);
325
326     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
327
328     grfdex &= ~FDEX_VERSION_MASK;
329
330     if(!This->desc)
331         return E_UNEXPECTED;
332
333     /* Tests show that fdexNameCaseSensitive is ignored */
334
335     if(grfdex & ~(fdexNameEnsure|fdexNameCaseInsensitive|fdexNameCaseSensitive)) {
336         FIXME("unsupported flags %x\n", grfdex);
337         return E_NOTIMPL;
338     }
339
340     return vbdisp_get_id(This, bstrName, VBDISP_ANY, FALSE, pid);
341 }
342
343 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
344         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
345 {
346     vbdisp_t *This = impl_from_IDispatchEx(iface);
347
348     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
349
350     if(!This->desc)
351         return E_UNEXPECTED;
352
353     if(pvarRes)
354         V_VT(pvarRes) = VT_EMPTY;
355
356     if(id < 0)
357         return DISP_E_MEMBERNOTFOUND;
358
359     if(is_func_id(This, id)) {
360         function_t *func;
361
362         switch(wFlags) {
363         case DISPATCH_METHOD:
364         case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
365             func = This->desc->funcs[id].entries[VBDISP_CALLGET];
366             if(!func) {
367                 FIXME("no invoke/getter\n");
368                 return DISP_E_MEMBERNOTFOUND;
369             }
370
371             return exec_script(This->desc->ctx, func, (IDispatch*)&This->IDispatchEx_iface, pdp, pvarRes);
372         case DISPATCH_PROPERTYPUT: {
373             VARIANT *put_val;
374             DISPPARAMS dp = {NULL, NULL, 1, 0};
375
376             if(arg_cnt(pdp)) {
377                 FIXME("arguments not implemented\n");
378                 return E_NOTIMPL;
379             }
380
381             put_val = get_propput_arg(pdp);
382             if(!put_val) {
383                 WARN("no value to set\n");
384                 return DISP_E_PARAMNOTOPTIONAL;
385             }
386
387             dp.rgvarg = put_val;
388             func = This->desc->funcs[id].entries[V_VT(put_val) == VT_DISPATCH ? VBDISP_SET : VBDISP_LET];
389             if(!func) {
390                 FIXME("no letter/setter\n");
391                 return DISP_E_MEMBERNOTFOUND;
392             }
393
394             return exec_script(This->desc->ctx, func, (IDispatch*)&This->IDispatchEx_iface, &dp, NULL);
395         }
396         default:
397             FIXME("flags %x\n", wFlags);
398             return DISP_E_MEMBERNOTFOUND;
399         }
400     }
401
402     if(id < This->desc->prop_cnt + This->desc->func_cnt)
403         return invoke_variant_prop(This->props+(id-This->desc->func_cnt), wFlags, pdp, pvarRes);
404
405     if(This->desc->builtin_prop_cnt) {
406         unsigned min = 0, max = This->desc->builtin_prop_cnt-1, i;
407
408         while(min <= max) {
409             i = (min+max)/2;
410             if(This->desc->builtin_props[i].id == id)
411                 return invoke_builtin(This, This->desc->builtin_props+i, wFlags, pdp, pvarRes);
412             if(This->desc->builtin_props[i].id < id)
413                 min = i+1;
414             else
415                 max = i-1;
416         }
417     }
418
419     return DISP_E_MEMBERNOTFOUND;
420 }
421
422 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
423 {
424     vbdisp_t *This = impl_from_IDispatchEx(iface);
425     FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
426     return E_NOTIMPL;
427 }
428
429 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
430 {
431     vbdisp_t *This = impl_from_IDispatchEx(iface);
432     FIXME("(%p)->(%x)\n", This, id);
433     return E_NOTIMPL;
434 }
435
436 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
437 {
438     vbdisp_t *This = impl_from_IDispatchEx(iface);
439     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
440     return E_NOTIMPL;
441 }
442
443 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
444 {
445     vbdisp_t *This = impl_from_IDispatchEx(iface);
446     FIXME("(%p)->(%x %p)\n", This, id, pbstrName);
447     return E_NOTIMPL;
448 }
449
450 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
451 {
452     vbdisp_t *This = impl_from_IDispatchEx(iface);
453     FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
454     return E_NOTIMPL;
455 }
456
457 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
458 {
459     vbdisp_t *This = impl_from_IDispatchEx(iface);
460     FIXME("(%p)->(%p)\n", This, ppunk);
461     return E_NOTIMPL;
462 }
463
464 static IDispatchExVtbl DispatchExVtbl = {
465     DispatchEx_QueryInterface,
466     DispatchEx_AddRef,
467     DispatchEx_Release,
468     DispatchEx_GetTypeInfoCount,
469     DispatchEx_GetTypeInfo,
470     DispatchEx_GetIDsOfNames,
471     DispatchEx_Invoke,
472     DispatchEx_GetDispID,
473     DispatchEx_InvokeEx,
474     DispatchEx_DeleteMemberByName,
475     DispatchEx_DeleteMemberByDispID,
476     DispatchEx_GetMemberProperties,
477     DispatchEx_GetMemberName,
478     DispatchEx_GetNextDispID,
479     DispatchEx_GetNameSpaceParent
480 };
481
482 static inline vbdisp_t *unsafe_impl_from_IDispatch(IDispatch *iface)
483 {
484     return iface->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl
485         ? CONTAINING_RECORD(iface, vbdisp_t, IDispatchEx_iface)
486         : NULL;
487 }
488
489 HRESULT create_vbdisp(const class_desc_t *desc, vbdisp_t **ret)
490 {
491     vbdisp_t *vbdisp;
492
493     vbdisp = heap_alloc_zero( FIELD_OFFSET( vbdisp_t, props[desc->prop_cnt] ));
494     if(!vbdisp)
495         return E_OUTOFMEMORY;
496
497     vbdisp->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
498     vbdisp->ref = 1;
499     vbdisp->desc = desc;
500
501     list_add_tail(&desc->ctx->objects, &vbdisp->entry);
502
503     if(desc->class_initialize_id) {
504         DISPPARAMS dp = {0};
505         HRESULT hres;
506
507         hres = exec_script(desc->ctx, desc->funcs[desc->class_initialize_id].entries[VBDISP_CALLGET],
508                            (IDispatch*)&vbdisp->IDispatchEx_iface, &dp, NULL);
509         if(FAILED(hres)) {
510             IDispatchEx_Release(&vbdisp->IDispatchEx_iface);
511             return hres;
512         }
513     }
514
515     *ret = vbdisp;
516     return S_OK;
517 }
518
519 static HRESULT Procedure_invoke(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res)
520 {
521     script_ctx_t *ctx = This->desc->ctx;
522     HRESULT hres;
523
524     TRACE("\n");
525
526     IActiveScriptSite_OnEnterScript(ctx->site);
527     hres = exec_script(ctx, This->desc->value_func, NULL, NULL, NULL);
528     IActiveScriptSite_OnLeaveScript(ctx->site);
529
530     return hres;
531 }
532
533 static const builtin_prop_t procedure_props[] = {
534     {DISPID_VALUE,  Procedure_invoke, 0}
535 };
536
537 HRESULT create_procedure_disp(script_ctx_t *ctx, vbscode_t *code, IDispatch **ret)
538 {
539     class_desc_t *desc;
540     vbdisp_t *vbdisp;
541     HRESULT hres;
542
543     desc = heap_alloc_zero(sizeof(*desc));
544     if(!desc)
545         return E_OUTOFMEMORY;
546
547     desc->ctx = ctx;
548     desc->builtin_prop_cnt = sizeof(procedure_props)/sizeof(*procedure_props);
549     desc->builtin_props = procedure_props;
550     desc->value_func = &code->main_code;
551
552     hres = create_vbdisp(desc, &vbdisp);
553     if(FAILED(hres)) {
554         heap_free(desc);
555         return hres;
556     }
557
558     desc->next = ctx->procs;
559     ctx->procs = desc;
560
561     *ret = (IDispatch*)&vbdisp->IDispatchEx_iface;
562     return S_OK;
563 }
564
565 struct _ident_map_t {
566     const WCHAR *name;
567     BOOL is_var;
568     union {
569         dynamic_var_t *var;
570         function_t *func;
571     } u;
572 };
573
574 static inline DISPID ident_to_id(ScriptDisp *This, ident_map_t *ident)
575 {
576     return (ident-This->ident_map)+1;
577 }
578
579 static inline ident_map_t *id_to_ident(ScriptDisp *This, DISPID id)
580 {
581     return 0 < id && id <= This->ident_map_cnt ? This->ident_map+id-1 : NULL;
582 }
583
584 static ident_map_t *add_ident(ScriptDisp *This, const WCHAR *name)
585 {
586     ident_map_t *ret;
587
588     if(!This->ident_map_size) {
589         This->ident_map = heap_alloc(4 * sizeof(*This->ident_map));
590         if(!This->ident_map)
591             return NULL;
592         This->ident_map_size = 4;
593     }else if(This->ident_map_cnt == This->ident_map_size) {
594         ident_map_t *new_map;
595
596         new_map = heap_realloc(This->ident_map, 2*This->ident_map_size*sizeof(*new_map));
597         if(!new_map)
598             return NULL;
599         This->ident_map = new_map;
600         This->ident_map_size *= 2;
601     }
602
603     ret = This->ident_map + This->ident_map_cnt++;
604     ret->name = name;
605     return ret;
606 }
607
608 static inline ScriptDisp *ScriptDisp_from_IDispatchEx(IDispatchEx *iface)
609 {
610     return CONTAINING_RECORD(iface, ScriptDisp, IDispatchEx_iface);
611 }
612
613 static HRESULT WINAPI ScriptDisp_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
614 {
615     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
616
617     if(IsEqualGUID(&IID_IUnknown, riid)) {
618         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
619         *ppv = &This->IDispatchEx_iface;
620     }else if(IsEqualGUID(&IID_IDispatch, riid)) {
621         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
622         *ppv = &This->IDispatchEx_iface;
623     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
624         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
625         *ppv = &This->IDispatchEx_iface;
626     }else {
627         WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
628         *ppv = NULL;
629         return E_NOINTERFACE;
630     }
631
632     IUnknown_AddRef((IUnknown*)*ppv);
633     return S_OK;
634 }
635
636 static ULONG WINAPI ScriptDisp_AddRef(IDispatchEx *iface)
637 {
638     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
639     LONG ref = InterlockedIncrement(&This->ref);
640
641     TRACE("(%p) ref=%d\n", This, ref);
642
643     return ref;
644 }
645
646 static ULONG WINAPI ScriptDisp_Release(IDispatchEx *iface)
647 {
648     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
649     LONG ref = InterlockedDecrement(&This->ref);
650
651     TRACE("(%p) ref=%d\n", This, ref);
652
653     if(!ref) {
654         assert(!This->ctx);
655         heap_free(This->ident_map);
656         heap_free(This);
657     }
658
659     return ref;
660 }
661
662 static HRESULT WINAPI ScriptDisp_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
663 {
664     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
665
666     TRACE("(%p)->(%p)\n", This, pctinfo);
667
668     *pctinfo = 1;
669     return S_OK;
670 }
671
672 static HRESULT WINAPI ScriptDisp_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
673                                               ITypeInfo **ppTInfo)
674 {
675     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
676     FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
677     return E_NOTIMPL;
678 }
679
680 static HRESULT WINAPI ScriptDisp_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
681         LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
682 {
683     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
684     UINT i;
685     HRESULT hres;
686
687     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
688           lcid, rgDispId);
689
690     for(i=0; i < cNames; i++) {
691         hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
692         if(FAILED(hres))
693             return hres;
694     }
695
696     return S_OK;
697 }
698
699 static HRESULT WINAPI ScriptDisp_Invoke(IDispatchEx *iface, DISPID dispIdMember, REFIID riid, LCID lcid,
700          WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
701 {
702     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
703
704     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
705           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
706
707     return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags,
708             pDispParams, pVarResult, pExcepInfo, NULL);
709 }
710
711 static HRESULT WINAPI ScriptDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
712 {
713     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
714     dynamic_var_t *var;
715     ident_map_t *ident;
716     function_t *func;
717
718     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
719
720     if(!This->ctx)
721         return E_UNEXPECTED;
722
723     for(ident = This->ident_map; ident < This->ident_map+This->ident_map_cnt; ident++) {
724         if(!strcmpiW(ident->name, bstrName)) {
725             *pid = ident_to_id(This, ident);
726             return S_OK;
727         }
728     }
729
730     for(var = This->ctx->global_vars; var; var = var->next) {
731         if(!strcmpiW(var->name, bstrName)) {
732             ident = add_ident(This, var->name);
733             if(!ident)
734                 return E_OUTOFMEMORY;
735
736             ident->is_var = TRUE;
737             ident->u.var = var;
738             *pid = ident_to_id(This, ident);
739             return S_OK;
740         }
741     }
742
743     for(func = This->ctx->global_funcs; func; func = func->next) {
744         if(!strcmpiW(func->name, bstrName)) {
745             ident = add_ident(This, func->name);
746             if(!ident)
747                 return E_OUTOFMEMORY;
748
749             ident->is_var = FALSE;
750             ident->u.func = func;
751             *pid =  ident_to_id(This, ident);
752             return S_OK;
753         }
754     }
755
756     *pid = -1;
757     return DISP_E_UNKNOWNNAME;
758 }
759
760 static HRESULT WINAPI ScriptDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
761         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
762 {
763     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
764     ident_map_t *ident;
765     HRESULT hres;
766
767     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
768
769     ident = id_to_ident(This, id);
770     if(!ident)
771         return DISP_E_MEMBERNOTFOUND;
772
773     if(ident->is_var) {
774         if(ident->u.var->is_const) {
775             FIXME("const not supported\n");
776             return E_NOTIMPL;
777         }
778
779         return invoke_variant_prop(&ident->u.var->v, wFlags, pdp, pvarRes);
780     }
781
782     switch(wFlags) {
783     case DISPATCH_METHOD:
784     case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
785         IActiveScriptSite_OnEnterScript(This->ctx->site);
786         hres = exec_script(This->ctx, ident->u.func, NULL, pdp, pvarRes);
787         IActiveScriptSite_OnLeaveScript(This->ctx->site);
788         break;
789     default:
790         FIXME("Unsupported flags %x\n", wFlags);
791         hres = E_NOTIMPL;
792     }
793
794     return hres;
795 }
796
797 static HRESULT WINAPI ScriptDisp_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
798 {
799     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
800     FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
801     return E_NOTIMPL;
802 }
803
804 static HRESULT WINAPI ScriptDisp_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
805 {
806     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
807     FIXME("(%p)->(%x)\n", This, id);
808     return E_NOTIMPL;
809 }
810
811 static HRESULT WINAPI ScriptDisp_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
812 {
813     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
814     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
815     return E_NOTIMPL;
816 }
817
818 static HRESULT WINAPI ScriptDisp_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
819 {
820     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
821     FIXME("(%p)->(%x %p)\n", This, id, pbstrName);
822     return E_NOTIMPL;
823 }
824
825 static HRESULT WINAPI ScriptDisp_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
826 {
827     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
828     FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
829     return E_NOTIMPL;
830 }
831
832 static HRESULT WINAPI ScriptDisp_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
833 {
834     ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface);
835     FIXME("(%p)->(%p)\n", This, ppunk);
836     return E_NOTIMPL;
837 }
838
839 static IDispatchExVtbl ScriptDispVtbl = {
840     ScriptDisp_QueryInterface,
841     ScriptDisp_AddRef,
842     ScriptDisp_Release,
843     ScriptDisp_GetTypeInfoCount,
844     ScriptDisp_GetTypeInfo,
845     ScriptDisp_GetIDsOfNames,
846     ScriptDisp_Invoke,
847     ScriptDisp_GetDispID,
848     ScriptDisp_InvokeEx,
849     ScriptDisp_DeleteMemberByName,
850     ScriptDisp_DeleteMemberByDispID,
851     ScriptDisp_GetMemberProperties,
852     ScriptDisp_GetMemberName,
853     ScriptDisp_GetNextDispID,
854     ScriptDisp_GetNameSpaceParent
855 };
856
857 HRESULT create_script_disp(script_ctx_t *ctx, ScriptDisp **ret)
858 {
859     ScriptDisp *script_disp;
860
861     script_disp = heap_alloc_zero(sizeof(*script_disp));
862     if(!script_disp)
863         return E_OUTOFMEMORY;
864
865     script_disp->IDispatchEx_iface.lpVtbl = &ScriptDispVtbl;
866     script_disp->ref = 1;
867     script_disp->ctx = ctx;
868
869     *ret = script_disp;
870     return S_OK;
871 }
872
873 void collect_objects(script_ctx_t *ctx)
874 {
875     vbdisp_t *iter, *iter2;
876
877     LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &ctx->objects, vbdisp_t, entry)
878         run_terminator(iter);
879
880     while(!list_empty(&ctx->objects)) {
881         iter = LIST_ENTRY(list_head(&ctx->objects), vbdisp_t, entry);
882
883         IDispatchEx_AddRef(&iter->IDispatchEx_iface);
884         clean_props(iter);
885         iter->desc = NULL;
886         list_remove(&iter->entry);
887         list_init(&iter->entry);
888         IDispatchEx_Release(&iter->IDispatchEx_iface);
889     }
890 }
891
892 HRESULT disp_get_id(IDispatch *disp, BSTR name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id)
893 {
894     IDispatchEx *dispex;
895     vbdisp_t *vbdisp;
896     HRESULT hres;
897
898     vbdisp = unsafe_impl_from_IDispatch(disp);
899     if(vbdisp)
900         return vbdisp_get_id(vbdisp, name, invoke_type, search_private, id);
901
902     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
903     if(FAILED(hres)) {
904         TRACE("using IDispatch\n");
905         return IDispatch_GetIDsOfNames(disp, &IID_NULL, &name, 1, 0, id);
906     }
907
908     hres = IDispatchEx_GetDispID(dispex, name, fdexNameCaseInsensitive, id);
909     IDispatchEx_Release(dispex);
910     return hres;
911 }
912
913 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, DISPPARAMS *dp, VARIANT *retv)
914 {
915     const WORD flags = DISPATCH_METHOD|(retv ? DISPATCH_PROPERTYGET : 0);
916     IDispatchEx *dispex;
917     EXCEPINFO ei;
918     HRESULT hres;
919
920     memset(&ei, 0, sizeof(ei));
921     if(retv)
922         V_VT(retv) = VT_EMPTY;
923
924     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
925     if(FAILED(hres)) {
926         UINT err = 0;
927
928         TRACE("using IDispatch\n");
929         return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, dp, retv, &ei, &err);
930     }
931
932     hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, dp, retv, &ei, NULL /* CALLER_FIXME */);
933     IDispatchEx_Release(dispex);
934     return hres;
935 }
936
937 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, DISPPARAMS *dp)
938 {
939     IDispatchEx *dispex;
940     EXCEPINFO ei = {0};
941     HRESULT hres;
942
943     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
944     if(SUCCEEDED(hres)) {
945         hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_PROPERTYPUT, dp, NULL, &ei, NULL /* FIXME! */);
946         IDispatchEx_Release(dispex);
947     }else {
948         ULONG err = 0;
949
950         TRACE("using IDispatch\n");
951         hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, DISPATCH_PROPERTYPUT, dp, NULL, &ei, &err);
952     }
953
954     return hres;
955 }