widl: Properly align name table entries.
[wine.git] / dlls / jscript / dispex.c
blob57882bf499e73f9d6aad30e5bb6f899365be2c10
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, IDispatch *jsthis, 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, prop_obj, 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_disp(jsthis),
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, jsthis ? jsthis : (IDispatch *)&This->IDispatchEx_iface, 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_jsdisp(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 continue;
676 case PROP_JSVAL:
677 jsval_release(prop->u.val);
678 break;
679 case PROP_ACCESSOR:
680 if(prop->u.accessor.getter)
681 jsdisp_release(prop->u.accessor.getter);
682 if(prop->u.accessor.setter)
683 jsdisp_release(prop->u.accessor.setter);
684 break;
685 default:
686 break;
688 prop->type = PROP_DELETED;
691 if(jsdisp->prototype) {
692 jsdisp_release(jsdisp->prototype);
693 jsdisp->prototype = NULL;
696 if(jsdisp->builtin_info->gc_traverse)
697 jsdisp->builtin_info->gc_traverse(NULL, GC_TRAVERSE_UNLINK, jsdisp);
703 * To deal with circular refcounts, a basic Garbage Collector is used with a variant of the
704 * mark-and-sweep algorithm that doesn't require knowing or traversing any specific "roots".
705 * This works based on the assumption that circular references can only happen when objects
706 * end up pointing to each other, and each other alone, without any external refs.
708 * An "external ref" is a ref to the object that's not from any other object. Example of such
709 * refs can be local variables, the script ctx (which keeps a ref to the global object), etc.
711 * At a high level, there are 3 logical passes done on the entire list of objects:
713 * 1. Speculatively decrease refcounts of each linked-to-object from each object. This ensures
714 * that the only remaining refcount on each object is the number of "external refs" to it.
715 * At the same time, mark all of the objects so that they can be potentially collected.
717 * 2. For each object with a non-zero "external refcount", clear the mark from step 1, and
718 * recursively traverse all linked objects from it, clearing their marks as well (regardless
719 * of their refcount), stopping a given path when the object is unmarked (and then going back
720 * up the GC stack). This basically unmarks all of the objects with "external refcounts"
721 * and those accessible from them, and only the leaked dangling objects will still be marked.
723 * 3. For each object that is marked, unlink all of the objects linked from it, because they
724 * are dangling in a circular refcount and not accessible. This should release them.
726 * During unlinking (GC_TRAVERSE_UNLINK), it is important that we unlink *all* linked objects
727 * from the object, to be certain that releasing the object later will not delete any other
728 * objects. Otherwise calculating the "next" object in the list becomes impossible.
730 * This collection process has to be done periodically, but can be pretty expensive so there
731 * has to be a balance between reclaiming dangling objects and performance.
734 struct gc_stack_chunk {
735 jsdisp_t *objects[1020];
736 struct gc_stack_chunk *prev;
739 struct gc_ctx {
740 struct gc_stack_chunk *chunk;
741 struct gc_stack_chunk *next;
742 unsigned idx;
745 static HRESULT gc_stack_push(struct gc_ctx *gc_ctx, jsdisp_t *obj)
747 if(!gc_ctx->idx) {
748 if(gc_ctx->next)
749 gc_ctx->chunk = gc_ctx->next;
750 else {
751 struct gc_stack_chunk *prev, *tmp = malloc(sizeof(*tmp));
752 if(!tmp)
753 return E_OUTOFMEMORY;
754 prev = gc_ctx->chunk;
755 gc_ctx->chunk = tmp;
756 gc_ctx->chunk->prev = prev;
758 gc_ctx->idx = ARRAY_SIZE(gc_ctx->chunk->objects);
759 gc_ctx->next = NULL;
761 gc_ctx->chunk->objects[--gc_ctx->idx] = obj;
762 return S_OK;
765 static jsdisp_t *gc_stack_pop(struct gc_ctx *gc_ctx)
767 jsdisp_t *obj = gc_ctx->chunk->objects[gc_ctx->idx];
769 if(++gc_ctx->idx == ARRAY_SIZE(gc_ctx->chunk->objects)) {
770 free(gc_ctx->next);
771 gc_ctx->next = gc_ctx->chunk;
772 gc_ctx->chunk = gc_ctx->chunk->prev;
773 gc_ctx->idx = 0;
775 return obj;
778 HRESULT gc_run(script_ctx_t *ctx)
780 /* Save original refcounts in a linked list of chunks */
781 struct chunk
783 struct chunk *next;
784 LONG ref[1020];
785 } *head, *chunk;
786 struct thread_data *thread_data = ctx->thread_data;
787 jsdisp_t *obj, *obj2, *link, *link2;
788 dispex_prop_t *prop, *props_end;
789 struct gc_ctx gc_ctx = { 0 };
790 unsigned chunk_idx = 0;
791 HRESULT hres = S_OK;
792 struct list *iter;
794 /* Prevent recursive calls from side-effects during unlinking (e.g. CollectGarbage from host object's Release) */
795 if(thread_data->gc_is_unlinking)
796 return S_OK;
798 if(!(head = malloc(sizeof(*head))))
799 return E_OUTOFMEMORY;
800 head->next = NULL;
801 chunk = head;
803 /* 1. Save actual refcounts and decrease them speculatively as-if we unlinked the objects */
804 LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
805 if(chunk_idx == ARRAY_SIZE(chunk->ref)) {
806 if(!(chunk->next = malloc(sizeof(*chunk)))) {
807 do {
808 chunk = head->next;
809 free(head);
810 head = chunk;
811 } while(head);
812 return E_OUTOFMEMORY;
814 chunk = chunk->next; chunk_idx = 0;
815 chunk->next = NULL;
817 chunk->ref[chunk_idx++] = obj->ref;
819 LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
820 for(prop = obj->props, props_end = prop + obj->prop_cnt; prop < props_end; prop++) {
821 switch(prop->type) {
822 case PROP_JSVAL:
823 if(is_object_instance(prop->u.val) && (link = to_jsdisp(get_object(prop->u.val))))
824 link->ref--;
825 break;
826 case PROP_ACCESSOR:
827 if(prop->u.accessor.getter)
828 prop->u.accessor.getter->ref--;
829 if(prop->u.accessor.setter)
830 prop->u.accessor.setter->ref--;
831 break;
832 default:
833 break;
837 if(obj->prototype)
838 obj->prototype->ref--;
839 if(obj->builtin_info->gc_traverse)
840 obj->builtin_info->gc_traverse(&gc_ctx, GC_TRAVERSE_SPECULATIVELY, obj);
841 obj->gc_marked = TRUE;
844 /* 2. Clear mark on objects with non-zero "external refcount" and all objects accessible from them */
845 LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
846 if(!obj->ref || !obj->gc_marked)
847 continue;
849 hres = gc_stack_push(&gc_ctx, NULL);
850 if(FAILED(hres))
851 break;
853 obj2 = obj;
856 obj2->gc_marked = FALSE;
858 for(prop = obj2->props, props_end = prop + obj2->prop_cnt; prop < props_end; prop++) {
859 switch(prop->type) {
860 case PROP_JSVAL:
861 if(!is_object_instance(prop->u.val))
862 continue;
863 link = to_jsdisp(get_object(prop->u.val));
864 link2 = NULL;
865 break;
866 case PROP_ACCESSOR:
867 link = prop->u.accessor.getter;
868 link2 = prop->u.accessor.setter;
869 break;
870 default:
871 continue;
873 if(link && link->gc_marked) {
874 hres = gc_stack_push(&gc_ctx, link);
875 if(FAILED(hres))
876 break;
878 if(link2 && link2->gc_marked) {
879 hres = gc_stack_push(&gc_ctx, link2);
880 if(FAILED(hres))
881 break;
885 if(FAILED(hres))
886 break;
888 if(obj2->prototype && obj2->prototype->gc_marked) {
889 hres = gc_stack_push(&gc_ctx, obj2->prototype);
890 if(FAILED(hres))
891 break;
894 if(obj2->builtin_info->gc_traverse) {
895 hres = obj2->builtin_info->gc_traverse(&gc_ctx, GC_TRAVERSE, obj2);
896 if(FAILED(hres))
897 break;
900 /* For weak refs, traverse paths accessible from it via the WeakMaps, if the WeakMaps are alive at this point.
901 We need both the key and the WeakMap for the entry to actually be accessible (and thus traversed). */
902 if(obj2->has_weak_refs) {
903 struct list *list = &RB_ENTRY_VALUE(rb_get(&thread_data->weak_refs, obj2), struct weak_refs_entry, entry)->list;
904 struct weakmap_entry *entry;
906 LIST_FOR_EACH_ENTRY(entry, list, struct weakmap_entry, weak_refs_entry) {
907 if(!entry->weakmap->gc_marked && is_object_instance(entry->value) && (link = to_jsdisp(get_object(entry->value)))) {
908 hres = gc_stack_push(&gc_ctx, link);
909 if(FAILED(hres))
910 break;
914 if(FAILED(hres))
915 break;
918 do obj2 = gc_stack_pop(&gc_ctx); while(obj2 && !obj2->gc_marked);
919 } while(obj2);
921 if(FAILED(hres)) {
922 do obj2 = gc_stack_pop(&gc_ctx); while(obj2);
923 break;
926 free(gc_ctx.next);
928 /* Restore */
929 chunk = head; chunk_idx = 0;
930 LIST_FOR_EACH_ENTRY(obj, &thread_data->objects, jsdisp_t, entry) {
931 obj->ref = chunk->ref[chunk_idx++];
932 if(chunk_idx == ARRAY_SIZE(chunk->ref)) {
933 struct chunk *next = chunk->next;
934 free(chunk);
935 chunk = next; chunk_idx = 0;
938 free(chunk);
940 if(FAILED(hres))
941 return hres;
943 /* 3. Remove all the links from the marked objects, since they are dangling */
944 thread_data->gc_is_unlinking = TRUE;
946 iter = list_head(&thread_data->objects);
947 while(iter) {
948 obj = LIST_ENTRY(iter, jsdisp_t, entry);
949 if(!obj->gc_marked) {
950 iter = list_next(&thread_data->objects, iter);
951 continue;
954 /* Grab it since it gets removed when unlinked */
955 jsdisp_addref(obj);
956 unlink_jsdisp(obj);
958 /* Releasing unlinked object should not delete any other object,
959 so we can safely obtain the next pointer now */
960 iter = list_next(&thread_data->objects, iter);
961 jsdisp_release(obj);
964 thread_data->gc_is_unlinking = FALSE;
965 thread_data->gc_last_tick = GetTickCount();
966 return S_OK;
969 HRESULT gc_process_linked_obj(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *obj, jsdisp_t *link, void **unlink_ref)
971 if(op == GC_TRAVERSE_UNLINK) {
972 *unlink_ref = NULL;
973 jsdisp_release(link);
974 return S_OK;
977 if(op == GC_TRAVERSE_SPECULATIVELY)
978 link->ref--;
979 else if(link->gc_marked)
980 return gc_stack_push(gc_ctx, link);
981 return S_OK;
984 HRESULT gc_process_linked_val(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *obj, jsval_t *link)
986 jsdisp_t *jsdisp;
988 if(op == GC_TRAVERSE_UNLINK) {
989 jsval_t val = *link;
990 *link = jsval_undefined();
991 jsval_release(val);
992 return S_OK;
995 if(!is_object_instance(*link) || !(jsdisp = to_jsdisp(get_object(*link))))
996 return S_OK;
997 if(op == GC_TRAVERSE_SPECULATIVELY)
998 jsdisp->ref--;
999 else if(jsdisp->gc_marked)
1000 return gc_stack_push(gc_ctx, jsdisp);
1001 return S_OK;
1006 struct typeinfo_func {
1007 dispex_prop_t *prop;
1008 function_code_t *code;
1011 typedef struct {
1012 ITypeInfo ITypeInfo_iface;
1013 ITypeComp ITypeComp_iface;
1014 LONG ref;
1016 UINT num_funcs;
1017 UINT num_vars;
1018 struct typeinfo_func *funcs;
1019 dispex_prop_t **vars;
1021 jsdisp_t *jsdisp;
1022 } ScriptTypeInfo;
1024 static struct typeinfo_func *get_func_from_memid(const ScriptTypeInfo *typeinfo, MEMBERID memid)
1026 UINT a = 0, b = typeinfo->num_funcs;
1028 while (a < b)
1030 UINT i = (a + b - 1) / 2;
1031 MEMBERID func_memid = prop_to_id(typeinfo->jsdisp, typeinfo->funcs[i].prop);
1033 if (memid == func_memid)
1034 return &typeinfo->funcs[i];
1035 else if (memid < func_memid)
1036 b = i;
1037 else
1038 a = i + 1;
1040 return NULL;
1043 static dispex_prop_t *get_var_from_memid(const ScriptTypeInfo *typeinfo, MEMBERID memid)
1045 UINT a = 0, b = typeinfo->num_vars;
1047 while (a < b)
1049 UINT i = (a + b - 1) / 2;
1050 MEMBERID var_memid = prop_to_id(typeinfo->jsdisp, typeinfo->vars[i]);
1052 if (memid == var_memid)
1053 return typeinfo->vars[i];
1054 else if (memid < var_memid)
1055 b = i;
1056 else
1057 a = i + 1;
1059 return NULL;
1062 static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface)
1064 return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeInfo_iface);
1067 static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeComp(ITypeComp *iface)
1069 return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeComp_iface);
1072 static HRESULT WINAPI ScriptTypeInfo_QueryInterface(ITypeInfo *iface, REFIID riid, void **ppv)
1074 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1076 if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ITypeInfo, riid))
1077 *ppv = &This->ITypeInfo_iface;
1078 else if (IsEqualGUID(&IID_ITypeComp, riid))
1079 *ppv = &This->ITypeComp_iface;
1080 else
1082 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1083 *ppv = NULL;
1084 return E_NOINTERFACE;
1087 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1088 IUnknown_AddRef((IUnknown*)*ppv);
1089 return S_OK;
1092 static ULONG WINAPI ScriptTypeInfo_AddRef(ITypeInfo *iface)
1094 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1095 LONG ref = InterlockedIncrement(&This->ref);
1097 TRACE("(%p) ref=%ld\n", This, ref);
1099 return ref;
1102 static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface)
1104 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1105 LONG ref = InterlockedDecrement(&This->ref);
1106 UINT i;
1108 TRACE("(%p) ref=%ld\n", This, ref);
1110 if (!ref)
1112 for (i = This->num_funcs; i--;)
1113 release_bytecode(This->funcs[i].code->bytecode);
1114 IDispatchEx_Release(&This->jsdisp->IDispatchEx_iface);
1115 free(This->funcs);
1116 free(This->vars);
1117 free(This);
1119 return ref;
1122 static HRESULT WINAPI ScriptTypeInfo_GetTypeAttr(ITypeInfo *iface, TYPEATTR **ppTypeAttr)
1124 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1125 TYPEATTR *attr;
1127 TRACE("(%p)->(%p)\n", This, ppTypeAttr);
1129 if (!ppTypeAttr) return E_INVALIDARG;
1131 attr = calloc(1, sizeof(*attr));
1132 if (!attr) return E_OUTOFMEMORY;
1134 attr->guid = GUID_JScriptTypeInfo;
1135 attr->lcid = LOCALE_USER_DEFAULT;
1136 attr->memidConstructor = MEMBERID_NIL;
1137 attr->memidDestructor = MEMBERID_NIL;
1138 attr->cbSizeInstance = 4;
1139 attr->typekind = TKIND_DISPATCH;
1140 attr->cFuncs = This->num_funcs;
1141 attr->cVars = This->num_vars;
1142 attr->cImplTypes = 1;
1143 attr->cbSizeVft = sizeof(IDispatchVtbl);
1144 attr->cbAlignment = 4;
1145 attr->wTypeFlags = TYPEFLAG_FDISPATCHABLE;
1146 attr->wMajorVerNum = JSCRIPT_MAJOR_VERSION;
1147 attr->wMinorVerNum = JSCRIPT_MINOR_VERSION;
1149 *ppTypeAttr = attr;
1150 return S_OK;
1153 static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **ppTComp)
1155 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1157 TRACE("(%p)->(%p)\n", This, ppTComp);
1159 if (!ppTComp) return E_INVALIDARG;
1161 *ppTComp = &This->ITypeComp_iface;
1162 ITypeInfo_AddRef(iface);
1163 return S_OK;
1166 static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, FUNCDESC **ppFuncDesc)
1168 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1169 struct typeinfo_func *func;
1170 FUNCDESC *desc;
1171 unsigned i;
1173 TRACE("(%p)->(%u %p)\n", This, index, ppFuncDesc);
1175 if (!ppFuncDesc) return E_INVALIDARG;
1176 if (index >= This->num_funcs) return TYPE_E_ELEMENTNOTFOUND;
1177 func = &This->funcs[index];
1179 /* Store the parameter array after the FUNCDESC structure */
1180 desc = calloc(1, sizeof(*desc) + sizeof(ELEMDESC) * func->code->param_cnt);
1181 if (!desc) return E_OUTOFMEMORY;
1183 desc->memid = prop_to_id(This->jsdisp, func->prop);
1184 desc->funckind = FUNC_DISPATCH;
1185 desc->invkind = INVOKE_FUNC;
1186 desc->callconv = CC_STDCALL;
1187 desc->cParams = func->code->param_cnt;
1188 desc->elemdescFunc.tdesc.vt = VT_VARIANT;
1190 if (func->code->param_cnt) desc->lprgelemdescParam = (ELEMDESC*)(desc + 1);
1191 for (i = 0; i < func->code->param_cnt; i++)
1192 desc->lprgelemdescParam[i].tdesc.vt = VT_VARIANT;
1194 *ppFuncDesc = desc;
1195 return S_OK;
1198 static HRESULT WINAPI ScriptTypeInfo_GetVarDesc(ITypeInfo *iface, UINT index, VARDESC **ppVarDesc)
1200 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1201 VARDESC *desc;
1203 TRACE("(%p)->(%u %p)\n", This, index, ppVarDesc);
1205 if (!ppVarDesc) return E_INVALIDARG;
1206 if (index >= This->num_vars) return TYPE_E_ELEMENTNOTFOUND;
1208 desc = calloc(1, sizeof(*desc));
1209 if (!desc) return E_OUTOFMEMORY;
1211 desc->memid = prop_to_id(This->jsdisp, This->vars[index]);
1212 desc->varkind = VAR_DISPATCH;
1213 desc->elemdescVar.tdesc.vt = VT_VARIANT;
1215 *ppVarDesc = desc;
1216 return S_OK;
1219 static HRESULT WINAPI ScriptTypeInfo_GetNames(ITypeInfo *iface, MEMBERID memid, BSTR *rgBstrNames,
1220 UINT cMaxNames, UINT *pcNames)
1222 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1223 struct typeinfo_func *func;
1224 ITypeInfo *disp_typeinfo;
1225 dispex_prop_t *var;
1226 HRESULT hr;
1227 UINT i = 0;
1229 TRACE("(%p)->(%ld %p %u %p)\n", This, memid, rgBstrNames, cMaxNames, pcNames);
1231 if (!rgBstrNames || !pcNames) return E_INVALIDARG;
1232 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
1234 func = get_func_from_memid(This, memid);
1235 if (!func)
1237 var = get_var_from_memid(This, memid);
1238 if (!var)
1240 hr = get_dispatch_typeinfo(&disp_typeinfo);
1241 if (FAILED(hr)) return hr;
1243 return ITypeInfo_GetNames(disp_typeinfo, memid, rgBstrNames, cMaxNames, pcNames);
1247 *pcNames = 0;
1248 if (!cMaxNames) return S_OK;
1250 rgBstrNames[0] = SysAllocString(func ? func->prop->name : var->name);
1251 if (!rgBstrNames[0]) return E_OUTOFMEMORY;
1252 i++;
1254 if (func)
1256 unsigned num = min(cMaxNames, func->code->param_cnt + 1);
1258 for (; i < num; i++)
1260 if (!(rgBstrNames[i] = SysAllocString(func->code->params[i - 1])))
1262 do SysFreeString(rgBstrNames[--i]); while (i);
1263 return E_OUTOFMEMORY;
1268 *pcNames = i;
1269 return S_OK;
1272 static HRESULT WINAPI ScriptTypeInfo_GetRefTypeOfImplType(ITypeInfo *iface, UINT index, HREFTYPE *pRefType)
1274 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1276 TRACE("(%p)->(%u %p)\n", This, index, pRefType);
1278 /* We only inherit from IDispatch */
1279 if (!pRefType) return E_INVALIDARG;
1280 if (index != 0) return TYPE_E_ELEMENTNOTFOUND;
1282 *pRefType = 1;
1283 return S_OK;
1286 static HRESULT WINAPI ScriptTypeInfo_GetImplTypeFlags(ITypeInfo *iface, UINT index, INT *pImplTypeFlags)
1288 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1290 TRACE("(%p)->(%u %p)\n", This, index, pImplTypeFlags);
1292 if (!pImplTypeFlags) return E_INVALIDARG;
1293 if (index != 0) return TYPE_E_ELEMENTNOTFOUND;
1295 *pImplTypeFlags = 0;
1296 return S_OK;
1299 static HRESULT WINAPI ScriptTypeInfo_GetIDsOfNames(ITypeInfo *iface, LPOLESTR *rgszNames, UINT cNames,
1300 MEMBERID *pMemId)
1302 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1303 ITypeInfo *disp_typeinfo;
1304 const WCHAR *name;
1305 HRESULT hr = S_OK;
1306 int i, j, arg;
1308 TRACE("(%p)->(%p %u %p)\n", This, rgszNames, cNames, pMemId);
1310 if (!rgszNames || !cNames || !pMemId) return E_INVALIDARG;
1312 for (i = 0; i < cNames; i++) pMemId[i] = MEMBERID_NIL;
1313 name = rgszNames[0];
1315 for (i = 0; i < This->num_funcs; i++)
1317 struct typeinfo_func *func = &This->funcs[i];
1319 if (wcsicmp(name, func->prop->name)) continue;
1320 pMemId[0] = prop_to_id(This->jsdisp, func->prop);
1322 for (j = 1; j < cNames; j++)
1324 name = rgszNames[j];
1325 for (arg = func->code->param_cnt; --arg >= 0;)
1326 if (!wcsicmp(name, func->code->params[arg]))
1327 break;
1328 if (arg >= 0)
1329 pMemId[j] = arg;
1330 else
1331 hr = DISP_E_UNKNOWNNAME;
1333 return hr;
1336 for (i = 0; i < This->num_vars; i++)
1338 dispex_prop_t *var = This->vars[i];
1340 if (wcsicmp(name, var->name)) continue;
1341 pMemId[0] = prop_to_id(This->jsdisp, var);
1342 return S_OK;
1345 /* Look into the inherited IDispatch */
1346 hr = get_dispatch_typeinfo(&disp_typeinfo);
1347 if (FAILED(hr)) return hr;
1349 return ITypeInfo_GetIDsOfNames(disp_typeinfo, rgszNames, cNames, pMemId);
1352 static HRESULT WINAPI ScriptTypeInfo_Invoke(ITypeInfo *iface, PVOID pvInstance, MEMBERID memid, WORD wFlags,
1353 DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1355 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1356 ITypeInfo *disp_typeinfo;
1357 IDispatch *disp;
1358 HRESULT hr;
1360 TRACE("(%p)->(%p %ld %d %p %p %p %p)\n", This, pvInstance, memid, wFlags,
1361 pDispParams, pVarResult, pExcepInfo, puArgErr);
1363 if (!pvInstance) return E_INVALIDARG;
1364 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
1366 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1368 hr = get_dispatch_typeinfo(&disp_typeinfo);
1369 if (FAILED(hr)) return hr;
1371 return ITypeInfo_Invoke(disp_typeinfo, pvInstance, memid, wFlags, pDispParams,
1372 pVarResult, pExcepInfo, puArgErr);
1375 hr = IUnknown_QueryInterface((IUnknown*)pvInstance, &IID_IDispatch, (void**)&disp);
1376 if (FAILED(hr)) return hr;
1378 hr = IDispatch_Invoke(disp, memid, &IID_NULL, LOCALE_USER_DEFAULT, wFlags,
1379 pDispParams, pVarResult, pExcepInfo, puArgErr);
1380 IDispatch_Release(disp);
1382 return hr;
1385 static HRESULT WINAPI ScriptTypeInfo_GetDocumentation(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrName,
1386 BSTR *pBstrDocString, DWORD *pdwHelpContext, BSTR *pBstrHelpFile)
1388 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1389 struct typeinfo_func *func;
1390 ITypeInfo *disp_typeinfo;
1391 dispex_prop_t *var;
1392 HRESULT hr;
1394 TRACE("(%p)->(%ld %p %p %p %p)\n", This, memid, pBstrName, pBstrDocString, pdwHelpContext, pBstrHelpFile);
1396 if (pBstrDocString) *pBstrDocString = NULL;
1397 if (pdwHelpContext) *pdwHelpContext = 0;
1398 if (pBstrHelpFile) *pBstrHelpFile = NULL;
1400 if (memid == MEMBERID_NIL)
1402 if (pBstrName && !(*pBstrName = SysAllocString(L"JScriptTypeInfo")))
1403 return E_OUTOFMEMORY;
1404 if (pBstrDocString &&
1405 !(*pBstrDocString = SysAllocString(L"JScript Type Info")))
1407 if (pBstrName) SysFreeString(*pBstrName);
1408 return E_OUTOFMEMORY;
1410 return S_OK;
1412 if (memid <= 0) return TYPE_E_ELEMENTNOTFOUND;
1414 func = get_func_from_memid(This, memid);
1415 if (!func)
1417 var = get_var_from_memid(This, memid);
1418 if (!var)
1420 hr = get_dispatch_typeinfo(&disp_typeinfo);
1421 if (FAILED(hr)) return hr;
1423 return ITypeInfo_GetDocumentation(disp_typeinfo, memid, pBstrName, pBstrDocString,
1424 pdwHelpContext, pBstrHelpFile);
1428 if (pBstrName)
1430 *pBstrName = SysAllocString(func ? func->prop->name : var->name);
1432 if (!*pBstrName)
1433 return E_OUTOFMEMORY;
1435 return S_OK;
1438 static HRESULT WINAPI ScriptTypeInfo_GetDllEntry(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind,
1439 BSTR *pBstrDllName, BSTR *pBstrName, WORD *pwOrdinal)
1441 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1442 ITypeInfo *disp_typeinfo;
1443 HRESULT hr;
1445 TRACE("(%p)->(%ld %d %p %p %p)\n", This, memid, invKind, pBstrDllName, pBstrName, pwOrdinal);
1447 if (pBstrDllName) *pBstrDllName = NULL;
1448 if (pBstrName) *pBstrName = NULL;
1449 if (pwOrdinal) *pwOrdinal = 0;
1451 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1453 hr = get_dispatch_typeinfo(&disp_typeinfo);
1454 if (FAILED(hr)) return hr;
1456 return ITypeInfo_GetDllEntry(disp_typeinfo, memid, invKind, pBstrDllName, pBstrName, pwOrdinal);
1458 return TYPE_E_BADMODULEKIND;
1461 static HRESULT WINAPI ScriptTypeInfo_GetRefTypeInfo(ITypeInfo *iface, HREFTYPE hRefType, ITypeInfo **ppTInfo)
1463 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1464 HRESULT hr;
1466 TRACE("(%p)->(%lx %p)\n", This, hRefType, ppTInfo);
1468 if (!ppTInfo || (INT)hRefType < 0) return E_INVALIDARG;
1470 if (hRefType & ~3) return E_FAIL;
1471 if (hRefType & 1)
1473 hr = get_dispatch_typeinfo(ppTInfo);
1474 if (FAILED(hr)) return hr;
1476 else
1477 *ppTInfo = iface;
1479 ITypeInfo_AddRef(*ppTInfo);
1480 return S_OK;
1483 static HRESULT WINAPI ScriptTypeInfo_AddressOfMember(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind, PVOID *ppv)
1485 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1486 ITypeInfo *disp_typeinfo;
1487 HRESULT hr;
1489 TRACE("(%p)->(%ld %d %p)\n", This, memid, invKind, ppv);
1491 if (!ppv) return E_INVALIDARG;
1492 *ppv = NULL;
1494 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1496 hr = get_dispatch_typeinfo(&disp_typeinfo);
1497 if (FAILED(hr)) return hr;
1499 return ITypeInfo_AddressOfMember(disp_typeinfo, memid, invKind, ppv);
1501 return TYPE_E_BADMODULEKIND;
1504 static HRESULT WINAPI ScriptTypeInfo_CreateInstance(ITypeInfo *iface, IUnknown *pUnkOuter, REFIID riid, PVOID *ppvObj)
1506 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1508 TRACE("(%p)->(%p %s %p)\n", This, pUnkOuter, debugstr_guid(riid), ppvObj);
1510 if (!ppvObj) return E_INVALIDARG;
1512 *ppvObj = NULL;
1513 return TYPE_E_BADMODULEKIND;
1516 static HRESULT WINAPI ScriptTypeInfo_GetMops(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrMops)
1518 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1519 ITypeInfo *disp_typeinfo;
1520 HRESULT hr;
1522 TRACE("(%p)->(%ld %p)\n", This, memid, pBstrMops);
1524 if (!pBstrMops) return E_INVALIDARG;
1526 if (!get_func_from_memid(This, memid) && !get_var_from_memid(This, memid))
1528 hr = get_dispatch_typeinfo(&disp_typeinfo);
1529 if (FAILED(hr)) return hr;
1531 return ITypeInfo_GetMops(disp_typeinfo, memid, pBstrMops);
1534 *pBstrMops = NULL;
1535 return S_OK;
1538 static HRESULT WINAPI ScriptTypeInfo_GetContainingTypeLib(ITypeInfo *iface, ITypeLib **ppTLib, UINT *pIndex)
1540 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1542 FIXME("(%p)->(%p %p)\n", This, ppTLib, pIndex);
1544 return E_NOTIMPL;
1547 static void WINAPI ScriptTypeInfo_ReleaseTypeAttr(ITypeInfo *iface, TYPEATTR *pTypeAttr)
1549 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1551 TRACE("(%p)->(%p)\n", This, pTypeAttr);
1553 free(pTypeAttr);
1556 static void WINAPI ScriptTypeInfo_ReleaseFuncDesc(ITypeInfo *iface, FUNCDESC *pFuncDesc)
1558 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1560 TRACE("(%p)->(%p)\n", This, pFuncDesc);
1562 free(pFuncDesc);
1565 static void WINAPI ScriptTypeInfo_ReleaseVarDesc(ITypeInfo *iface, VARDESC *pVarDesc)
1567 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface);
1569 TRACE("(%p)->(%p)\n", This, pVarDesc);
1571 free(pVarDesc);
1574 static const ITypeInfoVtbl ScriptTypeInfoVtbl = {
1575 ScriptTypeInfo_QueryInterface,
1576 ScriptTypeInfo_AddRef,
1577 ScriptTypeInfo_Release,
1578 ScriptTypeInfo_GetTypeAttr,
1579 ScriptTypeInfo_GetTypeComp,
1580 ScriptTypeInfo_GetFuncDesc,
1581 ScriptTypeInfo_GetVarDesc,
1582 ScriptTypeInfo_GetNames,
1583 ScriptTypeInfo_GetRefTypeOfImplType,
1584 ScriptTypeInfo_GetImplTypeFlags,
1585 ScriptTypeInfo_GetIDsOfNames,
1586 ScriptTypeInfo_Invoke,
1587 ScriptTypeInfo_GetDocumentation,
1588 ScriptTypeInfo_GetDllEntry,
1589 ScriptTypeInfo_GetRefTypeInfo,
1590 ScriptTypeInfo_AddressOfMember,
1591 ScriptTypeInfo_CreateInstance,
1592 ScriptTypeInfo_GetMops,
1593 ScriptTypeInfo_GetContainingTypeLib,
1594 ScriptTypeInfo_ReleaseTypeAttr,
1595 ScriptTypeInfo_ReleaseFuncDesc,
1596 ScriptTypeInfo_ReleaseVarDesc
1599 static HRESULT WINAPI ScriptTypeComp_QueryInterface(ITypeComp *iface, REFIID riid, void **ppv)
1601 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1602 return ITypeInfo_QueryInterface(&This->ITypeInfo_iface, riid, ppv);
1605 static ULONG WINAPI ScriptTypeComp_AddRef(ITypeComp *iface)
1607 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1608 return ITypeInfo_AddRef(&This->ITypeInfo_iface);
1611 static ULONG WINAPI ScriptTypeComp_Release(ITypeComp *iface)
1613 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1614 return ITypeInfo_Release(&This->ITypeInfo_iface);
1617 static HRESULT WINAPI ScriptTypeComp_Bind(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal, WORD wFlags,
1618 ITypeInfo **ppTInfo, DESCKIND *pDescKind, BINDPTR *pBindPtr)
1620 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1621 UINT flags = wFlags ? wFlags : ~0;
1622 ITypeInfo *disp_typeinfo;
1623 ITypeComp *disp_typecomp;
1624 HRESULT hr;
1625 UINT i;
1627 TRACE("(%p)->(%s %08lx %d %p %p %p)\n", This, debugstr_w(szName), lHashVal,
1628 wFlags, ppTInfo, pDescKind, pBindPtr);
1630 if (!szName || !ppTInfo || !pDescKind || !pBindPtr)
1631 return E_INVALIDARG;
1633 for (i = 0; i < This->num_funcs; i++)
1635 if (wcsicmp(szName, This->funcs[i].prop->name)) continue;
1636 if (!(flags & INVOKE_FUNC)) return TYPE_E_TYPEMISMATCH;
1638 hr = ITypeInfo_GetFuncDesc(&This->ITypeInfo_iface, i, &pBindPtr->lpfuncdesc);
1639 if (FAILED(hr)) return hr;
1641 *pDescKind = DESCKIND_FUNCDESC;
1642 *ppTInfo = &This->ITypeInfo_iface;
1643 ITypeInfo_AddRef(*ppTInfo);
1644 return S_OK;
1647 for (i = 0; i < This->num_vars; i++)
1649 if (wcsicmp(szName, This->vars[i]->name)) continue;
1650 if (!(flags & INVOKE_PROPERTYGET)) return TYPE_E_TYPEMISMATCH;
1652 hr = ITypeInfo_GetVarDesc(&This->ITypeInfo_iface, i, &pBindPtr->lpvardesc);
1653 if (FAILED(hr)) return hr;
1655 *pDescKind = DESCKIND_VARDESC;
1656 *ppTInfo = &This->ITypeInfo_iface;
1657 ITypeInfo_AddRef(*ppTInfo);
1658 return S_OK;
1661 /* Look into the inherited IDispatch */
1662 hr = get_dispatch_typeinfo(&disp_typeinfo);
1663 if (FAILED(hr)) return hr;
1665 hr = ITypeInfo_GetTypeComp(disp_typeinfo, &disp_typecomp);
1666 if (FAILED(hr)) return hr;
1668 hr = ITypeComp_Bind(disp_typecomp, szName, lHashVal, wFlags, ppTInfo, pDescKind, pBindPtr);
1669 ITypeComp_Release(disp_typecomp);
1670 return hr;
1673 static HRESULT WINAPI ScriptTypeComp_BindType(ITypeComp *iface, LPOLESTR szName, ULONG lHashVal,
1674 ITypeInfo **ppTInfo, ITypeComp **ppTComp)
1676 ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeComp(iface);
1677 ITypeInfo *disp_typeinfo;
1678 ITypeComp *disp_typecomp;
1679 HRESULT hr;
1681 TRACE("(%p)->(%s %08lx %p %p)\n", This, debugstr_w(szName), lHashVal, ppTInfo, ppTComp);
1683 if (!szName || !ppTInfo || !ppTComp)
1684 return E_INVALIDARG;
1686 /* Look into the inherited IDispatch */
1687 hr = get_dispatch_typeinfo(&disp_typeinfo);
1688 if (FAILED(hr)) return hr;
1690 hr = ITypeInfo_GetTypeComp(disp_typeinfo, &disp_typecomp);
1691 if (FAILED(hr)) return hr;
1693 hr = ITypeComp_BindType(disp_typecomp, szName, lHashVal, ppTInfo, ppTComp);
1694 ITypeComp_Release(disp_typecomp);
1695 return hr;
1698 static const ITypeCompVtbl ScriptTypeCompVtbl = {
1699 ScriptTypeComp_QueryInterface,
1700 ScriptTypeComp_AddRef,
1701 ScriptTypeComp_Release,
1702 ScriptTypeComp_Bind,
1703 ScriptTypeComp_BindType
1706 static inline jsdisp_t *impl_from_IDispatchEx(IDispatchEx *iface)
1708 return CONTAINING_RECORD(iface, jsdisp_t, IDispatchEx_iface);
1711 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
1713 jsdisp_t *This = impl_from_IDispatchEx(iface);
1715 if(IsEqualGUID(&IID_IUnknown, riid)) {
1716 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
1717 *ppv = &This->IDispatchEx_iface;
1718 }else if(IsEqualGUID(&IID_IDispatch, riid)) {
1719 TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
1720 *ppv = &This->IDispatchEx_iface;
1721 }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
1722 TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
1723 *ppv = &This->IDispatchEx_iface;
1724 }else {
1725 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1726 *ppv = NULL;
1727 return E_NOINTERFACE;
1730 jsdisp_addref(This);
1731 return S_OK;
1734 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
1736 jsdisp_t *This = impl_from_IDispatchEx(iface);
1737 jsdisp_addref(This);
1738 return This->ref;
1741 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
1743 jsdisp_t *This = impl_from_IDispatchEx(iface);
1744 ULONG ref = --This->ref;
1745 TRACE("(%p) ref=%ld\n", This, ref);
1746 if(!ref)
1747 jsdisp_free(This);
1748 return ref;
1751 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
1753 jsdisp_t *This = impl_from_IDispatchEx(iface);
1755 TRACE("(%p)->(%p)\n", This, pctinfo);
1757 *pctinfo = 1;
1758 return S_OK;
1761 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid,
1762 ITypeInfo **ppTInfo)
1764 jsdisp_t *This = impl_from_IDispatchEx(iface);
1765 dispex_prop_t *prop, *cur, *end, **typevar;
1766 UINT num_funcs = 0, num_vars = 0;
1767 struct typeinfo_func *typefunc;
1768 function_code_t *func_code;
1769 ScriptTypeInfo *typeinfo;
1770 unsigned pos;
1772 TRACE("(%p)->(%u %lu %p)\n", This, iTInfo, lcid, ppTInfo);
1774 if (iTInfo != 0) return DISP_E_BADINDEX;
1776 for (prop = This->props, end = prop + This->prop_cnt; prop != end; prop++)
1778 if (prop->type != PROP_JSVAL || !(prop->flags & PROPF_ENUMERABLE))
1779 continue;
1781 /* If two identifiers differ only by case, the TypeInfo fails */
1782 pos = This->props[get_props_idx(This, prop->hash)].bucket_head;
1783 while (pos != ~0)
1785 cur = This->props + pos;
1787 if (prop->hash == cur->hash && prop != cur &&
1788 cur->type == PROP_JSVAL && (cur->flags & PROPF_ENUMERABLE) &&
1789 !wcsicmp(prop->name, cur->name))
1791 return TYPE_E_AMBIGUOUSNAME;
1793 pos = cur->bucket_next;
1796 if (is_function_prop(prop))
1798 if (Function_get_code(as_jsdisp(get_object(prop->u.val))))
1799 num_funcs++;
1801 else num_vars++;
1804 if (!(typeinfo = malloc(sizeof(*typeinfo))))
1805 return E_OUTOFMEMORY;
1807 typeinfo->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl;
1808 typeinfo->ITypeComp_iface.lpVtbl = &ScriptTypeCompVtbl;
1809 typeinfo->ref = 1;
1810 typeinfo->num_vars = num_vars;
1811 typeinfo->num_funcs = num_funcs;
1812 typeinfo->jsdisp = This;
1814 typeinfo->funcs = malloc(sizeof(*typeinfo->funcs) * num_funcs);
1815 if (!typeinfo->funcs)
1817 free(typeinfo);
1818 return E_OUTOFMEMORY;
1821 typeinfo->vars = malloc(sizeof(*typeinfo->vars) * num_vars);
1822 if (!typeinfo->vars)
1824 free(typeinfo->funcs);
1825 free(typeinfo);
1826 return E_OUTOFMEMORY;
1829 typefunc = typeinfo->funcs;
1830 typevar = typeinfo->vars;
1831 for (prop = This->props; prop != end; prop++)
1833 if (prop->type != PROP_JSVAL || !(prop->flags & PROPF_ENUMERABLE))
1834 continue;
1836 if (is_function_prop(prop))
1838 func_code = Function_get_code(as_jsdisp(get_object(prop->u.val)));
1839 if (!func_code) continue;
1841 typefunc->prop = prop;
1842 typefunc->code = func_code;
1843 typefunc++;
1845 /* The function may be deleted, so keep a ref */
1846 bytecode_addref(func_code->bytecode);
1848 else
1849 *typevar++ = prop;
1852 /* Keep a ref to the props and their names */
1853 IDispatchEx_AddRef(&This->IDispatchEx_iface);
1855 *ppTInfo = &typeinfo->ITypeInfo_iface;
1856 return S_OK;
1859 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
1860 LPOLESTR *rgszNames, UINT cNames, LCID lcid,
1861 DISPID *rgDispId)
1863 jsdisp_t *This = impl_from_IDispatchEx(iface);
1864 UINT i;
1865 HRESULT hres;
1867 TRACE("(%p)->(%s %p %u %lu %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1868 lcid, rgDispId);
1870 if(cNames == 0)
1871 return S_OK;
1873 hres = jsdisp_get_id(This, rgszNames[0], 0, rgDispId);
1874 if(FAILED(hres))
1875 return hres;
1877 /* DISPIDs for parameters don't seem to be supported */
1878 if(cNames > 1) {
1879 for(i = 1; i < cNames; i++)
1880 rgDispId[i] = DISPID_UNKNOWN;
1881 hres = DISP_E_UNKNOWNNAME;
1884 return hres;
1887 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
1888 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1889 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1891 jsdisp_t *This = impl_from_IDispatchEx(iface);
1893 TRACE("(%p)->(%ld %s %ld %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1894 lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1896 return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags,
1897 pDispParams, pVarResult, pExcepInfo, NULL);
1900 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
1902 jsdisp_t *This = impl_from_IDispatchEx(iface);
1904 TRACE("(%p)->(%s %lx %p)\n", This, debugstr_w(bstrName), grfdex, pid);
1906 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK)) {
1907 FIXME("Unsupported grfdex %lx\n", grfdex);
1908 return E_NOTIMPL;
1911 return jsdisp_get_id(This, bstrName, grfdex, pid);
1914 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
1915 VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
1917 jsdisp_t *This = impl_from_IDispatchEx(iface);
1918 IServiceProvider *prev_caller;
1919 dispex_prop_t *prop;
1920 jsexcept_t ei;
1921 HRESULT hres;
1923 TRACE("(%p)->(%lx %lx %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
1925 if(pvarRes)
1926 V_VT(pvarRes) = VT_EMPTY;
1928 prop = get_prop(This, id);
1929 if(!prop && id != DISPID_VALUE) {
1930 TRACE("invalid id\n");
1931 return DISP_E_MEMBERNOTFOUND;
1934 enter_script(This->ctx, &ei);
1936 prev_caller = This->ctx->jscaller->caller;
1937 This->ctx->jscaller->caller = pspCaller;
1938 if(pspCaller)
1939 IServiceProvider_AddRef(pspCaller);
1941 switch(wFlags) {
1942 case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
1943 wFlags = DISPATCH_METHOD;
1944 /* fall through */
1945 case DISPATCH_METHOD:
1946 case DISPATCH_CONSTRUCT: {
1947 jsval_t *argv, buf[6], r;
1948 IDispatch *passed_this;
1949 unsigned argc;
1951 hres = convert_params(This->ctx, pdp, buf, &argc, &argv);
1952 if(FAILED(hres))
1953 break;
1955 passed_this = get_this(pdp);
1956 if(prop)
1957 hres = invoke_prop_func(This, passed_this, prop, wFlags, argc, argv, pvarRes ? &r : NULL, pspCaller);
1958 else
1959 hres = jsdisp_call_value(This, passed_this ? jsval_disp(passed_this) : jsval_undefined(), wFlags, argc, argv, pvarRes ? &r : NULL);
1961 while(argc--)
1962 jsval_release(argv[argc]);
1963 if(argv != buf)
1964 free(argv);
1965 if(SUCCEEDED(hres) && pvarRes) {
1966 hres = jsval_to_variant(r, pvarRes);
1967 jsval_release(r);
1969 break;
1971 case DISPATCH_PROPERTYGET: {
1972 jsval_t r;
1974 if(prop)
1975 hres = prop_get(This, to_disp(This), prop, &r);
1976 else {
1977 hres = to_primitive(This->ctx, jsval_obj(This), &r, NO_HINT);
1978 if(hres == JS_E_TO_PRIMITIVE)
1979 hres = DISP_E_MEMBERNOTFOUND;
1982 if(SUCCEEDED(hres)) {
1983 hres = jsval_to_variant(r, pvarRes);
1984 jsval_release(r);
1986 break;
1988 case DISPATCH_PROPERTYPUTREF | DISPATCH_PROPERTYPUT:
1989 case DISPATCH_PROPERTYPUTREF:
1990 case DISPATCH_PROPERTYPUT: {
1991 jsval_t val;
1992 DWORD i;
1994 if(!prop) {
1995 hres = DISP_E_MEMBERNOTFOUND;
1996 break;
1999 for(i=0; i < pdp->cNamedArgs; i++) {
2000 if(pdp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT)
2001 break;
2004 if(i == pdp->cNamedArgs) {
2005 TRACE("no value to set\n");
2006 hres = DISP_E_PARAMNOTOPTIONAL;
2007 break;
2010 hres = variant_to_jsval(This->ctx, pdp->rgvarg+i, &val);
2011 if(FAILED(hres))
2012 break;
2014 hres = prop_put(This, prop, val);
2015 jsval_release(val);
2016 break;
2018 default:
2019 FIXME("Unimplemented flags %x\n", wFlags);
2020 hres = E_INVALIDARG;
2021 break;
2024 This->ctx->jscaller->caller = prev_caller;
2025 if(pspCaller)
2026 IServiceProvider_Release(pspCaller);
2027 return leave_script(This->ctx, hres);
2030 static HRESULT delete_prop(dispex_prop_t *prop, BOOL *ret)
2032 if(prop->type == PROP_PROTREF) {
2033 *ret = TRUE;
2034 return S_OK;
2037 if(!(prop->flags & PROPF_CONFIGURABLE)) {
2038 *ret = FALSE;
2039 return S_OK;
2042 *ret = TRUE;
2044 if(prop->type == PROP_JSVAL)
2045 jsval_release(prop->u.val);
2046 if(prop->type == PROP_ACCESSOR) {
2047 if(prop->u.accessor.getter)
2048 jsdisp_release(prop->u.accessor.getter);
2049 if(prop->u.accessor.setter)
2050 jsdisp_release(prop->u.accessor.setter);
2052 prop->type = PROP_DELETED;
2053 return S_OK;
2056 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
2058 jsdisp_t *This = impl_from_IDispatchEx(iface);
2059 dispex_prop_t *prop;
2060 BOOL b;
2061 HRESULT hres;
2063 TRACE("(%p)->(%s %lx)\n", This, debugstr_w(bstrName), grfdex);
2065 if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
2066 FIXME("Unsupported grfdex %lx\n", grfdex);
2068 hres = find_prop_name(This, string_hash(bstrName), bstrName, grfdex & fdexNameCaseInsensitive, &prop);
2069 if(FAILED(hres))
2070 return hres;
2071 if(!prop) {
2072 TRACE("not found\n");
2073 return S_OK;
2076 return delete_prop(prop, &b);
2079 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
2081 jsdisp_t *This = impl_from_IDispatchEx(iface);
2082 dispex_prop_t *prop;
2083 BOOL b;
2085 TRACE("(%p)->(%lx)\n", This, id);
2087 prop = get_prop(This, id);
2088 if(!prop) {
2089 WARN("invalid id\n");
2090 return DISP_E_MEMBERNOTFOUND;
2093 return delete_prop(prop, &b);
2096 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
2098 jsdisp_t *This = impl_from_IDispatchEx(iface);
2099 FIXME("(%p)->(%lx %lx %p)\n", This, id, grfdexFetch, pgrfdex);
2100 return E_NOTIMPL;
2103 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
2105 jsdisp_t *This = impl_from_IDispatchEx(iface);
2106 dispex_prop_t *prop;
2108 TRACE("(%p)->(%lx %p)\n", This, id, pbstrName);
2110 prop = get_prop(This, id);
2111 if(!prop)
2112 return DISP_E_MEMBERNOTFOUND;
2114 *pbstrName = SysAllocString(prop->name);
2115 if(!*pbstrName)
2116 return E_OUTOFMEMORY;
2118 return S_OK;
2121 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
2123 jsdisp_t *This = impl_from_IDispatchEx(iface);
2124 HRESULT hres = S_FALSE;
2126 TRACE("(%p)->(%lx %lx %p)\n", This, grfdex, id, pid);
2128 if(id != DISPID_VALUE)
2129 hres = jsdisp_next_prop(This, id, JSDISP_ENUM_ALL, pid);
2130 if(hres == S_FALSE)
2131 *pid = DISPID_STARTENUM;
2132 return hres;
2135 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
2137 jsdisp_t *This = impl_from_IDispatchEx(iface);
2138 FIXME("(%p)->(%p)\n", This, ppunk);
2139 return E_NOTIMPL;
2142 static IDispatchExVtbl DispatchExVtbl = {
2143 DispatchEx_QueryInterface,
2144 DispatchEx_AddRef,
2145 DispatchEx_Release,
2146 DispatchEx_GetTypeInfoCount,
2147 DispatchEx_GetTypeInfo,
2148 DispatchEx_GetIDsOfNames,
2149 DispatchEx_Invoke,
2150 DispatchEx_GetDispID,
2151 DispatchEx_InvokeEx,
2152 DispatchEx_DeleteMemberByName,
2153 DispatchEx_DeleteMemberByDispID,
2154 DispatchEx_GetMemberProperties,
2155 DispatchEx_GetMemberName,
2156 DispatchEx_GetNextDispID,
2157 DispatchEx_GetNameSpaceParent
2160 jsdisp_t *as_jsdisp(IDispatch *disp)
2162 assert(disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl);
2163 return impl_from_IDispatchEx((IDispatchEx*)disp);
2166 jsdisp_t *to_jsdisp(IDispatch *disp)
2168 return disp->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl ? impl_from_IDispatchEx((IDispatchEx*)disp) : NULL;
2171 HRESULT init_dispex(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype)
2173 unsigned i;
2175 /* FIXME: Use better heuristics to decide when to run the GC */
2176 if(GetTickCount() - ctx->thread_data->gc_last_tick > 30000)
2177 gc_run(ctx);
2179 TRACE("%p (%p)\n", dispex, prototype);
2181 dispex->IDispatchEx_iface.lpVtbl = &DispatchExVtbl;
2182 dispex->ref = 1;
2183 dispex->builtin_info = builtin_info;
2184 dispex->extensible = TRUE;
2185 dispex->prop_cnt = 0;
2187 dispex->props = calloc(1, sizeof(dispex_prop_t)*(dispex->buf_size=4));
2188 if(!dispex->props)
2189 return E_OUTOFMEMORY;
2191 for(i = 0; i < dispex->buf_size; i++) {
2192 dispex->props[i].bucket_head = ~0;
2193 dispex->props[i].bucket_next = ~0;
2196 dispex->prototype = prototype;
2197 if(prototype)
2198 jsdisp_addref(prototype);
2200 script_addref(ctx);
2201 dispex->ctx = ctx;
2203 list_add_tail(&ctx->thread_data->objects, &dispex->entry);
2204 return S_OK;
2207 static const builtin_info_t dispex_info = {
2208 JSCLASS_NONE,
2209 NULL,
2210 0, NULL,
2211 NULL,
2212 NULL
2215 HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *prototype, jsdisp_t **dispex)
2217 jsdisp_t *ret;
2218 HRESULT hres;
2220 ret = calloc(1, sizeof(jsdisp_t));
2221 if(!ret)
2222 return E_OUTOFMEMORY;
2224 hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype);
2225 if(FAILED(hres)) {
2226 free(ret);
2227 return hres;
2230 *dispex = ret;
2231 return S_OK;
2234 void jsdisp_free(jsdisp_t *obj)
2236 dispex_prop_t *prop;
2238 list_remove(&obj->entry);
2240 TRACE("(%p)\n", obj);
2242 if(obj->has_weak_refs) {
2243 struct list *list = &RB_ENTRY_VALUE(rb_get(&obj->ctx->thread_data->weak_refs, obj), struct weak_refs_entry, entry)->list;
2244 do {
2245 remove_weakmap_entry(LIST_ENTRY(list->next, struct weakmap_entry, weak_refs_entry));
2246 } while(obj->has_weak_refs);
2249 for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) {
2250 switch(prop->type) {
2251 case PROP_JSVAL:
2252 jsval_release(prop->u.val);
2253 break;
2254 case PROP_ACCESSOR:
2255 if(prop->u.accessor.getter)
2256 jsdisp_release(prop->u.accessor.getter);
2257 if(prop->u.accessor.setter)
2258 jsdisp_release(prop->u.accessor.setter);
2259 break;
2260 default:
2261 break;
2263 free(prop->name);
2265 free(obj->props);
2266 script_release(obj->ctx);
2267 if(obj->prototype)
2268 jsdisp_release(obj->prototype);
2270 if(obj->builtin_info->destructor)
2271 obj->builtin_info->destructor(obj);
2272 else
2273 free(obj);
2276 #ifdef TRACE_REFCNT
2278 jsdisp_t *jsdisp_addref(jsdisp_t *jsdisp)
2280 ULONG ref = ++jsdisp->ref;
2281 TRACE("(%p) ref=%ld\n", jsdisp, ref);
2282 return jsdisp;
2285 void jsdisp_release(jsdisp_t *jsdisp)
2287 ULONG ref = --jsdisp->ref;
2289 TRACE("(%p) ref=%ld\n", jsdisp, ref);
2291 if(!ref)
2292 jsdisp_free(jsdisp);
2295 #endif
2297 HRESULT init_dispex_from_constr(jsdisp_t *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, jsdisp_t *constr)
2299 jsdisp_t *prot = NULL;
2300 dispex_prop_t *prop;
2301 HRESULT hres;
2303 hres = find_prop_name_prot(constr, string_hash(L"prototype"), L"prototype", FALSE, &prop);
2304 if(SUCCEEDED(hres) && prop && prop->type!=PROP_DELETED) {
2305 jsval_t val;
2307 hres = prop_get(constr, to_disp(constr), prop, &val);
2308 if(FAILED(hres)) {
2309 ERR("Could not get prototype\n");
2310 return hres;
2313 if(is_object_instance(val))
2314 prot = iface_to_jsdisp(get_object(val));
2315 else
2316 prot = jsdisp_addref(ctx->object_prototype);
2318 jsval_release(val);
2321 hres = init_dispex(dispex, ctx, builtin_info, prot);
2323 if(prot)
2324 jsdisp_release(prot);
2325 return hres;
2328 jsdisp_t *iface_to_jsdisp(IDispatch *iface)
2330 return iface->lpVtbl == (const IDispatchVtbl*)&DispatchExVtbl
2331 ? jsdisp_addref( impl_from_IDispatchEx((IDispatchEx*)iface))
2332 : NULL;
2335 HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id)
2337 dispex_prop_t *prop;
2338 HRESULT hres;
2340 if(jsdisp->extensible && (flags & fdexNameEnsure))
2341 hres = ensure_prop_name(jsdisp, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE,
2342 flags & fdexNameCaseInsensitive, &prop);
2343 else
2344 hres = find_prop_name_prot(jsdisp, string_hash(name), name, flags & fdexNameCaseInsensitive, &prop);
2345 if(FAILED(hres))
2346 return hres;
2348 if(prop && prop->type!=PROP_DELETED) {
2349 *id = prop_to_id(jsdisp, prop);
2350 return S_OK;
2353 TRACE("not found %s\n", debugstr_w(name));
2354 *id = DISPID_UNKNOWN;
2355 return DISP_E_UNKNOWNNAME;
2358 HRESULT jsdisp_get_idx_id(jsdisp_t *jsdisp, DWORD idx, DISPID *id)
2360 WCHAR name[11];
2362 swprintf(name, ARRAY_SIZE(name), L"%u", idx);
2363 return jsdisp_get_id(jsdisp, name, 0, id);
2366 HRESULT jsdisp_call_value(jsdisp_t *jsfunc, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
2368 HRESULT hres;
2370 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
2372 if(is_class(jsfunc, JSCLASS_FUNCTION)) {
2373 hres = Function_invoke(jsfunc, vthis, flags, argc, argv, r);
2374 }else {
2375 if(!jsfunc->builtin_info->call) {
2376 WARN("Not a function\n");
2377 return JS_E_FUNCTION_EXPECTED;
2380 if(jsfunc->ctx->state == SCRIPTSTATE_UNINITIALIZED || jsfunc->ctx->state == SCRIPTSTATE_CLOSED)
2381 return E_UNEXPECTED;
2383 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2384 hres = jsfunc->builtin_info->call(jsfunc->ctx, vthis, flags, argc, argv, r);
2386 return hres;
2389 HRESULT jsdisp_call(jsdisp_t *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
2391 dispex_prop_t *prop;
2393 prop = get_prop(disp, id);
2394 if(!prop)
2395 return DISP_E_MEMBERNOTFOUND;
2397 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, &disp->ctx->jscaller->IServiceProvider_iface);
2400 HRESULT jsdisp_call_name(jsdisp_t *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
2402 dispex_prop_t *prop;
2403 HRESULT hres;
2405 hres = find_prop_name_prot(disp, string_hash(name), name, FALSE, &prop);
2406 if(FAILED(hres))
2407 return hres;
2409 if(!prop || prop->type == PROP_DELETED)
2410 return JS_E_INVALID_PROPERTY;
2412 return invoke_prop_func(disp, to_disp(disp), prop, flags, argc, argv, r, &disp->ctx->jscaller->IServiceProvider_iface);
2415 static HRESULT disp_invoke(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, DISPPARAMS *params, VARIANT *r,
2416 IServiceProvider *caller)
2418 IDispatchEx *dispex;
2419 EXCEPINFO ei;
2420 HRESULT hres;
2422 memset(&ei, 0, sizeof(ei));
2423 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2424 if(SUCCEEDED(hres)) {
2425 hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, params, r, &ei, caller);
2426 IDispatchEx_Release(dispex);
2427 }else {
2428 UINT err = 0;
2430 if(flags == DISPATCH_CONSTRUCT) {
2431 WARN("IDispatch cannot be constructor\n");
2432 return DISP_E_MEMBERNOTFOUND;
2435 if(params->cNamedArgs == 1 && params->rgdispidNamedArgs[0] == DISPID_THIS) {
2436 params->cNamedArgs = 0;
2437 params->rgdispidNamedArgs = NULL;
2438 params->cArgs--;
2439 params->rgvarg++;
2442 TRACE("using IDispatch\n");
2443 hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, params, r, &ei, &err);
2446 if(hres == DISP_E_EXCEPTION) {
2447 TRACE("DISP_E_EXCEPTION: %08lx %s %s\n", ei.scode, debugstr_w(ei.bstrSource), debugstr_w(ei.bstrDescription));
2448 reset_ei(ctx->ei);
2449 ctx->ei->error = (SUCCEEDED(ei.scode) || ei.scode == DISP_E_EXCEPTION) ? E_FAIL : ei.scode;
2450 if(ei.bstrSource)
2451 ctx->ei->source = jsstr_alloc_len(ei.bstrSource, SysStringLen(ei.bstrSource));
2452 if(ei.bstrDescription)
2453 ctx->ei->message = jsstr_alloc_len(ei.bstrDescription, SysStringLen(ei.bstrDescription));
2454 SysFreeString(ei.bstrSource);
2455 SysFreeString(ei.bstrDescription);
2456 SysFreeString(ei.bstrHelpFile);
2459 return hres;
2462 HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret)
2464 VARIANT buf[6], retv;
2465 jsdisp_t *jsdisp;
2466 DISPPARAMS dp;
2467 unsigned i;
2468 HRESULT hres;
2470 jsdisp = iface_to_jsdisp(disp);
2471 if(jsdisp && jsdisp->ctx == ctx) {
2472 if(flags & DISPATCH_PROPERTYPUT) {
2473 FIXME("disp_call(propput) on builtin object\n");
2474 jsdisp_release(jsdisp);
2475 return E_FAIL;
2478 if(ctx != jsdisp->ctx)
2479 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2480 hres = jsdisp_call(jsdisp, id, flags, argc, argv, ret);
2481 jsdisp_release(jsdisp);
2482 return hres;
2484 if(jsdisp)
2485 jsdisp_release(jsdisp);
2487 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2488 if(ret && argc)
2489 flags |= DISPATCH_PROPERTYGET;
2491 dp.cArgs = argc;
2493 if(flags & DISPATCH_PROPERTYPUT) {
2494 static DISPID propput_dispid = DISPID_PROPERTYPUT;
2496 dp.cNamedArgs = 1;
2497 dp.rgdispidNamedArgs = &propput_dispid;
2498 }else {
2499 dp.cNamedArgs = 0;
2500 dp.rgdispidNamedArgs = NULL;
2503 if(dp.cArgs > ARRAY_SIZE(buf)) {
2504 dp.rgvarg = malloc(argc * sizeof(VARIANT));
2505 if(!dp.rgvarg)
2506 return E_OUTOFMEMORY;
2507 }else {
2508 dp.rgvarg = buf;
2511 for(i=0; i<argc; i++) {
2512 hres = jsval_to_variant(argv[i], dp.rgvarg+argc-i-1);
2513 if(FAILED(hres)) {
2514 while(i--)
2515 VariantClear(dp.rgvarg+argc-i-1);
2516 if(dp.rgvarg != buf)
2517 free(dp.rgvarg);
2518 return hres;
2522 V_VT(&retv) = VT_EMPTY;
2523 hres = disp_invoke(ctx, disp, id, flags, &dp, ret ? &retv : NULL, &ctx->jscaller->IServiceProvider_iface);
2525 for(i=0; i<argc; i++)
2526 VariantClear(dp.rgvarg+argc-i-1);
2527 if(dp.rgvarg != buf)
2528 free(dp.rgvarg);
2530 if(SUCCEEDED(hres) && ret)
2531 hres = variant_to_jsval(ctx, &retv, ret);
2532 VariantClear(&retv);
2533 return hres;
2536 HRESULT disp_call_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, WORD flags, unsigned argc, jsval_t *argv, jsval_t *ret)
2538 IDispatchEx *dispex;
2539 jsdisp_t *jsdisp;
2540 HRESULT hres;
2541 DISPID id;
2542 BSTR bstr;
2544 if((jsdisp = to_jsdisp(disp)) && jsdisp->ctx == ctx)
2545 return jsdisp_call_name(jsdisp, name, flags, argc, argv, ret);
2547 if(!(bstr = SysAllocString(name)))
2548 return E_OUTOFMEMORY;
2549 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2550 if(SUCCEEDED(hres) && dispex) {
2551 hres = IDispatchEx_GetDispID(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive), &id);
2552 IDispatchEx_Release(dispex);
2553 }else {
2554 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
2556 SysFreeString(bstr);
2557 if(FAILED(hres))
2558 return hres;
2560 return disp_call(ctx, disp, id, flags, argc, argv, ret);
2563 HRESULT disp_call_value_with_caller(script_ctx_t *ctx, IDispatch *disp, jsval_t vthis, WORD flags, unsigned argc,
2564 jsval_t *argv, jsval_t *r, IServiceProvider *caller)
2566 VARIANT buf[6], retv, *args = buf;
2567 IDispatch *jsthis;
2568 jsdisp_t *jsdisp;
2569 DISPPARAMS dp;
2570 unsigned i;
2571 HRESULT hres = S_OK;
2573 static DISPID this_id = DISPID_THIS;
2575 assert(!(flags & ~(DISPATCH_METHOD|DISPATCH_CONSTRUCT|DISPATCH_JSCRIPT_INTERNAL_MASK)));
2577 jsdisp = iface_to_jsdisp(disp);
2578 if(jsdisp && jsdisp->ctx == ctx) {
2579 hres = jsdisp_call_value(jsdisp, vthis, flags, argc, argv, r);
2580 jsdisp_release(jsdisp);
2581 return hres;
2583 if(jsdisp)
2584 jsdisp_release(jsdisp);
2586 if(is_object_instance(vthis) && (ctx->version < SCRIPTLANGUAGEVERSION_ES5 ||
2587 ((jsdisp = to_jsdisp(get_object(vthis))) && is_class(jsdisp, JSCLASS_OBJECT))))
2588 jsthis = get_object(vthis);
2589 else
2590 jsthis = NULL;
2592 flags &= ~DISPATCH_JSCRIPT_INTERNAL_MASK;
2593 if(r && argc && flags == DISPATCH_METHOD)
2594 flags |= DISPATCH_PROPERTYGET;
2596 if(jsthis) {
2597 dp.cArgs = argc + 1;
2598 dp.cNamedArgs = 1;
2599 dp.rgdispidNamedArgs = &this_id;
2600 }else {
2601 dp.cArgs = argc;
2602 dp.cNamedArgs = 0;
2603 dp.rgdispidNamedArgs = NULL;
2606 if(dp.cArgs > ARRAY_SIZE(buf) && !(args = malloc(dp.cArgs * sizeof(VARIANT))))
2607 return E_OUTOFMEMORY;
2608 dp.rgvarg = args;
2610 if(jsthis) {
2611 V_VT(dp.rgvarg) = VT_DISPATCH;
2612 V_DISPATCH(dp.rgvarg) = jsthis;
2615 for(i=0; SUCCEEDED(hres) && i < argc; i++)
2616 hres = jsval_to_variant(argv[i], dp.rgvarg+dp.cArgs-i-1);
2618 if(SUCCEEDED(hres)) {
2619 V_VT(&retv) = VT_EMPTY;
2620 hres = disp_invoke(ctx, disp, DISPID_VALUE, flags, &dp, r ? &retv : NULL, caller);
2623 for(i = 0; i < argc; i++)
2624 VariantClear(dp.rgvarg + dp.cArgs - i - 1);
2625 if(args != buf)
2626 free(args);
2628 if(FAILED(hres))
2629 return hres;
2630 if(!r)
2631 return S_OK;
2633 hres = variant_to_jsval(ctx, &retv, r);
2634 VariantClear(&retv);
2635 return hres;
2638 HRESULT jsdisp_propput(jsdisp_t *obj, const WCHAR *name, DWORD flags, BOOL throw, jsval_t val)
2640 dispex_prop_t *prop;
2641 HRESULT hres;
2643 if(obj->extensible)
2644 hres = ensure_prop_name(obj, name, flags, FALSE, &prop);
2645 else
2646 hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop);
2647 if(FAILED(hres))
2648 return hres;
2649 if(!prop || (prop->type == PROP_DELETED && !obj->extensible))
2650 return throw ? JS_E_INVALID_ACTION : S_OK;
2652 return prop_put(obj, prop, val);
2655 HRESULT jsdisp_propput_name(jsdisp_t *obj, const WCHAR *name, jsval_t val)
2657 return jsdisp_propput(obj, name, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, FALSE, val);
2660 HRESULT jsdisp_propput_idx(jsdisp_t *obj, DWORD idx, jsval_t val)
2662 WCHAR buf[12];
2664 swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
2665 return jsdisp_propput(obj, buf, PROPF_ENUMERABLE | PROPF_CONFIGURABLE | PROPF_WRITABLE, TRUE, val);
2668 HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t val)
2670 jsdisp_t *jsdisp;
2671 HRESULT hres;
2673 jsdisp = iface_to_jsdisp(disp);
2674 if(jsdisp && jsdisp->ctx == ctx) {
2675 dispex_prop_t *prop;
2677 prop = get_prop(jsdisp, id);
2678 if(prop)
2679 hres = prop_put(jsdisp, prop, val);
2680 else
2681 hres = DISP_E_MEMBERNOTFOUND;
2683 jsdisp_release(jsdisp);
2684 }else {
2685 DISPID dispid = DISPID_PROPERTYPUT;
2686 DWORD flags = DISPATCH_PROPERTYPUT;
2687 VARIANT var;
2688 DISPPARAMS dp = {&var, &dispid, 1, 1};
2690 if(jsdisp)
2691 jsdisp_release(jsdisp);
2692 hres = jsval_to_variant(val, &var);
2693 if(FAILED(hres))
2694 return hres;
2696 if(V_VT(&var) == VT_DISPATCH)
2697 flags |= DISPATCH_PROPERTYPUTREF;
2699 hres = disp_invoke(ctx, disp, id, flags, &dp, NULL, &ctx->jscaller->IServiceProvider_iface);
2700 VariantClear(&var);
2703 return hres;
2706 HRESULT disp_propput_name(script_ctx_t *ctx, IDispatch *disp, const WCHAR *name, jsval_t val)
2708 jsdisp_t *jsdisp;
2709 HRESULT hres;
2711 jsdisp = iface_to_jsdisp(disp);
2712 if(!jsdisp || jsdisp->ctx != ctx) {
2713 IDispatchEx *dispex;
2714 BSTR str;
2715 DISPID id;
2717 if(jsdisp)
2718 jsdisp_release(jsdisp);
2719 if(!(str = SysAllocString(name)))
2720 return E_OUTOFMEMORY;
2722 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2723 if(SUCCEEDED(hres)) {
2724 hres = IDispatchEx_GetDispID(dispex, str, make_grfdex(ctx, fdexNameEnsure|fdexNameCaseSensitive), &id);
2725 IDispatchEx_Release(dispex);
2726 }else {
2727 TRACE("using IDispatch\n");
2728 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &str, 1, 0, &id);
2730 SysFreeString(str);
2731 if(FAILED(hres))
2732 return hres;
2734 return disp_propput(ctx, disp, id, val);
2737 hres = jsdisp_propput_name(jsdisp, name, val);
2738 jsdisp_release(jsdisp);
2739 return hres;
2742 HRESULT jsdisp_propget_name(jsdisp_t *obj, const WCHAR *name, jsval_t *val)
2744 dispex_prop_t *prop;
2745 HRESULT hres;
2747 hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop);
2748 if(FAILED(hres))
2749 return hres;
2751 if(!prop || prop->type==PROP_DELETED) {
2752 *val = jsval_undefined();
2753 return S_OK;
2756 return prop_get(obj, to_disp(obj), prop, val);
2759 HRESULT jsdisp_get_idx(jsdisp_t *obj, DWORD idx, jsval_t *r)
2761 WCHAR name[12];
2762 dispex_prop_t *prop;
2763 HRESULT hres;
2765 swprintf(name, ARRAY_SIZE(name), L"%d", idx);
2767 hres = find_prop_name_prot(obj, string_hash(name), name, FALSE, &prop);
2768 if(FAILED(hres))
2769 return hres;
2771 if(!prop || prop->type==PROP_DELETED) {
2772 *r = jsval_undefined();
2773 return DISP_E_UNKNOWNNAME;
2776 return prop_get(obj, to_disp(obj), prop, r);
2779 HRESULT jsdisp_propget(jsdisp_t *jsdisp, DISPID id, jsval_t *val)
2781 dispex_prop_t *prop;
2783 prop = get_prop(jsdisp, id);
2784 if(!prop)
2785 return DISP_E_MEMBERNOTFOUND;
2787 return prop_get(jsdisp, to_disp(jsdisp), prop, val);
2790 HRESULT disp_propget(script_ctx_t *ctx, IDispatch *disp, DISPID id, jsval_t *val)
2792 DISPPARAMS dp = {NULL,NULL,0,0};
2793 jsdisp_t *jsdisp;
2794 VARIANT var;
2795 HRESULT hres;
2797 jsdisp = iface_to_jsdisp(disp);
2798 if(jsdisp && jsdisp->ctx == ctx) {
2799 hres = jsdisp_propget(jsdisp, id, val);
2800 jsdisp_release(jsdisp);
2801 return hres;
2803 if(jsdisp)
2804 jsdisp_release(jsdisp);
2806 V_VT(&var) = VT_EMPTY;
2807 hres = disp_invoke(ctx, disp, id, INVOKE_PROPERTYGET, &dp, &var, &ctx->jscaller->IServiceProvider_iface);
2808 if(SUCCEEDED(hres)) {
2809 hres = variant_to_jsval(ctx, &var, val);
2810 VariantClear(&var);
2812 return hres;
2815 HRESULT jsdisp_delete_idx(jsdisp_t *obj, DWORD idx)
2817 WCHAR buf[12];
2818 dispex_prop_t *prop;
2819 BOOL b;
2820 HRESULT hres;
2822 swprintf(buf, ARRAY_SIZE(buf), L"%d", idx);
2824 hres = find_prop_name(obj, string_hash(buf), buf, FALSE, &prop);
2825 if(FAILED(hres) || !prop)
2826 return hres;
2828 hres = delete_prop(prop, &b);
2829 if(FAILED(hres))
2830 return hres;
2831 return b ? S_OK : JS_E_INVALID_ACTION;
2834 HRESULT disp_delete(IDispatch *disp, DISPID id, BOOL *ret)
2836 IDispatchEx *dispex;
2837 jsdisp_t *jsdisp;
2838 HRESULT hres;
2840 jsdisp = iface_to_jsdisp(disp);
2841 if(jsdisp) {
2842 dispex_prop_t *prop;
2844 prop = get_prop(jsdisp, id);
2845 if(prop)
2846 hres = delete_prop(prop, ret);
2847 else
2848 hres = DISP_E_MEMBERNOTFOUND;
2850 jsdisp_release(jsdisp);
2851 return hres;
2854 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2855 if(FAILED(hres)) {
2856 *ret = FALSE;
2857 return S_OK;
2860 hres = IDispatchEx_DeleteMemberByDispID(dispex, id);
2861 IDispatchEx_Release(dispex);
2862 if(FAILED(hres))
2863 return hres;
2865 *ret = hres == S_OK;
2866 return S_OK;
2869 HRESULT jsdisp_next_prop(jsdisp_t *obj, DISPID id, enum jsdisp_enum_type enum_type, DISPID *ret)
2871 dispex_prop_t *iter;
2872 DWORD idx = id;
2873 HRESULT hres;
2875 if(id == DISPID_STARTENUM || idx >= obj->prop_cnt) {
2876 hres = (enum_type == JSDISP_ENUM_ALL) ? fill_protrefs(obj) : fill_props(obj);
2877 if(FAILED(hres))
2878 return hres;
2879 if(id == DISPID_STARTENUM)
2880 idx = 0;
2881 if(idx >= obj->prop_cnt)
2882 return S_FALSE;
2885 for(iter = &obj->props[idx]; iter < obj->props + obj->prop_cnt; iter++) {
2886 if(iter->type == PROP_DELETED)
2887 continue;
2888 if(enum_type != JSDISP_ENUM_ALL && iter->type == PROP_PROTREF)
2889 continue;
2890 if(enum_type != JSDISP_ENUM_OWN && !(get_flags(obj, iter) & PROPF_ENUMERABLE))
2891 continue;
2892 *ret = prop_to_id(obj, iter);
2893 return S_OK;
2896 if(obj->ctx->html_mode)
2897 return jsdisp_next_prop(obj, prop_to_id(obj, iter - 1), enum_type, ret);
2899 return S_FALSE;
2902 HRESULT disp_delete_name(script_ctx_t *ctx, IDispatch *disp, jsstr_t *name, BOOL *ret)
2904 IDispatchEx *dispex;
2905 jsdisp_t *jsdisp;
2906 BSTR bstr;
2907 HRESULT hres;
2909 jsdisp = iface_to_jsdisp(disp);
2910 if(jsdisp) {
2911 dispex_prop_t *prop;
2912 const WCHAR *ptr;
2914 ptr = jsstr_flatten(name);
2915 if(!ptr) {
2916 jsdisp_release(jsdisp);
2917 return E_OUTOFMEMORY;
2920 hres = find_prop_name(jsdisp, string_hash(ptr), ptr, FALSE, &prop);
2921 if(prop) {
2922 hres = delete_prop(prop, ret);
2923 }else {
2924 *ret = TRUE;
2925 hres = S_OK;
2928 jsdisp_release(jsdisp);
2929 return hres;
2932 bstr = SysAllocStringLen(NULL, jsstr_length(name));
2933 if(!bstr)
2934 return E_OUTOFMEMORY;
2935 jsstr_flush(name, bstr);
2937 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
2938 if(SUCCEEDED(hres)) {
2939 hres = IDispatchEx_DeleteMemberByName(dispex, bstr, make_grfdex(ctx, fdexNameCaseSensitive));
2940 if(SUCCEEDED(hres))
2941 *ret = hres == S_OK;
2942 IDispatchEx_Release(dispex);
2943 }else {
2944 DISPID id;
2946 hres = IDispatch_GetIDsOfNames(disp, &IID_NULL, &bstr, 1, 0, &id);
2947 if(SUCCEEDED(hres)) {
2948 /* Property exists and we can't delete it from pure IDispatch interface, so return false. */
2949 *ret = FALSE;
2950 }else if(hres == DISP_E_UNKNOWNNAME) {
2951 /* Property doesn't exist, so nothing to delete */
2952 *ret = TRUE;
2953 hres = S_OK;
2957 SysFreeString(bstr);
2958 return hres;
2961 HRESULT jsdisp_get_own_property(jsdisp_t *obj, const WCHAR *name, BOOL flags_only,
2962 property_desc_t *desc)
2964 dispex_prop_t *prop;
2965 HRESULT hres;
2967 hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop);
2968 if(FAILED(hres))
2969 return hres;
2971 if(!prop)
2972 return DISP_E_UNKNOWNNAME;
2974 memset(desc, 0, sizeof(*desc));
2976 switch(prop->type) {
2977 case PROP_BUILTIN:
2978 case PROP_JSVAL:
2979 case PROP_IDX:
2980 desc->mask |= PROPF_WRITABLE;
2981 desc->explicit_value = TRUE;
2982 if(!flags_only) {
2983 hres = prop_get(obj, to_disp(obj), prop, &desc->value);
2984 if(FAILED(hres))
2985 return hres;
2987 break;
2988 case PROP_ACCESSOR:
2989 desc->explicit_getter = desc->explicit_setter = TRUE;
2990 if(!flags_only) {
2991 desc->getter = prop->u.accessor.getter
2992 ? jsdisp_addref(prop->u.accessor.getter) : NULL;
2993 desc->setter = prop->u.accessor.setter
2994 ? jsdisp_addref(prop->u.accessor.setter) : NULL;
2996 break;
2997 default:
2998 return DISP_E_UNKNOWNNAME;
3001 desc->flags = prop->flags & (PROPF_ENUMERABLE | PROPF_WRITABLE | PROPF_CONFIGURABLE);
3002 desc->mask |= PROPF_ENUMERABLE | PROPF_CONFIGURABLE;
3003 return S_OK;
3006 HRESULT jsdisp_define_property(jsdisp_t *obj, const WCHAR *name, property_desc_t *desc)
3008 dispex_prop_t *prop;
3009 HRESULT hres;
3011 hres = find_prop_name(obj, string_hash(name), name, FALSE, &prop);
3012 if(FAILED(hres))
3013 return hres;
3015 if((!prop || prop->type == PROP_DELETED || prop->type == PROP_PROTREF) && !obj->extensible)
3016 return throw_error(obj->ctx, JS_E_OBJECT_NONEXTENSIBLE, name);
3018 if(!prop && !(prop = alloc_prop(obj, name, PROP_DELETED, 0)))
3019 return E_OUTOFMEMORY;
3021 if(prop->type == PROP_DELETED || prop->type == PROP_PROTREF) {
3022 prop->flags = desc->flags;
3023 if(desc->explicit_getter || desc->explicit_setter) {
3024 prop->type = PROP_ACCESSOR;
3025 prop->u.accessor.getter = desc->getter ? jsdisp_addref(desc->getter) : NULL;
3026 prop->u.accessor.setter = desc->setter ? jsdisp_addref(desc->setter) : NULL;
3027 TRACE("%s = accessor { get: %p set: %p }\n", debugstr_w(name),
3028 prop->u.accessor.getter, prop->u.accessor.setter);
3029 }else {
3030 prop->type = PROP_JSVAL;
3031 if(desc->explicit_value) {
3032 hres = jsval_copy(desc->value, &prop->u.val);
3033 if(FAILED(hres))
3034 return hres;
3035 }else {
3036 prop->u.val = jsval_undefined();
3038 TRACE("%s = %s\n", debugstr_w(name), debugstr_jsval(prop->u.val));
3040 return S_OK;
3043 TRACE("existing prop %s prop flags %lx desc flags %x desc mask %x\n", debugstr_w(name),
3044 prop->flags, desc->flags, desc->mask);
3046 if(!(prop->flags & PROPF_CONFIGURABLE)) {
3047 if(((desc->mask & PROPF_CONFIGURABLE) && (desc->flags & PROPF_CONFIGURABLE))
3048 || ((desc->mask & PROPF_ENUMERABLE)
3049 && ((desc->flags & PROPF_ENUMERABLE) != (prop->flags & PROPF_ENUMERABLE))))
3050 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3053 if(desc->explicit_value || (desc->mask & PROPF_WRITABLE)) {
3054 if(prop->type == PROP_ACCESSOR) {
3055 if(!(prop->flags & PROPF_CONFIGURABLE))
3056 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3057 if(prop->u.accessor.getter)
3058 jsdisp_release(prop->u.accessor.getter);
3059 if(prop->u.accessor.setter)
3060 jsdisp_release(prop->u.accessor.setter);
3062 prop->type = PROP_JSVAL;
3063 hres = jsval_copy(desc->value, &prop->u.val);
3064 if(FAILED(hres)) {
3065 prop->u.val = jsval_undefined();
3066 return hres;
3068 }else {
3069 if(!(prop->flags & PROPF_CONFIGURABLE) && !(prop->flags & PROPF_WRITABLE)) {
3070 if((desc->mask & PROPF_WRITABLE) && (desc->flags & PROPF_WRITABLE))
3071 return throw_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
3072 if(desc->explicit_value) {
3073 if(prop->type == PROP_JSVAL) {
3074 BOOL eq;
3075 hres = jsval_strict_equal(desc->value, prop->u.val, &eq);
3076 if(FAILED(hres))
3077 return hres;
3078 if(!eq)
3079 return throw_error(obj->ctx, JS_E_NONWRITABLE_MODIFIED, name);
3080 }else {
3081 FIXME("redefinition of property type %d\n", prop->type);
3085 if(desc->explicit_value) {
3086 if(prop->type == PROP_JSVAL)
3087 jsval_release(prop->u.val);
3088 else
3089 prop->type = PROP_JSVAL;
3090 hres = jsval_copy(desc->value, &prop->u.val);
3091 if(FAILED(hres)) {
3092 prop->u.val = jsval_undefined();
3093 return hres;
3097 }else if(desc->explicit_getter || desc->explicit_setter) {
3098 if(prop->type != PROP_ACCESSOR) {
3099 if(!(prop->flags & PROPF_CONFIGURABLE))
3100 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3101 if(prop->type == PROP_JSVAL)
3102 jsval_release(prop->u.val);
3103 prop->type = PROP_ACCESSOR;
3104 prop->u.accessor.getter = prop->u.accessor.setter = NULL;
3105 }else if(!(prop->flags & PROPF_CONFIGURABLE)) {
3106 if((desc->explicit_getter && desc->getter != prop->u.accessor.getter)
3107 || (desc->explicit_setter && desc->setter != prop->u.accessor.setter))
3108 return throw_error(obj->ctx, JS_E_NONCONFIGURABLE_REDEFINED, name);
3111 if(desc->explicit_getter) {
3112 if(prop->u.accessor.getter) {
3113 jsdisp_release(prop->u.accessor.getter);
3114 prop->u.accessor.getter = NULL;
3116 if(desc->getter)
3117 prop->u.accessor.getter = jsdisp_addref(desc->getter);
3119 if(desc->explicit_setter) {
3120 if(prop->u.accessor.setter) {
3121 jsdisp_release(prop->u.accessor.setter);
3122 prop->u.accessor.setter = NULL;
3124 if(desc->setter)
3125 prop->u.accessor.setter = jsdisp_addref(desc->setter);
3129 prop->flags = (prop->flags & ~desc->mask) | (desc->flags & desc->mask);
3130 return S_OK;
3133 HRESULT jsdisp_define_data_property(jsdisp_t *obj, const WCHAR *name, unsigned flags, jsval_t value)
3135 property_desc_t prop_desc = { flags, flags, TRUE };
3136 prop_desc.value = value;
3137 return jsdisp_define_property(obj, name, &prop_desc);
3140 HRESULT jsdisp_change_prototype(jsdisp_t *obj, jsdisp_t *proto)
3142 jsdisp_t *iter;
3143 DWORD i;
3145 if(obj->prototype == proto)
3146 return S_OK;
3147 if(!obj->extensible)
3148 return JS_E_CANNOT_CREATE_FOR_NONEXTENSIBLE;
3150 for(iter = proto; iter; iter = iter->prototype)
3151 if(iter == obj)
3152 return JS_E_CYCLIC_PROTO_VALUE;
3154 if(obj->prototype) {
3155 for(i = 0; i < obj->prop_cnt; i++)
3156 if(obj->props[i].type == PROP_PROTREF)
3157 obj->props[i].type = PROP_DELETED;
3158 jsdisp_release(obj->prototype);
3161 obj->prototype = proto;
3162 if(proto)
3163 jsdisp_addref(proto);
3164 return S_OK;
3167 void jsdisp_freeze(jsdisp_t *obj, BOOL seal)
3169 unsigned int i;
3171 for(i = 0; i < obj->prop_cnt; i++) {
3172 if(!seal && obj->props[i].type == PROP_JSVAL)
3173 obj->props[i].flags &= ~PROPF_WRITABLE;
3174 obj->props[i].flags &= ~PROPF_CONFIGURABLE;
3177 obj->extensible = FALSE;
3180 BOOL jsdisp_is_frozen(jsdisp_t *obj, BOOL sealed)
3182 unsigned int i;
3184 if(obj->extensible)
3185 return FALSE;
3187 for(i = 0; i < obj->prop_cnt; i++) {
3188 if(obj->props[i].type == PROP_JSVAL) {
3189 if(!sealed && (obj->props[i].flags & PROPF_WRITABLE))
3190 return FALSE;
3191 }else if(obj->props[i].type != PROP_ACCESSOR)
3192 continue;
3193 if(obj->props[i].flags & PROPF_CONFIGURABLE)
3194 return FALSE;
3197 return TRUE;
3200 HRESULT jsdisp_get_prop_name(jsdisp_t *obj, DISPID id, jsstr_t **r)
3202 dispex_prop_t *prop = get_prop(obj, id);
3204 if(!prop)
3205 return DISP_E_MEMBERNOTFOUND;
3207 *r = jsstr_alloc(prop->name);
3208 return *r ? S_OK : E_OUTOFMEMORY;