jscript: Support non-extensible objects.
[wine/zf.git] / dlls / jscript / dispex.c
blob303e5bb8c993f8e17a5377941b6de41d9930e04a
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <assert.h>
21 #include "jscript.h"
22 #include "engine.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
28 static const GUID GUID_JScriptTypeInfo = {0xc59c6b12,0xf6c1,0x11cf,{0x88,0x35,0x00,0xa0,0xc9,0x11,0xe8,0xb2}};
30 #define FDEX_VERSION_MASK 0xf0000000
31 #define GOLDEN_RATIO 0x9E3779B9U
33 typedef enum {
34 PROP_JSVAL,
35 PROP_BUILTIN,
36 PROP_PROTREF,
37 PROP_ACCESSOR,
38 PROP_DELETED,
39 PROP_IDX
40 } prop_type_t;
42 struct _dispex_prop_t {
43 WCHAR *name;
44 unsigned hash;
45 prop_type_t type;
46 DWORD flags;
48 union {
49 jsval_t val;
50 const builtin_prop_t *p;
51 DWORD ref;
52 unsigned idx;
53 struct {
54 jsdisp_t *getter;
55 jsdisp_t *setter;
56 } accessor;
57 } u;
59 int bucket_head;
60 int bucket_next;
63 static inline DISPID prop_to_id(jsdisp_t *This, dispex_prop_t *prop)
65 return prop - This->props;
68 static inline dispex_prop_t *get_prop(jsdisp_t *This, DISPID id)
70 if(id < 0 || id >= This->prop_cnt || This->props[id].type == PROP_DELETED)
71 return NULL;
73 return This->props+id;
76 static inline BOOL is_function_prop(dispex_prop_t *prop)
78 BOOL ret = FALSE;
80 if (is_object_instance(prop->u.val))
82 jsdisp_t *jsdisp = iface_to_jsdisp(get_object(prop->u.val));
84 if (jsdisp) ret = is_class(jsdisp, JSCLASS_FUNCTION);
85 jsdisp_release(jsdisp);
87 return ret;
90 static DWORD get_flags(jsdisp_t *This, dispex_prop_t *prop)
92 if(prop->type == PROP_PROTREF) {
93 dispex_prop_t *parent = get_prop(This->prototype, prop->u.ref);
94 if(!parent) {
95 prop->type = PROP_DELETED;
96 return 0;
99 return get_flags(This->prototype, parent);
102 return prop->flags;
105 static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name)
107 int min = 0, max, i, r;
109 max = This->builtin_info->props_cnt-1;
110 while(min <= max) {
111 i = (min+max)/2;
113 r = wcscmp(name, This->builtin_info->props[i].name);
114 if(!r) {
115 /* Skip prop if it's available only in higher compatibility mode. */
116 unsigned version = (This->builtin_info->props[i].flags & PROPF_VERSION_MASK)
117 >> PROPF_VERSION_SHIFT;
118 if(version && version > This->ctx->version)
119 return NULL;
121 /* Skip prop if it's available only in HTML mode and we're not running in HTML mode. */
122 if((This->builtin_info->props[i].flags & PROPF_HTML) && !This->ctx->html_mode)
123 return NULL;
125 return This->builtin_info->props + i;
128 if(r < 0)
129 max = i-1;
130 else
131 min = i+1;
134 return NULL;
137 static inline unsigned string_hash(const WCHAR *name)
139 unsigned h = 0;
140 for(; *name; name++)
141 h = (h>>(sizeof(unsigned)*8-4)) ^ (h<<4) ^ towlower(*name);
142 return h;
145 static inline unsigned get_props_idx(jsdisp_t *This, unsigned hash)
147 return (hash*GOLDEN_RATIO) & (This->buf_size-1);
150 static inline HRESULT resize_props(jsdisp_t *This)
152 dispex_prop_t *props;
153 int i, bucket;
155 if(This->buf_size != This->prop_cnt)
156 return S_FALSE;
158 props = heap_realloc(This->props, sizeof(dispex_prop_t)*This->buf_size*2);
159 if(!props)
160 return E_OUTOFMEMORY;
161 This->buf_size *= 2;
162 This->props = props;
164 for(i=0; i<This->buf_size; i++) {
165 This->props[i].bucket_head = 0;
166 This->props[i].bucket_next = 0;
169 for(i=1; i<This->prop_cnt; i++) {
170 props = This->props+i;
172 bucket = get_props_idx(This, props->hash);
173 props->bucket_next = This->props[bucket].bucket_head;
174 This->props[bucket].bucket_head = i;
177 return S_OK;
180 static inline dispex_prop_t* alloc_prop(jsdisp_t *This, const WCHAR *name, prop_type_t type, DWORD flags)
182 dispex_prop_t *prop;
183 unsigned bucket;
185 if(FAILED(resize_props(This)))
186 return NULL;
188 prop = &This->props[This->prop_cnt];
189 prop->name = heap_strdupW(name);
190 if(!prop->name)
191 return NULL;
192 prop->type = type;
193 prop->flags = flags;
194 prop->hash = string_hash(name);
196 bucket = get_props_idx(This, prop->hash);
197 prop->bucket_next = This->props[bucket].bucket_head;
198 This->props[bucket].bucket_head = This->prop_cnt++;
199 return prop;
202 static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref)
204 dispex_prop_t *ret;
206 ret = alloc_prop(This, name, PROP_PROTREF, 0);
207 if(!ret)
208 return NULL;
210 ret->u.ref = ref;
211 return ret;
214 static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret)
216 const builtin_prop_t *builtin;
217 unsigned bucket, pos, prev = 0;
218 dispex_prop_t *prop;
220 bucket = get_props_idx(This, hash);
221 pos = This->props[bucket].bucket_head;
222 while(pos != 0) {
223 if(!wcscmp(name, This->props[pos].name)) {
224 if(prev != 0) {
225 This->props[prev].bucket_next = This->props[pos].bucket_next;
226 This->props[pos].bucket_next = This->props[bucket].bucket_head;
227 This->props[bucket].bucket_head = pos;
230 *ret = &This->props[pos];
231 return S_OK;
234 prev = pos;
235 pos = This->props[pos].bucket_next;
238 builtin = find_builtin_prop(This, name);
239 if(builtin) {
240 unsigned flags = builtin->flags;
241 if(flags & PROPF_METHOD)
242 flags |= PROPF_WRITABLE | PROPF_CONFIGURABLE;
243 else if(builtin->setter)
244 flags |= PROPF_WRITABLE;
245 flags &= PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE;
246 prop = alloc_prop(This, name, PROP_BUILTIN, flags);
247 if(!prop)
248 return E_OUTOFMEMORY;
250 prop->u.p = builtin;
251 *ret = prop;
252 return S_OK;
255 if(This->builtin_info->idx_length) {
256 const WCHAR *ptr;
257 unsigned idx = 0;
259 for(ptr = name; is_digit(*ptr) && idx < 0x10000; ptr++)
260 idx = idx*10 + (*ptr-'0');
261 if(!*ptr && idx < This->builtin_info->idx_length(This)) {
262 prop = alloc_prop(This, name, PROP_IDX, This->builtin_info->idx_put ? PROPF_WRITABLE : 0);
263 if(!prop)
264 return E_OUTOFMEMORY;
266 prop->u.idx = idx;
267 *ret = prop;
268 return S_OK;
272 *ret = NULL;
273 return S_OK;
276 static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret)
278 dispex_prop_t *prop, *del=NULL;
279 HRESULT hres;
281 hres = find_prop_name(This, hash, name, &prop);
282 if(FAILED(hres))
283 return hres;
284 if(prop && prop->type==PROP_DELETED) {
285 del = prop;
286 } else if(prop) {
287 *ret = prop;
288 return S_OK;
291 if(This->prototype) {
292 hres = find_prop_name_prot(This->prototype, hash, name, &prop);
293 if(FAILED(hres))
294 return hres;
295 if(prop) {
296 if(del) {
297 del->type = PROP_PROTREF;
298 del->u.ref = prop - This->prototype->props;
299 prop = del;
300 }else {
301 prop = alloc_protref(This, prop->name, prop - This->prototype->props);
302 if(!prop)
303 return E_OUTOFMEMORY;
306 *ret = prop;
307 return S_OK;
311 *ret = del;
312 return S_OK;
315 static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, dispex_prop_t **ret)
317 dispex_prop_t *prop;
318 HRESULT hres;
320 hres = find_prop_name_prot(This, string_hash(name), name, &prop);
321 if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) {
322 TRACE("creating prop %s flags %x\n", debugstr_w(name), create_flags);
324 if(prop) {
325 prop->type = PROP_JSVAL;
326 prop->flags = create_flags;
327 prop->u.val = jsval_undefined();
328 }else {
329 prop = alloc_prop(This, name, PROP_JSVAL, create_flags);
330 if(!prop)
331 return E_OUTOFMEMORY;
334 prop->u.val = jsval_undefined();
337 *ret = prop;
338 return hres;
341 static IDispatch *get_this(DISPPARAMS *dp)
343 DWORD i;
345 for(i=0; i < dp->cNamedArgs; i++) {
346 if(dp->rgdispidNamedArgs[i] == DISPID_THIS) {
347 if(V_VT(dp->rgvarg+i) == VT_DISPATCH)
348 return V_DISPATCH(dp->rgvarg+i);
350 WARN("This is not VT_DISPATCH\n");
351 return NULL;
355 TRACE("no this passed\n");
356 return NULL;
359 static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret)
361 jsval_t *argv;
362 unsigned cnt;
363 unsigned i;
364 HRESULT hres;
366 cnt = dp->cArgs - dp->cNamedArgs;
368 if(cnt > 6) {
369 argv = heap_alloc(cnt * sizeof(*argv));
370 if(!argv)
371 return E_OUTOFMEMORY;
372 }else {
373 argv = buf;
376 for(i = 0; i < cnt; i++) {
377 hres = variant_to_jsval(dp->rgvarg+dp->cArgs-i-1, argv+i);
378 if(FAILED(hres)) {
379 while(i--)
380 jsval_release(argv[i]);
381 if(argv != buf)
382 heap_free(argv);
383 return hres;
387 *argc = cnt;
388 *ret = argv;
389 return S_OK;
392 static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, jsval_t *r)
394 jsdisp_t *prop_obj = This;
395 HRESULT hres;
397 while(prop->type == PROP_PROTREF) {
398 prop_obj = prop_obj->prototype;
399 prop = prop_obj->props + prop->u.ref;
402 switch(prop->type) {
403 case PROP_BUILTIN:
404 if(prop->u.p->getter) {
405 hres = prop->u.p->getter(This->ctx, This, r);
406 }else {
407 jsdisp_t *obj;
409 assert(prop->u.p->invoke != NULL);
410 hres = create_builtin_function(This->ctx, prop->u.p->invoke, prop->u.p->name, NULL,
411 prop->u.p->flags, NULL, &obj);
412 if(FAILED(hres))
413 break;
415 prop->type = PROP_JSVAL;
416 prop->u.val = jsval_obj(obj);
418 jsdisp_addref(obj);
419 *r = jsval_obj(obj);
421 break;
422 case PROP_JSVAL:
423 hres = jsval_copy(prop->u.val, r);
424 break;
425 case PROP_ACCESSOR:
426 if(prop->u.accessor.getter) {
427 hres = jsdisp_call_value(prop->u.accessor.getter, to_disp(This),
428 DISPATCH_METHOD, 0, NULL, r);
429 }else {
430 *r = jsval_undefined();
431 hres = S_OK;
433 break;
434 case PROP_IDX:
435 hres = prop_obj->builtin_info->idx_get(prop_obj, prop->u.idx, r);
436 break;
437 default:
438 ERR("type %d\n", prop->type);
439 return E_FAIL;
442 if(FAILED(hres)) {
443 TRACE("fail %08x\n", hres);
444 return hres;
447 TRACE("%p.%s ret %s\n", This, debugstr_w(prop->name), debugstr_jsval(*r));
448 return hres;
451 static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val)
453 HRESULT hres;
455 if(prop->type == PROP_PROTREF) {
456 dispex_prop_t *prop_iter = prop;
457 jsdisp_t *prototype_iter = This;
459 do {
460 prototype_iter = prototype_iter->prototype;
461 prop_iter = prototype_iter->props + prop_iter->u.ref;
462 } while(prop_iter->type == PROP_PROTREF);
464 if(prop_iter->type == PROP_ACCESSOR)
465 prop = prop_iter;
468 switch(prop->type) {
469 case PROP_BUILTIN:
470 if(!prop->u.p->setter) {
471 TRACE("getter with no setter\n");
472 return S_OK;
474 return prop->u.p->setter(This->ctx, This, val);
475 case PROP_PROTREF:
476 case PROP_DELETED:
477 if(!This->extensible)
478 return S_OK;
479 prop->type = PROP_JSVAL;
480 prop->flags = PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE;
481 prop->u.val = jsval_undefined();
482 break;
483 case PROP_JSVAL:
484 if(!(prop->flags & PROPF_WRITABLE))
485 return S_OK;
487 jsval_release(prop->u.val);
488 break;
489 case PROP_ACCESSOR:
490 if(!prop->u.accessor.setter) {
491 TRACE("no setter\n");
492 return S_OK;
494 return jsdisp_call_value(prop->u.accessor.setter, to_disp(This), DISPATCH_METHOD, 1, &val, NULL);
495 case PROP_IDX:
496 if(!This->builtin_info->idx_put) {
497 TRACE("no put_idx\n");
498 return S_OK;
500 return This->builtin_info->idx_put(This, prop->u.idx, val);
501 default:
502 ERR("type %d\n", prop->type);
503 return E_FAIL;
506 TRACE("%p.%s = %s\n", This, debugstr_w(prop->name), debugstr_jsval(val));
508 hres = jsval_copy(val, &prop->u.val);
509 if(FAILED(hres))
510 return hres;
512 if(This->builtin_info->on_put)
513 This->builtin_info->on_put(This, prop->name);
515 return S_OK;
518 static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t *prop, WORD flags,
519 unsigned argc, jsval_t *argv, jsval_t *r, IServiceProvider *caller)
521 HRESULT hres;
523 switch(prop->type) {
524 case PROP_BUILTIN: {
525 if(flags == DISPATCH_CONSTRUCT && (prop->flags & PROPF_METHOD)) {
526 WARN("%s is not a constructor\n", debugstr_w(prop->name));
527 return E_INVALIDARG;
530 if(prop->name || This->builtin_info->class != JSCLASS_FUNCTION) {
531 vdisp_t vthis;
533 if(This->builtin_info->class != JSCLASS_FUNCTION && prop->u.p->invoke != JSGlobal_eval)
534 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
535 if(jsthis)
536 set_disp(&vthis, jsthis);
537 else
538 set_jsdisp(&vthis, This);
539 hres = prop->u.p->invoke(This->ctx, &vthis, flags, argc, argv, r);
540 vdisp_release(&vthis);
541 }else {
542 /* Function object calls are special case */
543 hres = Function_invoke(This, jsthis, flags, argc, argv, r);
545 return hres;
547 case PROP_PROTREF:
548 return invoke_prop_func(This->prototype, jsthis ? jsthis : (IDispatch *)&This->IDispatchEx_iface,
549 This->prototype->props+prop->u.ref, flags, argc, argv, r, caller);
550 case PROP_JSVAL: {
551 if(!is_object_instance(prop->u.val) || !get_object(prop->u.val)) {
552 FIXME("invoke %s\n", debugstr_jsval(prop->u.val));
553 return E_FAIL;
556 TRACE("call %s %p\n", debugstr_w(prop->name), get_object(prop->u.val));
558 return disp_call_value(This->ctx, get_object(prop->u.val),
559 jsthis ? jsthis : (IDispatch*)&This->IDispatchEx_iface,
560 flags, argc, argv, r);
562 case PROP_ACCESSOR:
563 case PROP_IDX: {
564 jsval_t val;
566 hres = prop_get(This, prop, &val);
567 if(FAILED(hres))
568 return hres;
570 if(is_object_instance(val) && get_object(val)) {
571 hres = disp_call_value(This->ctx, get_object(val),
572 jsthis ? jsthis : (IDispatch*)&This->IDispatchEx_iface,
573 flags, argc, argv, r);
574 }else {
575 FIXME("invoke %s\n", debugstr_jsval(val));
576 hres = E_NOTIMPL;
579 jsval_release(val);
580 return hres;
582 case PROP_DELETED:
583 assert(0);
584 break;
587 return E_FAIL;
590 HRESULT builtin_set_const(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
592 TRACE("%p %s\n", jsthis, debugstr_jsval(value));
593 return S_OK;
596 static HRESULT fill_protrefs(jsdisp_t *This)
598 dispex_prop_t *iter, *prop;
599 HRESULT hres;
601 if(!This->prototype)
602 return S_OK;
604 fill_protrefs(This->prototype);
606 for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) {
607 if(!iter->name)
608 continue;
609 hres = find_prop_name(This, iter->hash, iter->name, &prop);
610 if(FAILED(hres))
611 return hres;
612 if(!prop || prop->type==PROP_DELETED) {
613 if(prop) {
614 prop->type = PROP_PROTREF;
615 prop->flags = 0;
616 prop->u.ref = iter - This->prototype->props;
617 }else {
618 prop = alloc_protref(This, iter->name, iter - This->prototype->props);
619 if(!prop)
620 return E_OUTOFMEMORY;
625 return S_OK;
628 struct typeinfo_func {
629 dispex_prop_t *prop;
630 function_code_t *code;
633 typedef struct {
634 ITypeInfo ITypeInfo_iface;
635 ITypeComp ITypeComp_iface;
636 LONG ref;
638 UINT num_funcs;
639 UINT num_vars;
640 struct typeinfo_func *funcs;
641 dispex_prop_t **vars;
643 jsdisp_t *jsdisp;
644 } ScriptTypeInfo;
646 static struct typeinfo_func *get_func_from_memid(const ScriptTypeInfo *typeinfo, MEMBERID memid)
648 UINT a = 0, b = typeinfo->num_funcs;
650 while (a < b)
652 UINT i = (a + b - 1) / 2;
653 MEMBERID func_memid = prop_to_id(typeinfo->jsdisp, typeinfo->funcs[i].prop);
655 if (memid == func_memid)
656 return &typeinfo->funcs[i];
657 else if (memid < func_memid)
658 b = i;
659 else
660 a = i + 1;
662 return NULL;
665 static dispex_prop_t *get_var_from_memid(const ScriptTypeInfo *typeinfo, MEMBERID memid)
667 UINT a = 0, b = typeinfo->num_vars;
669 while (a < b)
671 UINT i = (a + b - 1) / 2;
672 MEMBERID var_memid = prop_to_id(typeinfo->jsdisp, typeinfo->vars[i]);
674 if (memid == var_memid)
675 return typeinfo->vars[i];
676 else if (memid < var_memid)
677 b = i;
678 else
679 a = i + 1;
681 return NULL;
684 static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface)
686 return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeInfo_iface);
689 static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeComp(ITypeComp *iface)
691 return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeComp_iface);
694 static HRESULT WINAPI ScriptTypeInfo_QueryInterface(ITypeInfo *iface, REFIID riid, void **ppv)
696 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
698 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ITypeInfo, riid))
699 *ppv = &This->ITypeInfo_iface;
700 else if (IsEqualGUID(&IID_ITypeComp, riid))
701 *ppv = &This->ITypeComp_iface;
702 else
704 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
705 *ppv = NULL;
706 return E_NOINTERFACE;
709 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
710 IUnknown_AddRef((IUnknown*)*ppv);
711 return S_OK;
714 static ULONG WINAPI ScriptTypeInfo_AddRef(ITypeInfo *iface)
716 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
717 LONG ref = InterlockedIncrement(&This->ref);
719 TRACE("(%p) ref=%d\n", This, ref);
721 return ref;
724 static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface)
726 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
727 LONG ref = InterlockedDecrement(&This->ref);
728 UINT i;
730 TRACE("(%p) ref=%d\n", This, ref);
732 if (!ref)
734 for (i = This->num_funcs; i--;)
735 release_bytecode(This->funcs[i].code->bytecode);
736 IDispatchEx_Release(&This->jsdisp->IDispatchEx_iface);
737 heap_free(This->funcs);
738 heap_free(This->vars);
739 heap_free(This);
741 return ref;
744 static HRESULT WINAPI ScriptTypeInfo_GetTypeAttr(ITypeInfo *iface, TYPEATTR **ppTypeAttr)
746 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
747 TYPEATTR *attr;
749 TRACE("(%p)->(%p)\n", This, ppTypeAttr);
751 if (!ppTypeAttr) return E_INVALIDARG;
753 attr = heap_alloc_zero(sizeof(*attr));
754 if (!attr) return E_OUTOFMEMORY;
756 attr->guid = GUID_JScriptTypeInfo;
757 attr->lcid = LOCALE_USER_DEFAULT;
758 attr->memidConstructor = MEMBERID_NIL;
759 attr->memidDestructor = MEMBERID_NIL;
760 attr->cbSizeInstance = 4;
761 attr->typekind = TKIND_DISPATCH;
762 attr->cFuncs = This->num_funcs;
763 attr->cVars = This->num_vars;
764 attr->cImplTypes = 1;
765 attr->cbSizeVft = sizeof(IDispatchVtbl);
766 attr->cbAlignment = 4;
767 attr->wTypeFlags = TYPEFLAG_FDISPATCHABLE;
768 attr->wMajorVerNum = JSCRIPT_MAJOR_VERSION;
769 attr->wMinorVerNum = JSCRIPT_MINOR_VERSION;
771 *ppTypeAttr = attr;
772 return S_OK;
775 static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **ppTComp)
777 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
779 TRACE("(%p)->(%p)\n", This, ppTComp);
781 if (!ppTComp) return E_INVALIDARG;
783 *ppTComp = &This->ITypeComp_iface;
784 ITypeInfo_AddRef(iface);
785 return S_OK;
788 static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, FUNCDESC **ppFuncDesc)
790 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
791 struct typeinfo_func *func;
792 FUNCDESC *desc;
793 unsigned i;
795 TRACE("(%p)->(%u %p)\n", This, index, ppFuncDesc);
797 if (!ppFuncDesc) return E_INVALIDARG;
798 if (index >= This->num_funcs) return TYPE_E_ELEMENTNOTFOUND;
799 func = &This->funcs[index];
801 /* Store the parameter array after the FUNCDESC structure */
802 desc = heap_alloc_zero(sizeof(*desc) + sizeof(ELEMDESC) * func->code->param_cnt);
803 if (!desc) return E_OUTOFMEMORY;
805 desc->memid = prop_to_id(This->jsdisp, func->prop);
806 desc->funckind = FUNC_DISPATCH;
807 desc->invkind = INVOKE_FUNC;
808 desc->callconv = CC_STDCALL;
809 desc->cParams = func->code->param_cnt;
810 desc->elemdescFunc.tdesc.vt = VT_VARIANT;
812 if (func->code->param_cnt) desc->lprgelemdescParam = (ELEMDESC*)(desc + 1);
813 for (i = 0; i < func->code->param_cnt; i++)
814 desc->lprgelemdescParam[i].tdesc.vt = VT_VARIANT;
816 *ppFuncDesc = desc;
817 return S_OK;
820 static HRESULT WINAPI ScriptTypeInfo_GetVarDesc(ITypeInfo *iface, UINT index, VARDESC **ppVarDesc)
822 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
823 VARDESC *desc;
825 TRACE("(%p)->(%u %p)\n", This, index, ppVarDesc);
827 if (!ppVarDesc) return E_INVALIDARG;
828 if (index >= This->num_vars) return TYPE_E_ELEMENTNOTFOUND;
830 desc = heap_alloc_zero(sizeof(*desc));
831 if (!desc) return E_OUTOFMEMORY;
833 desc->memid = prop_to_id(This->jsdisp, This->vars[index]);
834 desc->varkind = VAR_DISPATCH;
835 desc->elemdescVar.tdesc.vt = VT_VARIANT;
837 *ppVarDesc = desc;
838 return S_OK;
841 static HRESULT WINAPI ScriptTypeInfo_GetNames(ITypeInfo *iface, MEMBERID memid, BSTR *rgBstrNames,
842 UINT cMaxNames, UINT *pcNames)
844 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
845 struct typeinfo_func *func;
846 ITypeInfo *disp_typeinfo;
847 dispex_prop_t *var;
848 HRESULT hr;
849 UINT i = 0;
851 TRACE("(%p)->(%d %p %u %p)\n", This, memid, rgBstrNames, cMaxNames, pcNames);
853 if (!rgBstrNames || !pcNames) return E_INVALIDARG;
854 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
856 func = get_func_from_memid(This, memid);
857 if (!func)
859 var = get_var_from_memid(This, memid);
860 if (!var)
862 hr = get_dispatch_typeinfo(&disp_typeinfo);
863 if (FAILED(hr)) return hr;
865 return ITypeInfo_GetNames(disp_typeinfo, memid, rgBstrNames, cMaxNames, pcNames);
869 *pcNames = 0;
870 if (!cMaxNames) return S_OK;
872 rgBstrNames[0] = SysAllocString(func ? func->prop->name : var->name);
873 if (!rgBstrNames[0]) return E_OUTOFMEMORY;
874 i++;
876 if (func)
878 unsigned num = min(cMaxNames, func->code->param_cnt + 1);
880 for (; i < num; i++)
882 if (!(rgBstrNames[i] = SysAllocString(func->code->params[i - 1])))
884 do SysFreeString(rgBstrNames[--i]); while (i);
885 return E_OUTOFMEMORY;
890 *pcNames = i;
891 return S_OK;
894 static HRESULT WINAPI ScriptTypeInfo_GetRefTypeOfImplType(ITypeInfo *iface, UINT index, HREFTYPE *pRefType)
896 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
898 TRACE("(%p)->(%u %p)\n", This, index, pRefType);
900 /* We only inherit from IDispatch */
901 if (!pRefType) return E_INVALIDARG;
902 if (index != 0) return TYPE_E_ELEMENTNOTFOUND;
904 *pRefType = 1;
905 return S_OK;
908 static HRESULT WINAPI ScriptTypeInfo_GetImplTypeFlags(ITypeInfo *iface, UINT index, INT *pImplTypeFlags)
910 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
912 TRACE("(%p)->(%u %p)\n", This, index, pImplTypeFlags);
914 if (!pImplTypeFlags) return E_INVALIDARG;
915 if (index != 0) return TYPE_E_ELEMENTNOTFOUND;
917 *pImplTypeFlags = 0;
918 return S_OK;
921 static HRESULT WINAPI ScriptTypeInfo_GetIDsOfNames(ITypeInfo *iface, LPOLESTR *rgszNames, UINT cNames,
922 MEMBERID *pMemId)
924 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
925 ITypeInfo *disp_typeinfo;
926 const WCHAR *name;
927 HRESULT hr = S_OK;
928 int i, j, arg;
930 TRACE("(%p)->(%p %u %p)\n", This, rgszNames, cNames, pMemId);
932 if (!rgszNames || !cNames || !pMemId) return E_INVALIDARG;
934 for (i = 0; i < cNames; i++) pMemId[i] = MEMBERID_NIL;
935 name = rgszNames[0];
937 for (i = 0; i < This->num_funcs; i++)
939 struct typeinfo_func *func = &This->funcs[i];
941 if (wcsicmp(name, func->prop->name)) continue;
942 pMemId[0] = prop_to_id(This->jsdisp, func->prop);
944 for (j = 1; j < cNames; j++)
946 name = rgszNames[j];
947 for (arg = func->code->param_cnt; --arg >= 0;)
948 if (!wcsicmp(name, func->code->params[arg]))
949 break;
950 if (arg >= 0)
951 pMemId[j] = arg;
952 else
953 hr = DISP_E_UNKNOWNNAME;
955 return hr;
958 for (i = 0; i < This->num_vars; i++)
960 dispex_prop_t *var = This->vars[i];
962 if (wcsicmp(name, var->name)) continue;
963 pMemId[0] = prop_to_id(This->jsdisp, var);
964 return S_OK;
967 /* Look into the inherited IDispatch */
968 hr = get_dispatch_typeinfo(&disp_typeinfo);
969 if (FAILED(hr)) return hr;
971 return ITypeInfo_GetIDsOfNames(disp_typeinfo, rgszNames, cNames, pMemId);
974 static HRESULT WINAPI ScriptTypeInfo_Invoke(ITypeInfo *iface, PVOID pvInstance, MEMBERID memid, WORD wFlags,
975 DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
977 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
978 ITypeInfo *disp_typeinfo;
979 IDispatch *disp;
980 HRESULT hr;
982 TRACE("(%p)->(%p %d %d %p %p %p %p)\n", This, pvInstance, memid, wFlags,
983 pDispParams, pVarResult, pExcepInfo, puArgErr);
985 if (!pvInstance) return E_INVALIDARG;
986 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
988 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
990 hr = get_dispatch_typeinfo(&disp_typeinfo);
991 if (FAILED(hr)) return hr;
993 return ITypeInfo_Invoke(disp_typeinfo, pvInstance, memid, wFlags, pDispParams,
994 pVarResult, pExcepInfo, puArgErr);
997 hr = IUnknown_QueryInterface((IUnknown*)pvInstance, &IID_IDispatch, (void**)&disp);
998 if (FAILED(hr)) return hr;
1000 hr = IDispatch_Invoke(disp, memid, &IID_NULL, LOCALE_USER_DEFAULT, wFlags,
1001 pDispParams, pVarResult, pExcepInfo, puArgErr);
1002 IDispatch_Release(disp);
1004 return hr;
1007 static HRESULT WINAPI ScriptTypeInfo_GetDocumentation(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrName,
1008 BSTR *pBstrDocString, DWORD *pdwHelpContext, BSTR *pBstrHelpFile)
1010 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1011 struct typeinfo_func *func;
1012 ITypeInfo *disp_typeinfo;
1013 dispex_prop_t *var;
1014 HRESULT hr;
1016 TRACE("(%p)->(%d %p %p %p %p)\n", This, memid, pBstrName, pBstrDocString, pdwHelpContext, pBstrHelpFile);
1018 if (pBstrDocString) *pBstrDocString = NULL;
1019 if (pdwHelpContext) *pdwHelpContext = 0;
1020 if (pBstrHelpFile) *pBstrHelpFile = NULL;
1022 if (memid == MEMBERID_NIL)
1024 if (pBstrName && !(*pBstrName = SysAllocString(L"JScriptTypeInfo")))
1025 return E_OUTOFMEMORY;
1026 if (pBstrDocString &&
1027 !(*pBstrDocString = SysAllocString(L"JScript Type Info")))
1029 if (pBstrName) SysFreeString(*pBstrName);
1030 return E_OUTOFMEMORY;
1032 return S_OK;
1034 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
1036 func = get_func_from_memid(This, memid);
1037 if (!func)
1039 var = get_var_from_memid(This, memid);
1040 if (!var)
1042 hr = get_dispatch_typeinfo(&disp_typeinfo);
1043 if (FAILED(hr)) return hr;
1045 return ITypeInfo_GetDocumentation(disp_typeinfo, memid, pBstrName, pBstrDocString,
1046 pdwHelpContext, pBstrHelpFile);
1050 if (pBstrName)
1052 *pBstrName = SysAllocString(func ? func->prop->name : var->name);
1054 if (!*pBstrName)
1055 return E_OUTOFMEMORY;
1057 return S_OK;
1060 static HRESULT WINAPI ScriptTypeInfo_GetDllEntry(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind,
1061 BSTR *pBstrDllName, BSTR *pBstrName, WORD *pwOrdinal)
1063 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1064 ITypeInfo *disp_typeinfo;
1065 HRESULT hr;
1067 TRACE("(%p)->(%d %d %p %p %p)\n", This, memid, invKind, pBstrDllName, pBstrName, pwOrdinal);
1069 if (pBstrDllName) *pBstrDllName = NULL;
1070 if (pBstrName) *pBstrName = NULL;
1071 if (pwOrdinal) *pwOrdinal = 0;
1073 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1075 hr = get_dispatch_typeinfo(&disp_typeinfo);
1076 if (FAILED(hr)) return hr;
1078 return ITypeInfo_GetDllEntry(disp_typeinfo, memid, invKind, pBstrDllName, pBstrName, pwOrdinal);
1080 return TYPE_E_BADMODULEKIND;
1083 static HRESULT WINAPI ScriptTypeInfo_GetRefTypeInfo(ITypeInfo *iface, HREFTYPE hRefType, ITypeInfo **ppTInfo)
1085 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1086 HRESULT hr;
1088 TRACE("(%p)->(%x %p)\n", This, hRefType, ppTInfo);
1090 if (!ppTInfo || (INT)hRefType < 0) return E_INVALIDARG;
1092 if (hRefType & ~3) return E_FAIL;
1093 if (hRefType & 1)
1095 hr = get_dispatch_typeinfo(ppTInfo);
1096 if (FAILED(hr)) return hr;
1098 else
1099 *ppTInfo = iface;
1101 ITypeInfo_AddRef(*ppTInfo);
1102 return S_OK;
1105 static HRESULT WINAPI ScriptTypeInfo_AddressOfMember(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind, PVOID *ppv)
1107 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1108 ITypeInfo *disp_typeinfo;
1109 HRESULT hr;
1111 TRACE("(%p)->(%d %d %p)\n", This, memid, invKind, ppv);
1113 if (!ppv) return E_INVALIDARG;
1114 *ppv = NULL;
1116 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1118 hr = get_dispatch_typeinfo(&disp_typeinfo);
1119 if (FAILED(hr)) return hr;
1121 return ITypeInfo_AddressOfMember(disp_typeinfo, memid, invKind, ppv);
1123 return TYPE_E_BADMODULEKIND;
1126 static HRESULT WINAPI ScriptTypeInfo_CreateInstance(ITypeInfo *iface, IUnknown *pUnkOuter, REFIID riid, PVOID *ppvObj)
1128 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1130 TRACE("(%p)->(%p %s %p)\n", This, pUnkOuter, debugstr_guid(riid), ppvObj);
1132 if (!ppvObj) return E_INVALIDARG;
1134 *ppvObj = NULL;
1135 return TYPE_E_BADMODULEKIND;
1138 static HRESULT WINAPI ScriptTypeInfo_GetMops(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrMops)
1140 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1141 ITypeInfo *disp_typeinfo;
1142 HRESULT hr;
1144 TRACE("(%p)->(%d %p)\n", This, memid, pBstrMops);
1146 if (!pBstrMops) return E_INVALIDARG;
1148 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1150 hr = get_dispatch_typeinfo(&disp_typeinfo);
1151 if (FAILED(hr)) return hr;
1153 return ITypeInfo_GetMops(disp_typeinfo, memid, pBstrMops);
1156 *pBstrMops = NULL;
1157 return S_OK;
1160 static HRESULT WINAPI ScriptTypeInfo_GetContainingTypeLib(ITypeInfo *iface, ITypeLib **ppTLib, UINT *pIndex)
1162 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1164 FIXME("(%p)->(%p %p)\n", This, ppTLib, pIndex);
1166 return E_NOTIMPL;
1169 static void WINAPI ScriptTypeInfo_ReleaseTypeAttr(ITypeInfo *iface, TYPEATTR *pTypeAttr)
1171 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1173 TRACE("(%p)->(%p)\n", This, pTypeAttr);
1175 heap_free(pTypeAttr);
1178 static void WINAPI ScriptTypeInfo_ReleaseFuncDesc(ITypeInfo *iface, FUNCDESC *pFuncDesc)
1180 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1182 TRACE("(%p)->(%p)\n", This, pFuncDesc);
1184 heap_free(pFuncDesc);
1187 static void WINAPI ScriptTypeInfo_ReleaseVarDesc(ITypeInfo *iface, VARDESC *pVarDesc)
1189 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1191 TRACE("(%p)->(%p)\n", This, pVarDesc);
1193 heap_free(pVarDesc);
1196 static const ITypeInfoVtbl ScriptTypeInfoVtbl = {
1197 ScriptTypeInfo_QueryInterface,
1198 ScriptTypeInfo_AddRef,
1199 ScriptTypeInfo_Release,
1200 ScriptTypeInfo_GetTypeAttr,
1201 ScriptTypeInfo_GetTypeComp,
1202 ScriptTypeInfo_GetFuncDesc,
1203 ScriptTypeInfo_GetVarDesc,
1204 ScriptTypeInfo_GetNames,
1205 ScriptTypeInfo_GetRefTypeOfImplType,
1206 ScriptTypeInfo_GetImplTypeFlags,
1207 ScriptTypeInfo_GetIDsOfNames,
1208 ScriptTypeInfo_Invoke,
1209 ScriptTypeInfo_GetDocumentation,
1210 ScriptTypeInfo_GetDllEntry,
1211 ScriptTypeInfo_GetRefTypeInfo,
1212 ScriptTypeInfo_AddressOfMember,
1213 ScriptTypeInfo_CreateInstance,
1214 ScriptTypeInfo_GetMops,
1215 ScriptTypeInfo_GetContainingTypeLib,
1216 ScriptTypeInfo_ReleaseTypeAttr,
1217 ScriptTypeInfo_ReleaseFuncDesc,
1218 ScriptTypeInfo_ReleaseVarDesc
1221 static HRESULT WINAPI ScriptTypeComp_QueryInterface(ITypeComp *iface, REFIID riid, void **ppv)
1223 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1224 return ITypeInfo_QueryInterface(&This->ITypeInfo_iface, riid, ppv);
1227 static ULONG WINAPI ScriptTypeComp_AddRef(ITypeComp *iface)
1229 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1230 return ITypeInfo_AddRef(&This->ITypeInfo_iface);
1233 static ULONG WINAPI ScriptTypeComp_Release(ITypeComp *iface)
1235 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1236 return ITypeInfo_Release(&This->ITypeInfo_iface);
1239 static HRESULT WINAPI ScriptTypeComp_Bind(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal, WORD wFlags,
1240 ITypeInfo **ppTInfo, DESCKIND *pDescKind, BINDPTR *pBindPtr)
1242 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1243 UINT flags = wFlags ? wFlags : ~0;
1244 ITypeInfo *disp_typeinfo;
1245 ITypeComp *disp_typecomp;
1246 HRESULT hr;
1247 UINT i;
1249 TRACE("(%p)->(%s %08x %d %p %p %p)\n", This, debugstr_w(szName), lHashVal,
1250 wFlags, ppTInfo, pDescKind, pBindPtr);
1252 if (!szName || !ppTInfo || !pDescKind || !pBindPtr)
1253 return E_INVALIDARG;
1255 for (i = 0; i < This->num_funcs; i++)
1257 if (wcsicmp(szName, This->funcs[i].prop->name)) continue;
1258 if (!(flags & INVOKE_FUNC)) return TYPE_E_TYPEMISMATCH;
1260 hr = ITypeInfo_GetFuncDesc(&This->ITypeInfo_iface, i, &pBindPtr->lpfuncdesc);
1261 if (FAILED(hr)) return hr;
1263 *pDescKind = DESCKIND_FUNCDESC;
1264 *ppTInfo = &This->ITypeInfo_iface;
1265 ITypeInfo_AddRef(*ppTInfo);
1266 return S_OK;
1269 for (i = 0; i < This->num_vars; i++)
1271 if (wcsicmp(szName, This->vars[i]->name)) continue;
1272 if (!(flags & INVOKE_PROPERTYGET)) return TYPE_E_TYPEMISMATCH;
1274 hr = ITypeInfo_GetVarDesc(&This->ITypeInfo_iface, i, &pBindPtr->lpvardesc);
1275 if (FAILED(hr)) return hr;
1277 *pDescKind = DESCKIND_VARDESC;
1278 *ppTInfo = &This->ITypeInfo_iface;
1279 ITypeInfo_AddRef(*ppTInfo);
1280 return S_OK;
1283 /* Look into the inherited IDispatch */
1284 hr = get_dispatch_typeinfo(&disp_typeinfo);
1285 if (FAILED(hr)) return hr;
1287 hr = ITypeInfo_GetTypeComp(disp_typeinfo, &disp_typecomp);
1288 if (FAILED(hr)) return hr;
1290 hr = ITypeComp_Bind(disp_typecomp, szName, lHashVal, wFlags, ppTInfo, pDescKind, pBindPtr);
1291 ITypeComp_Release(disp_typecomp);
1292 return hr;
1295 static HRESULT WINAPI ScriptTypeComp_BindType(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal,
1296 ITypeInfo **ppTInfo, ITypeComp **ppTComp)
1298 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1299 ITypeInfo *disp_typeinfo;
1300 ITypeComp *disp_typecomp;
1301 HRESULT hr;
1303 TRACE("(%p)->(%s %08x %p %p)\n", This, debugstr_w(szName), lHashVal, ppTInfo, ppTComp);
1305 if (!szName || !ppTInfo || !ppTComp)
1306 return E_INVALIDARG;
1308 /* Look into the inherited IDispatch */
1309 hr = get_dispatch_typeinfo(&disp_typeinfo);
1310 if (FAILED(hr)) return hr;
1312 hr = ITypeInfo_GetTypeComp(disp_typeinfo, &disp_typecomp);
1313 if (FAILED(hr)) return hr;
1315 hr = ITypeComp_BindType(disp_typecomp, szName, lHashVal, ppTInfo, ppTComp);
1316 ITypeComp_Release(disp_typecomp);
1317 return hr;
1320 static const ITypeCompVtbl ScriptTypeCompVtbl = {
1321 ScriptTypeComp_QueryInterface,
1322 ScriptTypeComp_AddRef,
1323 ScriptTypeComp_Release,
1324 ScriptTypeComp_Bind,
1325 ScriptTypeComp_BindType
1328 static inline jsdisp_t *impl_from_IDispatchEx(IDispatchEx *iface)
1330 return CONTAINING_RECORD(iface, jsdisp_t, IDispatchEx_iface);
1333 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1335 jsdisp_t *This = impl_from_IDispatchEx(iface);
1337 if(IsEqualGUID(&IID_IUnknown, riid)) {
1338 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
1339 *ppv = &This->IDispatchEx_iface;
1340 }else if(IsEqualGUID(&IID_IDispatch, riid)) {
1341 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
1342 *ppv = &This->IDispatchEx_iface;
1343 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
1344 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
1345 *ppv = &This->IDispatchEx_iface;
1346 }else {
1347 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1348 *ppv = NULL;
1349 return E_NOINTERFACE;
1352 jsdisp_addref(This);
1353 return S_OK;
1356 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1358 jsdisp_t *This = impl_from_IDispatchEx(iface);
1359 jsdisp_addref(This);
1360 return This->ref;
1363 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1365 jsdisp_t *This = impl_from_IDispatchEx(iface);
1366 ULONG ref = --This->ref;
1367 TRACE("(%p) ref=%d\n", This, ref);
1368 if(!ref)
1369 jsdisp_free(This);
1370 return ref;
1373 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1375 jsdisp_t *This = impl_from_IDispatchEx(iface);
1377 TRACE("(%p)->(%p)\n", This, pctinfo);
1379 *pctinfo = 1;
1380 return S_OK;
1383 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
1384 ITypeInfo **ppTInfo)
1386 jsdisp_t *This = impl_from_IDispatchEx(iface);
1387 dispex_prop_t *prop, *cur, *end, **typevar;
1388 UINT num_funcs = 0, num_vars = 0;
1389 struct typeinfo_func *typefunc;
1390 function_code_t *func_code;
1391 ScriptTypeInfo *typeinfo;
1392 unsigned pos;
1394 TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1396 if (iTInfo != 0) return DISP_E_BADINDEX;
1398 for (prop = This->props, end = prop + This->prop_cnt; prop != end; prop++)
1400 if (!prop->name || prop->type != PROP_JSVAL || !(prop->flags & PROPF_ENUMERABLE))
1401 continue;
1403 /* If two identifiers differ only by case, the TypeInfo fails */
1404 pos = This->props[get_props_idx(This, prop->hash)].bucket_head;
1405 while (pos)
1407 cur = This->props + pos;
1409 if (prop->hash == cur->hash && prop != cur &&
1410 cur->type == PROP_JSVAL && (cur->flags & PROPF_ENUMERABLE) &&
1411 !wcsicmp(prop->name, cur->name))
1413 return TYPE_E_AMBIGUOUSNAME;
1415 pos = cur->bucket_next;
1418 if (is_function_prop(prop))
1420 if (Function_get_code(as_jsdisp(get_object(prop->u.val))))
1421 num_funcs++;
1423 else num_vars++;
1426 if (!(typeinfo = heap_alloc(sizeof(*typeinfo))))
1427 return E_OUTOFMEMORY;
1429 typeinfo->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl;
1430 typeinfo->ITypeComp_iface.lpVtbl = &ScriptTypeCompVtbl;
1431 typeinfo->ref = 1;
1432 typeinfo->num_vars = num_vars;
1433 typeinfo->num_funcs = num_funcs;
1434 typeinfo->jsdisp = This;
1436 typeinfo->funcs = heap_alloc(sizeof(*typeinfo->funcs) * num_funcs);
1437 if (!typeinfo->funcs)
1439 heap_free(typeinfo);
1440 return E_OUTOFMEMORY;
1443 typeinfo->vars = heap_alloc(sizeof(*typeinfo->vars) * num_vars);
1444 if (!typeinfo->vars)
1446 heap_free(typeinfo->funcs);
1447 heap_free(typeinfo);
1448 return E_OUTOFMEMORY;
1451 typefunc = typeinfo->funcs;
1452 typevar = typeinfo->vars;
1453 for (prop = This->props; prop != end; prop++)
1455 if (!prop->name || prop->type != PROP_JSVAL || !(prop->flags & PROPF_ENUMERABLE))
1456 continue;
1458 if (is_function_prop(prop))
1460 func_code = Function_get_code(as_jsdisp(get_object(prop->u.val)));
1461 if (!func_code) continue;
1463 typefunc->prop = prop;
1464 typefunc->code = func_code;
1465 typefunc++;
1467 /* The function may be deleted, so keep a ref */
1468 bytecode_addref(func_code->bytecode);
1470 else
1471 *typevar++ = prop;
1474 /* Keep a ref to the props and their names */
1475 IDispatchEx_AddRef(&This->IDispatchEx_iface);
1477 *ppTInfo = &typeinfo->ITypeInfo_iface;
1478 return S_OK;
1481 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1482 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
1483 DISPID *rgDispId)
1485 jsdisp_t *This = impl_from_IDispatchEx(iface);
1486 UINT i;
1487 HRESULT hres;
1489 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1490 lcid, rgDispId);
1492 for(i=0; i < cNames; i++) {
1493 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
1494 if(FAILED(hres))
1495 return hres;
1498 return S_OK;
1501 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1502 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1503 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1505 jsdisp_t *This = impl_from_IDispatchEx(iface);
1507 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1508 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1510 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags,
1511 pDispParams, pVarResult, pExcepInfo, NULL);
1514 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1516 jsdisp_t *This = impl_from_IDispatchEx(iface);
1518 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
1520 if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) {
1521 FIXME("Unsupported grfdex %x\n", grfdex);
1522 return E_NOTIMPL;
1525 return jsdisp_get_id(This, bstrName, grfdex, pid);
1528 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1529 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1531 jsdisp_t *This = impl_from_IDispatchEx(iface);
1532 dispex_prop_t *prop;
1533 jsexcept_t ei;
1534 HRESULT hres;
1536 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1538 if(pvarRes)
1539 V_VT(pvarRes) = VT_EMPTY;
1541 prop = get_prop(This, id);
1542 if(!prop || prop->type == PROP_DELETED) {
1543 TRACE("invalid id\n");
1544 return DISP_E_MEMBERNOTFOUND;
1547 enter_script(This->ctx, &ei);
1549 switch(wFlags) {
1550 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1551 wFlags = DISPATCH_METHOD;
1552 /* fall through */
1553 case DISPATCH_METHOD:
1554 case DISPATCH_CONSTRUCT: {
1555 jsval_t *argv, buf[6], r;
1556 unsigned argc;
1558 hres = convert_params(pdp, buf, &argc, &argv);
1559 if(FAILED(hres))
1560 break;
1562 hres = invoke_prop_func(This, get_this(pdp), prop, wFlags, argc, argv, pvarRes ? &r : NULL, pspCaller);
1563 if(argv != buf)
1564 heap_free(argv);
1565 if(SUCCEEDED(hres) && pvarRes) {
1566 hres = jsval_to_variant(r, pvarRes);
1567 jsval_release(r);
1569 break;
1571 case DISPATCH_PROPERTYGET: {
1572 jsval_t r;
1574 hres = prop_get(This, prop, &r);
1575 if(SUCCEEDED(hres)) {
1576 hres = jsval_to_variant(r, pvarRes);
1577 jsval_release(r);
1579 break;
1581 case DISPATCH_PROPERTYPUT: {
1582 jsval_t val;
1583 DWORD i;
1585 for(i=0; i < pdp->cNamedArgs; i++) {
1586 if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
1587 break;
1590 if(i == pdp->cNamedArgs) {
1591 TRACE("no value to set\n");
1592 hres = DISP_E_PARAMNOTOPTIONAL;
1593 break;
1596 hres = variant_to_jsval(pdp->rgvarg+i, &val);
1597 if(FAILED(hres))
1598 break;
1600 hres = prop_put(This, prop, val);
1601 jsval_release(val);
1602 break;
1604 default:
1605 FIXME("Unimplemented flags %x\n", wFlags);
1606 hres = E_INVALIDARG;
1607 break;
1610 return leave_script(This->ctx, hres);
1613 static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret)
1615 if(!(prop->flags & PROPF_CONFIGURABLE)) {
1616 *ret = FALSE;
1617 return S_OK;
1620 *ret = TRUE; /* FIXME: not exactly right */
1622 if(prop->type == PROP_JSVAL) {
1623 jsval_release(prop->u.val);
1624 prop->type = PROP_DELETED;
1626 if(prop->type == PROP_ACCESSOR)
1627 FIXME("not supported on accessor property\n");
1628 return S_OK;
1631 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
1633 jsdisp_t *This = impl_from_IDispatchEx(iface);
1634 dispex_prop_t *prop;
1635 BOOL b;
1636 HRESULT hres;
1638 TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
1640 if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
1641 FIXME("Unsupported grfdex %x\n", grfdex);
1643 hres = find_prop_name(This, string_hash(bstrName), bstrName, &prop);
1644 if(FAILED(hres))
1645 return hres;
1646 if(!prop) {
1647 TRACE("not found\n");
1648 return S_OK;
1651 return delete_prop(prop, &b);
1654 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
1656 jsdisp_t *This = impl_from_IDispatchEx(iface);
1657 dispex_prop_t *prop;
1658 BOOL b;
1660 TRACE("(%p)->(%x)\n", This, id);
1662 prop = get_prop(This, id);
1663 if(!prop) {
1664 WARN("invalid id\n");
1665 return DISP_E_MEMBERNOTFOUND;
1668 return delete_prop(prop, &b);
1671 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
1673 jsdisp_t *This = impl_from_IDispatchEx(iface);
1674 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
1675 return E_NOTIMPL;
1678 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
1680 jsdisp_t *This = impl_from_IDispatchEx(iface);
1681 dispex_prop_t *prop;
1683 TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
1685 prop = get_prop(This, id);
1686 if(!prop || !prop->name || prop->type == PROP_DELETED)
1687 return DISP_E_MEMBERNOTFOUND;
1689 *pbstrName = SysAllocString(prop->name);
1690 if(!*pbstrName)
1691 return E_OUTOFMEMORY;
1693 return S_OK;
1696 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
1698 jsdisp_t *This = impl_from_IDispatchEx(iface);
1699 HRESULT hres;
1701 TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
1703 hres = jsdisp_next_prop(This, id, FALSE, pid);
1704 if(hres == S_FALSE)
1705 *pid = DISPID_STARTENUM;
1706 return hres;
1709 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
1711 jsdisp_t *This = impl_from_IDispatchEx(iface);
1712 FIXME("(%p)->(%p)\n", This, ppunk);
1713 return E_NOTIMPL;
1716 static IDispatchExVtbl DispatchExVtbl = {
1717 DispatchEx_QueryInterface,
1718 DispatchEx_AddRef,
1719 DispatchEx_Release,
1720 DispatchEx_GetTypeInfoCount,
1721 DispatchEx_GetTypeInfo,
1722 DispatchEx_GetIDsOfNames,
1723 DispatchEx_Invoke,
1724 DispatchEx_GetDispID,
1725 DispatchEx_InvokeEx,
1726 DispatchEx_DeleteMemberByName,
1727 DispatchEx_DeleteMemberByDispID,
1728 DispatchEx_GetMemberProperties,
1729 DispatchEx_GetMemberName,
1730 DispatchEx_GetNextDispID,
1731 DispatchEx_GetNameSpaceParent
1734 jsdisp_t *as_jsdisp(IDispatch *disp)
1736 assert(disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl);
1737 return impl_from_IDispatchEx((IDispatchEx*)disp);
1740 jsdisp_t *to_jsdisp(IDispatch *disp)
1742 return disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl ? impl_from_IDispatchEx((IDispatchEx*)disp) : NULL;
1745 HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype)
1747 TRACE("%p (%p)\n", dispex, prototype);
1749 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
1750 dispex->ref = 1;
1751 dispex->builtin_info = builtin_info;
1752 dispex->extensible = TRUE;
1754 dispex->props = heap_alloc_zero(sizeof(dispex_prop_t)*(dispex->buf_size=4));
1755 if(!dispex->props)
1756 return E_OUTOFMEMORY;
1758 dispex->prototype = prototype;
1759 if(prototype)
1760 jsdisp_addref(prototype);
1762 dispex->prop_cnt = 1;
1763 if(builtin_info->value_prop.invoke || builtin_info->value_prop.getter) {
1764 dispex->props[0].type = PROP_BUILTIN;
1765 dispex->props[0].u.p = &builtin_info->value_prop;
1766 }else {
1767 dispex->props[0].type = PROP_DELETED;
1770 script_addref(ctx);
1771 dispex->ctx = ctx;
1773 return S_OK;
1776 static const builtin_info_t dispex_info = {
1777 JSCLASS_NONE,
1778 {NULL, NULL, 0},
1779 0, NULL,
1780 NULL,
1781 NULL
1784 HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype, jsdisp_t **dispex)
1786 jsdisp_t *ret;
1787 HRESULT hres;
1789 ret = heap_alloc_zero(sizeof(jsdisp_t));
1790 if(!ret)
1791 return E_OUTOFMEMORY;
1793 hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype);
1794 if(FAILED(hres)) {
1795 heap_free(ret);
1796 return hres;
1799 *dispex = ret;
1800 return S_OK;
1803 void jsdisp_free(jsdisp_t *obj)
1805 dispex_prop_t *prop;
1807 TRACE("(%p)\n", obj);
1809 for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) {
1810 switch(prop->type) {
1811 case PROP_JSVAL:
1812 jsval_release(prop->u.val);
1813 break;
1814 case PROP_ACCESSOR:
1815 if(prop->u.accessor.getter)
1816 jsdisp_release(prop->u.accessor.getter);
1817 if(prop->u.accessor.setter)
1818 jsdisp_release(prop->u.accessor.setter);
1819 break;
1820 default:
1821 break;
1823 heap_free(prop->name);
1825 heap_free(obj->props);
1826 script_release(obj->ctx);
1827 if(obj->prototype)
1828 jsdisp_release(obj->prototype);
1830 if(obj->builtin_info->destructor)
1831 obj->builtin_info->destructor(obj);
1832 else
1833 heap_free(obj);
1836 #ifdef TRACE_REFCNT
1838 jsdisp_t *jsdisp_addref(jsdisp_t *jsdisp)
1840 ULONG ref = ++jsdisp->ref;
1841 TRACE("(%p) ref=%d\n", jsdisp, ref);
1842 return jsdisp;
1845 void jsdisp_release(jsdisp_t *jsdisp)
1847 ULONG ref = --jsdisp->ref;
1849 TRACE("(%p) ref=%d\n", jsdisp, ref);
1851 if(!ref)
1852 jsdisp_free(jsdisp);
1855 #endif
1857 HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *constr)
1859 jsdisp_t *prot = NULL;
1860 dispex_prop_t *prop;
1861 HRESULT hres;
1863 hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", &prop);
1864 if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) {
1865 jsval_t val;
1867 hres = prop_get(constr, prop, &val);
1868 if(FAILED(hres)) {
1869 ERR("Could not get prototype\n");
1870 return hres;
1873 if(is_object_instance(val) && get_object(val))
1874 prot = iface_to_jsdisp(get_object(val));
1875 else
1876 prot = ctx->object_prototype;
1878 jsval_release(val);
1881 hres = init_dispex(dispex, ctx, builtin_info, prot);
1883 if(prot)
1884 jsdisp_release(prot);
1885 return hres;
1888 jsdisp_t *iface_to_jsdisp(IDispatch *iface)
1890 return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl
1891 ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface))
1892 : NULL;
1895 HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id)
1897 dispex_prop_t *prop;
1898 HRESULT hres;
1900 if(jsdisp->extensible && (flags & fdexNameEnsure))
1901 hres = ensure_prop_name(jsdisp, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE,
1902 &prop);
1903 else
1904 hres = find_prop_name_prot(jsdisp, string_hash(name), name, &prop);
1905 if(FAILED(hres))
1906 return hres;
1908 if(prop && prop->type!=PROP_DELETED) {
1909 *id = prop_to_id(jsdisp, prop);
1910 return S_OK;
1913 TRACE("not found %s\n", debugstr_w(name));
1914 *id = DISPID_UNKNOWN;
1915 return DISP_E_UNKNOWNNAME;
1918 HRESULT jsdisp_call_value(jsdisp_t *jsfunc, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1920 HRESULT hres;
1922 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
1924 if(is_class(jsfunc, JSCLASS_FUNCTION)) {
1925 hres = Function_invoke(jsfunc, jsthis, flags, argc, argv, r);
1926 }else {
1927 vdisp_t vdisp;
1929 if(!jsfunc->builtin_info->value_prop.invoke) {
1930 WARN("Not a function\n");
1931 return JS_E_FUNCTION_EXPECTED;
1934 set_disp(&vdisp, jsthis);
1935 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1936 hres = jsfunc->builtin_info->value_prop.invoke(jsfunc->ctx, &vdisp, flags, argc, argv, r);
1937 vdisp_release(&vdisp);
1939 return hres;
1942 HRESULT jsdisp_call(jsdisp_t *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1944 dispex_prop_t *prop;
1946 prop = get_prop(disp, id);
1947 if(!prop)
1948 return DISP_E_MEMBERNOTFOUND;
1950 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL);
1953 HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1955 dispex_prop_t *prop;
1956 HRESULT hres;
1958 hres = find_prop_name_prot(disp, string_hash(name), name, &prop);
1959 if(FAILED(hres))
1960 return hres;
1962 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL);
1965 static HRESULT disp_invoke(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, DISPPARAMS *params, VARIANT *r)
1967 IDispatchEx *dispex;
1968 EXCEPINFO ei;
1969 HRESULT hres;
1971 memset(&ei, 0, sizeof(ei));
1972 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1973 if(SUCCEEDED(hres)) {
1974 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, params, r, &ei, &ctx->jscaller->IServiceProvider_iface);
1975 IDispatchEx_Release(dispex);
1976 }else {
1977 UINT err = 0;
1979 if(flags == DISPATCH_CONSTRUCT) {
1980 WARN("IDispatch cannot be constructor\n");
1981 return DISP_E_MEMBERNOTFOUND;
1984 if(params->cNamedArgs == 1 && params->rgdispidNamedArgs[0] == DISPID_THIS) {
1985 params->cNamedArgs = 0;
1986 params->rgdispidNamedArgs = NULL;
1987 params->cArgs--;
1988 params->rgvarg++;
1991 TRACE("using IDispatch\n");
1992 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, params, r, &ei, &err);
1995 if(hres == DISP_E_EXCEPTION) {
1996 TRACE("DISP_E_EXCEPTION: %08x %s %s\n", ei.scode, debugstr_w(ei.bstrSource), debugstr_w(ei.bstrDescription));
1997 reset_ei(ctx->ei);
1998 ctx->ei->error = (SUCCEEDED(ei.scode) || ei.scode == DISP_E_EXCEPTION) ? E_FAIL : ei.scode;
1999 if(ei.bstrSource)
2000 ctx->ei->source = jsstr_alloc_len(ei.bstrSource, SysStringLen(ei.bstrSource));
2001 if(ei.bstrDescription)
2002 ctx->ei->message = jsstr_alloc_len(ei.bstrDescription, SysStringLen(ei.bstrDescription));
2003 SysFreeString(ei.bstrSource);
2004 SysFreeString(ei.bstrDescription);
2005 SysFreeString(ei.bstrHelpFile);
2008 return hres;
2011 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret)
2013 VARIANT buf[6], retv;
2014 jsdisp_t *jsdisp;
2015 DISPPARAMS dp;
2016 unsigned i;
2017 HRESULT hres;
2019 jsdisp = iface_to_jsdisp(disp);
2020 if(jsdisp && jsdisp->ctx == ctx) {
2021 if(flags & DISPATCH_PROPERTYPUT) {
2022 FIXME("disp_call(propput) on builtin object\n");
2023 return E_FAIL;
2026 if(ctx != jsdisp->ctx)
2027 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2028 hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret);
2029 jsdisp_release(jsdisp);
2030 return hres;
2033 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2034 if(ret && argc)
2035 flags |= DISPATCH_PROPERTYGET;
2037 dp.cArgs = argc;
2039 if(flags & DISPATCH_PROPERTYPUT) {
2040 static DISPID propput_dispid = DISPID_PROPERTYPUT;
2042 dp.cNamedArgs = 1;
2043 dp.rgdispidNamedArgs = &propput_dispid;
2044 }else {
2045 dp.cNamedArgs = 0;
2046 dp.rgdispidNamedArgs = NULL;
2049 if(dp.cArgs > ARRAY_SIZE(buf)) {
2050 dp.rgvarg = heap_alloc(argc*sizeof(VARIANT));
2051 if(!dp.rgvarg)
2052 return E_OUTOFMEMORY;
2053 }else {
2054 dp.rgvarg = buf;
2057 for(i=0; i<argc; i++) {
2058 hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1);
2059 if(FAILED(hres)) {
2060 while(i--)
2061 VariantClear(dp.rgvarg+argc-i-1);
2062 if(dp.rgvarg != buf)
2063 heap_free(dp.rgvarg);
2064 return hres;
2068 V_VT(&retv) = VT_EMPTY;
2069 hres = disp_invoke(ctx, disp, id, flags, &dp, ret ? &retv : NULL);
2071 for(i=0; i<argc; i++)
2072 VariantClear(dp.rgvarg+argc-i-1);
2073 if(dp.rgvarg != buf)
2074 heap_free(dp.rgvarg);
2076 if(SUCCEEDED(hres) && ret)
2077 hres = variant_to_jsval(&retv, ret);
2078 VariantClear(&retv);
2079 return hres;
2082 HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv,
2083 jsval_t *r)
2085 VARIANT buf[6], retv, *args = buf;
2086 jsdisp_t *jsdisp;
2087 DISPPARAMS dp;
2088 unsigned i;
2089 HRESULT hres = S_OK;
2091 static DISPID this_id = DISPID_THIS;
2093 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
2095 jsdisp = iface_to_jsdisp(disp);
2096 if(jsdisp && jsdisp->ctx == ctx) {
2097 if(ctx != jsdisp->ctx)
2098 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2099 hres = jsdisp_call_value(jsdisp, jsthis, flags, argc, argv, r);
2100 jsdisp_release(jsdisp);
2101 return hres;
2104 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2105 if(r && argc && flags == DISPATCH_METHOD)
2106 flags |= DISPATCH_PROPERTYGET;
2108 if(jsthis) {
2109 dp.cArgs = argc + 1;
2110 dp.cNamedArgs = 1;
2111 dp.rgdispidNamedArgs = &this_id;
2112 }else {
2113 dp.cArgs = argc;
2114 dp.cNamedArgs = 0;
2115 dp.rgdispidNamedArgs = NULL;
2118 if(dp.cArgs > ARRAY_SIZE(buf) && !(args = heap_alloc(dp.cArgs * sizeof(VARIANT))))
2119 return E_OUTOFMEMORY;
2120 dp.rgvarg = args;
2122 if(jsthis) {
2123 V_VT(dp.rgvarg) = VT_DISPATCH;
2124 V_DISPATCH(dp.rgvarg) = jsthis;
2127 for(i=0; SUCCEEDED(hres) && i < argc; i++)
2128 hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1);
2130 if(SUCCEEDED(hres)) {
2131 V_VT(&retv) = VT_EMPTY;
2132 hres = disp_invoke(ctx, disp, DISPID_VALUE, flags, &dp, r ? &retv : NULL);
2135 for(i = 0; i < argc; i++)
2136 VariantClear(dp.rgvarg + dp.cArgs - i - 1);
2137 if(args != buf)
2138 heap_free(args);
2140 if(!r)
2141 return S_OK;
2143 hres = variant_to_jsval(&retv, r);
2144 VariantClear(&retv);
2145 return hres;
2148 HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, jsval_t val)
2150 dispex_prop_t *prop;
2151 HRESULT hres;
2153 if(obj->extensible)
2154 hres = ensure_prop_name(obj, name, flags, &prop);
2155 else
2156 hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
2157 if(FAILED(hres) || !prop)
2158 return hres;
2160 return prop_put(obj, prop, val);
2163 HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val)
2165 return jsdisp_propput(obj, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, val);
2168 HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val)
2170 WCHAR buf[12];
2172 swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
2173 return jsdisp_propput_name(obj, buf, val);
2176 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val)
2178 jsdisp_t *jsdisp;
2179 HRESULT hres;
2181 jsdisp = iface_to_jsdisp(disp);
2182 if(jsdisp && jsdisp->ctx == ctx) {
2183 dispex_prop_t *prop;
2185 prop = get_prop(jsdisp, id);
2186 if(prop)
2187 hres = prop_put(jsdisp, prop, val);
2188 else
2189 hres = DISP_E_MEMBERNOTFOUND;
2191 jsdisp_release(jsdisp);
2192 }else {
2193 DISPID dispid = DISPID_PROPERTYPUT;
2194 DWORD flags = DISPATCH_PROPERTYPUT;
2195 VARIANT var;
2196 DISPPARAMS dp = {&var, &dispid, 1, 1};
2198 hres = jsval_to_variant(val, &var);
2199 if(FAILED(hres))
2200 return hres;
2202 if(V_VT(&var) == VT_DISPATCH)
2203 flags |= DISPATCH_PROPERTYPUTREF;
2205 hres = disp_invoke(ctx, disp, id, flags, &dp, NULL);
2206 VariantClear(&var);
2209 return hres;
2212 HRESULT disp_propput_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, jsval_t val)
2214 jsdisp_t *jsdisp;
2215 HRESULT hres;
2217 jsdisp = iface_to_jsdisp(disp);
2218 if(!jsdisp || jsdisp->ctx != ctx) {
2219 IDispatchEx *dispex;
2220 BSTR str;
2221 DISPID id;
2223 if(!(str = SysAllocString(name)))
2224 return E_OUTOFMEMORY;
2226 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2227 if(SUCCEEDED(hres)) {
2228 hres = IDispatchEx_GetDispID(dispex, str, make_grfdex(ctx, fdexNameEnsure|fdexNameCaseSensitive), &id);
2229 IDispatchEx_Release(dispex);
2230 }else {
2231 TRACE("using IDispatch\n");
2232 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &str, 1, 0, &id);
2234 SysFreeString(str);
2235 if(FAILED(hres))
2236 return hres;
2238 return disp_propput(ctx, disp, id, val);
2241 return jsdisp_propput_name(jsdisp, name, val);
2244 HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val)
2246 dispex_prop_t *prop;
2247 HRESULT hres;
2249 hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
2250 if(FAILED(hres))
2251 return hres;
2253 if(!prop || prop->type==PROP_DELETED) {
2254 *val = jsval_undefined();
2255 return S_OK;
2258 return prop_get(obj, prop, val);
2261 HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r)
2263 WCHAR name[12];
2264 dispex_prop_t *prop;
2265 HRESULT hres;
2267 swprintf(name, ARRAY_SIZE(name), L"%d", idx);
2269 hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
2270 if(FAILED(hres))
2271 return hres;
2273 if(!prop || prop->type==PROP_DELETED) {
2274 *r = jsval_undefined();
2275 return DISP_E_UNKNOWNNAME;
2278 return prop_get(obj, prop, r);
2281 HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val)
2283 dispex_prop_t *prop;
2285 prop = get_prop(jsdisp, id);
2286 if(!prop)
2287 return DISP_E_MEMBERNOTFOUND;
2289 return prop_get(jsdisp, prop, val);
2292 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val)
2294 DISPPARAMS dp = {NULL,NULL,0,0};
2295 jsdisp_t *jsdisp;
2296 VARIANT var;
2297 HRESULT hres;
2299 jsdisp = iface_to_jsdisp(disp);
2300 if(jsdisp && jsdisp->ctx == ctx) {
2301 hres = jsdisp_propget(jsdisp, id, val);
2302 jsdisp_release(jsdisp);
2303 return hres;
2306 V_VT(&var) = VT_EMPTY;
2307 hres = disp_invoke(ctx, disp, id, INVOKE_PROPERTYGET, &dp, &var);
2308 if(SUCCEEDED(hres)) {
2309 hres = variant_to_jsval(&var, val);
2310 VariantClear(&var);
2312 return hres;
2315 HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
2317 WCHAR buf[12];
2318 dispex_prop_t *prop;
2319 BOOL b;
2320 HRESULT hres;
2322 swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
2324 hres = find_prop_name(obj, string_hash(buf), buf, &prop);
2325 if(FAILED(hres) || !prop)
2326 return hres;
2328 return delete_prop(prop, &b);
2331 HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret)
2333 IDispatchEx *dispex;
2334 jsdisp_t *jsdisp;
2335 HRESULT hres;
2337 jsdisp = iface_to_jsdisp(disp);
2338 if(jsdisp) {
2339 dispex_prop_t *prop;
2341 prop = get_prop(jsdisp, id);
2342 if(prop)
2343 hres = delete_prop(prop, ret);
2344 else
2345 hres = DISP_E_MEMBERNOTFOUND;
2347 jsdisp_release(jsdisp);
2348 return hres;
2351 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2352 if(FAILED(hres)) {
2353 *ret = FALSE;
2354 return S_OK;
2357 hres = IDispatchEx_DeleteMemberByDispID(dispex, id);
2358 IDispatchEx_Release(dispex);
2359 if(FAILED(hres))
2360 return hres;
2362 *ret = hres == S_OK;
2363 return S_OK;
2366 HRESULT jsdisp_next_prop(jsdisp_t *obj, DISPID id, BOOL own_only, DISPID *ret)
2368 dispex_prop_t *iter;
2369 HRESULT hres;
2371 if(id == DISPID_STARTENUM && !own_only) {
2372 hres = fill_protrefs(obj);
2373 if(FAILED(hres))
2374 return hres;
2377 if(id + 1 < 0 || id+1 >= obj->prop_cnt)
2378 return S_FALSE;
2380 for(iter = &obj->props[id + 1]; iter < obj->props + obj->prop_cnt; iter++) {
2381 if(!iter->name || iter->type == PROP_DELETED)
2382 continue;
2383 if(own_only && iter->type == PROP_PROTREF)
2384 continue;
2385 if(!(get_flags(obj, iter) & PROPF_ENUMERABLE))
2386 continue;
2387 *ret = prop_to_id(obj, iter);
2388 return S_OK;
2391 return S_FALSE;
2394 HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret)
2396 IDispatchEx *dispex;
2397 jsdisp_t *jsdisp;
2398 BSTR bstr;
2399 HRESULT hres;
2401 jsdisp = iface_to_jsdisp(disp);
2402 if(jsdisp) {
2403 dispex_prop_t *prop;
2404 const WCHAR *ptr;
2406 ptr = jsstr_flatten(name);
2407 if(!ptr) {
2408 jsdisp_release(jsdisp);
2409 return E_OUTOFMEMORY;
2412 hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop);
2413 if(prop) {
2414 hres = delete_prop(prop, ret);
2415 }else {
2416 *ret = TRUE;
2417 hres = S_OK;
2420 jsdisp_release(jsdisp);
2421 return hres;
2424 bstr = SysAllocStringLen(NULL, jsstr_length(name));
2425 if(!bstr)
2426 return E_OUTOFMEMORY;
2427 jsstr_flush(name, bstr);
2429 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2430 if(SUCCEEDED(hres)) {
2431 hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive));
2432 if(SUCCEEDED(hres))
2433 *ret = hres == S_OK;
2434 IDispatchEx_Release(dispex);
2435 }else {
2436 DISPID id;
2438 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
2439 if(SUCCEEDED(hres)) {
2440 /* Property exists and we can't delete it from pure IDispatch interface, so return false. */
2441 *ret = FALSE;
2442 }else if(hres == DISP_E_UNKNOWNNAME) {
2443 /* Property doesn't exist, so nothing to delete */
2444 *ret = TRUE;
2445 hres = S_OK;
2449 SysFreeString(bstr);
2450 return hres;
2453 HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_only,
2454 property_desc_t *desc)
2456 dispex_prop_t *prop;
2457 HRESULT hres;
2459 hres = find_prop_name(obj, string_hash(name), name, &prop);
2460 if(FAILED(hres))
2461 return hres;
2463 if(!prop)
2464 return DISP_E_UNKNOWNNAME;
2466 memset(desc, 0, sizeof(*desc));
2468 switch(prop->type) {
2469 case PROP_BUILTIN:
2470 case PROP_JSVAL:
2471 desc->mask |= PROPF_WRITABLE;
2472 desc->explicit_value = TRUE;
2473 if(!flags_only) {
2474 hres = prop_get(obj, prop, &desc->value);
2475 if(FAILED(hres))
2476 return hres;
2478 break;
2479 case PROP_ACCESSOR:
2480 desc->explicit_getter = desc->explicit_setter = TRUE;
2481 if(!flags_only) {
2482 desc->getter = prop->u.accessor.getter
2483 ? jsdisp_addref(prop->u.accessor.getter) : NULL;
2484 desc->setter = prop->u.accessor.setter
2485 ? jsdisp_addref(prop->u.accessor.setter) : NULL;
2487 break;
2488 default:
2489 return DISP_E_UNKNOWNNAME;
2492 desc->flags = prop->flags & (PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE);
2493 desc->mask |= PROPF_ENUMERABLE | PROPF_CONFIGURABLE;
2494 return S_OK;
2497 HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t *desc)
2499 dispex_prop_t *prop;
2500 HRESULT hres;
2502 hres = find_prop_name(obj, string_hash(name), name, &prop);
2503 if(FAILED(hres))
2504 return hres;
2506 if(!prop && !(prop = alloc_prop(obj, name, PROP_DELETED, 0)))
2507 return E_OUTOFMEMORY;
2509 if(prop->type == PROP_DELETED || prop->type == PROP_PROTREF) {
2510 prop->flags = desc->flags;
2511 if(desc->explicit_getter || desc->explicit_setter) {
2512 prop->type = PROP_ACCESSOR;
2513 prop->u.accessor.getter = desc->getter ? jsdisp_addref(desc->getter) : NULL;
2514 prop->u.accessor.setter = desc->setter ? jsdisp_addref(desc->setter) : NULL;
2515 TRACE("%s = accessor { get: %p set: %p }\n", debugstr_w(name),
2516 prop->u.accessor.getter, prop->u.accessor.setter);
2517 }else {
2518 prop->type = PROP_JSVAL;
2519 if(desc->explicit_value) {
2520 hres = jsval_copy(desc->value, &prop->u.val);
2521 if(FAILED(hres))
2522 return hres;
2523 }else {
2524 prop->u.val = jsval_undefined();
2526 TRACE("%s = %s\n", debugstr_w(name), debugstr_jsval(prop->u.val));
2528 return S_OK;
2531 TRACE("existing prop %s prop flags %x desc flags %x desc mask %x\n", debugstr_w(name),
2532 prop->flags, desc->flags, desc->mask);
2534 if(!(prop->flags & PROPF_CONFIGURABLE)) {
2535 if(((desc->mask & PROPF_CONFIGURABLE) && (desc->flags & PROPF_CONFIGURABLE))
2536 || ((desc->mask & PROPF_ENUMERABLE)
2537 && ((desc->flags & PROPF_ENUMERABLE) != (prop->flags & PROPF_ENUMERABLE))))
2538 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
2541 if(desc->explicit_value || (desc->mask & PROPF_WRITABLE)) {
2542 if(prop->type == PROP_ACCESSOR) {
2543 if(!(prop->flags & PROPF_CONFIGURABLE))
2544 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
2545 if(prop->u.accessor.getter)
2546 jsdisp_release(prop->u.accessor.getter);
2547 if(prop->u.accessor.setter)
2548 jsdisp_release(prop->u.accessor.setter);
2550 prop->type = PROP_JSVAL;
2551 hres = jsval_copy(desc->value, &prop->u.val);
2552 if(FAILED(hres)) {
2553 prop->u.val = jsval_undefined();
2554 return hres;
2556 }else {
2557 if(!(prop->flags & PROPF_CONFIGURABLE) && !(prop->flags & PROPF_WRITABLE)) {
2558 if((desc->mask & PROPF_WRITABLE) && (desc->flags & PROPF_WRITABLE))
2559 return throw_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
2560 if(desc->explicit_value) {
2561 if(prop->type == PROP_JSVAL) {
2562 BOOL eq;
2563 hres = jsval_strict_equal(desc->value, prop->u.val, &eq);
2564 if(FAILED(hres))
2565 return hres;
2566 if(!eq)
2567 return throw_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
2568 }else {
2569 FIXME("redefinition of property type %d\n", prop->type);
2573 if(desc->explicit_value) {
2574 if(prop->type == PROP_JSVAL)
2575 jsval_release(prop->u.val);
2576 else
2577 prop->type = PROP_JSVAL;
2578 hres = jsval_copy(desc->value, &prop->u.val);
2579 if(FAILED(hres)) {
2580 prop->u.val = jsval_undefined();
2581 return hres;
2585 }else if(desc->explicit_getter || desc->explicit_setter) {
2586 if(prop->type != PROP_ACCESSOR) {
2587 if(!(prop->flags & PROPF_CONFIGURABLE))
2588 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
2589 if(prop->type == PROP_JSVAL)
2590 jsval_release(prop->u.val);
2591 prop->type = PROP_ACCESSOR;
2592 prop->u.accessor.getter = prop->u.accessor.setter = NULL;
2593 }else if(!(prop->flags & PROPF_CONFIGURABLE)) {
2594 if((desc->explicit_getter && desc->getter != prop->u.accessor.getter)
2595 || (desc->explicit_setter && desc->setter != prop->u.accessor.setter))
2596 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
2599 if(desc->explicit_getter) {
2600 if(prop->u.accessor.getter) {
2601 jsdisp_release(prop->u.accessor.getter);
2602 prop->u.accessor.getter = NULL;
2604 if(desc->getter)
2605 prop->u.accessor.getter = jsdisp_addref(desc->getter);
2607 if(desc->explicit_setter) {
2608 if(prop->u.accessor.setter) {
2609 jsdisp_release(prop->u.accessor.setter);
2610 prop->u.accessor.setter = NULL;
2612 if(desc->setter)
2613 prop->u.accessor.setter = jsdisp_addref(desc->setter);
2617 prop->flags = (prop->flags & ~desc->mask) | (desc->flags & desc->mask);
2618 return S_OK;
2621 HRESULT jsdisp_define_data_property(jsdisp_t *obj, const WCHAR *name, unsigned flags, jsval_t value)
2623 property_desc_t prop_desc = { flags, flags, TRUE };
2624 prop_desc.value = value;
2625 return jsdisp_define_property(obj, name, &prop_desc);
2628 HRESULT jsdisp_get_prop_name(jsdisp_t *obj, DISPID id, jsstr_t **r)
2630 dispex_prop_t *prop = get_prop(obj, id);
2632 if(!prop || !prop->name || prop->type == PROP_DELETED)
2633 return DISP_E_MEMBERNOTFOUND;
2635 *r = jsstr_alloc(prop->name);
2636 return *r ? S_OK : E_OUTOFMEMORY;