jscript: Support getting value of accessor property.
[wine.git] / dlls / jscript / dispex.c
blob255747107ece39cbc70f350e19934ee0d239746b
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"
23 #include "wine/unicode.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
28 #define FDEX_VERSION_MASK 0xf0000000
29 #define GOLDEN_RATIO 0x9E3779B9U
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;
40 struct _dispex_prop_t {
41 WCHAR *name;
42 unsigned hash;
43 prop_type_t type;
44 DWORD flags;
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;
57 int bucket_head;
58 int bucket_next;
61 static inline DISPID prop_to_id(jsdisp_t *This, dispex_prop_t *prop)
63 return prop - This->props;
66 static inline dispex_prop_t *get_prop(jsdisp_t *This, DISPID id)
68 if(id < 0 || id >= This->prop_cnt || This->props[id].type == PROP_DELETED)
69 return NULL;
71 return This->props+id;
74 static DWORD get_flags(jsdisp_t *This, dispex_prop_t *prop)
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;
83 return get_flags(This->prototype, parent);
86 return prop->flags;
89 static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name)
91 int min = 0, max, i, r;
93 max = This->builtin_info->props_cnt-1;
94 while(min <= max) {
95 i = (min+max)/2;
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;
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;
109 return This->builtin_info->props + i;
112 if(r < 0)
113 max = i-1;
114 else
115 min = i+1;
118 return NULL;
121 static inline unsigned string_hash(const WCHAR *name)
123 unsigned h = 0;
124 for(; *name; name++)
125 h = (h>>(sizeof(unsigned)*8-4)) ^ (h<<4) ^ tolowerW(*name);
126 return h;
129 static inline unsigned get_props_idx(jsdisp_t *This, unsigned hash)
131 return (hash*GOLDEN_RATIO) & (This->buf_size-1);
134 static inline HRESULT resize_props(jsdisp_t *This)
136 dispex_prop_t *props;
137 int i, bucket;
139 if(This->buf_size != This->prop_cnt)
140 return S_FALSE;
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;
148 for(i=0; i<This->buf_size; i++) {
149 This->props[i].bucket_head = 0;
150 This->props[i].bucket_next = 0;
153 for(i=1; i<This->prop_cnt; i++) {
154 props = This->props+i;
156 bucket = get_props_idx(This, props->hash);
157 props->bucket_next = This->props[bucket].bucket_head;
158 This->props[bucket].bucket_head = i;
161 return S_OK;
164 static inline dispex_prop_t* alloc_prop(jsdisp_t *This, const WCHAR *name, prop_type_t type, DWORD flags)
166 dispex_prop_t *prop;
167 unsigned bucket;
169 if(FAILED(resize_props(This)))
170 return NULL;
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);
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;
186 static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref)
188 dispex_prop_t *ret;
190 ret = alloc_prop(This, name, PROP_PROTREF, 0);
191 if(!ret)
192 return NULL;
194 ret->u.ref = ref;
195 return ret;
198 static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret)
200 const builtin_prop_t *builtin;
201 unsigned bucket, pos, prev = 0;
202 dispex_prop_t *prop;
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;
214 *ret = &This->props[pos];
215 return S_OK;
218 prev = pos;
219 pos = This->props[pos].bucket_next;
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;
234 prop->u.p = builtin;
235 *ret = prop;
236 return S_OK;
239 if(This->builtin_info->idx_length) {
240 const WCHAR *ptr;
241 unsigned idx = 0;
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;
250 prop->u.idx = idx;
251 *ret = prop;
252 return S_OK;
256 *ret = NULL;
257 return S_OK;
260 static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, dispex_prop_t **ret)
262 dispex_prop_t *prop, *del=NULL;
263 HRESULT hres;
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;
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;
290 *ret = prop;
291 return S_OK;
295 *ret = del;
296 return S_OK;
299 static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, BOOL search_prot, DWORD create_flags, dispex_prop_t **ret)
301 dispex_prop_t *prop;
302 HRESULT hres;
304 if(search_prot)
305 hres = find_prop_name_prot(This, string_hash(name), name, &prop);
306 else
307 hres = find_prop_name(This, string_hash(name), name, &prop);
308 if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) {
309 TRACE("creating prop %s flags %x\n", debugstr_w(name), create_flags);
311 if(prop) {
312 prop->type = PROP_JSVAL;
313 prop->flags = create_flags;
314 prop->u.val = jsval_undefined();
315 }else {
316 prop = alloc_prop(This, name, PROP_JSVAL, create_flags);
317 if(!prop)
318 return E_OUTOFMEMORY;
321 prop->u.val = jsval_undefined();
324 *ret = prop;
325 return hres;
328 static IDispatch *get_this(DISPPARAMS *dp)
330 DWORD i;
332 for(i=0; i < dp->cNamedArgs; i++) {
333 if(dp->rgdispidNamedArgs[i] == DISPID_THIS) {
334 if(V_VT(dp->rgvarg+i) == VT_DISPATCH)
335 return V_DISPATCH(dp->rgvarg+i);
337 WARN("This is not VT_DISPATCH\n");
338 return NULL;
342 TRACE("no this passed\n");
343 return NULL;
346 static HRESULT convert_params(const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret)
348 jsval_t *argv;
349 unsigned cnt;
350 unsigned i;
351 HRESULT hres;
353 cnt = dp->cArgs - dp->cNamedArgs;
355 if(cnt > 6) {
356 argv = heap_alloc(cnt * sizeof(*argv));
357 if(!argv)
358 return E_OUTOFMEMORY;
359 }else {
360 argv = buf;
363 for(i = 0; i < cnt; i++) {
364 hres = variant_to_jsval(dp->rgvarg+dp->cArgs-i-1, argv+i);
365 if(FAILED(hres)) {
366 while(i--)
367 jsval_release(argv[i]);
368 if(argv != buf)
369 heap_free(argv);
370 return hres;
374 *argc = cnt;
375 *ret = argv;
376 return S_OK;
379 static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t *prop, WORD flags,
380 unsigned argc, jsval_t *argv, jsval_t *r, IServiceProvider *caller)
382 HRESULT hres;
384 switch(prop->type) {
385 case PROP_BUILTIN: {
386 if(flags == DISPATCH_CONSTRUCT && (prop->flags & PROPF_METHOD)) {
387 WARN("%s is not a constructor\n", debugstr_w(prop->name));
388 return E_INVALIDARG;
391 if(prop->name || This->builtin_info->class != JSCLASS_FUNCTION) {
392 vdisp_t vthis;
394 if(This->builtin_info->class != JSCLASS_FUNCTION && prop->u.p->invoke != JSGlobal_eval)
395 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
396 if(jsthis)
397 set_disp(&vthis, jsthis);
398 else
399 set_jsdisp(&vthis, This);
400 hres = prop->u.p->invoke(This->ctx, &vthis, flags, argc, argv, r);
401 vdisp_release(&vthis);
402 }else {
403 /* Function object calls are special case */
404 hres = Function_invoke(This, jsthis, flags, argc, argv, r);
406 return hres;
408 case PROP_PROTREF:
409 return invoke_prop_func(This->prototype, jsthis, This->prototype->props+prop->u.ref,
410 flags, argc, argv, r, caller);
411 case PROP_JSVAL: {
412 if(!is_object_instance(prop->u.val)) {
413 FIXME("invoke %s\n", debugstr_jsval(prop->u.val));
414 return E_FAIL;
417 TRACE("call %s %p\n", debugstr_w(prop->name), get_object(prop->u.val));
419 return disp_call_value(This->ctx, get_object(prop->u.val), jsthis, flags, argc, argv, r);
421 case PROP_ACCESSOR:
422 FIXME("accessor\n");
423 return E_NOTIMPL;
424 case PROP_IDX:
425 FIXME("Invoking PROP_IDX not yet supported\n");
426 return E_NOTIMPL;
427 case PROP_DELETED:
428 assert(0);
431 assert(0);
432 return E_FAIL;
435 static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, jsval_t *r)
437 jsdisp_t *prop_obj = This;
438 HRESULT hres;
440 while(prop->type == PROP_PROTREF) {
441 prop_obj = prop_obj->prototype;
442 prop = prop_obj->props + prop->u.ref;
445 switch(prop->type) {
446 case PROP_BUILTIN:
447 if(prop->u.p->getter) {
448 hres = prop->u.p->getter(This->ctx, This, r);
449 }else {
450 jsdisp_t *obj;
452 assert(prop->u.p->invoke != NULL);
453 hres = create_builtin_function(This->ctx, prop->u.p->invoke, prop->u.p->name, NULL,
454 prop->u.p->flags, NULL, &obj);
455 if(FAILED(hres))
456 break;
458 prop->type = PROP_JSVAL;
459 prop->u.val = jsval_obj(obj);
461 jsdisp_addref(obj);
462 *r = jsval_obj(obj);
464 break;
465 case PROP_JSVAL:
466 hres = jsval_copy(prop->u.val, r);
467 break;
468 case PROP_ACCESSOR:
469 if(prop->u.accessor.getter) {
470 hres = jsdisp_call_value(prop->u.accessor.getter, to_disp(This),
471 DISPATCH_METHOD, 0, NULL, r);
472 }else {
473 *r = jsval_undefined();
474 hres = S_OK;
476 break;
477 case PROP_IDX:
478 hres = prop_obj->builtin_info->idx_get(prop_obj, prop->u.idx, r);
479 break;
480 default:
481 ERR("type %d\n", prop->type);
482 return E_FAIL;
485 if(FAILED(hres)) {
486 TRACE("fail %08x\n", hres);
487 return hres;
490 TRACE("%s ret %s\n", debugstr_w(prop->name), debugstr_jsval(*r));
491 return hres;
494 static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val)
496 HRESULT hres;
498 if(!(prop->flags & PROPF_WRITABLE) && prop->type != PROP_PROTREF)
499 return S_OK;
501 switch(prop->type) {
502 case PROP_BUILTIN:
503 if(prop->u.p->setter)
504 return prop->u.p->setter(This->ctx, This, val);
506 if(prop->u.p->setter) {
507 FIXME("getter with no setter\n");
508 return E_FAIL;
510 /* fall through */
511 case PROP_PROTREF:
512 prop->type = PROP_JSVAL;
513 prop->flags = PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE;
514 prop->u.val = jsval_undefined();
515 break;
516 case PROP_JSVAL:
517 jsval_release(prop->u.val);
518 break;
519 case PROP_IDX:
520 return This->builtin_info->idx_put(This, prop->u.idx, val);
521 default:
522 ERR("type %d\n", prop->type);
523 return E_FAIL;
526 TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_jsval(val));
528 hres = jsval_copy(val, &prop->u.val);
529 if(FAILED(hres))
530 return hres;
532 if(This->builtin_info->on_put)
533 This->builtin_info->on_put(This, prop->name);
535 return S_OK;
538 HRESULT builtin_set_const(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
540 TRACE("%p %s\n", jsthis, debugstr_jsval(value));
541 return S_OK;
544 static HRESULT fill_protrefs(jsdisp_t *This)
546 dispex_prop_t *iter, *prop;
547 HRESULT hres;
549 if(!This->prototype)
550 return S_OK;
552 fill_protrefs(This->prototype);
554 for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) {
555 if(!iter->name)
556 continue;
557 hres = find_prop_name(This, iter->hash, iter->name, &prop);
558 if(FAILED(hres))
559 return hres;
560 if(!prop || prop->type==PROP_DELETED) {
561 if(prop) {
562 prop->type = PROP_PROTREF;
563 prop->flags = 0;
564 prop->u.ref = iter - This->prototype->props;
565 }else {
566 prop = alloc_protref(This, iter->name, iter - This->prototype->props);
567 if(!prop)
568 return E_OUTOFMEMORY;
573 return S_OK;
576 static inline jsdisp_t *impl_from_IDispatchEx(IDispatchEx *iface)
578 return CONTAINING_RECORD(iface, jsdisp_t, IDispatchEx_iface);
581 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
583 jsdisp_t *This = impl_from_IDispatchEx(iface);
585 if(IsEqualGUID(&IID_IUnknown, riid)) {
586 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
587 *ppv = &This->IDispatchEx_iface;
588 }else if(IsEqualGUID(&IID_IDispatch, riid)) {
589 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
590 *ppv = &This->IDispatchEx_iface;
591 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
592 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
593 *ppv = &This->IDispatchEx_iface;
594 }else {
595 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
596 *ppv = NULL;
597 return E_NOINTERFACE;
600 jsdisp_addref(This);
601 return S_OK;
604 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
606 jsdisp_t *This = impl_from_IDispatchEx(iface);
607 jsdisp_addref(This);
608 return This->ref;
611 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
613 jsdisp_t *This = impl_from_IDispatchEx(iface);
614 ULONG ref = --This->ref;
615 TRACE("(%p) ref=%d\n", This, ref);
616 if(!ref)
617 jsdisp_free(This);
618 return ref;
621 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
623 jsdisp_t *This = impl_from_IDispatchEx(iface);
625 TRACE("(%p)->(%p)\n", This, pctinfo);
627 *pctinfo = 1;
628 return S_OK;
631 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
632 ITypeInfo **ppTInfo)
634 jsdisp_t *This = impl_from_IDispatchEx(iface);
635 FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
636 return E_NOTIMPL;
639 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
640 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
641 DISPID *rgDispId)
643 jsdisp_t *This = impl_from_IDispatchEx(iface);
644 UINT i;
645 HRESULT hres;
647 TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
648 lcid, rgDispId);
650 for(i=0; i < cNames; i++) {
651 hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i);
652 if(FAILED(hres))
653 return hres;
656 return S_OK;
659 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
660 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
661 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
663 jsdisp_t *This = impl_from_IDispatchEx(iface);
665 TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
666 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
668 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags,
669 pDispParams, pVarResult, pExcepInfo, NULL);
672 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
674 jsdisp_t *This = impl_from_IDispatchEx(iface);
676 TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
678 if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) {
679 FIXME("Unsupported grfdex %x\n", grfdex);
680 return E_NOTIMPL;
683 return jsdisp_get_id(This, bstrName, grfdex, pid);
686 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
687 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
689 jsdisp_t *This = impl_from_IDispatchEx(iface);
690 dispex_prop_t *prop;
691 HRESULT hres;
693 TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
695 if(pvarRes)
696 V_VT(pvarRes) = VT_EMPTY;
698 prop = get_prop(This, id);
699 if(!prop || prop->type == PROP_DELETED) {
700 TRACE("invalid id\n");
701 return DISP_E_MEMBERNOTFOUND;
704 clear_ei(This->ctx);
706 switch(wFlags) {
707 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
708 wFlags = DISPATCH_METHOD;
709 /* fall through */
710 case DISPATCH_METHOD:
711 case DISPATCH_CONSTRUCT: {
712 jsval_t *argv, buf[6], r;
713 unsigned argc;
715 hres = convert_params(pdp, buf, &argc, &argv);
716 if(FAILED(hres))
717 return hres;
719 hres = invoke_prop_func(This, get_this(pdp), prop, wFlags, argc, argv, pvarRes ? &r : NULL, pspCaller);
720 if(argv != buf)
721 heap_free(argv);
722 if(SUCCEEDED(hres) && pvarRes) {
723 hres = jsval_to_variant(r, pvarRes);
724 jsval_release(r);
726 break;
728 case DISPATCH_PROPERTYGET: {
729 jsval_t r;
731 hres = prop_get(This, prop, &r);
732 if(SUCCEEDED(hres)) {
733 hres = jsval_to_variant(r, pvarRes);
734 jsval_release(r);
736 break;
738 case DISPATCH_PROPERTYPUT: {
739 jsval_t val;
740 DWORD i;
742 for(i=0; i < pdp->cNamedArgs; i++) {
743 if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
744 break;
747 if(i == pdp->cNamedArgs) {
748 TRACE("no value to set\n");
749 return DISP_E_PARAMNOTOPTIONAL;
752 hres = variant_to_jsval(pdp->rgvarg+i, &val);
753 if(FAILED(hres))
754 return hres;
756 hres = prop_put(This, prop, val);
757 jsval_release(val);
758 break;
760 default:
761 FIXME("Unimplemented flags %x\n", wFlags);
762 return E_INVALIDARG;
765 if(pei)
766 *pei = This->ctx->ei.ei;
767 return hres;
770 static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret)
772 if(!(prop->flags & PROPF_CONFIGURABLE)) {
773 *ret = FALSE;
774 return S_OK;
777 *ret = TRUE; /* FIXME: not exactly right */
779 if(prop->type == PROP_JSVAL) {
780 jsval_release(prop->u.val);
781 prop->type = PROP_DELETED;
783 if(prop->type == PROP_ACCESSOR)
784 FIXME("not supported on accessor property\n");
785 return S_OK;
788 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
790 jsdisp_t *This = impl_from_IDispatchEx(iface);
791 dispex_prop_t *prop;
792 BOOL b;
793 HRESULT hres;
795 TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
797 if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
798 FIXME("Unsupported grfdex %x\n", grfdex);
800 hres = find_prop_name(This, string_hash(bstrName), bstrName, &prop);
801 if(FAILED(hres))
802 return hres;
803 if(!prop) {
804 TRACE("not found\n");
805 return S_OK;
808 return delete_prop(prop, &b);
811 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
813 jsdisp_t *This = impl_from_IDispatchEx(iface);
814 dispex_prop_t *prop;
815 BOOL b;
817 TRACE("(%p)->(%x)\n", This, id);
819 prop = get_prop(This, id);
820 if(!prop) {
821 WARN("invalid id\n");
822 return DISP_E_MEMBERNOTFOUND;
825 return delete_prop(prop, &b);
828 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
830 jsdisp_t *This = impl_from_IDispatchEx(iface);
831 FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
832 return E_NOTIMPL;
835 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
837 jsdisp_t *This = impl_from_IDispatchEx(iface);
838 dispex_prop_t *prop;
840 TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
842 prop = get_prop(This, id);
843 if(!prop || !prop->name || prop->type == PROP_DELETED)
844 return DISP_E_MEMBERNOTFOUND;
846 *pbstrName = SysAllocString(prop->name);
847 if(!*pbstrName)
848 return E_OUTOFMEMORY;
850 return S_OK;
853 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
855 jsdisp_t *This = impl_from_IDispatchEx(iface);
856 dispex_prop_t *iter;
857 HRESULT hres;
859 TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
861 if(id == DISPID_STARTENUM) {
862 hres = fill_protrefs(This);
863 if(FAILED(hres))
864 return hres;
867 if(id+1>=0 && id+1<This->prop_cnt) {
868 iter = &This->props[id+1];
869 }else {
870 *pid = DISPID_STARTENUM;
871 return S_FALSE;
874 while(iter < This->props + This->prop_cnt) {
875 if(iter->name && (get_flags(This, iter) & PROPF_ENUMERABLE) && iter->type!=PROP_DELETED) {
876 *pid = prop_to_id(This, iter);
877 return S_OK;
879 iter++;
882 *pid = DISPID_STARTENUM;
883 return S_FALSE;
886 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
888 jsdisp_t *This = impl_from_IDispatchEx(iface);
889 FIXME("(%p)->(%p)\n", This, ppunk);
890 return E_NOTIMPL;
893 static IDispatchExVtbl DispatchExVtbl = {
894 DispatchEx_QueryInterface,
895 DispatchEx_AddRef,
896 DispatchEx_Release,
897 DispatchEx_GetTypeInfoCount,
898 DispatchEx_GetTypeInfo,
899 DispatchEx_GetIDsOfNames,
900 DispatchEx_Invoke,
901 DispatchEx_GetDispID,
902 DispatchEx_InvokeEx,
903 DispatchEx_DeleteMemberByName,
904 DispatchEx_DeleteMemberByDispID,
905 DispatchEx_GetMemberProperties,
906 DispatchEx_GetMemberName,
907 DispatchEx_GetNextDispID,
908 DispatchEx_GetNameSpaceParent
911 jsdisp_t *as_jsdisp(IDispatch *disp)
913 assert(disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl);
914 return impl_from_IDispatchEx((IDispatchEx*)disp);
917 jsdisp_t *to_jsdisp(IDispatch *disp)
919 return disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl ? impl_from_IDispatchEx((IDispatchEx*)disp) : NULL;
922 HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype)
924 TRACE("%p (%p)\n", dispex, prototype);
926 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
927 dispex->ref = 1;
928 dispex->builtin_info = builtin_info;
930 dispex->props = heap_alloc_zero(sizeof(dispex_prop_t)*(dispex->buf_size=4));
931 if(!dispex->props)
932 return E_OUTOFMEMORY;
934 dispex->prototype = prototype;
935 if(prototype)
936 jsdisp_addref(prototype);
938 dispex->prop_cnt = 1;
939 if(builtin_info->value_prop.invoke || builtin_info->value_prop.getter) {
940 dispex->props[0].type = PROP_BUILTIN;
941 dispex->props[0].u.p = &builtin_info->value_prop;
942 }else {
943 dispex->props[0].type = PROP_DELETED;
946 script_addref(ctx);
947 dispex->ctx = ctx;
949 return S_OK;
952 static const builtin_info_t dispex_info = {
953 JSCLASS_NONE,
954 {NULL, NULL, 0},
955 0, NULL,
956 NULL,
957 NULL
960 HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype, jsdisp_t **dispex)
962 jsdisp_t *ret;
963 HRESULT hres;
965 ret = heap_alloc_zero(sizeof(jsdisp_t));
966 if(!ret)
967 return E_OUTOFMEMORY;
969 hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype);
970 if(FAILED(hres)) {
971 heap_free(ret);
972 return hres;
975 *dispex = ret;
976 return S_OK;
979 void jsdisp_free(jsdisp_t *obj)
981 dispex_prop_t *prop;
983 TRACE("(%p)\n", obj);
985 for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) {
986 switch(prop->type) {
987 case PROP_JSVAL:
988 jsval_release(prop->u.val);
989 break;
990 case PROP_ACCESSOR:
991 if(prop->u.accessor.getter)
992 jsdisp_release(prop->u.accessor.getter);
993 if(prop->u.accessor.setter)
994 jsdisp_release(prop->u.accessor.setter);
995 break;
996 default:
997 break;
999 heap_free(prop->name);
1001 heap_free(obj->props);
1002 script_release(obj->ctx);
1003 if(obj->prototype)
1004 jsdisp_release(obj->prototype);
1006 if(obj->builtin_info->destructor)
1007 obj->builtin_info->destructor(obj);
1008 else
1009 heap_free(obj);
1012 #ifdef TRACE_REFCNT
1014 jsdisp_t *jsdisp_addref(jsdisp_t *jsdisp)
1016 ULONG ref = ++jsdisp->ref;
1017 TRACE("(%p) ref=%d\n", jsdisp, ref);
1018 return jsdisp;
1021 void jsdisp_release(jsdisp_t *jsdisp)
1023 ULONG ref = --jsdisp->ref;
1025 TRACE("(%p) ref=%d\n", jsdisp, ref);
1027 if(!ref)
1028 jsdisp_free(jsdisp);
1031 #endif
1033 HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *constr)
1035 jsdisp_t *prot = NULL;
1036 dispex_prop_t *prop;
1037 HRESULT hres;
1039 static const WCHAR prototypeW[] = {'p','r','o','t','o','t','y','p','e',0};
1041 hres = find_prop_name_prot(constr, string_hash(prototypeW), prototypeW, &prop);
1042 if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) {
1043 jsval_t val;
1045 hres = prop_get(constr, prop, &val);
1046 if(FAILED(hres)) {
1047 ERR("Could not get prototype\n");
1048 return hres;
1051 if(is_object_instance(val))
1052 prot = iface_to_jsdisp(get_object(val));
1053 jsval_release(val);
1056 hres = init_dispex(dispex, ctx, builtin_info, prot);
1058 if(prot)
1059 jsdisp_release(prot);
1060 return hres;
1063 jsdisp_t *iface_to_jsdisp(IDispatch *iface)
1065 return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl
1066 ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface))
1067 : NULL;
1070 HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id)
1072 dispex_prop_t *prop;
1073 HRESULT hres;
1075 if(flags & fdexNameEnsure)
1076 hres = ensure_prop_name(jsdisp, name, TRUE, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE,
1077 &prop);
1078 else
1079 hres = find_prop_name_prot(jsdisp, string_hash(name), name, &prop);
1080 if(FAILED(hres))
1081 return hres;
1083 if(prop && prop->type!=PROP_DELETED) {
1084 *id = prop_to_id(jsdisp, prop);
1085 return S_OK;
1088 TRACE("not found %s\n", debugstr_w(name));
1089 return DISP_E_UNKNOWNNAME;
1092 HRESULT jsdisp_call_value(jsdisp_t *jsfunc, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1094 HRESULT hres;
1096 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
1098 if(is_class(jsfunc, JSCLASS_FUNCTION)) {
1099 hres = Function_invoke(jsfunc, jsthis, flags, argc, argv, r);
1100 }else {
1101 vdisp_t vdisp;
1103 if(!jsfunc->builtin_info->value_prop.invoke) {
1104 WARN("Not a function\n");
1105 return throw_type_error(jsfunc->ctx, JS_E_FUNCTION_EXPECTED, NULL);
1108 set_disp(&vdisp, jsthis);
1109 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1110 hres = jsfunc->builtin_info->value_prop.invoke(jsfunc->ctx, &vdisp, flags, argc, argv, r);
1111 vdisp_release(&vdisp);
1113 return hres;
1116 HRESULT jsdisp_call(jsdisp_t *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1118 dispex_prop_t *prop;
1120 prop = get_prop(disp, id);
1121 if(!prop)
1122 return DISP_E_MEMBERNOTFOUND;
1124 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL);
1127 HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
1129 dispex_prop_t *prop;
1130 HRESULT hres;
1132 hres = find_prop_name_prot(disp, string_hash(name), name, &prop);
1133 if(FAILED(hres))
1134 return hres;
1136 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, NULL);
1139 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret)
1141 IDispatchEx *dispex;
1142 jsdisp_t *jsdisp;
1143 VARIANT buf[6], retv;
1144 DISPPARAMS dp;
1145 unsigned i;
1146 HRESULT hres;
1148 jsdisp = iface_to_jsdisp(disp);
1149 if(jsdisp) {
1150 if(flags & DISPATCH_PROPERTYPUT) {
1151 FIXME("disp_call(propput) on builtin object\n");
1152 return E_FAIL;
1155 if(ctx != jsdisp->ctx)
1156 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1157 hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret);
1158 jsdisp_release(jsdisp);
1159 return hres;
1162 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1163 if(ret && argc)
1164 flags |= DISPATCH_PROPERTYGET;
1166 dp.cArgs = argc;
1168 if(flags & DISPATCH_PROPERTYPUT) {
1169 static DISPID propput_dispid = DISPID_PROPERTYPUT;
1171 dp.cNamedArgs = 1;
1172 dp.rgdispidNamedArgs = &propput_dispid;
1173 }else {
1174 dp.cNamedArgs = 0;
1175 dp.rgdispidNamedArgs = NULL;
1178 if(argc > 6) {
1179 dp.rgvarg = heap_alloc(argc*sizeof(VARIANT));
1180 if(!dp.rgvarg)
1181 return E_OUTOFMEMORY;
1182 }else {
1183 dp.rgvarg = buf;
1186 for(i=0; i<argc; i++) {
1187 hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1);
1188 if(FAILED(hres)) {
1189 while(i--)
1190 VariantClear(dp.rgvarg+argc-i-1);
1191 if(dp.rgvarg != buf)
1192 heap_free(dp.rgvarg);
1193 return hres;
1197 V_VT(&retv) = VT_EMPTY;
1198 clear_ei(ctx);
1199 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1200 if(SUCCEEDED(hres)) {
1201 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei,
1202 &ctx->jscaller->IServiceProvider_iface);
1203 IDispatchEx_Release(dispex);
1204 }else {
1205 UINT err = 0;
1207 if(flags == DISPATCH_CONSTRUCT) {
1208 WARN("IDispatch cannot be constructor\n");
1209 return DISP_E_MEMBERNOTFOUND;
1212 TRACE("using IDispatch\n");
1213 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, ret ? &retv : NULL, &ctx->ei.ei, &err);
1216 for(i=0; i<argc; i++)
1217 VariantClear(dp.rgvarg+argc-i-1);
1218 if(dp.rgvarg != buf)
1219 heap_free(dp.rgvarg);
1220 if(FAILED(hres))
1221 return hres;
1223 if(ret) {
1224 hres = variant_to_jsval(&retv, ret);
1225 VariantClear(&retv);
1228 return hres;
1231 HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, IDispatch *jsthis, WORD flags, unsigned argc, jsval_t *argv,
1232 jsval_t *r)
1234 jsdisp_t *jsdisp;
1235 IDispatchEx *dispex;
1236 VARIANT buf[6], retv;
1237 DISPPARAMS dp;
1238 unsigned i;
1239 HRESULT hres;
1241 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
1243 jsdisp = iface_to_jsdisp(disp);
1244 if(jsdisp) {
1245 if(ctx != jsdisp->ctx)
1246 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1247 hres = jsdisp_call_value(jsdisp, jsthis, flags, argc, argv, r);
1248 jsdisp_release(jsdisp);
1249 return hres;
1252 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
1253 if(r && argc && flags == DISPATCH_METHOD)
1254 flags |= DISPATCH_PROPERTYGET;
1256 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1257 if(FAILED(hres)) {
1258 TRACE("using IDispatch\n");
1259 dispex = NULL;
1260 jsthis = NULL;
1263 if(jsthis) {
1264 static DISPID this_id = DISPID_THIS;
1266 dp.cArgs = argc+1;
1267 dp.cNamedArgs = 1;
1268 dp.rgdispidNamedArgs = &this_id;
1269 }else {
1270 dp.cArgs = argc;
1271 dp.cNamedArgs = 0;
1272 dp.rgdispidNamedArgs = NULL;
1275 if(dp.cArgs > sizeof(buf)/sizeof(*buf)) {
1276 dp.rgvarg = heap_alloc(dp.cArgs*sizeof(VARIANT));
1277 if(!dp.rgvarg) {
1278 if(dispex)
1279 IDispatchEx_Release(dispex);
1280 return E_OUTOFMEMORY;
1282 }else {
1283 dp.rgvarg = buf;
1286 for(i=0; i<argc; i++) {
1287 hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1);
1288 if(FAILED(hres)) {
1289 while(i--)
1290 VariantClear(dp.rgvarg+dp.cArgs-i-1);
1291 if(dp.rgvarg != buf)
1292 heap_free(dp.rgvarg);
1293 if(dispex)
1294 IDispatchEx_Release(dispex);
1295 return hres;
1298 if(jsthis) {
1299 V_VT(dp.rgvarg) = VT_DISPATCH;
1300 V_DISPATCH(dp.rgvarg) = jsthis;
1303 V_VT(&retv) = VT_EMPTY;
1304 clear_ei(ctx);
1305 if(dispex) {
1306 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei,
1307 &ctx->jscaller->IServiceProvider_iface);
1308 IDispatchEx_Release(dispex);
1309 }else {
1310 UINT err = 0;
1312 if(flags == DISPATCH_CONSTRUCT) {
1313 WARN("IDispatch cannot be constructor\n");
1314 return DISP_E_MEMBERNOTFOUND;
1317 hres = IDispatch_Invoke(disp, DISPID_VALUE, &IID_NULL, ctx->lcid, flags, &dp, r ? &retv : NULL, &ctx->ei.ei, &err);
1320 for(i=0; i<argc; i++)
1321 VariantClear(dp.rgvarg+dp.cArgs-i-1);
1322 if(dp.rgvarg != buf)
1323 heap_free(dp.rgvarg);
1324 if(FAILED(hres))
1325 return hres;
1327 if(!r)
1328 return S_OK;
1330 hres = variant_to_jsval(&retv, r);
1331 VariantClear(&retv);
1332 return hres;
1335 HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, jsval_t val)
1337 dispex_prop_t *prop;
1338 HRESULT hres;
1340 hres = ensure_prop_name(obj, name, FALSE, flags, &prop);
1341 if(FAILED(hres))
1342 return hres;
1344 return prop_put(obj, prop, val);
1347 HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val)
1349 return jsdisp_propput(obj, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, val);
1352 HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val)
1354 WCHAR buf[12];
1356 static const WCHAR formatW[] = {'%','d',0};
1358 sprintfW(buf, formatW, idx);
1359 return jsdisp_propput_name(obj, buf, val);
1362 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val)
1364 jsdisp_t *jsdisp;
1365 HRESULT hres;
1367 jsdisp = iface_to_jsdisp(disp);
1368 if(jsdisp) {
1369 dispex_prop_t *prop;
1371 prop = get_prop(jsdisp, id);
1372 if(prop)
1373 hres = prop_put(jsdisp, prop, val);
1374 else
1375 hres = DISP_E_MEMBERNOTFOUND;
1377 jsdisp_release(jsdisp);
1378 }else {
1379 DISPID dispid = DISPID_PROPERTYPUT;
1380 DWORD flags = DISPATCH_PROPERTYPUT;
1381 VARIANT var;
1382 DISPPARAMS dp = {&var, &dispid, 1, 1};
1383 IDispatchEx *dispex;
1385 hres = jsval_to_variant(val, &var);
1386 if(FAILED(hres))
1387 return hres;
1389 if(V_VT(&var) == VT_DISPATCH)
1390 flags |= DISPATCH_PROPERTYPUTREF;
1392 clear_ei(ctx);
1393 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1394 if(SUCCEEDED(hres)) {
1395 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei,
1396 &ctx->jscaller->IServiceProvider_iface);
1397 IDispatchEx_Release(dispex);
1398 }else {
1399 ULONG err = 0;
1401 TRACE("using IDispatch\n");
1402 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, &dp, NULL, &ctx->ei.ei, &err);
1405 VariantClear(&var);
1408 return hres;
1411 HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val)
1413 dispex_prop_t *prop;
1414 HRESULT hres;
1416 hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
1417 if(FAILED(hres))
1418 return hres;
1420 if(!prop || prop->type==PROP_DELETED) {
1421 *val = jsval_undefined();
1422 return S_OK;
1425 return prop_get(obj, prop, val);
1428 HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r)
1430 WCHAR name[12];
1431 dispex_prop_t *prop;
1432 HRESULT hres;
1434 static const WCHAR formatW[] = {'%','d',0};
1436 sprintfW(name, formatW, idx);
1438 hres = find_prop_name_prot(obj, string_hash(name), name, &prop);
1439 if(FAILED(hres))
1440 return hres;
1442 if(!prop || prop->type==PROP_DELETED) {
1443 *r = jsval_undefined();
1444 return DISP_E_UNKNOWNNAME;
1447 return prop_get(obj, prop, r);
1450 HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val)
1452 dispex_prop_t *prop;
1454 prop = get_prop(jsdisp, id);
1455 if(!prop)
1456 return DISP_E_MEMBERNOTFOUND;
1458 return prop_get(jsdisp, prop, val);
1461 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val)
1463 DISPPARAMS dp = {NULL,NULL,0,0};
1464 IDispatchEx *dispex;
1465 jsdisp_t *jsdisp;
1466 VARIANT var;
1467 HRESULT hres;
1469 jsdisp = iface_to_jsdisp(disp);
1470 if(jsdisp) {
1471 hres = jsdisp_propget(jsdisp, id, val);
1472 jsdisp_release(jsdisp);
1473 return hres;
1476 V_VT(&var) = VT_EMPTY;
1477 clear_ei(ctx);
1478 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1479 if(SUCCEEDED(hres)) {
1480 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei,
1481 &ctx->jscaller->IServiceProvider_iface);
1482 IDispatchEx_Release(dispex);
1483 }else {
1484 ULONG err = 0;
1486 TRACE("using IDispatch\n");
1487 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, INVOKE_PROPERTYGET, &dp, &var, &ctx->ei.ei, &err);
1489 if(FAILED(hres))
1490 return hres;
1492 hres = variant_to_jsval(&var, val);
1493 VariantClear(&var);
1494 return hres;
1497 HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
1499 static const WCHAR formatW[] = {'%','d',0};
1500 WCHAR buf[12];
1501 dispex_prop_t *prop;
1502 BOOL b;
1503 HRESULT hres;
1505 sprintfW(buf, formatW, idx);
1507 hres = find_prop_name(obj, string_hash(buf), buf, &prop);
1508 if(FAILED(hres) || !prop)
1509 return hres;
1511 return delete_prop(prop, &b);
1514 HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret)
1516 IDispatchEx *dispex;
1517 jsdisp_t *jsdisp;
1518 HRESULT hres;
1520 jsdisp = iface_to_jsdisp(disp);
1521 if(jsdisp) {
1522 dispex_prop_t *prop;
1524 prop = get_prop(jsdisp, id);
1525 if(prop)
1526 hres = delete_prop(prop, ret);
1527 else
1528 hres = DISP_E_MEMBERNOTFOUND;
1530 jsdisp_release(jsdisp);
1531 return hres;
1534 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1535 if(FAILED(hres)) {
1536 *ret = FALSE;
1537 return S_OK;
1540 hres = IDispatchEx_DeleteMemberByDispID(dispex, id);
1541 IDispatchEx_Release(dispex);
1542 if(FAILED(hres))
1543 return hres;
1545 *ret = hres == S_OK;
1546 return S_OK;
1549 HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret)
1551 IDispatchEx *dispex;
1552 jsdisp_t *jsdisp;
1553 BSTR bstr;
1554 HRESULT hres;
1556 jsdisp = iface_to_jsdisp(disp);
1557 if(jsdisp) {
1558 dispex_prop_t *prop;
1559 const WCHAR *ptr;
1561 ptr = jsstr_flatten(name);
1562 if(!ptr) {
1563 jsdisp_release(jsdisp);
1564 return E_OUTOFMEMORY;
1567 hres = find_prop_name(jsdisp, string_hash(ptr), ptr, &prop);
1568 if(prop) {
1569 hres = delete_prop(prop, ret);
1570 }else {
1571 *ret = TRUE;
1572 hres = S_OK;
1575 jsdisp_release(jsdisp);
1576 return hres;
1579 bstr = SysAllocStringLen(NULL, jsstr_length(name));
1580 if(!bstr)
1581 return E_OUTOFMEMORY;
1582 jsstr_flush(name, bstr);
1584 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1585 if(SUCCEEDED(hres)) {
1586 hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive));
1587 if(SUCCEEDED(hres))
1588 *ret = hres == S_OK;
1589 IDispatchEx_Release(dispex);
1590 }else {
1591 DISPID id;
1593 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
1594 if(SUCCEEDED(hres)) {
1595 /* Property exists and we can't delete it from pure IDispatch interface, so return false. */
1596 *ret = FALSE;
1597 }else if(hres == DISP_E_UNKNOWNNAME) {
1598 /* Property doesn't exist, so nothing to delete */
1599 *ret = TRUE;
1600 hres = S_OK;
1604 SysFreeString(bstr);
1605 return hres;
1608 HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_only,
1609 property_desc_t *desc)
1611 dispex_prop_t *prop;
1612 HRESULT hres;
1614 hres = find_prop_name(obj, string_hash(name), name, &prop);
1615 if(FAILED(hres))
1616 return hres;
1618 if(!prop)
1619 return DISP_E_UNKNOWNNAME;
1621 memset(desc, 0, sizeof(*desc));
1623 switch(prop->type) {
1624 case PROP_BUILTIN:
1625 case PROP_JSVAL:
1626 desc->mask |= PROPF_WRITABLE;
1627 desc->explicit_value = TRUE;
1628 if(!flags_only) {
1629 hres = prop_get(obj, prop, &desc->value);
1630 if(FAILED(hres))
1631 return hres;
1633 break;
1634 case PROP_ACCESSOR:
1635 desc->explicit_getter = desc->explicit_setter = TRUE;
1636 if(!flags_only) {
1637 desc->getter = prop->u.accessor.getter
1638 ? jsdisp_addref(prop->u.accessor.getter) : NULL;
1639 desc->setter = prop->u.accessor.setter
1640 ? jsdisp_addref(prop->u.accessor.setter) : NULL;
1642 break;
1643 default:
1644 return DISP_E_UNKNOWNNAME;
1647 desc->flags = prop->flags & (PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE);
1648 desc->mask |= PROPF_ENUMERABLE | PROPF_CONFIGURABLE;
1649 return S_OK;
1652 HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t *desc)
1654 dispex_prop_t *prop;
1655 HRESULT hres;
1657 hres = find_prop_name(obj, string_hash(name), name, &prop);
1658 if(FAILED(hres))
1659 return hres;
1661 if(!prop && !(prop = alloc_prop(obj, name, PROP_DELETED, 0)))
1662 return E_OUTOFMEMORY;
1664 if(prop->type == PROP_DELETED || prop->type == PROP_PROTREF) {
1665 prop->flags = desc->flags;
1666 if(desc->explicit_getter || desc->explicit_setter) {
1667 prop->type = PROP_ACCESSOR;
1668 prop->u.accessor.getter = desc->getter ? jsdisp_addref(desc->getter) : NULL;
1669 prop->u.accessor.setter = desc->setter ? jsdisp_addref(desc->setter) : NULL;
1670 TRACE("%s = accessor { get: %p set: %p }\n", debugstr_w(name),
1671 prop->u.accessor.getter, prop->u.accessor.setter);
1672 }else {
1673 prop->type = PROP_JSVAL;
1674 if(desc->explicit_value) {
1675 hres = jsval_copy(desc->value, &prop->u.val);
1676 if(FAILED(hres))
1677 return hres;
1678 }else {
1679 prop->u.val = jsval_undefined();
1681 TRACE("%s = %s\n", debugstr_w(name), debugstr_jsval(prop->u.val));
1683 return S_OK;
1686 TRACE("existing prop %s prop flags %x desc flags %x desc mask %x\n", debugstr_w(name),
1687 prop->flags, desc->flags, desc->mask);
1689 if(!(prop->flags & PROPF_CONFIGURABLE)) {
1690 if(((desc->mask & PROPF_CONFIGURABLE) && (desc->flags & PROPF_CONFIGURABLE))
1691 || ((desc->mask & PROPF_ENUMERABLE)
1692 && ((desc->flags & PROPF_ENUMERABLE) != (prop->flags & PROPF_ENUMERABLE))))
1693 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
1696 if(desc->explicit_value || (desc->mask & PROPF_WRITABLE)) {
1697 if(prop->type == PROP_ACCESSOR) {
1698 if(!(prop->flags & PROPF_CONFIGURABLE))
1699 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
1700 if(prop->u.accessor.getter)
1701 jsdisp_release(prop->u.accessor.getter);
1702 if(prop->u.accessor.setter)
1703 jsdisp_release(prop->u.accessor.setter);
1705 prop->type = PROP_JSVAL;
1706 hres = jsval_copy(desc->value, &prop->u.val);
1707 if(FAILED(hres)) {
1708 prop->u.val = jsval_undefined();
1709 return hres;
1711 }else {
1712 if(!(prop->flags & PROPF_CONFIGURABLE) && !(prop->flags & PROPF_WRITABLE)) {
1713 if((desc->mask & PROPF_WRITABLE) && (desc->flags & PROPF_WRITABLE))
1714 return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
1715 if(desc->explicit_value) {
1716 if(prop->type == PROP_JSVAL) {
1717 BOOL eq;
1718 hres = jsval_strict_equal(desc->value, prop->u.val, &eq);
1719 if(FAILED(hres))
1720 return hres;
1721 if(!eq)
1722 return throw_type_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
1723 }else {
1724 FIXME("redefinition of property type %d\n", prop->type);
1728 if(desc->explicit_value) {
1729 if(prop->type == PROP_JSVAL)
1730 jsval_release(prop->u.val);
1731 else
1732 prop->type = PROP_JSVAL;
1733 hres = jsval_copy(desc->value, &prop->u.val);
1734 if(FAILED(hres)) {
1735 prop->u.val = jsval_undefined();
1736 return hres;
1740 }else if(desc->explicit_getter || desc->explicit_setter) {
1741 if(prop->type != PROP_ACCESSOR) {
1742 if(!(prop->flags & PROPF_CONFIGURABLE))
1743 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
1744 if(prop->type == PROP_JSVAL)
1745 jsval_release(prop->u.val);
1746 prop->type = PROP_ACCESSOR;
1747 prop->u.accessor.getter = prop->u.accessor.setter = NULL;
1748 }else if(!(prop->flags & PROPF_CONFIGURABLE)) {
1749 if((desc->explicit_getter && desc->getter != prop->u.accessor.getter)
1750 || (desc->explicit_setter && desc->setter != prop->u.accessor.setter))
1751 return throw_type_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
1754 if(desc->explicit_getter) {
1755 if(prop->u.accessor.getter) {
1756 jsdisp_release(prop->u.accessor.getter);
1757 prop->u.accessor.getter = NULL;
1759 if(desc->getter)
1760 prop->u.accessor.getter = jsdisp_addref(desc->getter);
1762 if(desc->explicit_setter) {
1763 if(prop->u.accessor.setter) {
1764 jsdisp_release(prop->u.accessor.setter);
1765 prop->u.accessor.setter = NULL;
1767 if(desc->setter)
1768 prop->u.accessor.setter = jsdisp_addref(desc->setter);
1772 prop->flags = (prop->flags & ~desc->mask) | (desc->flags & desc->mask);
1773 return S_OK;
1776 HRESULT jsdisp_define_data_property(jsdisp_t *obj, const WCHAR *name, unsigned flags, jsval_t value)
1778 property_desc_t prop_desc = { flags, flags, TRUE, value };
1779 return jsdisp_define_property(obj, name, &prop_desc);