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