-- v5: 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 | 6 +- dlls/jscript/object.c | 1 + dlls/jscript/set.c | 134 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 46644b8247b..d907da3a36c 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -117,6 +117,7 @@ typedef enum { JSCLASS_JSON, JSCLASS_MAP, JSCLASS_SET, + JSCLASS_WEAKMAP, } jsclass_t;
jsdisp_t *iface_to_jsdisp(IDispatch*); @@ -418,11 +419,12 @@ 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));
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..2afb763f4d7 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -614,6 +614,125 @@ static HRESULT Set_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, uns } }
+typedef struct { + jsdisp_t dispex; +} WeakMapInstance; + +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; + + free(weakmap); +} + +static HRESULT WeakMap_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex) +{ + 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; + + *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 +768,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 | 25 ++++++++ dlls/jscript/error.c | 1 + dlls/jscript/jscript.c | 8 +++ dlls/jscript/jscript.h | 18 ++++++ dlls/jscript/jscript.rc | 1 + dlls/jscript/resource.h | 1 + dlls/jscript/set.c | 136 +++++++++++++++++++++++++++++++++++++++- 7 files changed, 188 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index 9e87c38004f..2da2270fb0f 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,13 @@ void jsdisp_free(jsdisp_t *obj)
TRACE("(%p)\n", obj);
+ if(obj->has_weak_refs) { + struct list *list = &RB_ENTRY_VALUE(rb_get(&obj->ctx->weak_refs, obj), struct weak_refs_entry, entry)->list; + do { + remove_weakmap_entry(LIST_ENTRY(list->next, struct weakmap_entry, weak_refs_entry)); + } while(obj->has_weak_refs); + } + 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 d907da3a36c..650c9278793 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 @@ -179,6 +180,7 @@ struct jsdisp_t {
LONG ref;
+ BOOLEAN has_weak_refs; BOOLEAN extensible; BOOLEAN gc_marked;
@@ -362,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;
@@ -371,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; @@ -426,6 +434,15 @@ struct _script_ctx_t { }; 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; + jsdisp_t *weakmap; + struct list weak_refs_entry; +}; +void remove_weakmap_entry(struct weakmap_entry*); + void script_release(script_ctx_t*);
static inline void script_addref(script_ctx_t *ctx) @@ -550,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 2afb763f4d7..f8c07474632 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -616,8 +616,56 @@ 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 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"); + throw_error(ctx, JS_E_WRONG_THIS, L"WeakMap"); + return DISP_E_EXCEPTION; + } + + *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); +} + +void remove_weakmap_entry(struct weakmap_entry *entry) +{ + WeakMapInstance *weakmap = (WeakMapInstance*)entry->weakmap; + 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); + } + rb_remove(&weakmap->map, &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) { @@ -642,8 +690,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, @@ -664,11 +771,35 @@ static void WeakMap_destructor(jsdisp_t *dispex) { WeakMapInstance *weakmap = (WeakMapInstance*)dispex;
+ while(weakmap->map.root) + remove_weakmap_entry(RB_ENTRY_VALUE(weakmap->map.root, struct 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; + HRESULT hres; + + if(op == GC_TRAVERSE_UNLINK) { + while(weakmap->map.root) + remove_weakmap_entry(RB_ENTRY_VALUE(weakmap->map.root, struct weakmap_entry, entry)); + return S_OK; + } + + 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; + } return S_OK; }
@@ -721,6 +852,7 @@ static HRESULT WeakMap_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, if(FAILED(hres)) return hres;
+ rb_init(&weakmap->map, weakmap_compare); *r = jsval_obj(&weakmap->dispex); return S_OK;
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 f8c07474632..2ad6c683a46 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -683,8 +683,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 2ad6c683a46..96375ce4b16 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -676,8 +676,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 | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index 96375ce4b16..1730abb0328 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -669,8 +669,20 @@ 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; + WeakMapInstance *weakmap; + HRESULT hres; + + hres = get_weakmap_this(ctx, vthis, &weakmap); + if(FAILED(hres)) + return hres; + + TRACE("%p\n", weakmap); + + while(weakmap->map.root) + remove_weakmap_entry(RB_ENTRY_VALUE(weakmap->map.root, struct weakmap_entry, entry)); + + 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 1730abb0328..ac9efbb4da0 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -796,8 +796,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:
This merge request was approved by Jacek Caban.