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;