wineps.drv: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / jscript / dispex.c
blobb5fc369921250c0d9cecab0db793dafa9160967d
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 void fix_protref_prop(jsdisp_t *jsdisp, dispex_prop_t *prop)
65 DWORD ref;
67 if(prop->type != PROP_PROTREF)
68 return;
69 ref = prop->u.ref;
71 while((jsdisp = jsdisp->prototype)) {
72 if(ref >= jsdisp->prop_cnt || jsdisp->props[ref].type == PROP_DELETED)
73 break;
74 if(jsdisp->props[ref].type != PROP_PROTREF)
75 return;
76 ref = jsdisp->props[ref].u.ref;
78 prop->type = PROP_DELETED;
81 static inline DISPID prop_to_id(jsdisp_t *This, dispex_prop_t *prop)
83 /* don't overlap with DISPID_VALUE */
84 return prop - This->props + 1;
87 static inline dispex_prop_t *get_prop(jsdisp_t *This, DISPID id)
89 DWORD idx = id - 1;
91 if(idx >= This->prop_cnt)
92 return NULL;
93 fix_protref_prop(This, &This->props[idx]);
95 return This->props[idx].type == PROP_DELETED ? NULL : &This->props[idx];
98 static inline BOOL is_function_prop(dispex_prop_t *prop)
100 BOOL ret = FALSE;
102 if (is_object_instance(prop->u.val))
104 jsdisp_t *jsdisp = to_jsdisp(get_object(prop->u.val));
106 if (jsdisp) ret = is_class(jsdisp, JSCLASS_FUNCTION);
108 return ret;
111 static DWORD get_flags(jsdisp_t *This, dispex_prop_t *prop)
113 if(prop->type == PROP_PROTREF) {
114 dispex_prop_t *parent = NULL;
116 if(prop->u.ref < This->prototype->prop_cnt)
117 parent = &This->prototype->props[prop->u.ref];
119 if(!parent || parent->type == PROP_DELETED) {
120 prop->type = PROP_DELETED;
121 return 0;
124 return get_flags(This->prototype, parent);
127 return prop->flags;
130 static const builtin_prop_t *find_builtin_prop(jsdisp_t *This, const WCHAR *name, BOOL case_insens)
132 int min = 0, max = This->builtin_info->props_cnt-1, i, r;
133 unsigned version;
135 if(case_insens) {
136 for(i = min; i <= max; i++)
137 if(!wcsicmp(name, This->builtin_info->props[i].name))
138 goto found;
139 return NULL;
142 while(min <= max) {
143 i = (min+max)/2;
145 r = wcscmp(name, This->builtin_info->props[i].name);
146 if(!r)
147 goto found;
149 if(r < 0)
150 max = i-1;
151 else
152 min = i+1;
155 return NULL;
157 found:
158 /* Skip prop if it's available only in higher compatibility mode. */
159 version = (This->builtin_info->props[i].flags & PROPF_VERSION_MASK) >> PROPF_VERSION_SHIFT;
160 if(version && version > This->ctx->version)
161 return NULL;
163 /* Skip prop if it's available only in HTML mode and we're not running in HTML mode. */
164 if((This->builtin_info->props[i].flags & PROPF_HTML) && !This->ctx->html_mode)
165 return NULL;
167 return This->builtin_info->props + i;
170 static inline unsigned string_hash(const WCHAR *name)
172 unsigned h = 0;
173 for(; *name; name++)
174 h = (h>>(sizeof(unsigned)*8-4)) ^ (h<<4) ^ towlower(*name);
175 return h;
178 static inline unsigned get_props_idx(jsdisp_t *This, unsigned hash)
180 return (hash*GOLDEN_RATIO) & (This->buf_size-1);
183 static inline HRESULT resize_props(jsdisp_t *This)
185 dispex_prop_t *props;
186 int i, bucket;
188 if(This->buf_size != This->prop_cnt)
189 return S_FALSE;
191 props = realloc(This->props, sizeof(dispex_prop_t) * This->buf_size * 2);
192 if(!props)
193 return E_OUTOFMEMORY;
194 This->buf_size *= 2;
195 This->props = props;
197 for(i=0; i<This->buf_size; i++) {
198 This->props[i].bucket_head = ~0;
199 This->props[i].bucket_next = ~0;
202 for(i=0; i<This->prop_cnt; i++) {
203 props = This->props+i;
205 bucket = get_props_idx(This, props->hash);
206 props->bucket_next = This->props[bucket].bucket_head;
207 This->props[bucket].bucket_head = i;
210 return S_OK;
213 static inline dispex_prop_t* alloc_prop(jsdisp_t *This, const WCHAR *name, prop_type_t type, DWORD flags)
215 dispex_prop_t *prop;
216 unsigned bucket;
218 if(FAILED(resize_props(This)))
219 return NULL;
221 prop = &This->props[This->prop_cnt];
222 prop->name = wcsdup(name);
223 if(!prop->name)
224 return NULL;
225 prop->type = type;
226 prop->flags = flags;
227 prop->hash = string_hash(name);
229 bucket = get_props_idx(This, prop->hash);
230 prop->bucket_next = This->props[bucket].bucket_head;
231 This->props[bucket].bucket_head = This->prop_cnt++;
232 return prop;
235 static dispex_prop_t *alloc_protref(jsdisp_t *This, const WCHAR *name, DWORD ref)
237 dispex_prop_t *ret;
239 ret = alloc_prop(This, name, PROP_PROTREF, 0);
240 if(!ret)
241 return NULL;
243 ret->u.ref = ref;
244 return ret;
247 static HRESULT find_prop_name(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret)
249 const builtin_prop_t *builtin;
250 unsigned bucket, pos, prev = ~0;
251 dispex_prop_t *prop;
252 HRESULT hres;
254 bucket = get_props_idx(This, hash);
255 pos = This->props[bucket].bucket_head;
256 while(pos != ~0) {
257 if(case_insens ? !wcsicmp(name, This->props[pos].name) : !wcscmp(name, This->props[pos].name)) {
258 if(prev != ~0) {
259 This->props[prev].bucket_next = This->props[pos].bucket_next;
260 This->props[pos].bucket_next = This->props[bucket].bucket_head;
261 This->props[bucket].bucket_head = pos;
264 *ret = &This->props[pos];
265 return S_OK;
268 prev = pos;
269 pos = This->props[pos].bucket_next;
272 builtin = find_builtin_prop(This, name, case_insens);
273 if(builtin) {
274 unsigned flags = builtin->flags;
275 if(flags & PROPF_METHOD) {
276 jsdisp_t *obj;
278 hres = create_builtin_function(This->ctx, builtin->invoke, builtin->name, NULL, flags, NULL, &obj);
279 if(FAILED(hres))
280 return hres;
282 prop = alloc_prop(This, builtin->name, PROP_JSVAL, (flags & PROPF_ALL) | PROPF_WRITABLE | PROPF_CONFIGURABLE);
283 if(!prop) {
284 jsdisp_release(obj);
285 return E_OUTOFMEMORY;
288 prop->type = PROP_JSVAL;
289 prop->u.val = jsval_obj(obj);
290 *ret = prop;
291 return S_OK;
292 }else if(builtin->setter)
293 flags |= PROPF_WRITABLE;
294 flags &= PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE;
295 prop = alloc_prop(This, builtin->name, PROP_BUILTIN, flags);
296 if(!prop)
297 return E_OUTOFMEMORY;
299 prop->u.p = builtin;
300 *ret = prop;
301 return S_OK;
304 if(This->builtin_info->idx_length) {
305 const WCHAR *ptr;
306 unsigned idx = 0;
308 for(ptr = name; is_digit(*ptr) && idx < 0x10000; ptr++)
309 idx = idx*10 + (*ptr-'0');
310 if(!*ptr && idx < This->builtin_info->idx_length(This)) {
311 unsigned flags = PROPF_ENUMERABLE;
312 if(This->builtin_info->idx_put)
313 flags |= PROPF_WRITABLE;
314 prop = alloc_prop(This, name, PROP_IDX, flags);
315 if(!prop)
316 return E_OUTOFMEMORY;
318 prop->u.idx = idx;
319 *ret = prop;
320 return S_OK;
324 *ret = NULL;
325 return S_OK;
328 static HRESULT find_prop_name_prot(jsdisp_t *This, unsigned hash, const WCHAR *name, BOOL case_insens, dispex_prop_t **ret)
330 dispex_prop_t *prop, *del=NULL;
331 HRESULT hres;
333 hres = find_prop_name(This, hash, name, case_insens, &prop);
334 if(FAILED(hres))
335 return hres;
336 if(prop && prop->type==PROP_DELETED) {
337 del = prop;
338 } else if(prop) {
339 fix_protref_prop(This, prop);
340 *ret = prop;
341 return S_OK;
344 if(This->prototype) {
345 hres = find_prop_name_prot(This->prototype, hash, name, case_insens, &prop);
346 if(FAILED(hres))
347 return hres;
348 if(prop && prop->type != PROP_DELETED) {
349 if(del) {
350 del->type = PROP_PROTREF;
351 del->u.ref = prop - This->prototype->props;
352 prop = del;
353 }else {
354 prop = alloc_protref(This, prop->name, prop - This->prototype->props);
355 if(!prop)
356 return E_OUTOFMEMORY;
359 *ret = prop;
360 return S_OK;
364 *ret = del;
365 return S_OK;
368 static HRESULT ensure_prop_name(jsdisp_t *This, const WCHAR *name, DWORD create_flags, BOOL case_insens, dispex_prop_t **ret)
370 dispex_prop_t *prop;
371 HRESULT hres;
373 hres = find_prop_name_prot(This, string_hash(name), name, case_insens, &prop);
374 if(SUCCEEDED(hres) && (!prop || prop->type == PROP_DELETED)) {
375 TRACE("creating prop %s flags %lx\n", debugstr_w(name), create_flags);
377 if(prop) {
378 prop->type = PROP_JSVAL;
379 prop->flags = create_flags;
380 prop->u.val = jsval_undefined();
381 }else {
382 prop = alloc_prop(This, name, PROP_JSVAL, create_flags);
383 if(!prop)
384 return E_OUTOFMEMORY;
387 prop->u.val = jsval_undefined();
390 *ret = prop;
391 return hres;
394 static IDispatch *get_this(DISPPARAMS *dp)
396 DWORD i;
398 for(i=0; i < dp->cNamedArgs; i++) {
399 if(dp->rgdispidNamedArgs[i] == DISPID_THIS) {
400 if(V_VT(dp->rgvarg+i) == VT_DISPATCH)
401 return V_DISPATCH(dp->rgvarg+i);
403 WARN("This is not VT_DISPATCH\n");
404 return NULL;
408 TRACE("no this passed\n");
409 return NULL;
412 static HRESULT convert_params(script_ctx_t *ctx, const DISPPARAMS *dp, jsval_t *buf, unsigned *argc, jsval_t **ret)
414 jsval_t *argv;
415 unsigned cnt;
416 unsigned i;
417 HRESULT hres;
419 cnt = dp->cArgs - dp->cNamedArgs;
421 if(cnt > 6) {
422 argv = malloc(cnt * sizeof(*argv));
423 if(!argv)
424 return E_OUTOFMEMORY;
425 }else {
426 argv = buf;
429 for(i = 0; i < cnt; i++) {
430 hres = variant_to_jsval(ctx, dp->rgvarg+dp->cArgs-i-1, argv+i);
431 if(FAILED(hres)) {
432 while(i--)
433 jsval_release(argv[i]);
434 if(argv != buf)
435 free(argv);
436 return hres;
440 *argc = cnt;
441 *ret = argv;
442 return S_OK;
445 static HRESULT prop_get(jsdisp_t *This, dispex_prop_t *prop, jsval_t *r)
447 jsdisp_t *prop_obj = This;
448 HRESULT hres;
450 while(prop->type == PROP_PROTREF) {
451 prop_obj = prop_obj->prototype;
452 prop = prop_obj->props + prop->u.ref;
455 switch(prop->type) {
456 case PROP_BUILTIN:
457 hres = prop->u.p->getter(This->ctx, This, r);
458 break;
459 case PROP_JSVAL:
460 hres = jsval_copy(prop->u.val, r);
461 break;
462 case PROP_ACCESSOR:
463 if(prop->u.accessor.getter) {
464 hres = jsdisp_call_value(prop->u.accessor.getter, jsval_obj(This),
465 DISPATCH_METHOD, 0, NULL, r);
466 }else {
467 *r = jsval_undefined();
468 hres = S_OK;
470 break;
471 case PROP_IDX:
472 hres = prop_obj->builtin_info->idx_get(prop_obj, prop->u.idx, r);
473 break;
474 default:
475 ERR("type %d\n", prop->type);
476 return E_FAIL;
479 if(FAILED(hres)) {
480 TRACE("fail %08lx\n", hres);
481 return hres;
484 TRACE("%p.%s ret %s\n", This, debugstr_w(prop->name), debugstr_jsval(*r));
485 return hres;
488 static HRESULT prop_put(jsdisp_t *This, dispex_prop_t *prop, jsval_t val)
490 HRESULT hres;
492 if(prop->type == PROP_PROTREF) {
493 dispex_prop_t *prop_iter = prop;
494 jsdisp_t *prototype_iter = This;
496 do {
497 prototype_iter = prototype_iter->prototype;
498 prop_iter = prototype_iter->props + prop_iter->u.ref;
499 } while(prop_iter->type == PROP_PROTREF);
501 if(prop_iter->type == PROP_ACCESSOR)
502 prop = prop_iter;
505 switch(prop->type) {
506 case PROP_BUILTIN:
507 if(!prop->u.p->setter) {
508 TRACE("getter with no setter\n");
509 return S_OK;
511 return prop->u.p->setter(This->ctx, This, val);
512 case PROP_PROTREF:
513 case PROP_DELETED:
514 if(!This->extensible)
515 return S_OK;
516 prop->type = PROP_JSVAL;
517 prop->flags = PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE;
518 prop->u.val = jsval_undefined();
519 break;
520 case PROP_JSVAL:
521 if(!(prop->flags & PROPF_WRITABLE))
522 return S_OK;
524 jsval_release(prop->u.val);
525 break;
526 case PROP_ACCESSOR:
527 if(!prop->u.accessor.setter) {
528 TRACE("no setter\n");
529 return S_OK;
531 return jsdisp_call_value(prop->u.accessor.setter, jsval_obj(This), DISPATCH_METHOD, 1, &val, NULL);
532 case PROP_IDX:
533 if(!This->builtin_info->idx_put) {
534 TRACE("no put_idx\n");
535 return S_OK;
537 return This->builtin_info->idx_put(This, prop->u.idx, val);
538 default:
539 ERR("type %d\n", prop->type);
540 return E_FAIL;
543 TRACE("%p.%s = %s\n", This, debugstr_w(prop->name), debugstr_jsval(val));
545 hres = jsval_copy(val, &prop->u.val);
546 if(FAILED(hres))
547 return hres;
549 if(This->builtin_info->on_put)
550 This->builtin_info->on_put(This, prop->name);
552 return S_OK;
555 static HRESULT invoke_prop_func(jsdisp_t *This, IDispatch *jsthis, dispex_prop_t *prop, WORD flags,
556 unsigned argc, jsval_t *argv, jsval_t *r, IServiceProvider *caller)
558 HRESULT hres;
560 switch(prop->type) {
561 case PROP_BUILTIN:
562 return JS_E_FUNCTION_EXPECTED;
563 case PROP_PROTREF:
564 return invoke_prop_func(This->prototype, jsthis ? jsthis : (IDispatch *)&This->IDispatchEx_iface,
565 This->prototype->props+prop->u.ref, flags, argc, argv, r, caller);
566 case PROP_JSVAL: {
567 if(!is_object_instance(prop->u.val)) {
568 FIXME("invoke %s\n", debugstr_jsval(prop->u.val));
569 return E_FAIL;
572 TRACE("call %s %p\n", debugstr_w(prop->name), get_object(prop->u.val));
574 return disp_call_value_with_caller(This->ctx, get_object(prop->u.val),
575 jsval_disp(jsthis ? jsthis : (IDispatch*)&This->IDispatchEx_iface),
576 flags, argc, argv, r, caller);
578 case PROP_ACCESSOR:
579 case PROP_IDX: {
580 jsval_t val;
582 hres = prop_get(This, prop, &val);
583 if(FAILED(hres))
584 return hres;
586 if(is_object_instance(val)) {
587 hres = disp_call_value_with_caller(This->ctx, get_object(val),
588 jsval_disp(jsthis ? jsthis : (IDispatch*)&This->IDispatchEx_iface),
589 flags, argc, argv, r, caller);
590 }else {
591 FIXME("invoke %s\n", debugstr_jsval(val));
592 hres = E_NOTIMPL;
595 jsval_release(val);
596 return hres;
598 case PROP_DELETED:
599 assert(0);
600 break;
603 return E_FAIL;
606 HRESULT builtin_set_const(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
608 TRACE("%p %s\n", jsthis, debugstr_jsval(value));
609 return S_OK;
612 static HRESULT fill_props(jsdisp_t *obj)
614 dispex_prop_t *prop;
615 HRESULT hres;
617 if(obj->builtin_info->idx_length) {
618 unsigned i = 0, len = obj->builtin_info->idx_length(obj);
619 WCHAR name[12];
621 for(i = 0; i < len; i++) {
622 swprintf(name, ARRAY_SIZE(name), L"%u", i);
623 hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop);
624 if(FAILED(hres))
625 return hres;
629 return S_OK;
632 static HRESULT fill_protrefs(jsdisp_t *This)
634 dispex_prop_t *iter, *prop;
635 HRESULT hres;
637 hres = fill_props(This);
638 if(FAILED(hres))
639 return hres;
641 if(!This->prototype)
642 return S_OK;
644 hres = fill_protrefs(This->prototype);
645 if(FAILED(hres))
646 return hres;
648 for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) {
649 hres = find_prop_name(This, iter->hash, iter->name, FALSE, &prop);
650 if(FAILED(hres))
651 return hres;
652 if(!prop || prop->type==PROP_DELETED) {
653 if(prop) {
654 prop->type = PROP_PROTREF;
655 prop->flags = 0;
656 prop->u.ref = iter - This->prototype->props;
657 }else {
658 prop = alloc_protref(This, iter->name, iter - This->prototype->props);
659 if(!prop)
660 return E_OUTOFMEMORY;
665 return S_OK;
668 static void unlink_props(jsdisp_t *jsdisp)
670 dispex_prop_t *prop = jsdisp->props, *end;
672 for(end = prop + jsdisp->prop_cnt; prop < end; prop++) {
673 switch(prop->type) {
674 case PROP_DELETED:
675 case PROP_PROTREF:
676 continue;
677 case PROP_JSVAL:
678 jsval_release(prop->u.val);
679 break;
680 case PROP_ACCESSOR:
681 if(prop->u.accessor.getter)
682 jsdisp_release(prop->u.accessor.getter);
683 if(prop->u.accessor.setter)
684 jsdisp_release(prop->u.accessor.setter);
685 break;
686 default:
687 break;
689 prop->type = PROP_DELETED;
696 * To deal with circular refcounts, a basic Garbage Collector is used with a variant of the
697 * mark-and-sweep algorithm that doesn't require knowing or traversing any specific "roots".
698 * This works based on the assumption that circular references can only happen when objects
699 * end up pointing to each other, and each other alone, without any external refs.
701 * An "external ref" is a ref to the object that's not from any other object. Example of such
702 * refs can be local variables, the script ctx (which keeps a ref to the global object), etc.
704 * At a high level, there are 3 logical passes done on the entire list of objects:
706 * 1. Speculatively decrease refcounts of each linked-to-object from each object. This ensures
707 * that the only remaining refcount on each object is the number of "external refs" to it.
708 * At the same time, mark all of the objects so that they can be potentially collected.
710 * 2. For each object with a non-zero "external refcount", clear the mark from step 1, and
711 * recursively traverse all linked objects from it, clearing their marks as well (regardless
712 * of their refcount), stopping a given path when the object is unmarked (and then going back
713 * up the GC stack). This basically unmarks all of the objects with "external refcounts"
714 * and those accessible from them, and only the leaked dangling objects will still be marked.
716 * 3. For each object that is marked, unlink all of the objects linked from it, because they
717 * are dangling in a circular refcount and not accessible. This should release them.
719 * During unlinking (GC_TRAVERSE_UNLINK), it is important that we unlink *all* linked objects
720 * from the object, to be certain that releasing the object later will not delete any other
721 * objects. Otherwise calculating the "next" object in the list becomes impossible.
723 * This collection process has to be done periodically, but can be pretty expensive so there
724 * has to be a balance between reclaiming dangling objects and performance.
727 struct gc_stack_chunk {
728 jsdisp_t *objects[1020];
729 struct gc_stack_chunk *prev;
732 struct gc_ctx {
733 struct gc_stack_chunk *chunk;
734 struct gc_stack_chunk *next;
735 unsigned idx;
738 static HRESULT gc_stack_push(struct gc_ctx *gc_ctx, jsdisp_t *obj)
740 if(!gc_ctx->idx) {
741 if(gc_ctx->next)
742 gc_ctx->chunk = gc_ctx->next;
743 else {
744 struct gc_stack_chunk *prev, *tmp = malloc(sizeof(*tmp));
745 if(!tmp)
746 return E_OUTOFMEMORY;
747 prev = gc_ctx->chunk;
748 gc_ctx->chunk = tmp;
749 gc_ctx->chunk->prev = prev;
751 gc_ctx->idx = ARRAY_SIZE(gc_ctx->chunk->objects);
752 gc_ctx->next = NULL;
754 gc_ctx->chunk->objects[--gc_ctx->idx] = obj;
755 return S_OK;
758 static jsdisp_t *gc_stack_pop(struct gc_ctx *gc_ctx)
760 jsdisp_t *obj = gc_ctx->chunk->objects[gc_ctx->idx];
762 if(++gc_ctx->idx == ARRAY_SIZE(gc_ctx->chunk->objects)) {
763 free(gc_ctx->next);
764 gc_ctx->next = gc_ctx->chunk;
765 gc_ctx->chunk = gc_ctx->chunk->prev;
766 gc_ctx->idx = 0;
768 return obj;
771 HRESULT gc_run(script_ctx_t *ctx)
773 /* Save original refcounts in a linked list of chunks */
774 struct chunk
776 struct chunk *next;
777 LONG ref[1020];
778 } *head, *chunk;
779 jsdisp_t *obj, *obj2, *link, *link2;
780 dispex_prop_t *prop, *props_end;
781 struct gc_ctx gc_ctx = { 0 };
782 unsigned chunk_idx = 0;
783 HRESULT hres = S_OK;
784 struct list *iter;
786 /* Prevent recursive calls from side-effects during unlinking (e.g. CollectGarbage from host object's Release) */
787 if(ctx->gc_is_unlinking)
788 return S_OK;
790 if(!(head = malloc(sizeof(*head))))
791 return E_OUTOFMEMORY;
792 head->next = NULL;
793 chunk = head;
795 /* 1. Save actual refcounts and decrease them speculatively as-if we unlinked the objects */
796 LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) {
797 if(chunk_idx == ARRAY_SIZE(chunk->ref)) {
798 if(!(chunk->next = malloc(sizeof(*chunk)))) {
799 do {
800 chunk = head->next;
801 free(head);
802 head = chunk;
803 } while(head);
804 return E_OUTOFMEMORY;
806 chunk = chunk->next; chunk_idx = 0;
807 chunk->next = NULL;
809 chunk->ref[chunk_idx++] = obj->ref;
811 LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) {
812 for(prop = obj->props, props_end = prop + obj->prop_cnt; prop < props_end; prop++) {
813 switch(prop->type) {
814 case PROP_JSVAL:
815 if(is_object_instance(prop->u.val) && (link = to_jsdisp(get_object(prop->u.val))) && link->ctx == ctx)
816 link->ref--;
817 break;
818 case PROP_ACCESSOR:
819 if(prop->u.accessor.getter && prop->u.accessor.getter->ctx == ctx)
820 prop->u.accessor.getter->ref--;
821 if(prop->u.accessor.setter && prop->u.accessor.setter->ctx == ctx)
822 prop->u.accessor.setter->ref--;
823 break;
824 default:
825 break;
829 if(obj->prototype && obj->prototype->ctx == ctx)
830 obj->prototype->ref--;
831 if(obj->builtin_info->gc_traverse)
832 obj->builtin_info->gc_traverse(&gc_ctx, GC_TRAVERSE_SPECULATIVELY, obj);
833 obj->gc_marked = TRUE;
836 /* 2. Clear mark on objects with non-zero "external refcount" and all objects accessible from them */
837 LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) {
838 if(!obj->ref || !obj->gc_marked)
839 continue;
841 hres = gc_stack_push(&gc_ctx, NULL);
842 if(FAILED(hres))
843 break;
845 obj2 = obj;
848 obj2->gc_marked = FALSE;
850 for(prop = obj2->props, props_end = prop + obj2->prop_cnt; prop < props_end; prop++) {
851 switch(prop->type) {
852 case PROP_JSVAL:
853 if(!is_object_instance(prop->u.val))
854 continue;
855 link = to_jsdisp(get_object(prop->u.val));
856 link2 = NULL;
857 break;
858 case PROP_ACCESSOR:
859 link = prop->u.accessor.getter;
860 link2 = prop->u.accessor.setter;
861 break;
862 default:
863 continue;
865 if(link && link->gc_marked && link->ctx == ctx) {
866 hres = gc_stack_push(&gc_ctx, link);
867 if(FAILED(hres))
868 break;
870 if(link2 && link2->gc_marked && link2->ctx == ctx) {
871 hres = gc_stack_push(&gc_ctx, link2);
872 if(FAILED(hres))
873 break;
877 if(FAILED(hres))
878 break;
880 if(obj2->prototype && obj2->prototype->gc_marked && obj2->prototype->ctx == ctx) {
881 hres = gc_stack_push(&gc_ctx, obj2->prototype);
882 if(FAILED(hres))
883 break;
886 if(obj2->builtin_info->gc_traverse) {
887 hres = obj2->builtin_info->gc_traverse(&gc_ctx, GC_TRAVERSE, obj2);
888 if(FAILED(hres))
889 break;
892 do obj2 = gc_stack_pop(&gc_ctx); while(obj2 && !obj2->gc_marked);
893 } while(obj2);
895 if(FAILED(hres)) {
896 do obj2 = gc_stack_pop(&gc_ctx); while(obj2);
897 break;
900 free(gc_ctx.next);
902 /* Restore */
903 chunk = head; chunk_idx = 0;
904 LIST_FOR_EACH_ENTRY(obj, &ctx->objects, jsdisp_t, entry) {
905 obj->ref = chunk->ref[chunk_idx++];
906 if(chunk_idx == ARRAY_SIZE(chunk->ref)) {
907 struct chunk *next = chunk->next;
908 free(chunk);
909 chunk = next; chunk_idx = 0;
912 free(chunk);
914 if(FAILED(hres))
915 return hres;
917 /* 3. Remove all the links from the marked objects, since they are dangling */
918 ctx->gc_is_unlinking = TRUE;
920 iter = list_head(&ctx->objects);
921 while(iter) {
922 obj = LIST_ENTRY(iter, jsdisp_t, entry);
923 if(!obj->gc_marked) {
924 iter = list_next(&ctx->objects, iter);
925 continue;
928 /* Grab it since it gets removed when unlinked */
929 jsdisp_addref(obj);
930 unlink_props(obj);
932 if(obj->prototype) {
933 jsdisp_release(obj->prototype);
934 obj->prototype = NULL;
937 if(obj->builtin_info->gc_traverse)
938 obj->builtin_info->gc_traverse(&gc_ctx, GC_TRAVERSE_UNLINK, obj);
940 /* Releasing unlinked object should not delete any other object,
941 so we can safely obtain the next pointer now */
942 iter = list_next(&ctx->objects, iter);
943 jsdisp_release(obj);
946 ctx->gc_is_unlinking = FALSE;
947 ctx->gc_last_tick = GetTickCount();
948 return S_OK;
951 HRESULT gc_process_linked_obj(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *obj, jsdisp_t *link, void **unlink_ref)
953 if(op == GC_TRAVERSE_UNLINK) {
954 *unlink_ref = NULL;
955 jsdisp_release(link);
956 return S_OK;
959 if(link->ctx != obj->ctx)
960 return S_OK;
961 if(op == GC_TRAVERSE_SPECULATIVELY)
962 link->ref--;
963 else if(link->gc_marked)
964 return gc_stack_push(gc_ctx, link);
965 return S_OK;
968 HRESULT gc_process_linked_val(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *obj, jsval_t *link)
970 jsdisp_t *jsdisp;
972 if(op == GC_TRAVERSE_UNLINK) {
973 jsval_t val = *link;
974 *link = jsval_undefined();
975 jsval_release(val);
976 return S_OK;
979 if(!is_object_instance(*link) || !(jsdisp = to_jsdisp(get_object(*link))) || jsdisp->ctx != obj->ctx)
980 return S_OK;
981 if(op == GC_TRAVERSE_SPECULATIVELY)
982 jsdisp->ref--;
983 else if(jsdisp->gc_marked)
984 return gc_stack_push(gc_ctx, jsdisp);
985 return S_OK;
990 struct typeinfo_func {
991 dispex_prop_t *prop;
992 function_code_t *code;
995 typedef struct {
996 ITypeInfo ITypeInfo_iface;
997 ITypeComp ITypeComp_iface;
998 LONG ref;
1000 UINT num_funcs;
1001 UINT num_vars;
1002 struct typeinfo_func *funcs;
1003 dispex_prop_t **vars;
1005 jsdisp_t *jsdisp;
1006 } ScriptTypeInfo;
1008 static struct typeinfo_func *get_func_from_memid(const ScriptTypeInfo *typeinfo, MEMBERID memid)
1010 UINT a = 0, b = typeinfo->num_funcs;
1012 while (a < b)
1014 UINT i = (a + b - 1) / 2;
1015 MEMBERID func_memid = prop_to_id(typeinfo->jsdisp, typeinfo->funcs[i].prop);
1017 if (memid == func_memid)
1018 return &typeinfo->funcs[i];
1019 else if (memid < func_memid)
1020 b = i;
1021 else
1022 a = i + 1;
1024 return NULL;
1027 static dispex_prop_t *get_var_from_memid(const ScriptTypeInfo *typeinfo, MEMBERID memid)
1029 UINT a = 0, b = typeinfo->num_vars;
1031 while (a < b)
1033 UINT i = (a + b - 1) / 2;
1034 MEMBERID var_memid = prop_to_id(typeinfo->jsdisp, typeinfo->vars[i]);
1036 if (memid == var_memid)
1037 return typeinfo->vars[i];
1038 else if (memid < var_memid)
1039 b = i;
1040 else
1041 a = i + 1;
1043 return NULL;
1046 static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface)
1048 return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeInfo_iface);
1051 static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeComp(ITypeComp *iface)
1053 return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeComp_iface);
1056 static HRESULT WINAPI ScriptTypeInfo_QueryInterface(ITypeInfo *iface, REFIID riid, void **ppv)
1058 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1060 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ITypeInfo, riid))
1061 *ppv = &This->ITypeInfo_iface;
1062 else if (IsEqualGUID(&IID_ITypeComp, riid))
1063 *ppv = &This->ITypeComp_iface;
1064 else
1066 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1067 *ppv = NULL;
1068 return E_NOINTERFACE;
1071 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1072 IUnknown_AddRef((IUnknown*)*ppv);
1073 return S_OK;
1076 static ULONG WINAPI ScriptTypeInfo_AddRef(ITypeInfo *iface)
1078 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1079 LONG ref = InterlockedIncrement(&This->ref);
1081 TRACE("(%p) ref=%ld\n", This, ref);
1083 return ref;
1086 static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface)
1088 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1089 LONG ref = InterlockedDecrement(&This->ref);
1090 UINT i;
1092 TRACE("(%p) ref=%ld\n", This, ref);
1094 if (!ref)
1096 for (i = This->num_funcs; i--;)
1097 release_bytecode(This->funcs[i].code->bytecode);
1098 IDispatchEx_Release(&This->jsdisp->IDispatchEx_iface);
1099 free(This->funcs);
1100 free(This->vars);
1101 free(This);
1103 return ref;
1106 static HRESULT WINAPI ScriptTypeInfo_GetTypeAttr(ITypeInfo *iface, TYPEATTR **ppTypeAttr)
1108 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1109 TYPEATTR *attr;
1111 TRACE("(%p)->(%p)\n", This, ppTypeAttr);
1113 if (!ppTypeAttr) return E_INVALIDARG;
1115 attr = calloc(1, sizeof(*attr));
1116 if (!attr) return E_OUTOFMEMORY;
1118 attr->guid = GUID_JScriptTypeInfo;
1119 attr->lcid = LOCALE_USER_DEFAULT;
1120 attr->memidConstructor = MEMBERID_NIL;
1121 attr->memidDestructor = MEMBERID_NIL;
1122 attr->cbSizeInstance = 4;
1123 attr->typekind = TKIND_DISPATCH;
1124 attr->cFuncs = This->num_funcs;
1125 attr->cVars = This->num_vars;
1126 attr->cImplTypes = 1;
1127 attr->cbSizeVft = sizeof(IDispatchVtbl);
1128 attr->cbAlignment = 4;
1129 attr->wTypeFlags = TYPEFLAG_FDISPATCHABLE;
1130 attr->wMajorVerNum = JSCRIPT_MAJOR_VERSION;
1131 attr->wMinorVerNum = JSCRIPT_MINOR_VERSION;
1133 *ppTypeAttr = attr;
1134 return S_OK;
1137 static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **ppTComp)
1139 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1141 TRACE("(%p)->(%p)\n", This, ppTComp);
1143 if (!ppTComp) return E_INVALIDARG;
1145 *ppTComp = &This->ITypeComp_iface;
1146 ITypeInfo_AddRef(iface);
1147 return S_OK;
1150 static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, FUNCDESC **ppFuncDesc)
1152 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1153 struct typeinfo_func *func;
1154 FUNCDESC *desc;
1155 unsigned i;
1157 TRACE("(%p)->(%u %p)\n", This, index, ppFuncDesc);
1159 if (!ppFuncDesc) return E_INVALIDARG;
1160 if (index >= This->num_funcs) return TYPE_E_ELEMENTNOTFOUND;
1161 func = &This->funcs[index];
1163 /* Store the parameter array after the FUNCDESC structure */
1164 desc = calloc(1, sizeof(*desc) + sizeof(ELEMDESC) * func->code->param_cnt);
1165 if (!desc) return E_OUTOFMEMORY;
1167 desc->memid = prop_to_id(This->jsdisp, func->prop);
1168 desc->funckind = FUNC_DISPATCH;
1169 desc->invkind = INVOKE_FUNC;
1170 desc->callconv = CC_STDCALL;
1171 desc->cParams = func->code->param_cnt;
1172 desc->elemdescFunc.tdesc.vt = VT_VARIANT;
1174 if (func->code->param_cnt) desc->lprgelemdescParam = (ELEMDESC*)(desc + 1);
1175 for (i = 0; i < func->code->param_cnt; i++)
1176 desc->lprgelemdescParam[i].tdesc.vt = VT_VARIANT;
1178 *ppFuncDesc = desc;
1179 return S_OK;
1182 static HRESULT WINAPI ScriptTypeInfo_GetVarDesc(ITypeInfo *iface, UINT index, VARDESC **ppVarDesc)
1184 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1185 VARDESC *desc;
1187 TRACE("(%p)->(%u %p)\n", This, index, ppVarDesc);
1189 if (!ppVarDesc) return E_INVALIDARG;
1190 if (index >= This->num_vars) return TYPE_E_ELEMENTNOTFOUND;
1192 desc = calloc(1, sizeof(*desc));
1193 if (!desc) return E_OUTOFMEMORY;
1195 desc->memid = prop_to_id(This->jsdisp, This->vars[index]);
1196 desc->varkind = VAR_DISPATCH;
1197 desc->elemdescVar.tdesc.vt = VT_VARIANT;
1199 *ppVarDesc = desc;
1200 return S_OK;
1203 static HRESULT WINAPI ScriptTypeInfo_GetNames(ITypeInfo *iface, MEMBERID memid, BSTR *rgBstrNames,
1204 UINT cMaxNames, UINT *pcNames)
1206 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1207 struct typeinfo_func *func;
1208 ITypeInfo *disp_typeinfo;
1209 dispex_prop_t *var;
1210 HRESULT hr;
1211 UINT i = 0;
1213 TRACE("(%p)->(%ld %p %u %p)\n", This, memid, rgBstrNames, cMaxNames, pcNames);
1215 if (!rgBstrNames || !pcNames) return E_INVALIDARG;
1216 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
1218 func = get_func_from_memid(This, memid);
1219 if (!func)
1221 var = get_var_from_memid(This, memid);
1222 if (!var)
1224 hr = get_dispatch_typeinfo(&disp_typeinfo);
1225 if (FAILED(hr)) return hr;
1227 return ITypeInfo_GetNames(disp_typeinfo, memid, rgBstrNames, cMaxNames, pcNames);
1231 *pcNames = 0;
1232 if (!cMaxNames) return S_OK;
1234 rgBstrNames[0] = SysAllocString(func ? func->prop->name : var->name);
1235 if (!rgBstrNames[0]) return E_OUTOFMEMORY;
1236 i++;
1238 if (func)
1240 unsigned num = min(cMaxNames, func->code->param_cnt + 1);
1242 for (; i < num; i++)
1244 if (!(rgBstrNames[i] = SysAllocString(func->code->params[i - 1])))
1246 do SysFreeString(rgBstrNames[--i]); while (i);
1247 return E_OUTOFMEMORY;
1252 *pcNames = i;
1253 return S_OK;
1256 static HRESULT WINAPI ScriptTypeInfo_GetRefTypeOfImplType(ITypeInfo *iface, UINT index, HREFTYPE *pRefType)
1258 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1260 TRACE("(%p)->(%u %p)\n", This, index, pRefType);
1262 /* We only inherit from IDispatch */
1263 if (!pRefType) return E_INVALIDARG;
1264 if (index != 0) return TYPE_E_ELEMENTNOTFOUND;
1266 *pRefType = 1;
1267 return S_OK;
1270 static HRESULT WINAPI ScriptTypeInfo_GetImplTypeFlags(ITypeInfo *iface, UINT index, INT *pImplTypeFlags)
1272 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1274 TRACE("(%p)->(%u %p)\n", This, index, pImplTypeFlags);
1276 if (!pImplTypeFlags) return E_INVALIDARG;
1277 if (index != 0) return TYPE_E_ELEMENTNOTFOUND;
1279 *pImplTypeFlags = 0;
1280 return S_OK;
1283 static HRESULT WINAPI ScriptTypeInfo_GetIDsOfNames(ITypeInfo *iface, LPOLESTR *rgszNames, UINT cNames,
1284 MEMBERID *pMemId)
1286 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1287 ITypeInfo *disp_typeinfo;
1288 const WCHAR *name;
1289 HRESULT hr = S_OK;
1290 int i, j, arg;
1292 TRACE("(%p)->(%p %u %p)\n", This, rgszNames, cNames, pMemId);
1294 if (!rgszNames || !cNames || !pMemId) return E_INVALIDARG;
1296 for (i = 0; i < cNames; i++) pMemId[i] = MEMBERID_NIL;
1297 name = rgszNames[0];
1299 for (i = 0; i < This->num_funcs; i++)
1301 struct typeinfo_func *func = &This->funcs[i];
1303 if (wcsicmp(name, func->prop->name)) continue;
1304 pMemId[0] = prop_to_id(This->jsdisp, func->prop);
1306 for (j = 1; j < cNames; j++)
1308 name = rgszNames[j];
1309 for (arg = func->code->param_cnt; --arg >= 0;)
1310 if (!wcsicmp(name, func->code->params[arg]))
1311 break;
1312 if (arg >= 0)
1313 pMemId[j] = arg;
1314 else
1315 hr = DISP_E_UNKNOWNNAME;
1317 return hr;
1320 for (i = 0; i < This->num_vars; i++)
1322 dispex_prop_t *var = This->vars[i];
1324 if (wcsicmp(name, var->name)) continue;
1325 pMemId[0] = prop_to_id(This->jsdisp, var);
1326 return S_OK;
1329 /* Look into the inherited IDispatch */
1330 hr = get_dispatch_typeinfo(&disp_typeinfo);
1331 if (FAILED(hr)) return hr;
1333 return ITypeInfo_GetIDsOfNames(disp_typeinfo, rgszNames, cNames, pMemId);
1336 static HRESULT WINAPI ScriptTypeInfo_Invoke(ITypeInfo *iface, PVOID pvInstance, MEMBERID memid, WORD wFlags,
1337 DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1339 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1340 ITypeInfo *disp_typeinfo;
1341 IDispatch *disp;
1342 HRESULT hr;
1344 TRACE("(%p)->(%p %ld %d %p %p %p %p)\n", This, pvInstance, memid, wFlags,
1345 pDispParams, pVarResult, pExcepInfo, puArgErr);
1347 if (!pvInstance) return E_INVALIDARG;
1348 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
1350 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1352 hr = get_dispatch_typeinfo(&disp_typeinfo);
1353 if (FAILED(hr)) return hr;
1355 return ITypeInfo_Invoke(disp_typeinfo, pvInstance, memid, wFlags, pDispParams,
1356 pVarResult, pExcepInfo, puArgErr);
1359 hr = IUnknown_QueryInterface((IUnknown*)pvInstance, &IID_IDispatch, (void**)&disp);
1360 if (FAILED(hr)) return hr;
1362 hr = IDispatch_Invoke(disp, memid, &IID_NULL, LOCALE_USER_DEFAULT, wFlags,
1363 pDispParams, pVarResult, pExcepInfo, puArgErr);
1364 IDispatch_Release(disp);
1366 return hr;
1369 static HRESULT WINAPI ScriptTypeInfo_GetDocumentation(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrName,
1370 BSTR *pBstrDocString, DWORD *pdwHelpContext, BSTR *pBstrHelpFile)
1372 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1373 struct typeinfo_func *func;
1374 ITypeInfo *disp_typeinfo;
1375 dispex_prop_t *var;
1376 HRESULT hr;
1378 TRACE("(%p)->(%ld %p %p %p %p)\n", This, memid, pBstrName, pBstrDocString, pdwHelpContext, pBstrHelpFile);
1380 if (pBstrDocString) *pBstrDocString = NULL;
1381 if (pdwHelpContext) *pdwHelpContext = 0;
1382 if (pBstrHelpFile) *pBstrHelpFile = NULL;
1384 if (memid == MEMBERID_NIL)
1386 if (pBstrName && !(*pBstrName = SysAllocString(L"JScriptTypeInfo")))
1387 return E_OUTOFMEMORY;
1388 if (pBstrDocString &&
1389 !(*pBstrDocString = SysAllocString(L"JScript Type Info")))
1391 if (pBstrName) SysFreeString(*pBstrName);
1392 return E_OUTOFMEMORY;
1394 return S_OK;
1396 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
1398 func = get_func_from_memid(This, memid);
1399 if (!func)
1401 var = get_var_from_memid(This, memid);
1402 if (!var)
1404 hr = get_dispatch_typeinfo(&disp_typeinfo);
1405 if (FAILED(hr)) return hr;
1407 return ITypeInfo_GetDocumentation(disp_typeinfo, memid, pBstrName, pBstrDocString,
1408 pdwHelpContext, pBstrHelpFile);
1412 if (pBstrName)
1414 *pBstrName = SysAllocString(func ? func->prop->name : var->name);
1416 if (!*pBstrName)
1417 return E_OUTOFMEMORY;
1419 return S_OK;
1422 static HRESULT WINAPI ScriptTypeInfo_GetDllEntry(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind,
1423 BSTR *pBstrDllName, BSTR *pBstrName, WORD *pwOrdinal)
1425 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1426 ITypeInfo *disp_typeinfo;
1427 HRESULT hr;
1429 TRACE("(%p)->(%ld %d %p %p %p)\n", This, memid, invKind, pBstrDllName, pBstrName, pwOrdinal);
1431 if (pBstrDllName) *pBstrDllName = NULL;
1432 if (pBstrName) *pBstrName = NULL;
1433 if (pwOrdinal) *pwOrdinal = 0;
1435 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1437 hr = get_dispatch_typeinfo(&disp_typeinfo);
1438 if (FAILED(hr)) return hr;
1440 return ITypeInfo_GetDllEntry(disp_typeinfo, memid, invKind, pBstrDllName, pBstrName, pwOrdinal);
1442 return TYPE_E_BADMODULEKIND;
1445 static HRESULT WINAPI ScriptTypeInfo_GetRefTypeInfo(ITypeInfo *iface, HREFTYPE hRefType, ITypeInfo **ppTInfo)
1447 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1448 HRESULT hr;
1450 TRACE("(%p)->(%lx %p)\n", This, hRefType, ppTInfo);
1452 if (!ppTInfo || (INT)hRefType < 0) return E_INVALIDARG;
1454 if (hRefType & ~3) return E_FAIL;
1455 if (hRefType & 1)
1457 hr = get_dispatch_typeinfo(ppTInfo);
1458 if (FAILED(hr)) return hr;
1460 else
1461 *ppTInfo = iface;
1463 ITypeInfo_AddRef(*ppTInfo);
1464 return S_OK;
1467 static HRESULT WINAPI ScriptTypeInfo_AddressOfMember(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind, PVOID *ppv)
1469 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1470 ITypeInfo *disp_typeinfo;
1471 HRESULT hr;
1473 TRACE("(%p)->(%ld %d %p)\n", This, memid, invKind, ppv);
1475 if (!ppv) return E_INVALIDARG;
1476 *ppv = NULL;
1478 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1480 hr = get_dispatch_typeinfo(&disp_typeinfo);
1481 if (FAILED(hr)) return hr;
1483 return ITypeInfo_AddressOfMember(disp_typeinfo, memid, invKind, ppv);
1485 return TYPE_E_BADMODULEKIND;
1488 static HRESULT WINAPI ScriptTypeInfo_CreateInstance(ITypeInfo *iface, IUnknown *pUnkOuter, REFIID riid, PVOID *ppvObj)
1490 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1492 TRACE("(%p)->(%p %s %p)\n", This, pUnkOuter, debugstr_guid(riid), ppvObj);
1494 if (!ppvObj) return E_INVALIDARG;
1496 *ppvObj = NULL;
1497 return TYPE_E_BADMODULEKIND;
1500 static HRESULT WINAPI ScriptTypeInfo_GetMops(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrMops)
1502 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1503 ITypeInfo *disp_typeinfo;
1504 HRESULT hr;
1506 TRACE("(%p)->(%ld %p)\n", This, memid, pBstrMops);
1508 if (!pBstrMops) return E_INVALIDARG;
1510 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1512 hr = get_dispatch_typeinfo(&disp_typeinfo);
1513 if (FAILED(hr)) return hr;
1515 return ITypeInfo_GetMops(disp_typeinfo, memid, pBstrMops);
1518 *pBstrMops = NULL;
1519 return S_OK;
1522 static HRESULT WINAPI ScriptTypeInfo_GetContainingTypeLib(ITypeInfo *iface, ITypeLib **ppTLib, UINT *pIndex)
1524 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1526 FIXME("(%p)->(%p %p)\n", This, ppTLib, pIndex);
1528 return E_NOTIMPL;
1531 static void WINAPI ScriptTypeInfo_ReleaseTypeAttr(ITypeInfo *iface, TYPEATTR *pTypeAttr)
1533 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1535 TRACE("(%p)->(%p)\n", This, pTypeAttr);
1537 free(pTypeAttr);
1540 static void WINAPI ScriptTypeInfo_ReleaseFuncDesc(ITypeInfo *iface, FUNCDESC *pFuncDesc)
1542 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1544 TRACE("(%p)->(%p)\n", This, pFuncDesc);
1546 free(pFuncDesc);
1549 static void WINAPI ScriptTypeInfo_ReleaseVarDesc(ITypeInfo *iface, VARDESC *pVarDesc)
1551 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1553 TRACE("(%p)->(%p)\n", This, pVarDesc);
1555 free(pVarDesc);
1558 static const ITypeInfoVtbl ScriptTypeInfoVtbl = {
1559 ScriptTypeInfo_QueryInterface,
1560 ScriptTypeInfo_AddRef,
1561 ScriptTypeInfo_Release,
1562 ScriptTypeInfo_GetTypeAttr,
1563 ScriptTypeInfo_GetTypeComp,
1564 ScriptTypeInfo_GetFuncDesc,
1565 ScriptTypeInfo_GetVarDesc,
1566 ScriptTypeInfo_GetNames,
1567 ScriptTypeInfo_GetRefTypeOfImplType,
1568 ScriptTypeInfo_GetImplTypeFlags,
1569 ScriptTypeInfo_GetIDsOfNames,
1570 ScriptTypeInfo_Invoke,
1571 ScriptTypeInfo_GetDocumentation,
1572 ScriptTypeInfo_GetDllEntry,
1573 ScriptTypeInfo_GetRefTypeInfo,
1574 ScriptTypeInfo_AddressOfMember,
1575 ScriptTypeInfo_CreateInstance,
1576 ScriptTypeInfo_GetMops,
1577 ScriptTypeInfo_GetContainingTypeLib,
1578 ScriptTypeInfo_ReleaseTypeAttr,
1579 ScriptTypeInfo_ReleaseFuncDesc,
1580 ScriptTypeInfo_ReleaseVarDesc
1583 static HRESULT WINAPI ScriptTypeComp_QueryInterface(ITypeComp *iface, REFIID riid, void **ppv)
1585 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1586 return ITypeInfo_QueryInterface(&This->ITypeInfo_iface, riid, ppv);
1589 static ULONG WINAPI ScriptTypeComp_AddRef(ITypeComp *iface)
1591 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1592 return ITypeInfo_AddRef(&This->ITypeInfo_iface);
1595 static ULONG WINAPI ScriptTypeComp_Release(ITypeComp *iface)
1597 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1598 return ITypeInfo_Release(&This->ITypeInfo_iface);
1601 static HRESULT WINAPI ScriptTypeComp_Bind(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal, WORD wFlags,
1602 ITypeInfo **ppTInfo, DESCKIND *pDescKind, BINDPTR *pBindPtr)
1604 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1605 UINT flags = wFlags ? wFlags : ~0;
1606 ITypeInfo *disp_typeinfo;
1607 ITypeComp *disp_typecomp;
1608 HRESULT hr;
1609 UINT i;
1611 TRACE("(%p)->(%s %08lx %d %p %p %p)\n", This, debugstr_w(szName), lHashVal,
1612 wFlags, ppTInfo, pDescKind, pBindPtr);
1614 if (!szName || !ppTInfo || !pDescKind || !pBindPtr)
1615 return E_INVALIDARG;
1617 for (i = 0; i < This->num_funcs; i++)
1619 if (wcsicmp(szName, This->funcs[i].prop->name)) continue;
1620 if (!(flags & INVOKE_FUNC)) return TYPE_E_TYPEMISMATCH;
1622 hr = ITypeInfo_GetFuncDesc(&This->ITypeInfo_iface, i, &pBindPtr->lpfuncdesc);
1623 if (FAILED(hr)) return hr;
1625 *pDescKind = DESCKIND_FUNCDESC;
1626 *ppTInfo = &This->ITypeInfo_iface;
1627 ITypeInfo_AddRef(*ppTInfo);
1628 return S_OK;
1631 for (i = 0; i < This->num_vars; i++)
1633 if (wcsicmp(szName, This->vars[i]->name)) continue;
1634 if (!(flags & INVOKE_PROPERTYGET)) return TYPE_E_TYPEMISMATCH;
1636 hr = ITypeInfo_GetVarDesc(&This->ITypeInfo_iface, i, &pBindPtr->lpvardesc);
1637 if (FAILED(hr)) return hr;
1639 *pDescKind = DESCKIND_VARDESC;
1640 *ppTInfo = &This->ITypeInfo_iface;
1641 ITypeInfo_AddRef(*ppTInfo);
1642 return S_OK;
1645 /* Look into the inherited IDispatch */
1646 hr = get_dispatch_typeinfo(&disp_typeinfo);
1647 if (FAILED(hr)) return hr;
1649 hr = ITypeInfo_GetTypeComp(disp_typeinfo, &disp_typecomp);
1650 if (FAILED(hr)) return hr;
1652 hr = ITypeComp_Bind(disp_typecomp, szName, lHashVal, wFlags, ppTInfo, pDescKind, pBindPtr);
1653 ITypeComp_Release(disp_typecomp);
1654 return hr;
1657 static HRESULT WINAPI ScriptTypeComp_BindType(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal,
1658 ITypeInfo **ppTInfo, ITypeComp **ppTComp)
1660 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1661 ITypeInfo *disp_typeinfo;
1662 ITypeComp *disp_typecomp;
1663 HRESULT hr;
1665 TRACE("(%p)->(%s %08lx %p %p)\n", This, debugstr_w(szName), lHashVal, ppTInfo, ppTComp);
1667 if (!szName || !ppTInfo || !ppTComp)
1668 return E_INVALIDARG;
1670 /* Look into the inherited IDispatch */
1671 hr = get_dispatch_typeinfo(&disp_typeinfo);
1672 if (FAILED(hr)) return hr;
1674 hr = ITypeInfo_GetTypeComp(disp_typeinfo, &disp_typecomp);
1675 if (FAILED(hr)) return hr;
1677 hr = ITypeComp_BindType(disp_typecomp, szName, lHashVal, ppTInfo, ppTComp);
1678 ITypeComp_Release(disp_typecomp);
1679 return hr;
1682 static const ITypeCompVtbl ScriptTypeCompVtbl = {
1683 ScriptTypeComp_QueryInterface,
1684 ScriptTypeComp_AddRef,
1685 ScriptTypeComp_Release,
1686 ScriptTypeComp_Bind,
1687 ScriptTypeComp_BindType
1690 static inline jsdisp_t *impl_from_IDispatchEx(IDispatchEx *iface)
1692 return CONTAINING_RECORD(iface, jsdisp_t, IDispatchEx_iface);
1695 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1697 jsdisp_t *This = impl_from_IDispatchEx(iface);
1699 if(IsEqualGUID(&IID_IUnknown, riid)) {
1700 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
1701 *ppv = &This->IDispatchEx_iface;
1702 }else if(IsEqualGUID(&IID_IDispatch, riid)) {
1703 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
1704 *ppv = &This->IDispatchEx_iface;
1705 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
1706 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
1707 *ppv = &This->IDispatchEx_iface;
1708 }else {
1709 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1710 *ppv = NULL;
1711 return E_NOINTERFACE;
1714 jsdisp_addref(This);
1715 return S_OK;
1718 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1720 jsdisp_t *This = impl_from_IDispatchEx(iface);
1721 jsdisp_addref(This);
1722 return This->ref;
1725 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1727 jsdisp_t *This = impl_from_IDispatchEx(iface);
1728 ULONG ref = --This->ref;
1729 TRACE("(%p) ref=%ld\n", This, ref);
1730 if(!ref)
1731 jsdisp_free(This);
1732 return ref;
1735 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1737 jsdisp_t *This = impl_from_IDispatchEx(iface);
1739 TRACE("(%p)->(%p)\n", This, pctinfo);
1741 *pctinfo = 1;
1742 return S_OK;
1745 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
1746 ITypeInfo **ppTInfo)
1748 jsdisp_t *This = impl_from_IDispatchEx(iface);
1749 dispex_prop_t *prop, *cur, *end, **typevar;
1750 UINT num_funcs = 0, num_vars = 0;
1751 struct typeinfo_func *typefunc;
1752 function_code_t *func_code;
1753 ScriptTypeInfo *typeinfo;
1754 unsigned pos;
1756 TRACE("(%p)->(%u %lu %p)\n", This, iTInfo, lcid, ppTInfo);
1758 if (iTInfo != 0) return DISP_E_BADINDEX;
1760 for (prop = This->props, end = prop + This->prop_cnt; prop != end; prop++)
1762 if (prop->type != PROP_JSVAL || !(prop->flags & PROPF_ENUMERABLE))
1763 continue;
1765 /* If two identifiers differ only by case, the TypeInfo fails */
1766 pos = This->props[get_props_idx(This, prop->hash)].bucket_head;
1767 while (pos != ~0)
1769 cur = This->props + pos;
1771 if (prop->hash == cur->hash && prop != cur &&
1772 cur->type == PROP_JSVAL && (cur->flags & PROPF_ENUMERABLE) &&
1773 !wcsicmp(prop->name, cur->name))
1775 return TYPE_E_AMBIGUOUSNAME;
1777 pos = cur->bucket_next;
1780 if (is_function_prop(prop))
1782 if (Function_get_code(as_jsdisp(get_object(prop->u.val))))
1783 num_funcs++;
1785 else num_vars++;
1788 if (!(typeinfo = malloc(sizeof(*typeinfo))))
1789 return E_OUTOFMEMORY;
1791 typeinfo->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl;
1792 typeinfo->ITypeComp_iface.lpVtbl = &ScriptTypeCompVtbl;
1793 typeinfo->ref = 1;
1794 typeinfo->num_vars = num_vars;
1795 typeinfo->num_funcs = num_funcs;
1796 typeinfo->jsdisp = This;
1798 typeinfo->funcs = malloc(sizeof(*typeinfo->funcs) * num_funcs);
1799 if (!typeinfo->funcs)
1801 free(typeinfo);
1802 return E_OUTOFMEMORY;
1805 typeinfo->vars = malloc(sizeof(*typeinfo->vars) * num_vars);
1806 if (!typeinfo->vars)
1808 free(typeinfo->funcs);
1809 free(typeinfo);
1810 return E_OUTOFMEMORY;
1813 typefunc = typeinfo->funcs;
1814 typevar = typeinfo->vars;
1815 for (prop = This->props; prop != end; prop++)
1817 if (prop->type != PROP_JSVAL || !(prop->flags & PROPF_ENUMERABLE))
1818 continue;
1820 if (is_function_prop(prop))
1822 func_code = Function_get_code(as_jsdisp(get_object(prop->u.val)));
1823 if (!func_code) continue;
1825 typefunc->prop = prop;
1826 typefunc->code = func_code;
1827 typefunc++;
1829 /* The function may be deleted, so keep a ref */
1830 bytecode_addref(func_code->bytecode);
1832 else
1833 *typevar++ = prop;
1836 /* Keep a ref to the props and their names */
1837 IDispatchEx_AddRef(&This->IDispatchEx_iface);
1839 *ppTInfo = &typeinfo->ITypeInfo_iface;
1840 return S_OK;
1843 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1844 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
1845 DISPID *rgDispId)
1847 jsdisp_t *This = impl_from_IDispatchEx(iface);
1848 UINT i;
1849 HRESULT hres;
1851 TRACE("(%p)->(%s %p %u %lu %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1852 lcid, rgDispId);
1854 if(cNames == 0)
1855 return S_OK;
1857 hres = jsdisp_get_id(This, rgszNames[0], 0, rgDispId);
1858 if(FAILED(hres))
1859 return hres;
1861 /* DISPIDs for parameters don't seem to be supported */
1862 if(cNames > 1) {
1863 for(i = 1; i < cNames; i++)
1864 rgDispId[i] = DISPID_UNKNOWN;
1865 hres = DISP_E_UNKNOWNNAME;
1868 return hres;
1871 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1872 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1873 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1875 jsdisp_t *This = impl_from_IDispatchEx(iface);
1877 TRACE("(%p)->(%ld %s %ld %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1878 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1880 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags,
1881 pDispParams, pVarResult, pExcepInfo, NULL);
1884 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1886 jsdisp_t *This = impl_from_IDispatchEx(iface);
1888 TRACE("(%p)->(%s %lx %p)\n", This, debugstr_w(bstrName), grfdex, pid);
1890 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) {
1891 FIXME("Unsupported grfdex %lx\n", grfdex);
1892 return E_NOTIMPL;
1895 return jsdisp_get_id(This, bstrName, grfdex, pid);
1898 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1899 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1901 jsdisp_t *This = impl_from_IDispatchEx(iface);
1902 IServiceProvider *prev_caller;
1903 dispex_prop_t *prop;
1904 jsexcept_t ei;
1905 HRESULT hres;
1907 TRACE("(%p)->(%lx %lx %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1909 if(pvarRes)
1910 V_VT(pvarRes) = VT_EMPTY;
1912 prop = get_prop(This, id);
1913 if(!prop && id != DISPID_VALUE) {
1914 TRACE("invalid id\n");
1915 return DISP_E_MEMBERNOTFOUND;
1918 enter_script(This->ctx, &ei);
1920 prev_caller = This->ctx->jscaller->caller;
1921 This->ctx->jscaller->caller = pspCaller;
1922 if(pspCaller)
1923 IServiceProvider_AddRef(pspCaller);
1925 switch(wFlags) {
1926 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1927 wFlags = DISPATCH_METHOD;
1928 /* fall through */
1929 case DISPATCH_METHOD:
1930 case DISPATCH_CONSTRUCT: {
1931 jsval_t *argv, buf[6], r;
1932 IDispatch *passed_this;
1933 unsigned argc;
1935 hres = convert_params(This->ctx, pdp, buf, &argc, &argv);
1936 if(FAILED(hres))
1937 break;
1939 passed_this = get_this(pdp);
1940 if(prop)
1941 hres = invoke_prop_func(This, passed_this, prop, wFlags, argc, argv, pvarRes ? &r : NULL, pspCaller);
1942 else
1943 hres = jsdisp_call_value(This, passed_this ? jsval_disp(passed_this) : jsval_undefined(), wFlags, argc, argv, pvarRes ? &r : NULL);
1945 while(argc--)
1946 jsval_release(argv[argc]);
1947 if(argv != buf)
1948 free(argv);
1949 if(SUCCEEDED(hres) && pvarRes) {
1950 hres = jsval_to_variant(r, pvarRes);
1951 jsval_release(r);
1953 break;
1955 case DISPATCH_PROPERTYGET: {
1956 jsval_t r;
1958 if(prop)
1959 hres = prop_get(This, prop, &r);
1960 else {
1961 hres = to_primitive(This->ctx, jsval_obj(This), &r, NO_HINT);
1962 if(hres == JS_E_TO_PRIMITIVE)
1963 hres = DISP_E_MEMBERNOTFOUND;
1966 if(SUCCEEDED(hres)) {
1967 hres = jsval_to_variant(r, pvarRes);
1968 jsval_release(r);
1970 break;
1972 case DISPATCH_PROPERTYPUTREF | DISPATCH_PROPERTYPUT:
1973 case DISPATCH_PROPERTYPUTREF:
1974 case DISPATCH_PROPERTYPUT: {
1975 jsval_t val;
1976 DWORD i;
1978 if(!prop) {
1979 hres = DISP_E_MEMBERNOTFOUND;
1980 break;
1983 for(i=0; i < pdp->cNamedArgs; i++) {
1984 if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
1985 break;
1988 if(i == pdp->cNamedArgs) {
1989 TRACE("no value to set\n");
1990 hres = DISP_E_PARAMNOTOPTIONAL;
1991 break;
1994 hres = variant_to_jsval(This->ctx, pdp->rgvarg+i, &val);
1995 if(FAILED(hres))
1996 break;
1998 hres = prop_put(This, prop, val);
1999 jsval_release(val);
2000 break;
2002 default:
2003 FIXME("Unimplemented flags %x\n", wFlags);
2004 hres = E_INVALIDARG;
2005 break;
2008 This->ctx->jscaller->caller = prev_caller;
2009 if(pspCaller)
2010 IServiceProvider_Release(pspCaller);
2011 return leave_script(This->ctx, hres);
2014 static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret)
2016 if(prop->type == PROP_PROTREF) {
2017 *ret = TRUE;
2018 return S_OK;
2021 if(!(prop->flags & PROPF_CONFIGURABLE)) {
2022 *ret = FALSE;
2023 return S_OK;
2026 *ret = TRUE;
2028 if(prop->type == PROP_JSVAL)
2029 jsval_release(prop->u.val);
2030 if(prop->type == PROP_ACCESSOR) {
2031 if(prop->u.accessor.getter)
2032 jsdisp_release(prop->u.accessor.getter);
2033 if(prop->u.accessor.setter)
2034 jsdisp_release(prop->u.accessor.setter);
2036 prop->type = PROP_DELETED;
2037 return S_OK;
2040 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
2042 jsdisp_t *This = impl_from_IDispatchEx(iface);
2043 dispex_prop_t *prop;
2044 BOOL b;
2045 HRESULT hres;
2047 TRACE("(%p)->(%s %lx)\n", This, debugstr_w(bstrName), grfdex);
2049 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
2050 FIXME("Unsupported grfdex %lx\n", grfdex);
2052 hres = find_prop_name(This, string_hash(bstrName), bstrName, grfdex & fdexNameCaseInsensitive, &prop);
2053 if(FAILED(hres))
2054 return hres;
2055 if(!prop) {
2056 TRACE("not found\n");
2057 return S_OK;
2060 return delete_prop(prop, &b);
2063 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
2065 jsdisp_t *This = impl_from_IDispatchEx(iface);
2066 dispex_prop_t *prop;
2067 BOOL b;
2069 TRACE("(%p)->(%lx)\n", This, id);
2071 prop = get_prop(This, id);
2072 if(!prop) {
2073 WARN("invalid id\n");
2074 return DISP_E_MEMBERNOTFOUND;
2077 return delete_prop(prop, &b);
2080 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
2082 jsdisp_t *This = impl_from_IDispatchEx(iface);
2083 FIXME("(%p)->(%lx %lx %p)\n", This, id, grfdexFetch, pgrfdex);
2084 return E_NOTIMPL;
2087 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
2089 jsdisp_t *This = impl_from_IDispatchEx(iface);
2090 dispex_prop_t *prop;
2092 TRACE("(%p)->(%lx %p)\n", This, id, pbstrName);
2094 prop = get_prop(This, id);
2095 if(!prop)
2096 return DISP_E_MEMBERNOTFOUND;
2098 *pbstrName = SysAllocString(prop->name);
2099 if(!*pbstrName)
2100 return E_OUTOFMEMORY;
2102 return S_OK;
2105 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
2107 jsdisp_t *This = impl_from_IDispatchEx(iface);
2108 HRESULT hres = S_FALSE;
2110 TRACE("(%p)->(%lx %lx %p)\n", This, grfdex, id, pid);
2112 if(id != DISPID_VALUE)
2113 hres = jsdisp_next_prop(This, id, JSDISP_ENUM_ALL, pid);
2114 if(hres == S_FALSE)
2115 *pid = DISPID_STARTENUM;
2116 return hres;
2119 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
2121 jsdisp_t *This = impl_from_IDispatchEx(iface);
2122 FIXME("(%p)->(%p)\n", This, ppunk);
2123 return E_NOTIMPL;
2126 static IDispatchExVtbl DispatchExVtbl = {
2127 DispatchEx_QueryInterface,
2128 DispatchEx_AddRef,
2129 DispatchEx_Release,
2130 DispatchEx_GetTypeInfoCount,
2131 DispatchEx_GetTypeInfo,
2132 DispatchEx_GetIDsOfNames,
2133 DispatchEx_Invoke,
2134 DispatchEx_GetDispID,
2135 DispatchEx_InvokeEx,
2136 DispatchEx_DeleteMemberByName,
2137 DispatchEx_DeleteMemberByDispID,
2138 DispatchEx_GetMemberProperties,
2139 DispatchEx_GetMemberName,
2140 DispatchEx_GetNextDispID,
2141 DispatchEx_GetNameSpaceParent
2144 jsdisp_t *as_jsdisp(IDispatch *disp)
2146 assert(disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl);
2147 return impl_from_IDispatchEx((IDispatchEx*)disp);
2150 jsdisp_t *to_jsdisp(IDispatch *disp)
2152 return disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl ? impl_from_IDispatchEx((IDispatchEx*)disp) : NULL;
2155 HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype)
2157 unsigned i;
2159 /* FIXME: Use better heuristics to decide when to run the GC */
2160 if(GetTickCount() - ctx->gc_last_tick > 30000)
2161 gc_run(ctx);
2163 TRACE("%p (%p)\n", dispex, prototype);
2165 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
2166 dispex->ref = 1;
2167 dispex->builtin_info = builtin_info;
2168 dispex->extensible = TRUE;
2169 dispex->prop_cnt = 0;
2171 dispex->props = calloc(1, sizeof(dispex_prop_t)*(dispex->buf_size=4));
2172 if(!dispex->props)
2173 return E_OUTOFMEMORY;
2175 for(i = 0; i < dispex->buf_size; i++) {
2176 dispex->props[i].bucket_head = ~0;
2177 dispex->props[i].bucket_next = ~0;
2180 dispex->prototype = prototype;
2181 if(prototype)
2182 jsdisp_addref(prototype);
2184 script_addref(ctx);
2185 dispex->ctx = ctx;
2187 list_add_tail(&ctx->objects, &dispex->entry);
2188 return S_OK;
2191 static const builtin_info_t dispex_info = {
2192 JSCLASS_NONE,
2193 NULL,
2194 0, NULL,
2195 NULL,
2196 NULL
2199 HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype, jsdisp_t **dispex)
2201 jsdisp_t *ret;
2202 HRESULT hres;
2204 ret = calloc(1, sizeof(jsdisp_t));
2205 if(!ret)
2206 return E_OUTOFMEMORY;
2208 hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype);
2209 if(FAILED(hres)) {
2210 free(ret);
2211 return hres;
2214 *dispex = ret;
2215 return S_OK;
2218 void jsdisp_free(jsdisp_t *obj)
2220 dispex_prop_t *prop;
2222 list_remove(&obj->entry);
2224 TRACE("(%p)\n", obj);
2226 for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) {
2227 switch(prop->type) {
2228 case PROP_JSVAL:
2229 jsval_release(prop->u.val);
2230 break;
2231 case PROP_ACCESSOR:
2232 if(prop->u.accessor.getter)
2233 jsdisp_release(prop->u.accessor.getter);
2234 if(prop->u.accessor.setter)
2235 jsdisp_release(prop->u.accessor.setter);
2236 break;
2237 default:
2238 break;
2240 free(prop->name);
2242 free(obj->props);
2243 script_release(obj->ctx);
2244 if(obj->prototype)
2245 jsdisp_release(obj->prototype);
2247 if(obj->builtin_info->destructor)
2248 obj->builtin_info->destructor(obj);
2249 else
2250 free(obj);
2253 #ifdef TRACE_REFCNT
2255 jsdisp_t *jsdisp_addref(jsdisp_t *jsdisp)
2257 ULONG ref = ++jsdisp->ref;
2258 TRACE("(%p) ref=%ld\n", jsdisp, ref);
2259 return jsdisp;
2262 void jsdisp_release(jsdisp_t *jsdisp)
2264 ULONG ref = --jsdisp->ref;
2266 TRACE("(%p) ref=%ld\n", jsdisp, ref);
2268 if(!ref)
2269 jsdisp_free(jsdisp);
2272 #endif
2274 HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *constr)
2276 jsdisp_t *prot = NULL;
2277 dispex_prop_t *prop;
2278 HRESULT hres;
2280 hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", FALSE, &prop);
2281 if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) {
2282 jsval_t val;
2284 hres = prop_get(constr, prop, &val);
2285 if(FAILED(hres)) {
2286 ERR("Could not get prototype\n");
2287 return hres;
2290 if(is_object_instance(val))
2291 prot = iface_to_jsdisp(get_object(val));
2292 else
2293 prot = jsdisp_addref(ctx->object_prototype);
2295 jsval_release(val);
2298 hres = init_dispex(dispex, ctx, builtin_info, prot);
2300 if(prot)
2301 jsdisp_release(prot);
2302 return hres;
2305 jsdisp_t *iface_to_jsdisp(IDispatch *iface)
2307 return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl
2308 ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface))
2309 : NULL;
2312 HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id)
2314 dispex_prop_t *prop;
2315 HRESULT hres;
2317 if(jsdisp->extensible && (flags & fdexNameEnsure))
2318 hres = ensure_prop_name(jsdisp, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE,
2319 flags & fdexNameCaseInsensitive, &prop);
2320 else
2321 hres = find_prop_name_prot(jsdisp, string_hash(name), name, flags & fdexNameCaseInsensitive, &prop);
2322 if(FAILED(hres))
2323 return hres;
2325 if(prop && prop->type!=PROP_DELETED) {
2326 *id = prop_to_id(jsdisp, prop);
2327 return S_OK;
2330 TRACE("not found %s\n", debugstr_w(name));
2331 *id = DISPID_UNKNOWN;
2332 return DISP_E_UNKNOWNNAME;
2335 HRESULT jsdisp_get_idx_id(jsdisp_t *jsdisp, DWORD idx, DISPID *id)
2337 WCHAR name[11];
2339 swprintf(name, ARRAY_SIZE(name), L"%u", idx);
2340 return jsdisp_get_id(jsdisp, name, 0, id);
2343 HRESULT jsdisp_call_value(jsdisp_t *jsfunc, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
2345 HRESULT hres;
2347 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
2349 if(is_class(jsfunc, JSCLASS_FUNCTION)) {
2350 hres = Function_invoke(jsfunc, vthis, flags, argc, argv, r);
2351 }else {
2352 if(!jsfunc->builtin_info->call) {
2353 WARN("Not a function\n");
2354 return JS_E_FUNCTION_EXPECTED;
2357 if(jsfunc->ctx->state == SCRIPTSTATE_UNINITIALIZED || jsfunc->ctx->state == SCRIPTSTATE_CLOSED)
2358 return E_UNEXPECTED;
2360 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2361 hres = jsfunc->builtin_info->call(jsfunc->ctx, vthis, flags, argc, argv, r);
2363 return hres;
2366 HRESULT jsdisp_call(jsdisp_t *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
2368 dispex_prop_t *prop;
2370 prop = get_prop(disp, id);
2371 if(!prop)
2372 return DISP_E_MEMBERNOTFOUND;
2374 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, &disp->ctx->jscaller->IServiceProvider_iface);
2377 HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
2379 dispex_prop_t *prop;
2380 HRESULT hres;
2382 hres = find_prop_name_prot(disp, string_hash(name), name, FALSE, &prop);
2383 if(FAILED(hres))
2384 return hres;
2386 if(!prop || prop->type == PROP_DELETED)
2387 return JS_E_INVALID_PROPERTY;
2389 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, &disp->ctx->jscaller->IServiceProvider_iface);
2392 static HRESULT disp_invoke(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, DISPPARAMS *params, VARIANT *r,
2393 IServiceProvider *caller)
2395 IDispatchEx *dispex;
2396 EXCEPINFO ei;
2397 HRESULT hres;
2399 memset(&ei, 0, sizeof(ei));
2400 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2401 if(SUCCEEDED(hres)) {
2402 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, params, r, &ei, caller);
2403 IDispatchEx_Release(dispex);
2404 }else {
2405 UINT err = 0;
2407 if(flags == DISPATCH_CONSTRUCT) {
2408 WARN("IDispatch cannot be constructor\n");
2409 return DISP_E_MEMBERNOTFOUND;
2412 if(params->cNamedArgs == 1 && params->rgdispidNamedArgs[0] == DISPID_THIS) {
2413 params->cNamedArgs = 0;
2414 params->rgdispidNamedArgs = NULL;
2415 params->cArgs--;
2416 params->rgvarg++;
2419 TRACE("using IDispatch\n");
2420 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, params, r, &ei, &err);
2423 if(hres == DISP_E_EXCEPTION) {
2424 TRACE("DISP_E_EXCEPTION: %08lx %s %s\n", ei.scode, debugstr_w(ei.bstrSource), debugstr_w(ei.bstrDescription));
2425 reset_ei(ctx->ei);
2426 ctx->ei->error = (SUCCEEDED(ei.scode) || ei.scode == DISP_E_EXCEPTION) ? E_FAIL : ei.scode;
2427 if(ei.bstrSource)
2428 ctx->ei->source = jsstr_alloc_len(ei.bstrSource, SysStringLen(ei.bstrSource));
2429 if(ei.bstrDescription)
2430 ctx->ei->message = jsstr_alloc_len(ei.bstrDescription, SysStringLen(ei.bstrDescription));
2431 SysFreeString(ei.bstrSource);
2432 SysFreeString(ei.bstrDescription);
2433 SysFreeString(ei.bstrHelpFile);
2436 return hres;
2439 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret)
2441 VARIANT buf[6], retv;
2442 jsdisp_t *jsdisp;
2443 DISPPARAMS dp;
2444 unsigned i;
2445 HRESULT hres;
2447 jsdisp = iface_to_jsdisp(disp);
2448 if(jsdisp && jsdisp->ctx == ctx) {
2449 if(flags & DISPATCH_PROPERTYPUT) {
2450 FIXME("disp_call(propput) on builtin object\n");
2451 jsdisp_release(jsdisp);
2452 return E_FAIL;
2455 if(ctx != jsdisp->ctx)
2456 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2457 hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret);
2458 jsdisp_release(jsdisp);
2459 return hres;
2461 if(jsdisp)
2462 jsdisp_release(jsdisp);
2464 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2465 if(ret && argc)
2466 flags |= DISPATCH_PROPERTYGET;
2468 dp.cArgs = argc;
2470 if(flags & DISPATCH_PROPERTYPUT) {
2471 static DISPID propput_dispid = DISPID_PROPERTYPUT;
2473 dp.cNamedArgs = 1;
2474 dp.rgdispidNamedArgs = &propput_dispid;
2475 }else {
2476 dp.cNamedArgs = 0;
2477 dp.rgdispidNamedArgs = NULL;
2480 if(dp.cArgs > ARRAY_SIZE(buf)) {
2481 dp.rgvarg = malloc(argc * sizeof(VARIANT));
2482 if(!dp.rgvarg)
2483 return E_OUTOFMEMORY;
2484 }else {
2485 dp.rgvarg = buf;
2488 for(i=0; i<argc; i++) {
2489 hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1);
2490 if(FAILED(hres)) {
2491 while(i--)
2492 VariantClear(dp.rgvarg+argc-i-1);
2493 if(dp.rgvarg != buf)
2494 free(dp.rgvarg);
2495 return hres;
2499 V_VT(&retv) = VT_EMPTY;
2500 hres = disp_invoke(ctx, disp, id, flags, &dp, ret ? &retv : NULL, &ctx->jscaller->IServiceProvider_iface);
2502 for(i=0; i<argc; i++)
2503 VariantClear(dp.rgvarg+argc-i-1);
2504 if(dp.rgvarg != buf)
2505 free(dp.rgvarg);
2507 if(SUCCEEDED(hres) && ret)
2508 hres = variant_to_jsval(ctx, &retv, ret);
2509 VariantClear(&retv);
2510 return hres;
2513 HRESULT disp_call_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret)
2515 IDispatchEx *dispex;
2516 jsdisp_t *jsdisp;
2517 HRESULT hres;
2518 DISPID id;
2519 BSTR bstr;
2521 if((jsdisp = to_jsdisp(disp)) && jsdisp->ctx == ctx)
2522 return jsdisp_call_name(jsdisp, name, flags, argc, argv, ret);
2524 if(!(bstr = SysAllocString(name)))
2525 return E_OUTOFMEMORY;
2526 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2527 if(SUCCEEDED(hres) && dispex) {
2528 hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id);
2529 IDispatchEx_Release(dispex);
2530 }else {
2531 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
2533 SysFreeString(bstr);
2534 if(FAILED(hres))
2535 return hres;
2537 return disp_call(ctx, disp, id, flags, argc, argv, ret);
2540 HRESULT disp_call_value_with_caller(script_ctx_t *ctx, IDispatch *disp, jsval_t vthis, WORD flags, unsigned argc,
2541 jsval_t *argv, jsval_t *r, IServiceProvider *caller)
2543 VARIANT buf[6], retv, *args = buf;
2544 IDispatch *jsthis;
2545 jsdisp_t *jsdisp;
2546 DISPPARAMS dp;
2547 unsigned i;
2548 HRESULT hres = S_OK;
2550 static DISPID this_id = DISPID_THIS;
2552 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
2554 jsdisp = iface_to_jsdisp(disp);
2555 if(jsdisp && jsdisp->ctx == ctx) {
2556 hres = jsdisp_call_value(jsdisp, vthis, flags, argc, argv, r);
2557 jsdisp_release(jsdisp);
2558 return hres;
2560 if(jsdisp)
2561 jsdisp_release(jsdisp);
2563 if(is_object_instance(vthis) && (ctx->version < SCRIPTLANGUAGEVERSION_ES5 ||
2564 ((jsdisp = to_jsdisp(get_object(vthis))) && is_class(jsdisp, JSCLASS_OBJECT))))
2565 jsthis = get_object(vthis);
2566 else
2567 jsthis = NULL;
2569 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2570 if(r && argc && flags == DISPATCH_METHOD)
2571 flags |= DISPATCH_PROPERTYGET;
2573 if(jsthis) {
2574 dp.cArgs = argc + 1;
2575 dp.cNamedArgs = 1;
2576 dp.rgdispidNamedArgs = &this_id;
2577 }else {
2578 dp.cArgs = argc;
2579 dp.cNamedArgs = 0;
2580 dp.rgdispidNamedArgs = NULL;
2583 if(dp.cArgs > ARRAY_SIZE(buf) && !(args = malloc(dp.cArgs * sizeof(VARIANT))))
2584 return E_OUTOFMEMORY;
2585 dp.rgvarg = args;
2587 if(jsthis) {
2588 V_VT(dp.rgvarg) = VT_DISPATCH;
2589 V_DISPATCH(dp.rgvarg) = jsthis;
2592 for(i=0; SUCCEEDED(hres) && i < argc; i++)
2593 hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1);
2595 if(SUCCEEDED(hres)) {
2596 V_VT(&retv) = VT_EMPTY;
2597 hres = disp_invoke(ctx, disp, DISPID_VALUE, flags, &dp, r ? &retv : NULL, caller);
2600 for(i = 0; i < argc; i++)
2601 VariantClear(dp.rgvarg + dp.cArgs - i - 1);
2602 if(args != buf)
2603 free(args);
2605 if(FAILED(hres))
2606 return hres;
2607 if(!r)
2608 return S_OK;
2610 hres = variant_to_jsval(ctx, &retv, r);
2611 VariantClear(&retv);
2612 return hres;
2615 HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, BOOL throw, jsval_t val)
2617 dispex_prop_t *prop;
2618 HRESULT hres;
2620 if(obj->extensible)
2621 hres = ensure_prop_name(obj, name, flags, FALSE, &prop);
2622 else
2623 hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop);
2624 if(FAILED(hres))
2625 return hres;
2626 if(!prop || (prop->type == PROP_DELETED && !obj->extensible))
2627 return throw ? JS_E_INVALID_ACTION : S_OK;
2629 return prop_put(obj, prop, val);
2632 HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val)
2634 return jsdisp_propput(obj, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, FALSE, val);
2637 HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val)
2639 WCHAR buf[12];
2641 swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
2642 return jsdisp_propput(obj, buf, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, TRUE, val);
2645 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val)
2647 jsdisp_t *jsdisp;
2648 HRESULT hres;
2650 jsdisp = iface_to_jsdisp(disp);
2651 if(jsdisp && jsdisp->ctx == ctx) {
2652 dispex_prop_t *prop;
2654 prop = get_prop(jsdisp, id);
2655 if(prop)
2656 hres = prop_put(jsdisp, prop, val);
2657 else
2658 hres = DISP_E_MEMBERNOTFOUND;
2660 jsdisp_release(jsdisp);
2661 }else {
2662 DISPID dispid = DISPID_PROPERTYPUT;
2663 DWORD flags = DISPATCH_PROPERTYPUT;
2664 VARIANT var;
2665 DISPPARAMS dp = {&var, &dispid, 1, 1};
2667 if(jsdisp)
2668 jsdisp_release(jsdisp);
2669 hres = jsval_to_variant(val, &var);
2670 if(FAILED(hres))
2671 return hres;
2673 if(V_VT(&var) == VT_DISPATCH)
2674 flags |= DISPATCH_PROPERTYPUTREF;
2676 hres = disp_invoke(ctx, disp, id, flags, &dp, NULL, &ctx->jscaller->IServiceProvider_iface);
2677 VariantClear(&var);
2680 return hres;
2683 HRESULT disp_propput_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, jsval_t val)
2685 jsdisp_t *jsdisp;
2686 HRESULT hres;
2688 jsdisp = iface_to_jsdisp(disp);
2689 if(!jsdisp || jsdisp->ctx != ctx) {
2690 IDispatchEx *dispex;
2691 BSTR str;
2692 DISPID id;
2694 if(jsdisp)
2695 jsdisp_release(jsdisp);
2696 if(!(str = SysAllocString(name)))
2697 return E_OUTOFMEMORY;
2699 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2700 if(SUCCEEDED(hres)) {
2701 hres = IDispatchEx_GetDispID(dispex, str, make_grfdex(ctx, fdexNameEnsure|fdexNameCaseSensitive), &id);
2702 IDispatchEx_Release(dispex);
2703 }else {
2704 TRACE("using IDispatch\n");
2705 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &str, 1, 0, &id);
2707 SysFreeString(str);
2708 if(FAILED(hres))
2709 return hres;
2711 return disp_propput(ctx, disp, id, val);
2714 hres = jsdisp_propput_name(jsdisp, name, val);
2715 jsdisp_release(jsdisp);
2716 return hres;
2719 HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val)
2721 dispex_prop_t *prop;
2722 HRESULT hres;
2724 hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop);
2725 if(FAILED(hres))
2726 return hres;
2728 if(!prop || prop->type==PROP_DELETED) {
2729 *val = jsval_undefined();
2730 return S_OK;
2733 return prop_get(obj, prop, val);
2736 HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r)
2738 WCHAR name[12];
2739 dispex_prop_t *prop;
2740 HRESULT hres;
2742 swprintf(name, ARRAY_SIZE(name), L"%d", idx);
2744 hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop);
2745 if(FAILED(hres))
2746 return hres;
2748 if(!prop || prop->type==PROP_DELETED) {
2749 *r = jsval_undefined();
2750 return DISP_E_UNKNOWNNAME;
2753 return prop_get(obj, prop, r);
2756 HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val)
2758 dispex_prop_t *prop;
2760 prop = get_prop(jsdisp, id);
2761 if(!prop)
2762 return DISP_E_MEMBERNOTFOUND;
2764 return prop_get(jsdisp, prop, val);
2767 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val)
2769 DISPPARAMS dp = {NULL,NULL,0,0};
2770 jsdisp_t *jsdisp;
2771 VARIANT var;
2772 HRESULT hres;
2774 jsdisp = iface_to_jsdisp(disp);
2775 if(jsdisp && jsdisp->ctx == ctx) {
2776 hres = jsdisp_propget(jsdisp, id, val);
2777 jsdisp_release(jsdisp);
2778 return hres;
2780 if(jsdisp)
2781 jsdisp_release(jsdisp);
2783 V_VT(&var) = VT_EMPTY;
2784 hres = disp_invoke(ctx, disp, id, INVOKE_PROPERTYGET, &dp, &var, &ctx->jscaller->IServiceProvider_iface);
2785 if(SUCCEEDED(hres)) {
2786 hres = variant_to_jsval(ctx, &var, val);
2787 VariantClear(&var);
2789 return hres;
2792 HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
2794 WCHAR buf[12];
2795 dispex_prop_t *prop;
2796 BOOL b;
2797 HRESULT hres;
2799 swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
2801 hres = find_prop_name(obj, string_hash(buf), buf, FALSE, &prop);
2802 if(FAILED(hres) || !prop)
2803 return hres;
2805 hres = delete_prop(prop, &b);
2806 if(FAILED(hres))
2807 return hres;
2808 return b ? S_OK : JS_E_INVALID_ACTION;
2811 HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret)
2813 IDispatchEx *dispex;
2814 jsdisp_t *jsdisp;
2815 HRESULT hres;
2817 jsdisp = iface_to_jsdisp(disp);
2818 if(jsdisp) {
2819 dispex_prop_t *prop;
2821 prop = get_prop(jsdisp, id);
2822 if(prop)
2823 hres = delete_prop(prop, ret);
2824 else
2825 hres = DISP_E_MEMBERNOTFOUND;
2827 jsdisp_release(jsdisp);
2828 return hres;
2831 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2832 if(FAILED(hres)) {
2833 *ret = FALSE;
2834 return S_OK;
2837 hres = IDispatchEx_DeleteMemberByDispID(dispex, id);
2838 IDispatchEx_Release(dispex);
2839 if(FAILED(hres))
2840 return hres;
2842 *ret = hres == S_OK;
2843 return S_OK;
2846 HRESULT jsdisp_next_prop(jsdisp_t *obj, DISPID id, enum jsdisp_enum_type enum_type, DISPID *ret)
2848 dispex_prop_t *iter;
2849 DWORD idx = id;
2850 HRESULT hres;
2852 if(id == DISPID_STARTENUM || idx >= obj->prop_cnt) {
2853 hres = (enum_type == JSDISP_ENUM_ALL) ? fill_protrefs(obj) : fill_props(obj);
2854 if(FAILED(hres))
2855 return hres;
2856 if(id == DISPID_STARTENUM)
2857 idx = 0;
2858 if(idx >= obj->prop_cnt)
2859 return S_FALSE;
2862 for(iter = &obj->props[idx]; iter < obj->props + obj->prop_cnt; iter++) {
2863 if(iter->type == PROP_DELETED)
2864 continue;
2865 if(enum_type != JSDISP_ENUM_ALL && iter->type == PROP_PROTREF)
2866 continue;
2867 if(enum_type != JSDISP_ENUM_OWN && !(get_flags(obj, iter) & PROPF_ENUMERABLE))
2868 continue;
2869 *ret = prop_to_id(obj, iter);
2870 return S_OK;
2873 if(obj->ctx->html_mode)
2874 return jsdisp_next_prop(obj, prop_to_id(obj, iter - 1), enum_type, ret);
2876 return S_FALSE;
2879 HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret)
2881 IDispatchEx *dispex;
2882 jsdisp_t *jsdisp;
2883 BSTR bstr;
2884 HRESULT hres;
2886 jsdisp = iface_to_jsdisp(disp);
2887 if(jsdisp) {
2888 dispex_prop_t *prop;
2889 const WCHAR *ptr;
2891 ptr = jsstr_flatten(name);
2892 if(!ptr) {
2893 jsdisp_release(jsdisp);
2894 return E_OUTOFMEMORY;
2897 hres = find_prop_name(jsdisp, string_hash(ptr), ptr, FALSE, &prop);
2898 if(prop) {
2899 hres = delete_prop(prop, ret);
2900 }else {
2901 *ret = TRUE;
2902 hres = S_OK;
2905 jsdisp_release(jsdisp);
2906 return hres;
2909 bstr = SysAllocStringLen(NULL, jsstr_length(name));
2910 if(!bstr)
2911 return E_OUTOFMEMORY;
2912 jsstr_flush(name, bstr);
2914 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2915 if(SUCCEEDED(hres)) {
2916 hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive));
2917 if(SUCCEEDED(hres))
2918 *ret = hres == S_OK;
2919 IDispatchEx_Release(dispex);
2920 }else {
2921 DISPID id;
2923 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
2924 if(SUCCEEDED(hres)) {
2925 /* Property exists and we can't delete it from pure IDispatch interface, so return false. */
2926 *ret = FALSE;
2927 }else if(hres == DISP_E_UNKNOWNNAME) {
2928 /* Property doesn't exist, so nothing to delete */
2929 *ret = TRUE;
2930 hres = S_OK;
2934 SysFreeString(bstr);
2935 return hres;
2938 HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_only,
2939 property_desc_t *desc)
2941 dispex_prop_t *prop;
2942 HRESULT hres;
2944 hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop);
2945 if(FAILED(hres))
2946 return hres;
2948 if(!prop)
2949 return DISP_E_UNKNOWNNAME;
2951 memset(desc, 0, sizeof(*desc));
2953 switch(prop->type) {
2954 case PROP_BUILTIN:
2955 case PROP_JSVAL:
2956 case PROP_IDX:
2957 desc->mask |= PROPF_WRITABLE;
2958 desc->explicit_value = TRUE;
2959 if(!flags_only) {
2960 hres = prop_get(obj, prop, &desc->value);
2961 if(FAILED(hres))
2962 return hres;
2964 break;
2965 case PROP_ACCESSOR:
2966 desc->explicit_getter = desc->explicit_setter = TRUE;
2967 if(!flags_only) {
2968 desc->getter = prop->u.accessor.getter
2969 ? jsdisp_addref(prop->u.accessor.getter) : NULL;
2970 desc->setter = prop->u.accessor.setter
2971 ? jsdisp_addref(prop->u.accessor.setter) : NULL;
2973 break;
2974 default:
2975 return DISP_E_UNKNOWNNAME;
2978 desc->flags = prop->flags & (PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE);
2979 desc->mask |= PROPF_ENUMERABLE | PROPF_CONFIGURABLE;
2980 return S_OK;
2983 HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t *desc)
2985 dispex_prop_t *prop;
2986 HRESULT hres;
2988 hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop);
2989 if(FAILED(hres))
2990 return hres;
2992 if((!prop || prop->type == PROP_DELETED || prop->type == PROP_PROTREF) && !obj->extensible)
2993 return throw_error(obj->ctx, JS_E_OBJECT_NONEXTENSIBLE, name);
2995 if(!prop && !(prop = alloc_prop(obj, name, PROP_DELETED, 0)))
2996 return E_OUTOFMEMORY;
2998 if(prop->type == PROP_DELETED || prop->type == PROP_PROTREF) {
2999 prop->flags = desc->flags;
3000 if(desc->explicit_getter || desc->explicit_setter) {
3001 prop->type = PROP_ACCESSOR;
3002 prop->u.accessor.getter = desc->getter ? jsdisp_addref(desc->getter) : NULL;
3003 prop->u.accessor.setter = desc->setter ? jsdisp_addref(desc->setter) : NULL;
3004 TRACE("%s = accessor { get: %p set: %p }\n", debugstr_w(name),
3005 prop->u.accessor.getter, prop->u.accessor.setter);
3006 }else {
3007 prop->type = PROP_JSVAL;
3008 if(desc->explicit_value) {
3009 hres = jsval_copy(desc->value, &prop->u.val);
3010 if(FAILED(hres))
3011 return hres;
3012 }else {
3013 prop->u.val = jsval_undefined();
3015 TRACE("%s = %s\n", debugstr_w(name), debugstr_jsval(prop->u.val));
3017 return S_OK;
3020 TRACE("existing prop %s prop flags %lx desc flags %x desc mask %x\n", debugstr_w(name),
3021 prop->flags, desc->flags, desc->mask);
3023 if(!(prop->flags & PROPF_CONFIGURABLE)) {
3024 if(((desc->mask & PROPF_CONFIGURABLE) && (desc->flags & PROPF_CONFIGURABLE))
3025 || ((desc->mask & PROPF_ENUMERABLE)
3026 && ((desc->flags & PROPF_ENUMERABLE) != (prop->flags & PROPF_ENUMERABLE))))
3027 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3030 if(desc->explicit_value || (desc->mask & PROPF_WRITABLE)) {
3031 if(prop->type == PROP_ACCESSOR) {
3032 if(!(prop->flags & PROPF_CONFIGURABLE))
3033 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3034 if(prop->u.accessor.getter)
3035 jsdisp_release(prop->u.accessor.getter);
3036 if(prop->u.accessor.setter)
3037 jsdisp_release(prop->u.accessor.setter);
3039 prop->type = PROP_JSVAL;
3040 hres = jsval_copy(desc->value, &prop->u.val);
3041 if(FAILED(hres)) {
3042 prop->u.val = jsval_undefined();
3043 return hres;
3045 }else {
3046 if(!(prop->flags & PROPF_CONFIGURABLE) && !(prop->flags & PROPF_WRITABLE)) {
3047 if((desc->mask & PROPF_WRITABLE) && (desc->flags & PROPF_WRITABLE))
3048 return throw_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
3049 if(desc->explicit_value) {
3050 if(prop->type == PROP_JSVAL) {
3051 BOOL eq;
3052 hres = jsval_strict_equal(desc->value, prop->u.val, &eq);
3053 if(FAILED(hres))
3054 return hres;
3055 if(!eq)
3056 return throw_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
3057 }else {
3058 FIXME("redefinition of property type %d\n", prop->type);
3062 if(desc->explicit_value) {
3063 if(prop->type == PROP_JSVAL)
3064 jsval_release(prop->u.val);
3065 else
3066 prop->type = PROP_JSVAL;
3067 hres = jsval_copy(desc->value, &prop->u.val);
3068 if(FAILED(hres)) {
3069 prop->u.val = jsval_undefined();
3070 return hres;
3074 }else if(desc->explicit_getter || desc->explicit_setter) {
3075 if(prop->type != PROP_ACCESSOR) {
3076 if(!(prop->flags & PROPF_CONFIGURABLE))
3077 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3078 if(prop->type == PROP_JSVAL)
3079 jsval_release(prop->u.val);
3080 prop->type = PROP_ACCESSOR;
3081 prop->u.accessor.getter = prop->u.accessor.setter = NULL;
3082 }else if(!(prop->flags & PROPF_CONFIGURABLE)) {
3083 if((desc->explicit_getter && desc->getter != prop->u.accessor.getter)
3084 || (desc->explicit_setter && desc->setter != prop->u.accessor.setter))
3085 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3088 if(desc->explicit_getter) {
3089 if(prop->u.accessor.getter) {
3090 jsdisp_release(prop->u.accessor.getter);
3091 prop->u.accessor.getter = NULL;
3093 if(desc->getter)
3094 prop->u.accessor.getter = jsdisp_addref(desc->getter);
3096 if(desc->explicit_setter) {
3097 if(prop->u.accessor.setter) {
3098 jsdisp_release(prop->u.accessor.setter);
3099 prop->u.accessor.setter = NULL;
3101 if(desc->setter)
3102 prop->u.accessor.setter = jsdisp_addref(desc->setter);
3106 prop->flags = (prop->flags & ~desc->mask) | (desc->flags & desc->mask);
3107 return S_OK;
3110 HRESULT jsdisp_define_data_property(jsdisp_t *obj, const WCHAR *name, unsigned flags, jsval_t value)
3112 property_desc_t prop_desc = { flags, flags, TRUE };
3113 prop_desc.value = value;
3114 return jsdisp_define_property(obj, name, &prop_desc);
3117 HRESULT jsdisp_change_prototype(jsdisp_t *obj, jsdisp_t *proto)
3119 jsdisp_t *iter;
3120 DWORD i;
3122 if(obj->prototype == proto)
3123 return S_OK;
3124 if(!obj->extensible)
3125 return JS_E_CANNOT_CREATE_FOR_NONEXTENSIBLE;
3127 for(iter = proto; iter; iter = iter->prototype)
3128 if(iter == obj)
3129 return JS_E_CYCLIC_PROTO_VALUE;
3131 if(obj->prototype) {
3132 for(i = 0; i < obj->prop_cnt; i++)
3133 if(obj->props[i].type == PROP_PROTREF)
3134 obj->props[i].type = PROP_DELETED;
3135 jsdisp_release(obj->prototype);
3138 obj->prototype = proto;
3139 if(proto)
3140 jsdisp_addref(proto);
3141 return S_OK;
3144 void jsdisp_freeze(jsdisp_t *obj, BOOL seal)
3146 unsigned int i;
3148 for(i = 0; i < obj->prop_cnt; i++) {
3149 if(!seal && obj->props[i].type == PROP_JSVAL)
3150 obj->props[i].flags &= ~PROPF_WRITABLE;
3151 obj->props[i].flags &= ~PROPF_CONFIGURABLE;
3154 obj->extensible = FALSE;
3157 BOOL jsdisp_is_frozen(jsdisp_t *obj, BOOL sealed)
3159 unsigned int i;
3161 if(obj->extensible)
3162 return FALSE;
3164 for(i = 0; i < obj->prop_cnt; i++) {
3165 if(obj->props[i].type == PROP_JSVAL) {
3166 if(!sealed && (obj->props[i].flags & PROPF_WRITABLE))
3167 return FALSE;
3168 }else if(obj->props[i].type != PROP_ACCESSOR)
3169 continue;
3170 if(obj->props[i].flags & PROPF_CONFIGURABLE)
3171 return FALSE;
3174 return TRUE;
3177 HRESULT jsdisp_get_prop_name(jsdisp_t *obj, DISPID id, jsstr_t **r)
3179 dispex_prop_t *prop = get_prop(obj, id);
3181 if(!prop)
3182 return DISP_E_MEMBERNOTFOUND;
3184 *r = jsstr_alloc(prop->name);
3185 return *r ? S_OK : E_OUTOFMEMORY;