-- v3: jscript: Properly set JS_E_WRONG_THIS as a TypeError. mshtml/tests: Add tests for WeakMap. jscript: Implement WeakMap.has(). jscript: Implement WeakMap.clear(). jscript: Implement WeakMap.delete(). jscript: Implement WeakMap.get(). jscript: Implement WeakMap.set(). jscript: Implement WeakMap instance stub and constructor. jscript: Convert unlink_props to a helper that unlinks the entire object.
From: Gabriel Ivăncescu gabrielopcode@gmail.com
It will be useful for other cases, and we don't need the gc_ctx for unlinking. Also set the PROP_PROTREFs to PROP_DELETED since we're unliking the prototype.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index b5fc3699212..9e87c38004f 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -665,14 +665,13 @@ static HRESULT fill_protrefs(jsdisp_t *This) return S_OK; }
-static void unlink_props(jsdisp_t *jsdisp) +static void unlink_jsdisp(jsdisp_t *jsdisp) { dispex_prop_t *prop = jsdisp->props, *end;
for(end = prop + jsdisp->prop_cnt; prop < end; prop++) { switch(prop->type) { case PROP_DELETED: - case PROP_PROTREF: continue; case PROP_JSVAL: jsval_release(prop->u.val); @@ -688,6 +687,14 @@ static void unlink_props(jsdisp_t *jsdisp) } prop->type = PROP_DELETED; } + + if(jsdisp->prototype) { + jsdisp_release(jsdisp->prototype); + jsdisp->prototype = NULL; + } + + if(jsdisp->builtin_info->gc_traverse) + jsdisp->builtin_info->gc_traverse(NULL, GC_TRAVERSE_UNLINK, jsdisp); }
@@ -927,15 +934,7 @@ HRESULT gc_run(script_ctx_t *ctx)
/* Grab it since it gets removed when unlinked */ jsdisp_addref(obj); - unlink_props(obj); - - if(obj->prototype) { - jsdisp_release(obj->prototype); - obj->prototype = NULL; - } - - if(obj->builtin_info->gc_traverse) - obj->builtin_info->gc_traverse(&gc_ctx, GC_TRAVERSE_UNLINK, obj); + unlink_jsdisp(obj);
/* Releasing unlinked object should not delete any other object, so we can safely obtain the next pointer now */
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/jscript.h | 13 +++- dlls/jscript/object.c | 1 + dlls/jscript/set.c | 168 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 46644b8247b..72e114e92e0 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -32,6 +32,7 @@ #include "resource.h"
#include "wine/list.h" +#include "wine/rbtree.h"
/* * This is Wine jscript extension for ES5 compatible mode. Native IE9+ implements @@ -117,6 +118,7 @@ typedef enum { JSCLASS_JSON, JSCLASS_MAP, JSCLASS_SET, + JSCLASS_WEAKMAP, } jsclass_t;
jsdisp_t *iface_to_jsdisp(IDispatch*); @@ -418,11 +420,18 @@ struct _script_ctx_t { jsdisp_t *vbarray_constr; jsdisp_t *map_prototype; jsdisp_t *set_prototype; + jsdisp_t *weakmap_prototype; }; - jsdisp_t *global_objects[22]; + jsdisp_t *global_objects[23]; }; }; -C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, set_prototype) == RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, global_objects)); +C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, weakmap_prototype) == RTL_SIZEOF_THROUGH_FIELD(script_ctx_t, global_objects)); + +struct weakmap_entry { + struct rb_entry entry; + jsdisp_t *key; + jsval_t value; +};
void script_release(script_ctx_t*);
diff --git a/dlls/jscript/object.c b/dlls/jscript/object.c index 4f6acacbc95..65b25cab239 100644 --- a/dlls/jscript/object.c +++ b/dlls/jscript/object.c @@ -51,6 +51,7 @@ static HRESULT Object_toString(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns L"[object Object]", L"[object Object]", L"[object Object]", + L"[object Object]", L"[object Object]" };
diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index eca26a890f7..6c0de9240d3 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -614,6 +614,159 @@ static HRESULT Set_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns } }
+typedef struct { + jsdisp_t dispex; + struct rb_tree map; +} WeakMapInstance; + +static int weakmap_compare(const void *k, const struct rb_entry *e) +{ + ULONG_PTR a = (ULONG_PTR)k, b = (ULONG_PTR)RB_ENTRY_VALUE(e, const struct weakmap_entry, entry)->key; + return (a > b) - (a < b); +} + +static void release_weakmap_entry(struct weakmap_entry *entry) +{ + jsval_release(entry->value); + free(entry); +} + +static HRESULT WeakMap_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WeakMap_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WeakMap_get(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WeakMap_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WeakMap_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WeakMap_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static void WeakMap_destructor(jsdisp_t *dispex) +{ + WeakMapInstance *weakmap = (WeakMapInstance*)dispex; + struct weakmap_entry *entry, *entry2; + + RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry) + release_weakmap_entry(entry); + + free(weakmap); +} + +static HRESULT WeakMap_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex) +{ + WeakMapInstance *weakmap = (WeakMapInstance*)dispex; + struct weakmap_entry *entry, *entry2; + HRESULT hres; + + if(op == GC_TRAVERSE_UNLINK) { + RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry) + release_weakmap_entry(entry); + rb_destroy(&weakmap->map, NULL, NULL); + return S_OK; + } + + RB_FOR_EACH_ENTRY(entry, &weakmap->map, struct weakmap_entry, entry) { + hres = gc_process_linked_val(gc_ctx, op, dispex, &entry->value); + if(FAILED(hres)) + return hres; + } + return S_OK; +} + +static const builtin_prop_t WeakMap_prototype_props[] = { + {L"clear", WeakMap_clear, PROPF_METHOD}, + {L"delete", WeakMap_delete, PROPF_METHOD|1}, + {L"get", WeakMap_get, PROPF_METHOD|1}, + {L"has", WeakMap_has, PROPF_METHOD|1}, + {L"set", WeakMap_set, PROPF_METHOD|2}, +}; + +static const builtin_info_t WeakMap_prototype_info = { + JSCLASS_OBJECT, + WeakMap_value, + ARRAY_SIZE(WeakMap_prototype_props), + WeakMap_prototype_props, + NULL, + NULL +}; + +static const builtin_info_t WeakMap_info = { + JSCLASS_WEAKMAP, + WeakMap_value, + 0, + NULL, + WeakMap_destructor, + NULL, + NULL, + NULL, + NULL, + WeakMap_gc_traverse +}; + +static HRESULT WeakMap_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, + jsval_t *r) +{ + WeakMapInstance *weakmap; + HRESULT hres; + + switch(flags) { + case DISPATCH_CONSTRUCT: + TRACE("\n"); + + if(!r) + return S_OK; + if(!(weakmap = calloc(1, sizeof(*weakmap)))) + return E_OUTOFMEMORY; + + hres = init_dispex(&weakmap->dispex, ctx, &WeakMap_info, ctx->weakmap_prototype); + if(FAILED(hres)) + return hres; + + rb_init(&weakmap->map, weakmap_compare); + *r = jsval_obj(&weakmap->dispex); + return S_OK; + + case DISPATCH_METHOD: + return throw_error(ctx, JS_E_WRONG_THIS, L"WeakMap"); + + default: + FIXME("unimplemented flags %x\n", flags); + return E_NOTIMPL; + } +} + HRESULT init_set_constructor(script_ctx_t *ctx) { jsdisp_t *constructor; @@ -649,5 +802,20 @@ HRESULT init_set_constructor(script_ctx_t *ctx) hres = jsdisp_define_data_property(ctx->global, L"Map", PROPF_WRITABLE, jsval_obj(constructor)); jsdisp_release(constructor); + if(FAILED(hres)) + return hres; + + hres = create_dispex(ctx, &WeakMap_prototype_info, ctx->object_prototype, &ctx->weakmap_prototype); + if(FAILED(hres)) + return hres; + + hres = create_builtin_constructor(ctx, WeakMap_constructor, L"WeakMap", NULL, + PROPF_CONSTR, ctx->weakmap_prototype, &constructor); + if(FAILED(hres)) + return hres; + + hres = jsdisp_define_data_property(ctx->global, L"WeakMap", PROPF_WRITABLE, + jsval_obj(constructor)); + jsdisp_release(constructor); return hres; }
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/dispex.c | 27 ++++++++++ dlls/jscript/error.c | 1 + dlls/jscript/jscript.c | 8 +++ dlls/jscript/jscript.h | 11 ++++ dlls/jscript/jscript.rc | 1 + dlls/jscript/resource.h | 1 + dlls/jscript/set.c | 108 +++++++++++++++++++++++++++++++++++++++- 7 files changed, 155 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 9e87c38004f..78d1c66c401 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -896,6 +896,24 @@ HRESULT gc_run(script_ctx_t *ctx) break; }
+ /* For weak refs, traverse paths accessible from it via the WeakMaps, if the WeakMaps are alive at this point. + We need both the key and the WeakMap for the entry to actually be accessible (and thus traversed). */ + if(obj2->has_weak_refs) { + struct list *list = &RB_ENTRY_VALUE(rb_get(&ctx->weak_refs, obj2), struct weak_refs_entry, entry)->list; + struct weakmap_entry *entry; + + LIST_FOR_EACH_ENTRY(entry, list, struct weakmap_entry, weak_refs_entry) { + if(!entry->weakmap->gc_marked && is_object_instance(entry->value) && (link = to_jsdisp(get_object(entry->value)))) { + hres = gc_stack_push(&gc_ctx, link); + if(FAILED(hres)) + break; + } + } + + if(FAILED(hres)) + break; + } + do obj2 = gc_stack_pop(&gc_ctx); while(obj2 && !obj2->gc_marked); } while(obj2);
@@ -2222,6 +2240,15 @@ void jsdisp_free(jsdisp_t *obj)
TRACE("(%p)\n", obj);
+ if(obj->has_weak_refs) { + struct list *iter, *next, *list = &RB_ENTRY_VALUE(rb_get(&obj->ctx->weak_refs, obj), struct weak_refs_entry, entry)->list; + iter = list_head(list); + do { + next = list_next(list, iter); + remove_weakmap_entry(LIST_ENTRY(iter, struct weakmap_entry, weak_refs_entry)); + } while((iter = next)); + } + for(prop = obj->props; prop < obj->props+obj->prop_cnt; prop++) { switch(prop->type) { case PROP_JSVAL: diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index c3768668178..cf0ece42f15 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -483,6 +483,7 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_OBJECT_NONEXTENSIBLE: case JS_E_NONCONFIGURABLE_REDEFINED: case JS_E_NONWRITABLE_MODIFIED: + case JS_E_KEY_NOT_OBJECT: case JS_E_PROP_DESC_MISMATCH: case JS_E_INVALID_WRITABLE_PROP_DESC: constr = ctx->type_error_constr; diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 401f6ca85b0..b1940b770d3 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -715,6 +715,13 @@ static ULONG WINAPI JScript_Release(IActiveScript *iface) return ref; }
+static int weak_refs_compare(const void *key, const struct rb_entry *entry) +{ + const struct weak_refs_entry *weak_refs_entry = RB_ENTRY_VALUE(entry, const struct weak_refs_entry, entry); + ULONG_PTR a = (ULONG_PTR)key, b = (ULONG_PTR)LIST_ENTRY(weak_refs_entry->list.next, struct weakmap_entry, weak_refs_entry)->key; + return (a > b) - (a < b); +} + static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, IActiveScriptSite *pass) { @@ -748,6 +755,7 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, ctx->acc = jsval_undefined(); list_init(&ctx->named_items); list_init(&ctx->objects); + rb_init(&ctx->weak_refs, weak_refs_compare); heap_pool_init(&ctx->tmp_heap);
hres = create_jscaller(ctx); diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 72e114e92e0..650c9278793 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -180,6 +180,7 @@ struct jsdisp_t {
LONG ref;
+ BOOLEAN has_weak_refs; BOOLEAN extensible; BOOLEAN gc_marked;
@@ -363,6 +364,11 @@ typedef struct { unsigned length; } match_result_t;
+struct weak_refs_entry { + struct rb_entry entry; + struct list list; +}; + struct _script_ctx_t { LONG ref;
@@ -372,6 +378,7 @@ struct _script_ctx_t { struct _call_frame_t *call_ctx; struct list named_items; struct list objects; + struct rb_tree weak_refs; IActiveScriptSite *site; IInternetHostSecurityManager *secmgr; DWORD safeopt; @@ -431,7 +438,10 @@ struct weakmap_entry { struct rb_entry entry; jsdisp_t *key; jsval_t value; + jsdisp_t *weakmap; + struct list weak_refs_entry; }; +void remove_weakmap_entry(struct weakmap_entry*);
void script_release(script_ctx_t*);
@@ -557,6 +567,7 @@ static inline HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, jsval_ #define JS_E_NONCONFIGURABLE_REDEFINED MAKE_JSERROR(IDS_NONCONFIGURABLE_REDEFINED) #define JS_E_NONWRITABLE_MODIFIED MAKE_JSERROR(IDS_NONWRITABLE_MODIFIED) #define JS_E_WRONG_THIS MAKE_JSERROR(IDS_WRONG_THIS) +#define JS_E_KEY_NOT_OBJECT MAKE_JSERROR(IDS_KEY_NOT_OBJECT) #define JS_E_PROP_DESC_MISMATCH MAKE_JSERROR(IDS_PROP_DESC_MISMATCH) #define JS_E_INVALID_WRITABLE_PROP_DESC MAKE_JSERROR(IDS_INVALID_WRITABLE_PROP_DESC)
diff --git a/dlls/jscript/jscript.rc b/dlls/jscript/jscript.rc index fbf965fb7e8..e5289a550a3 100644 --- a/dlls/jscript/jscript.rc +++ b/dlls/jscript/jscript.rc @@ -77,6 +77,7 @@ STRINGTABLE IDS_NONCONFIGURABLE_REDEFINED "Cannot redefine non-configurable property '|'" IDS_NONWRITABLE_MODIFIED "Cannot modify non-writable property '|'" IDS_WRONG_THIS "'this' is not a | object" + IDS_KEY_NOT_OBJECT "'key' is not an object" IDS_PROP_DESC_MISMATCH "Property cannot have both accessors and a value"
IDS_COMPILATION_ERROR "Microsoft JScript compilation error" diff --git a/dlls/jscript/resource.h b/dlls/jscript/resource.h index 0ac457d740d..ad771b67475 100644 --- a/dlls/jscript/resource.h +++ b/dlls/jscript/resource.h @@ -75,6 +75,7 @@ #define IDS_NONCONFIGURABLE_REDEFINED 0x13D6 #define IDS_NONWRITABLE_MODIFIED 0x13D7 #define IDS_WRONG_THIS 0x13FC +#define IDS_KEY_NOT_OBJECT 0x13FD /* FIXME: This is not compatible with native, but we would * conflict with IDS_UNSUPPORTED_ACTION otherwise */ #define IDS_PROP_DESC_MISMATCH 0x1F00 diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index 6c0de9240d3..3ee84e36f00 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -625,12 +625,51 @@ static int weakmap_compare(const void *k, const struct rb_entry *e) return (a > b) - (a < b); }
+static HRESULT get_weakmap_this(script_ctx_t *ctx, jsval_t vthis, WeakMapInstance **ret) +{ + jsdisp_t *jsdisp; + + if(!is_object_instance(vthis)) + return JS_E_OBJECT_EXPECTED; + if(!(jsdisp = to_jsdisp(get_object(vthis))) || !is_class(jsdisp, JSCLASS_WEAKMAP)) { + WARN("not a WeakMap object passed as 'this'\n"); + return throw_error(ctx, JS_E_WRONG_THIS, L"WeakMap"); + } + + *ret = CONTAINING_RECORD(jsdisp, WeakMapInstance, dispex); + return S_OK; +} + +static struct weakmap_entry *get_weakmap_entry(WeakMapInstance *weakmap, jsdisp_t *key) +{ + struct rb_entry *entry; + if(!(entry = rb_get(&weakmap->map, key))) return NULL; + return CONTAINING_RECORD(entry, struct weakmap_entry, entry); +} + static void release_weakmap_entry(struct weakmap_entry *entry) { + struct list *next = entry->weak_refs_entry.next; + + if(next->next != &entry->weak_refs_entry) + list_remove(&entry->weak_refs_entry); + else { + struct weak_refs_entry *weak_refs_entry = LIST_ENTRY(next, struct weak_refs_entry, list); + entry->key->has_weak_refs = FALSE; + rb_remove(&entry->key->ctx->weak_refs, &weak_refs_entry->entry); + free(weak_refs_entry); + } jsval_release(entry->value); free(entry); }
+void remove_weakmap_entry(struct weakmap_entry *entry) +{ + WeakMapInstance *weakmap = (WeakMapInstance*)entry->weakmap; + rb_remove(&weakmap->map, &entry->entry); + release_weakmap_entry(entry); +} + static HRESULT WeakMap_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { @@ -655,8 +694,67 @@ static HRESULT WeakMap_get(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigne static HRESULT WeakMap_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL; + jsval_t value = argc >= 2 ? argv[1] : jsval_undefined(); + struct weakmap_entry *entry; + WeakMapInstance *weakmap; + HRESULT hres; + + hres = get_weakmap_this(ctx, vthis, &weakmap); + if(FAILED(hres)) + return hres; + + TRACE("%p (%p %s)\n", weakmap, key, debugstr_jsval(value)); + + if(!key) + return JS_E_KEY_NOT_OBJECT; + + if(key->ctx != ctx) { + FIXME("different ctx not supported\n"); + return JS_E_KEY_NOT_OBJECT; + } + + if((entry = get_weakmap_entry(weakmap, key))) { + jsval_t val; + hres = jsval_copy(value, &val); + if(FAILED(hres)) + return hres; + + jsval_release(entry->value); + entry->value = val; + }else { + struct weak_refs_entry *weak_refs_entry; + + if(!(entry = malloc(sizeof(*entry)))) + return E_OUTOFMEMORY; + + hres = jsval_copy(value, &entry->value); + if(FAILED(hres)) { + free(entry); + return hres; + } + + if(key->has_weak_refs) + weak_refs_entry = RB_ENTRY_VALUE(rb_get(&ctx->weak_refs, key), struct weak_refs_entry, entry); + else { + if(!(weak_refs_entry = malloc(sizeof(*weak_refs_entry)))) { + jsval_release(entry->value); + free(entry); + return E_OUTOFMEMORY; + } + rb_put(&ctx->weak_refs, key, &weak_refs_entry->entry); + list_init(&weak_refs_entry->list); + key->has_weak_refs = TRUE; + } + list_add_tail(&weak_refs_entry->list, &entry->weak_refs_entry); + + entry->key = key; + entry->weakmap = &weakmap->dispex; + rb_put(&weakmap->map, key, &entry->entry); + } + + if(r) *r = jsval_undefined(); + return S_OK; }
static HRESULT WeakMap_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, @@ -698,6 +796,12 @@ static HRESULT WeakMap_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op }
RB_FOR_EACH_ENTRY(entry, &weakmap->map, struct weakmap_entry, entry) { + /* Only traverse the values if the key turned out to be alive, which means it might not have traversed + the associated values with it from this WeakMap yet (because it wasn't considered alive back then). + We need both the key and the WeakMap for the entry to actually be accessible (and thus traversed). */ + if(op == GC_TRAVERSE && entry->key->gc_marked) + continue; + hres = gc_process_linked_val(gc_ctx, op, dispex, &entry->value); if(FAILED(hres)) return hres;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/set.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index 3ee84e36f00..7824aed2c1e 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -687,8 +687,23 @@ static HRESULT WeakMap_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsi static HRESULT WeakMap_get(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL; + struct weakmap_entry *entry; + WeakMapInstance *weakmap; + HRESULT hres; + + hres = get_weakmap_this(ctx, vthis, &weakmap); + if(FAILED(hres)) + return hres; + + TRACE("%p (%p)\n", weakmap, key); + + if(!(entry = get_weakmap_entry(weakmap, key))) { + if(r) *r = jsval_undefined(); + return S_OK; + } + + return r ? jsval_copy(entry->value, r) : S_OK; }
static HRESULT WeakMap_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/set.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index 7824aed2c1e..6cb16edd49c 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -680,8 +680,21 @@ static HRESULT WeakMap_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsig static HRESULT WeakMap_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL; + struct weakmap_entry *entry; + WeakMapInstance *weakmap; + HRESULT hres; + + hres = get_weakmap_this(ctx, vthis, &weakmap); + if(FAILED(hres)) + return hres; + + TRACE("%p (%p)\n", weakmap, key); + + if((entry = get_weakmap_entry(weakmap, key))) + remove_weakmap_entry(entry); + if(r) *r = jsval_bool(!!entry); + return S_OK; }
static HRESULT WeakMap_get(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/set.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index 6cb16edd49c..5a0a42a8c75 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -673,8 +673,22 @@ void remove_weakmap_entry(struct weakmap_entry *entry) static HRESULT WeakMap_clear(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + struct weakmap_entry *entry, *entry2; + WeakMapInstance *weakmap; + HRESULT hres; + + hres = get_weakmap_this(ctx, vthis, &weakmap); + if(FAILED(hres)) + return hres; + + TRACE("%p\n", weakmap); + + RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry) + release_weakmap_entry(entry); + rb_destroy(&weakmap->map, NULL, NULL); + + if(r) *r = jsval_undefined(); + return S_OK; }
static HRESULT WeakMap_delete(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/set.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index 5a0a42a8c75..1e2f243f682 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -802,8 +802,18 @@ static HRESULT WeakMap_set(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigne static HRESULT WeakMap_has(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { - FIXME("\n"); - return E_NOTIMPL; + jsdisp_t *key = (argc >= 1 && is_object_instance(argv[0])) ? to_jsdisp(get_object(argv[0])) : NULL; + WeakMapInstance *weakmap; + HRESULT hres; + + hres = get_weakmap_this(ctx, vthis, &weakmap); + if(FAILED(hres)) + return hres; + + TRACE("%p (%p)\n", weakmap, key); + + if(r) *r = jsval_bool(!!get_weakmap_entry(weakmap, key)); + return S_OK; }
static HRESULT WeakMap_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/mshtml/tests/documentmode.js | 130 ++++++++++++++++++ dlls/mshtml/tests/script.c | 210 ++++++++++++++++++++++++++++++ 2 files changed, 340 insertions(+)
diff --git a/dlls/mshtml/tests/documentmode.js b/dlls/mshtml/tests/documentmode.js index 3533d721960..00a6eded3fe 100644 --- a/dlls/mshtml/tests/documentmode.js +++ b/dlls/mshtml/tests/documentmode.js @@ -470,6 +470,8 @@ sync_test("window_props", function() { test_exposed("requestAnimationFrame", v >= 10); test_exposed("Map", v >= 11); test_exposed("Set", v >= 11); + test_exposed("WeakMap", v >= 11); + test_exposed("WeakSet", false); test_exposed("performance", true); test_exposed("console", v >= 10); test_exposed("matchMedia", v >= 10); @@ -1608,6 +1610,134 @@ sync_test("map_obj", function() { ok(r === 1, "r = " + r); });
+async_test("weakmap_obj", function() { + if(!("WeakMap" in window)) { next_test(); return; } + + try { + var s = WeakMap(); + ok(false, "expected exception calling constructor as method"); + }catch(e) { + ok(e.number === 0xa13fc - 0x80000000, "calling constructor as method threw " + e.number); + } + + var s = new WeakMap, r, o, o2; + ok(Object.getPrototypeOf(s) === WeakMap.prototype, "unexpected WeakMap prototype"); + + function test_length(name, len) { + ok(WeakMap.prototype[name].length === len, "WeakMap.prototype." + name + " = " + WeakMap.prototype[name].length); + } + test_length("clear", 0); + test_length("delete", 1); + test_length("get", 1); + test_length("has", 1); + test_length("set", 2); + ok(!("entries" in s), "entries is in WeakMap"); + ok(!("forEach" in s), "forEach is in WeakMap"); + ok(!("keys" in s), "keys is in WeakMap"); + ok(!("size" in s), "size is in WeakMap"); + ok(!("values" in s), "values is in WeakMap"); + + r = Object.prototype.toString.call(s); + ok(r === "[object Object]", "toString returned " + r); + + r = s.get("test"); + ok(r === undefined, "get('test') returned " + r); + r = s.has("test"); + ok(r === false, "has('test') returned " + r); + + try { + r = s.set("test", 1); + ok(false, "set('test') did not throw"); + }catch(e) { + ok(e.number === 0xa13fd - 0x80000000, "set('test') threw " + e.number); + } + try { + r = s.set(external.testHostContext(true), 1); + ok(false, "set(host_obj) did not throw"); + }catch(e) { + ok(e.number === 0xa13fd - 0x80000000, "set(host_obj) threw " + e.number); + } + + r = s.set({}, 1); + ok(r === undefined, "set({}, 1) returned " + r); + + o = {}, o2 = {}; + r = s.get({}); + ok(r === undefined, "get({}) returned " + r); + r = s.has({}); + ok(r === false, "has({}) returned " + r); + + r = s.set(o, 2); + ok(r === undefined, "set(o, 2) returned " + r); + r = s.get(o); + ok(r === 2, "get(o) returned " + r); + r = s.has(o); + ok(r === true, "has(o) returned " + r); + r = s.get(o2); + ok(r === undefined, "get(o2) before set returned " + r); + r = s.has(o2); + ok(r === false, "has(o2) before set returned " + r); + r = s.set(o2, "test"); + ok(r === undefined, "set(o2, 'test') returned " + r); + r = s.get(o2); + ok(r === "test", "get(o2) returned " + r); + r = s.has(o2); + ok(r === true, "has(o2) returned " + r); + + r = s["delete"]("test"); /* using s.delete() would break parsing in quirks mode */ + ok(r === false, "delete('test') returned " + r); + r = s["delete"]({}); + ok(r === false, "delete({}) returned " + r); + r = s["delete"](o); + ok(r === true, "delete(o) returned " + r); + + r = s.get(o); + ok(r === undefined, "get(o) after delete returned " + r); + r = s.has(o); + ok(r === false, "has(o) after delete returned " + r); + r = s.get(o2); + ok(r === "test", "get(o2) after delete returned " + r); + r = s.has(o2); + ok(r === true, "has(o2) after delete returned " + r); + + r = s.set(o, undefined); + ok(r === undefined, "set(o, undefined) returned " + r); + r = s.get(o); + ok(r === undefined, "get(o) after re-set returned " + r); + r = s.has(o); + ok(r === true, "has(o) after re-set returned " + r); + + r = s.clear(); + ok(r === undefined, "clear() returned " + r); + r = s.get(o); + ok(r === undefined, "get(o) after clear returned " + r); + r = s.has(o); + ok(r === false, "has(o) after clear returned " + r); + r = s.get(o2); + ok(r === undefined, "get(o2) after clear returned " + r); + r = s.has(o2); + ok(r === false, "has(o2) after clear returned " + r); + + r = external.newRefTest(); + ok(r.ref === 1, "wrong ref after newRefTest: " + r.ref); + o = { val: r.get(), map: s }; + s.set(o, o); + ok(r.ref > 1, "map entry released"); + + o = Date.now(); + CollectGarbage(); + function retry() { + if(r.ref > 1 && Date.now() - o < 5000) { + CollectGarbage(); + window.setTimeout(retry); + return; + } + ok(r.ref === 1, "map entry not released"); + next_test(); + } + window.setTimeout(retry); +}); + sync_test("storage", function() { var v = document.documentMode, i, r, list;
diff --git a/dlls/mshtml/tests/script.c b/dlls/mshtml/tests/script.c index 2dc68f1456e..60f8b07e88a 100644 --- a/dlls/mshtml/tests/script.c +++ b/dlls/mshtml/tests/script.c @@ -155,6 +155,8 @@ DEFINE_EXPECT(GetTypeInfo);
#define DISPID_SCRIPT_TESTPROP 0x100000 #define DISPID_SCRIPT_TESTPROP2 0x100001 +#define DISPID_REFTEST_GET 0x100000 +#define DISPID_REFTEST_REF 0x100001
#define DISPID_EXTERNAL_OK 0x300000 #define DISPID_EXTERNAL_TRACE 0x300001 @@ -171,6 +173,7 @@ DEFINE_EXPECT(GetTypeInfo); #define DISPID_EXTERNAL_TESTHOSTCTX 0x30000C #define DISPID_EXTERNAL_GETMIMETYPE 0x30000D #define DISPID_EXTERNAL_SETVIEWSIZE 0x30000E +#define DISPID_EXTERNAL_NEWREFTEST 0x30000F
static const GUID CLSID_TestScript[] = { {0x178fc163,0xf585,0x4e24,{0x9c,0x13,0x4b,0xb7,0xfa,0xf8,0x07,0x46}}, @@ -641,6 +644,13 @@ static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DW return E_NOTIMPL; }
+static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + static HRESULT WINAPI funcDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) { @@ -840,6 +850,181 @@ static IDispatchExVtbl testHostContextDisp_no_this_vtbl = {
static IDispatchEx testHostContextDisp_no_this = { &testHostContextDisp_no_this_vtbl };
+struct refTestObj +{ + IDispatchEx IDispatchEx_iface; + LONG ref; +}; + +struct refTest +{ + IDispatchEx IDispatchEx_iface; + LONG ref; + struct refTestObj *obj; +}; + +static inline struct refTestObj *refTestObj_from_IDispatchEx(IDispatchEx *iface) +{ + return CONTAINING_RECORD(iface, struct refTestObj, IDispatchEx_iface); +} + +static inline struct refTest *refTest_from_IDispatchEx(IDispatchEx *iface) +{ + return CONTAINING_RECORD(iface, struct refTest, IDispatchEx_iface); +} + +static HRESULT WINAPI refTestObj_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) +{ + struct refTestObj *This = refTestObj_from_IDispatchEx(iface); + + if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, &IID_IDispatchEx)) + *ppv = &This->IDispatchEx_iface; + else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IDispatchEx_AddRef(&This->IDispatchEx_iface); + return S_OK; +} + +static ULONG WINAPI refTestObj_AddRef(IDispatchEx *iface) +{ + struct refTestObj *This = refTestObj_from_IDispatchEx(iface); + + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI refTestObj_Release(IDispatchEx *iface) +{ + struct refTestObj *This = refTestObj_from_IDispatchEx(iface); + LONG ref = InterlockedDecrement(&This->ref); + + if(!ref) + free(This); + + return ref; +} + +static IDispatchExVtbl refTestObj_vtbl = { + refTestObj_QueryInterface, + refTestObj_AddRef, + refTestObj_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + DispatchEx_InvokeEx, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static HRESULT WINAPI refTest_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) +{ + struct refTest *This = refTest_from_IDispatchEx(iface); + + if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, &IID_IDispatchEx)) + *ppv = &This->IDispatchEx_iface; + else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IDispatchEx_AddRef(&This->IDispatchEx_iface); + return S_OK; +} + +static ULONG WINAPI refTest_AddRef(IDispatchEx *iface) +{ + struct refTest *This = refTest_from_IDispatchEx(iface); + + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI refTest_Release(IDispatchEx *iface) +{ + struct refTest *This = refTest_from_IDispatchEx(iface); + LONG ref = InterlockedDecrement(&This->ref); + + if(!ref) { + IDispatchEx_Release(&This->obj->IDispatchEx_iface); + free(This); + } + + return ref; +} + +static HRESULT WINAPI refTest_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) +{ + if(!wcscmp(bstrName, L"get")) { + *pid = DISPID_REFTEST_GET; + return S_OK; + } + if(!wcscmp(bstrName, L"ref")) { + *pid = DISPID_REFTEST_REF; + return S_OK; + } + ok(0, "unexpected call %s\n", wine_dbgstr_w(bstrName)); + return E_NOTIMPL; +} + +static HRESULT WINAPI refTest_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + struct refTest *This = refTest_from_IDispatchEx(iface); + + ok(pdp != NULL, "pdp == NULL\n"); + ok(!pdp->cArgs, "pdp->cArgs = %d\n", pdp->cArgs); + ok(pvarRes != NULL, "pvarRes == NULL\n"); + ok(pei != NULL, "pei == NULL\n"); + ok(pspCaller != NULL, "pspCaller == NULL\n"); + + switch(id) { + case DISPID_REFTEST_GET: { + ok(wFlags == DISPATCH_METHOD, "DISPID_REFTEST_GET wFlags = %x\n", wFlags); + V_VT(pvarRes) = VT_DISPATCH; + V_DISPATCH(pvarRes) = (IDispatch*)&This->obj->IDispatchEx_iface; + IDispatchEx_AddRef(&This->obj->IDispatchEx_iface); + break; + } + case DISPID_REFTEST_REF: + ok(wFlags == DISPATCH_PROPERTYGET, "DISPID_REFTEST_REF wFlags = %x\n", wFlags); + V_VT(pvarRes) = VT_I4; + V_I4(pvarRes) = This->obj->ref; + break; + + default: + ok(0, "id = %ld", id); + V_VT(pvarRes) = VT_EMPTY; + break; + } + + return S_OK; +} + +static IDispatchExVtbl refTest_vtbl = { + refTest_QueryInterface, + refTest_AddRef, + refTest_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + refTest_GetDispID, + refTest_InvokeEx, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) { if(!lstrcmpW(bstrName, L"ok")) { @@ -902,6 +1087,10 @@ static HRESULT WINAPI externalDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, *pid = DISPID_EXTERNAL_SETVIEWSIZE; return S_OK; } + if(!lstrcmpW(bstrName, L"newRefTest")) { + *pid = DISPID_EXTERNAL_NEWREFTEST; + return S_OK; + }
ok(0, "unexpected name %s\n", wine_dbgstr_w(bstrName)); return DISP_E_UNKNOWNNAME; @@ -1180,6 +1369,27 @@ static HRESULT WINAPI externalDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID return IOleDocumentView_SetRect(view, &rect); }
+ case DISPID_EXTERNAL_NEWREFTEST: { + struct refTest *obj = malloc(sizeof(*obj)); + + ok(pdp != NULL, "pdp == NULL\n"); + ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); + ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); + ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs); + ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs); + ok(pei != NULL, "pei == NULL\n"); + + obj->IDispatchEx_iface.lpVtbl = &refTest_vtbl; + obj->ref = 1; + obj->obj = malloc(sizeof(*obj->obj)); + obj->obj->IDispatchEx_iface.lpVtbl = &refTestObj_vtbl; + obj->obj->ref = 1; + + V_VT(pvarRes) = VT_DISPATCH; + V_DISPATCH(pvarRes) = (IDispatch*)&obj->IDispatchEx_iface; + return S_OK; + } + default: ok(0, "unexpected call\n"); return E_NOTIMPL;
From: Gabriel Ivăncescu gabrielopcode@gmail.com
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/jscript/error.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/jscript/error.c b/dlls/jscript/error.c index cf0ece42f15..f6eda62b88d 100644 --- a/dlls/jscript/error.c +++ b/dlls/jscript/error.c @@ -483,6 +483,7 @@ jsdisp_t *create_builtin_error(script_ctx_t *ctx) case JS_E_OBJECT_NONEXTENSIBLE: case JS_E_NONCONFIGURABLE_REDEFINED: case JS_E_NONWRITABLE_MODIFIED: + case JS_E_WRONG_THIS: case JS_E_KEY_NOT_OBJECT: case JS_E_PROP_DESC_MISMATCH: case JS_E_INVALID_WRITABLE_PROP_DESC:
Jacek Caban (@jacek) commented about dlls/jscript/set.c:
+}
+static HRESULT WeakMap_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
jsval_t *r)
+{
- FIXME("\n");
- return E_NOTIMPL;
+}
+static void WeakMap_destructor(jsdisp_t *dispex) +{
- WeakMapInstance *weakmap = (WeakMapInstance*)dispex;
- struct weakmap_entry *entry, *entry2;
- RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry)
release_weakmap_entry(entry);
`release_weakmap_entry` calls `jsval_release` which may free any other map entry, including entry2. You could, for example, keep removing root until map is empty instead.
Jacek Caban (@jacek) commented about dlls/jscript/set.c:
- RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry)
release_weakmap_entry(entry);
- free(weakmap);
+}
+static HRESULT WeakMap_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex) +{
- WeakMapInstance *weakmap = (WeakMapInstance*)dispex;
- struct weakmap_entry *entry, *entry2;
- HRESULT hres;
- if(op == GC_TRAVERSE_UNLINK) {
RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry)
release_weakmap_entry(entry);
Same, this is not safe.
Jacek Caban (@jacek) commented about dlls/jscript/dispex.c:
TRACE("(%p)\n", obj);
- if(obj->has_weak_refs) {
struct list *iter, *next, *list = &RB_ENTRY_VALUE(rb_get(&obj->ctx->weak_refs, obj), struct weak_refs_entry, entry)->list;
iter = list_head(list);
do {
next = list_next(list, iter);
remove_weakmap_entry(LIST_ENTRY(iter, struct weakmap_entry, weak_refs_entry));
} while((iter = next));
- }
This is also not safe, something like `while (!list_empty( list )) { ... }` would take care of it.
Jacek Caban (@jacek) commented about dlls/jscript/set.c:
jsval_t *r)
{
- FIXME("\n");
- return E_NOTIMPL;
- struct weakmap_entry *entry, *entry2;
- WeakMapInstance *weakmap;
- HRESULT hres;
- hres = get_weakmap_this(ctx, vthis, &weakmap);
- if(FAILED(hres))
return hres;
- TRACE("%p\n", weakmap);
- RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry)
release_weakmap_entry(entry);
Same as others, this is not safe.
Jacek Caban (@jacek) commented about dlls/jscript/set.c:
{
- FIXME("\n");
- return E_NOTIMPL;
- struct weakmap_entry *entry, *entry2;
- WeakMapInstance *weakmap;
- HRESULT hres;
- hres = get_weakmap_this(ctx, vthis, &weakmap);
- if(FAILED(hres))
return hres;
- TRACE("%p\n", weakmap);
- RB_FOR_EACH_ENTRY_DESTRUCTOR(entry, entry2, &weakmap->map, struct weakmap_entry, entry)
release_weakmap_entry(entry);
- rb_destroy(&weakmap->map, NULL, NULL);
It's redundant after removing all entries.
Other than mentioned comments, it looks mostly good to me now. It could perhaps be split a bit nicer, I'd suggest to move rb_map introduction from the patch adding WeakMap to the one implementing WeakMap.set.
On Tue Jul 18 09:11:52 2023 +0000, Jacek Caban wrote:
This is also not safe, something like `while (!list_empty( list )) { ... }` would take care of it.
To keep `has_weak_refs` in sync and avoid pointless lookup overhead when there's no weak refs, `remove_weakmap_entry` removes the list when it's the only entry in the list, so I can't check the list.
But obviously, I can just check `has_weak_refs` and I'll do just that.