From: Gabriel Ivăncescu <gabrielopcode@gmail.com> Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com> --- dlls/jscript/arraybuf.c | 22 ++++++++- dlls/jscript/dispex.c | 88 ++++++++++++++++++++++++++++++++++++ dlls/jscript/engine.c | 24 +++++++++- dlls/jscript/enumerator.c | 23 +++++++++- dlls/jscript/function.c | 84 ++++++++++++++++++++++++++++++---- dlls/jscript/jscript.c | 11 +++++ dlls/jscript/jscript.h | 20 +++++++- dlls/jscript/jscript_main.c | 2 +- dlls/jscript/jsdisp.idl | 15 ++++++ dlls/jscript/jsregexp.c | 13 +++++- dlls/jscript/set.c | 30 ++++++++++++ dlls/mshtml/dispex.c | 31 +++++++++++++ dlls/mshtml/htmlwindow.c | 24 ++++++++++ dlls/mshtml/main.c | 10 ++++ dlls/mshtml/mshtml_private.h | 4 ++ dlls/mshtml/nsembed.c | 12 ++++- 16 files changed, 393 insertions(+), 20 deletions(-) diff --git a/dlls/jscript/arraybuf.c b/dlls/jscript/arraybuf.c index c99b96e5cad..5401b96faac 100644 --- a/dlls/jscript/arraybuf.c +++ b/dlls/jscript/arraybuf.c @@ -648,18 +648,27 @@ static HRESULT DataView_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op o return gc_process_linked_obj(gc_ctx, op, dispex, &view->buffer->dispex, (void**)&view->buffer); } +static void DataView_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + DataViewInstance *view = dataview_from_jsdisp(dispex); + if(view->buffer) + cc_api.note_edge((IUnknown*)&view->buffer->dispex.IWineJSDispatch_iface, "buffer", cb); +} + static const builtin_info_t DataView_info = { .class = JSCLASS_DATAVIEW, .props_cnt = ARRAY_SIZE(DataView_props), .props = DataView_props, .destructor = DataView_destructor, - .gc_traverse = DataView_gc_traverse + .gc_traverse = DataView_gc_traverse, + .cc_traverse = DataView_cc_traverse }; static const builtin_info_t DataViewInst_info = { .class = JSCLASS_DATAVIEW, .destructor = DataView_destructor, - .gc_traverse = DataView_gc_traverse + .gc_traverse = DataView_gc_traverse, + .cc_traverse = DataView_cc_traverse }; static HRESULT DataViewConstr_value(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, @@ -1038,6 +1047,13 @@ static HRESULT TypedArray_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op return gc_process_linked_obj(gc_ctx, op, dispex, &typedarr->buffer->dispex, (void**)&typedarr->buffer); } +static void TypedArray_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + TypedArrayInstance *typedarr = typedarr_from_jsdisp(dispex); + if(typedarr->buffer) + cc_api.note_edge((IUnknown*)&typedarr->buffer->dispex.IWineJSDispatch_iface, "buffer", cb); +} + static const builtin_prop_t TypedArrayInst_props[] = { {L"buffer", NULL, 0, TypedArray_get_buffer}, {L"byteLength", NULL, 0, TypedArray_get_byteLength}, @@ -1234,6 +1250,7 @@ static const builtin_info_t name##_info = .prop_put = name##_prop_put, \ .fill_props = TypedArray_fill_props, \ .gc_traverse = TypedArray_gc_traverse, \ + .cc_traverse = TypedArray_cc_traverse, \ }; \ \ static const builtin_info_t name##Inst_info = \ @@ -1247,6 +1264,7 @@ static const builtin_info_t name##Inst_info = .prop_put = name##_prop_put, \ .fill_props = TypedArray_fill_props, \ .gc_traverse = TypedArray_gc_traverse, \ + .cc_traverse = TypedArray_cc_traverse, \ }; \ static HRESULT name ## Constr_value(script_ctx_t *ctx, jsval_t jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) \ { \ diff --git a/dlls/jscript/dispex.c b/dlls/jscript/dispex.c index a70fadf09be..6953dbebf21 100644 --- a/dlls/jscript/dispex.c +++ b/dlls/jscript/dispex.c @@ -924,6 +924,11 @@ HRESULT gc_run(script_ctx_t *ctx) if(thread_data->gc_is_unlinking) return S_OK; + thread_data->gc_is_unlinking = TRUE; + if(cc_api.collect) + cc_api.collect(); + thread_data->gc_is_unlinking = FALSE; + if(!(head = malloc(sizeof(*head)))) return E_OUTOFMEMORY; head->next = NULL; @@ -1921,6 +1926,21 @@ static HRESULT WINAPI DispatchEx_QueryInterface(IWineJSDispatch *iface, REFIID r }else if(IsEqualGUID(&IID_IWineJSDispatch, riid)) { TRACE("(%p)->(IID_IWineJSDispatch %p)\n", This, ppv); *ppv = &This->IWineJSDispatch_iface; + }else if(IsEqualGUID(&IID_nsXPCOMCycleCollectionParticipant, riid)) { + /* Only expose these during a full CC, as we can't have their refs change between incremental CC phases */ + if(!This->builtin_info->get_host_disp && cc_api.is_full_cc && cc_api.is_full_cc()) { + *ppv = &cc_api.participant; + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; + }else if(IsEqualGUID(&IID_nsCycleCollectionISupports, riid)) { + if(!This->builtin_info->get_host_disp && cc_api.is_full_cc && cc_api.is_full_cc()) { + *ppv = &This->IWineJSDispatch_iface; + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; }else { WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); *ppv = NULL; @@ -2369,6 +2389,41 @@ static void WINAPI WineJSDispatch_Free(IWineJSDispatch *iface) jsdisp_free(This); } +static void WINAPI WineJSDispatch_Traverse(IWineJSDispatch *iface, nsCycleCollectionTraversalCallback *cb) +{ + jsdisp_t *This = impl_from_IWineJSDispatch(iface); + note_edge_t note_edge = cc_api.note_edge; + dispex_prop_t *prop = This->props, *end; + + for(end = prop + This->prop_cnt; prop < end; prop++) { + switch(prop->type) { + case PROP_JSVAL: + if(is_object_instance(prop->u.val)) + note_edge(get_edge_obj(get_object(prop->u.val)), "prop", cb); + break; + case PROP_ACCESSOR: + if(prop->u.accessor.getter) + note_edge(jsdisp_get_edge_obj(prop->u.accessor.getter), "prop", cb); + if(prop->u.accessor.setter) + note_edge(jsdisp_get_edge_obj(prop->u.accessor.setter), "prop", cb); + break; + default: + break; + } + } + + if(This->prototype) + note_edge(jsdisp_get_edge_obj(This->prototype), "prototype", cb); + + if(This->builtin_info->cc_traverse) + This->builtin_info->cc_traverse(This, cb); +} + +static void WINAPI WineJSDispatch_Unlink(IWineJSDispatch *iface) +{ + unlink_jsdisp(impl_from_IWineJSDispatch(iface)); +} + static HRESULT WINAPI WineJSDispatch_GetPropertyFlags(IWineJSDispatch *iface, DISPID id, UINT32 *ret) { jsdisp_t *This = impl_from_IWineJSDispatch(iface); @@ -2454,6 +2509,8 @@ static IWineJSDispatchVtbl DispatchExVtbl = { DispatchEx_GetNextDispID, DispatchEx_GetNameSpaceParent, WineJSDispatch_Free, + WineJSDispatch_Traverse, + WineJSDispatch_Unlink, WineJSDispatch_GetPropertyFlags, WineJSDispatch_DefineProperty, WineJSDispatch_UpdateProperty, @@ -2570,6 +2627,34 @@ jsdisp_t *iface_to_jsdisp(IDispatch *iface) : NULL; } +static HRESULT WINAPI jsdisp_cc_traverse(void *ccp, void *p, nsCycleCollectionTraversalCallback *cb) +{ + jsdisp_t *This = impl_from_IWineJSDispatch(p); + + cc_api.describe_node(This->ref, "jsdisp", cb); + WineJSDispatch_Traverse(&This->IWineJSDispatch_iface, cb); + return S_OK; +} + +static HRESULT WINAPI jsdisp_cc_unlink(void *p) +{ + unlink_jsdisp(impl_from_IWineJSDispatch(p)); + return S_OK; +} + +struct jshost_cc_api cc_api; + +void init_cc_api(IWineJSDispatchHost *host_obj) +{ + static const CCObjCallback jsdisp_ccp_callback = { + jsdisp_cc_traverse, + jsdisp_cc_unlink, + NULL /* delete_cycle_collectable shouldn't ever be called, since we're never part of the purple buffer */ + }; + + IWineJSDispatchHost_InitCC(host_obj, &cc_api, &jsdisp_ccp_callback); +} + HRESULT jsdisp_get_id(jsdisp_t *jsdisp, const WCHAR *name, DWORD flags, DISPID *id) { dispex_prop_t *prop; @@ -3635,6 +3720,9 @@ HRESULT init_host_object(script_ctx_t *ctx, IWineJSDispatchHost *host_iface, IWi jsdisp_t *prototype; HRESULT hres; + if(!cc_api.note_edge) + init_cc_api(host_iface); + if(!(host_obj = calloc(1, sizeof(*host_obj)))) return E_OUTOFMEMORY; diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index 243b922c8f1..1fdee902f71 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -555,13 +555,35 @@ static HRESULT scope_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, return scope->obj && (jsobj = to_jsdisp(scope->obj)) ? gc_process_linked_obj(gc_ctx, op, dispex, jsobj, (void**)&scope->obj) : S_OK; } +static void scope_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + scope_chain_t *scope = scope_from_dispex(dispex); + note_edge_t note_edge = cc_api.note_edge; + + if(scope->detached_vars) { + struct vars_buffer *vars = scope->detached_vars; + unsigned i, cnt = vars->argc; + + for(i = 0; i < cnt; i++) + if(is_object_instance(vars->var[i])) + note_edge(get_edge_obj(get_object(vars->var[i])), "var", cb); + } + + if(scope->next) + note_edge((IUnknown*)&scope->next->dispex.IWineJSDispatch_iface, "next", cb); + + if(scope->obj) + note_edge(get_edge_obj(scope->obj), "obj", cb); +} + static const builtin_info_t scope_info = { JSCLASS_NONE, .destructor = scope_destructor, .lookup_prop = scope_lookup_prop, .prop_get = scope_prop_get, .prop_put = scope_prop_put, - .gc_traverse = scope_gc_traverse + .gc_traverse = scope_gc_traverse, + .cc_traverse = scope_cc_traverse }; static HRESULT scope_push(script_ctx_t *ctx, scope_chain_t *scope, IDispatch *obj, scope_chain_t **ret) diff --git a/dlls/jscript/enumerator.c b/dlls/jscript/enumerator.c index 1f57f6b5173..fa841aa3060 100644 --- a/dlls/jscript/enumerator.c +++ b/dlls/jscript/enumerator.c @@ -91,7 +91,25 @@ static void Enumerator_destructor(jsdisp_t *dispex) static HRESULT Enumerator_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, jsdisp_t *dispex) { - return gc_process_linked_val(gc_ctx, op, dispex, &enumerator_from_jsdisp(dispex)->item); + EnumeratorInstance *This = enumerator_from_jsdisp(dispex); + + if(op == GC_TRAVERSE_UNLINK) { + IEnumVARIANT *enumvar = This->enumvar; + if(enumvar) { + This->enumvar = NULL; + IEnumVARIANT_Release(enumvar); + } + } + return gc_process_linked_val(gc_ctx, op, dispex, &This->item); +} + +static void Enumerator_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + EnumeratorInstance *This = enumerator_from_jsdisp(dispex); + if(This->enumvar) + cc_api.note_edge((IUnknown*)This->enumvar, "enumvar", cb); + if(is_object_instance(This->item)) + cc_api.note_edge(get_edge_obj(get_object(This->item)), "item", cb); } static HRESULT Enumerator_atEnd(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, @@ -189,7 +207,8 @@ static const builtin_info_t Enumerator_info = { static const builtin_info_t EnumeratorInst_info = { .class = JSCLASS_ENUMERATOR, .destructor = Enumerator_destructor, - .gc_traverse = Enumerator_gc_traverse + .gc_traverse = Enumerator_gc_traverse, + .cc_traverse = Enumerator_cc_traverse }; static HRESULT alloc_enumerator(script_ctx_t *ctx, jsdisp_t *object_prototype, EnumeratorInstance **ret) diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index 3570fff72c4..85afe51a3bb 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -40,6 +40,7 @@ struct _function_vtbl_t { function_code_t* (*get_code)(FunctionInstance*); void (*destructor)(FunctionInstance*); HRESULT (*gc_traverse)(struct gc_ctx*,enum gc_traverse_op,FunctionInstance*); + void (*cc_traverse)(FunctionInstance*,nsCycleCollectionTraversalCallback*); }; typedef struct { @@ -91,6 +92,10 @@ static HRESULT no_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, Fun return S_OK; } +static void no_cc_traverse(FunctionInstance *function, nsCycleCollectionTraversalCallback *cb) +{ +} + static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp) { return CONTAINING_RECORD(jsdisp, FunctionInstance, dispex); @@ -202,6 +207,22 @@ static HRESULT Arguments_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op return S_OK; } +static void Arguments_cc_traverse(jsdisp_t *jsdisp, nsCycleCollectionTraversalCallback *cb) +{ + ArgumentsInstance *arguments = arguments_from_jsdisp(jsdisp); + note_edge_t note_edge = cc_api.note_edge; + unsigned i; + + if(arguments->buf) { + for(i = 0; i < arguments->argc; i++) + if(is_object_instance(arguments->buf[i])) + note_edge(get_edge_obj(get_object(arguments->buf[i])), "buf", cb); + } + + if(arguments->scope) + note_edge((IUnknown*)&arguments->scope->dispex.IWineJSDispatch_iface, "scope", cb); +} + static HRESULT Arguments_get_caller(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) { ArgumentsInstance *arguments = arguments_from_jsdisp(jsthis); @@ -243,7 +264,8 @@ static const builtin_info_t Arguments_info = { .prop_get = Arguments_prop_get, .prop_put = Arguments_prop_put, .fill_props = Arguments_fill_props, - .gc_traverse = Arguments_gc_traverse + .gc_traverse = Arguments_gc_traverse, + .cc_traverse = Arguments_cc_traverse }; static const builtin_info_t Arguments_ES5_info = { @@ -254,7 +276,8 @@ static const builtin_info_t Arguments_ES5_info = { .prop_get = Arguments_prop_get, .prop_put = Arguments_prop_put, .fill_props = Arguments_fill_props, - .gc_traverse = Arguments_gc_traverse + .gc_traverse = Arguments_gc_traverse, + .cc_traverse = Arguments_cc_traverse }; HRESULT setup_arguments_object(script_ctx_t *ctx, call_frame_t *frame) @@ -678,6 +701,12 @@ static HRESULT Function_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op o return function->vtbl->gc_traverse(gc_ctx, op, function); } +static void Function_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + FunctionInstance *function = function_from_jsdisp(dispex); + return function->vtbl->cc_traverse(function, cb); +} + static const builtin_prop_t Function_props[] = { {L"apply", Function_apply, PROPF_METHOD|2}, {L"arguments", NULL, PROPF_HTML, Function_get_arguments}, @@ -694,7 +723,8 @@ static const builtin_info_t Function_info = { .props_cnt = ARRAY_SIZE(Function_props), .props = Function_props, .destructor = Function_destructor, - .gc_traverse = Function_gc_traverse + .gc_traverse = Function_gc_traverse, + .cc_traverse = Function_cc_traverse }; static const builtin_prop_t FunctionInst_props[] = { @@ -709,7 +739,8 @@ static const builtin_info_t FunctionInst_info = { .props_cnt = ARRAY_SIZE(FunctionInst_props), .props = FunctionInst_props, .destructor = Function_destructor, - .gc_traverse = Function_gc_traverse + .gc_traverse = Function_gc_traverse, + .cc_traverse = Function_cc_traverse }; static HRESULT create_function(script_ctx_t *ctx, const builtin_info_t *builtin_info, const function_vtbl_t *vtbl, size_t size, @@ -771,7 +802,8 @@ static const function_vtbl_t NativeFunctionVtbl = { NativeFunction_toString, NativeFunction_get_code, NativeFunction_destructor, - no_gc_traverse + no_gc_traverse, + no_cc_traverse }; HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, const WCHAR *name, @@ -873,7 +905,8 @@ static const builtin_info_t InterpretedFunction_info = { .props_cnt = ARRAY_SIZE(InterpretedFunction_props), .props = InterpretedFunction_props, .destructor = Function_destructor, - .gc_traverse = Function_gc_traverse + .gc_traverse = Function_gc_traverse, + .cc_traverse = Function_cc_traverse }; static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *func, jsval_t vthis, unsigned flags, @@ -946,12 +979,20 @@ static HRESULT InterpretedFunction_gc_traverse(struct gc_ctx *gc_ctx, enum gc_tr (void**)&function->scope_chain); } +static void InterpretedFunction_cc_traverse(FunctionInstance *func, nsCycleCollectionTraversalCallback *cb) +{ + InterpretedFunction *function = (InterpretedFunction*)func; + if(function->scope_chain) + cc_api.note_edge((IUnknown*)&function->scope_chain->dispex.IWineJSDispatch_iface, "scope_chain", cb); +} + static const function_vtbl_t InterpretedFunctionVtbl = { InterpretedFunction_call, InterpretedFunction_toString, InterpretedFunction_get_code, InterpretedFunction_destructor, - InterpretedFunction_gc_traverse + InterpretedFunction_gc_traverse, + InterpretedFunction_cc_traverse }; HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_code_t *func_code, @@ -990,7 +1031,8 @@ static const builtin_info_t HostFunction_info = { .destructor = Function_destructor, .props_cnt = ARRAY_SIZE(HostFunction_props), .props = HostFunction_props, - .gc_traverse = Function_gc_traverse + .gc_traverse = Function_gc_traverse, + .cc_traverse = Function_cc_traverse }; static HRESULT HostFunction_call(script_ctx_t *ctx, FunctionInstance *func, jsval_t vthis, unsigned flags, @@ -1073,6 +1115,7 @@ static const function_vtbl_t HostFunctionVtbl = { HostFunction_get_code, HostFunction_destructor, no_gc_traverse, + no_cc_traverse }; HRESULT create_host_function(script_ctx_t *ctx, const struct property_info *desc, DWORD flags, jsdisp_t **ret) @@ -1118,6 +1161,7 @@ static const builtin_info_t HostConstructor_info = { .props = HostFunction_props, .get_host_disp = HostConstructor_get_host_disp, .gc_traverse = Function_gc_traverse, + .cc_traverse = Function_cc_traverse, .lookup_prop = HostConstructor_lookup_prop, }; @@ -1196,6 +1240,7 @@ static const function_vtbl_t HostConstructorVtbl = { HostConstructor_get_code, HostConstructor_destructor, no_gc_traverse, + no_cc_traverse }; HRESULT init_host_constructor(script_ctx_t *ctx, IWineJSDispatchHost *host_constr, const WCHAR *method_name, IWineJSDispatch **ret) @@ -1236,7 +1281,8 @@ static const builtin_info_t BindFunction_info = { .props_cnt = ARRAY_SIZE(BindFunction_props), .props = BindFunction_props, .destructor = Function_destructor, - .gc_traverse = Function_gc_traverse + .gc_traverse = Function_gc_traverse, + .cc_traverse = Function_cc_traverse }; static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, jsval_t vthis, unsigned flags, @@ -1311,12 +1357,30 @@ static HRESULT BindFunction_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_ return gc_process_linked_val(gc_ctx, op, &function->function.dispex, &function->this); } +static void BindFunction_cc_traverse(FunctionInstance *func, nsCycleCollectionTraversalCallback *cb) +{ + BindFunction *function = (BindFunction*)func; + note_edge_t note_edge = cc_api.note_edge; + unsigned i; + + for(i = 0; i < function->argc; i++) + if(is_object_instance(function->args[i])) + note_edge(get_edge_obj(get_object(function->args[i])), "arg", cb); + + if(function->target) + note_edge(jsdisp_get_edge_obj(&function->target->dispex), "target", cb); + + if(is_object_instance(function->this)) + note_edge(get_edge_obj(get_object(function->this)), "this", cb); +} + static const function_vtbl_t BindFunctionVtbl = { BindFunction_call, BindFunction_toString, BindFunction_get_code, BindFunction_destructor, - BindFunction_gc_traverse + BindFunction_gc_traverse, + BindFunction_cc_traverse }; static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target, jsval_t bound_this, unsigned argc, diff --git a/dlls/jscript/jscript.c b/dlls/jscript/jscript.c index 30727a5ab2b..2daf013589c 100644 --- a/dlls/jscript/jscript.c +++ b/dlls/jscript/jscript.c @@ -912,6 +912,17 @@ static HRESULT WINAPI JScript_AddNamedItem(IActiveScript *iface, WARN("object does not implement IDispatch\n"); return hres; } + + if(!cc_api.note_edge && This->ctx->html_mode) { + IWineJSDispatchHost *host; + + /* Init CC API to get rid of cycles in IE8 and below as well */ + hres = IDispatch_QueryInterface(disp, &IID_IWineJSDispatchHost, (void**)&host); + if(SUCCEEDED(hres) && host) { + init_cc_api(host); + IWineJSDispatchHost_Release(host); + } + } } item = malloc(sizeof(*item)); diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 9aee626cb8d..9d1aceae7a2 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -28,12 +28,12 @@ #include "ole2.h" #include "dispex.h" #include "activscp.h" -#include "jsdisp.h" #include "resource.h" #include "wine/list.h" #include "wine/rbtree.h" +#include "jsdisp.h" typedef struct _jsval_t jsval_t; typedef struct _jsstr_t jsstr_t; @@ -60,6 +60,7 @@ heap_pool_t *heap_pool_mark(heap_pool_t*); typedef struct jsdisp_t jsdisp_t; +extern struct jshost_cc_api cc_api; extern HINSTANCE jscript_hinstance ; HRESULT get_dispatch_typeinfo(ITypeInfo**); @@ -191,6 +192,7 @@ typedef struct { HRESULT (*fill_props)(jsdisp_t*); HRESULT (*to_string)(jsdisp_t*,jsstr_t**); HRESULT (*gc_traverse)(struct gc_ctx*,enum gc_traverse_op,jsdisp_t*); + void (*cc_traverse)(jsdisp_t*,nsCycleCollectionTraversalCallback*); } builtin_info_t; struct jsdisp_t { @@ -245,6 +247,7 @@ HRESULT init_dispex_from_constr(jsdisp_t*,script_ctx_t*,const builtin_info_t*,js HRESULT init_host_object(script_ctx_t*,IWineJSDispatchHost*,IWineJSDispatch*,UINT32,IWineJSDispatch**); HRESULT init_host_constructor(script_ctx_t*,IWineJSDispatchHost*,const WCHAR*,IWineJSDispatch**); HRESULT fill_globals(script_ctx_t*,IWineJSDispatchHost*); +void init_cc_api(IWineJSDispatchHost*); HRESULT disp_call(script_ctx_t*,IDispatch*,DISPID,WORD,unsigned,jsval_t*,jsval_t*); HRESULT disp_call_name(script_ctx_t*,IDispatch*,const WCHAR*,WORD,unsigned,jsval_t*,jsval_t*); @@ -526,6 +529,21 @@ static inline HRESULT disp_call_value(script_ctx_t *ctx, IDispatch *disp, jsval_ return disp_call_value_with_caller(ctx, disp, vthis, flags, argc, argv, r, &ctx->jscaller->IServiceProvider_iface); } +static inline IUnknown *jsdisp_get_edge_obj(jsdisp_t *jsdisp) +{ + if(jsdisp->builtin_info->get_host_disp) + return (IUnknown*)jsdisp->builtin_info->get_host_disp(jsdisp); + return (IUnknown*)&jsdisp->IWineJSDispatch_iface; +} + +static inline IUnknown *get_edge_obj(IDispatch *disp) +{ + jsdisp_t *jsdisp = to_jsdisp(disp); + if(jsdisp) + return jsdisp_get_edge_obj(jsdisp); + return (IUnknown*)disp; +} + #define MAKE_JSERROR(code) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_CONTROL, code) #define JS_E_TO_PRIMITIVE MAKE_JSERROR(IDS_TO_PRIMITIVE) diff --git a/dlls/jscript/jscript_main.c b/dlls/jscript/jscript_main.c index 6ccab362051..ccaa12761c2 100644 --- a/dlls/jscript/jscript_main.c +++ b/dlls/jscript/jscript_main.c @@ -27,7 +27,6 @@ #include "mshtmhst.h" #include "rpcproxy.h" #include "jscript_classes.h" -#include "jsdisp.h" #include "wine/debug.h" @@ -208,6 +207,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) if (lpv) break; if (dispatch_typeinfo) ITypeInfo_Release(dispatch_typeinfo); if(jscript_tls != TLS_OUT_OF_INDEXES) TlsFree(jscript_tls); + list_remove(&cc_api.entry); free_strings(); break; } diff --git a/dlls/jscript/jsdisp.idl b/dlls/jscript/jsdisp.idl index 93ede1bbc2b..7b9e54e1eb8 100644 --- a/dlls/jscript/jsdisp.idl +++ b/dlls/jscript/jsdisp.idl @@ -60,6 +60,18 @@ typedef struct { void (__stdcall *delete_cycle_collectable)(void*); } CCObjCallback; +typedef void (__cdecl *note_edge_t)(IUnknown*,const char*,nsCycleCollectionTraversalCallback*); + +struct jshost_cc_api +{ + ExternalCycleCollectionParticipant participant; + BOOL (__cdecl *is_full_cc)(void); + void (__cdecl *collect)(void); + void (__cdecl *describe_node)(ULONG ref, const char *obj_name, nsCycleCollectionTraversalCallback *cb); + note_edge_t note_edge; + struct list entry; +}; + interface IWineJSDispatchHost; [ @@ -70,6 +82,8 @@ interface IWineJSDispatchHost; interface IWineJSDispatch : IDispatchEx { void Free(); + void Traverse(nsCycleCollectionTraversalCallback *cb); + void Unlink(); HRESULT GetPropertyFlags(DISPID id, UINT32 *ret); HRESULT DefineProperty(const WCHAR *name, unsigned int flags, VARIANT *v); HRESULT UpdateProperty(struct property_info *desc); @@ -95,6 +109,7 @@ interface IWineJSDispatchHost : IDispatchEx HRESULT FillProperties(); HRESULT GetOuterDispatch(IWineJSDispatchHost **ret); HRESULT ToString(BSTR *str); + void InitCC(struct jshost_cc_api *cc_api, const CCObjCallback *callback); } const unsigned int SCRIPTLANGUAGEVERSION_HTML = 0x400; diff --git a/dlls/jscript/jsregexp.c b/dlls/jscript/jsregexp.c index 979f994ee55..1daaa0eac03 100644 --- a/dlls/jscript/jsregexp.c +++ b/dlls/jscript/jsregexp.c @@ -560,6 +560,13 @@ static HRESULT RegExp_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, return gc_process_linked_val(gc_ctx, op, dispex, ®exp_from_jsdisp(dispex)->last_index_val); } +static void RegExp_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + RegExpInstance *This = regexp_from_jsdisp(dispex); + if(is_object_instance(This->last_index_val)) + cc_api.note_edge(get_edge_obj(get_object(This->last_index_val)), "last_index_val", cb); +} + static const builtin_prop_t RegExp_props[] = { {L"exec", RegExp_exec, PROPF_METHOD|1}, {L"global", NULL,0, RegExp_get_global}, @@ -577,7 +584,8 @@ static const builtin_info_t RegExp_info = { .props_cnt = ARRAY_SIZE(RegExp_props), .props = RegExp_props, .destructor = RegExp_destructor, - .gc_traverse = RegExp_gc_traverse + .gc_traverse = RegExp_gc_traverse, + .cc_traverse = RegExp_cc_traverse }; static const builtin_prop_t RegExpInst_props[] = { @@ -594,7 +602,8 @@ static const builtin_info_t RegExpInst_info = { .props_cnt = ARRAY_SIZE(RegExpInst_props), .props = RegExpInst_props, .destructor = RegExp_destructor, - .gc_traverse = RegExp_gc_traverse + .gc_traverse = RegExp_gc_traverse, + .cc_traverse = RegExp_cc_traverse }; static HRESULT alloc_regexp(script_ctx_t *ctx, jsstr_t *str, jsdisp_t *object_prototype, RegExpInstance **ret) diff --git a/dlls/jscript/set.c b/dlls/jscript/set.c index c114e3fd6e2..16535cc46eb 100644 --- a/dlls/jscript/set.c +++ b/dlls/jscript/set.c @@ -385,6 +385,20 @@ static HRESULT Map_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op, js return S_OK; } +static void Map_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + note_edge_t note_edge = cc_api.note_edge; + MapInstance *map = (MapInstance*)dispex; + struct jsval_map_entry *entry; + + LIST_FOR_EACH_ENTRY(entry, &map->entries, struct jsval_map_entry, list_entry) { + if(is_object_instance(entry->key)) + note_edge(get_edge_obj(get_object(entry->key)), "key", cb); + if(is_object_instance(entry->value)) + note_edge(get_edge_obj(get_object(entry->value)), "value", cb); + } +} + static const builtin_prop_t Map_prototype_props[] = { {L"clear", Map_clear, PROPF_METHOD}, {L"delete" , Map_delete, PROPF_METHOD|1}, @@ -412,6 +426,7 @@ static const builtin_info_t Map_info = { .props = Map_props, .destructor = Map_destructor, .gc_traverse = Map_gc_traverse, + .cc_traverse = Map_cc_traverse }; static HRESULT Map_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, @@ -565,6 +580,7 @@ static const builtin_info_t Set_info = { .props = Map_props, .destructor = Map_destructor, .gc_traverse = Map_gc_traverse, + .cc_traverse = Map_cc_traverse }; static HRESULT Set_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, @@ -832,6 +848,19 @@ static HRESULT WeakMap_gc_traverse(struct gc_ctx *gc_ctx, enum gc_traverse_op op return S_OK; } +static void WeakMap_cc_traverse(jsdisp_t *dispex, nsCycleCollectionTraversalCallback *cb) +{ + WeakMapInstance *weakmap = (WeakMapInstance*)dispex; + note_edge_t note_edge = cc_api.note_edge; + struct weakmap_entry *entry; + + /* FIXME: WeakMaps need special handling (see above), but we can't do that with this API. + This will possibly leak objects that need the CC until the WeakMap has no more refs to it. */ + RB_FOR_EACH_ENTRY(entry, &weakmap->map, struct weakmap_entry, entry) + if(is_object_instance(entry->value)) + note_edge(get_edge_obj(get_object(entry->value)), "value", cb); +} + static const builtin_prop_t WeakMap_prototype_props[] = { {L"clear", WeakMap_clear, PROPF_METHOD}, {L"delete", WeakMap_delete, PROPF_METHOD|1}, @@ -852,6 +881,7 @@ static const builtin_info_t WeakMap_info = { .call = WeakMap_value, .destructor = WeakMap_destructor, .gc_traverse = WeakMap_gc_traverse, + .cc_traverse = WeakMap_cc_traverse }; static HRESULT WeakMap_constructor(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index a6b8afb0543..1df23829daa 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -2873,6 +2873,30 @@ static HRESULT WINAPI JSDispatchHost_ToString(IWineJSDispatchHost *iface, BSTR * return dispex_to_string(This, str); } +static BOOL __cdecl is_full_cc(void) +{ + thread_data_t *thread_data = get_thread_data(FALSE); + return thread_data ? thread_data->full_cc_in_progress : FALSE; +} + +static void __cdecl describe_node(ULONG ref, const char *obj_name, nsCycleCollectionTraversalCallback *cb) +{ + nsCycleCollectingAutoRefCnt ccref; + + ccref_init(&ccref, ref); + describe_cc_node(&ccref, obj_name, cb); +} + +static void WINAPI JSDispatchHost_InitCC(IWineJSDispatchHost *iface, struct jshost_cc_api *cc_api, const CCObjCallback *callback) +{ + ccp_init(&cc_api->participant, callback); + cc_api->is_full_cc = is_full_cc; + cc_api->collect = cc_api_collect; + cc_api->describe_node = describe_node; + cc_api->note_edge = (note_edge_t)note_cc_edge; + list_add_tail(&cc_api_list, &cc_api->entry); +} + static IWineJSDispatchHostVtbl JSDispatchHostVtbl = { DispatchEx_QueryInterface, DispatchEx_AddRef, @@ -2900,6 +2924,7 @@ static IWineJSDispatchHostVtbl JSDispatchHostVtbl = { JSDispatchHost_FillProperties, JSDispatchHost_GetOuterDispatch, JSDispatchHost_ToString, + JSDispatchHost_InitCC }; struct EnumVARIANT { @@ -3097,6 +3122,9 @@ static nsresult NSAPI dispex_traverse(void *ccp, void *p, nsCycleCollectionTrave if(This->info->vtbl->traverse) This->info->vtbl->traverse(This, cb); + if(This->jsdisp) + IWineJSDispatch_Traverse(This->jsdisp, cb); + if(!This->dynamic_data) return NS_OK; @@ -3152,6 +3180,9 @@ static nsresult NSAPI dispex_unlink(void *p) if(This->info->vtbl->unlink) This->info->vtbl->unlink(This); + if(This->jsdisp) + IWineJSDispatch_Unlink(This->jsdisp); + dispex_props_unlink(This); return NS_OK; } diff --git a/dlls/mshtml/htmlwindow.c b/dlls/mshtml/htmlwindow.c index 1977c6137ba..c6b654f3372 100644 --- a/dlls/mshtml/htmlwindow.c +++ b/dlls/mshtml/htmlwindow.c @@ -65,6 +65,22 @@ HTMLOuterWindow *mozwindow_to_window(const mozIDOMWindowProxy *mozwindow) return entry ? WINE_RB_ENTRY_VALUE(entry, HTMLOuterWindow, entry) : NULL; } +void __cdecl cc_api_collect(void) +{ + nsIDOMWindowUtils *window_utils = NULL; + HTMLOuterWindow *window; + + /* We can't rely on GetScriptGlobal as this can be initialized before any scripts are set up */ + if(!window_map.root || !(window = WINE_RB_ENTRY_VALUE(window_map.root, HTMLOuterWindow, entry))->browser) + return; + get_nsinterface((nsISupports*)window->browser->content_window->nswindow, &IID_nsIDOMWindowUtils, (void**)&window_utils); + + if(window_utils) { + cycle_collect(window_utils); + nsIDOMWindowUtils_Release(window_utils); + } +} + static HRESULT get_location(HTMLOuterWindow *This, HTMLLocation **ret) { if(!This->location) { @@ -3551,6 +3567,13 @@ static HRESULT WINAPI WindowDispEx_ToString(IWineJSDispatchHost *iface, BSTR *st return IWineJSDispatchHost_ToString(&This->base.inner_window->event_target.dispex.IWineJSDispatchHost_iface, str); } +static void WINAPI WindowDispEx_InitCC(IWineJSDispatchHost *iface, struct jshost_cc_api *cc_api, const CCObjCallback *callback) +{ + HTMLOuterWindow *This = impl_from_IWineJSDispatchHost(iface); + + IWineJSDispatchHost_InitCC(&This->base.inner_window->event_target.dispex.IWineJSDispatchHost_iface, cc_api, callback); +} + static const IWineJSDispatchHostVtbl WindowDispExVtbl = { WindowDispEx_QueryInterface, WindowDispEx_AddRef, @@ -3578,6 +3601,7 @@ static const IWineJSDispatchHostVtbl WindowDispExVtbl = { WindowDispEx_FillProperties, WindowDispEx_GetOuterDispatch, WindowDispEx_ToString, + WindowDispEx_InitCC }; static inline HTMLOuterWindow *impl_from_IEventTarget(IEventTarget *iface) diff --git a/dlls/mshtml/main.c b/dlls/mshtml/main.c index a45a2c111dc..1999a878dea 100644 --- a/dlls/mshtml/main.c +++ b/dlls/mshtml/main.c @@ -64,6 +64,7 @@ static IMultiLanguage2 *mlang; static IInternetSecurityManager *security_manager; static unsigned global_max_compat_mode = COMPAT_MODE_IE11; static struct list compat_config = LIST_INIT(compat_config); +struct list cc_api_list = LIST_INIT(cc_api_list); typedef struct { struct list entry; @@ -377,6 +378,15 @@ static void process_detach(void) close_gecko(); release_typelib(); + while(!list_empty(&cc_api_list)) { + struct jshost_cc_api *entry = LIST_ENTRY(list_head(&cc_api_list), struct jshost_cc_api, entry); + entry->is_full_cc = NULL; + entry->collect = NULL; + entry->describe_node = NULL; + entry->note_edge = NULL; + list_remove(&entry->entry); + } + while(!list_empty(&compat_config)) { config = LIST_ENTRY(list_head(&compat_config), compat_config_t, entry); list_remove(&config->entry); diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 32317c0aa9a..b0e545a5744 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -563,6 +563,7 @@ ALL_OBJECTS #undef X extern dispex_static_data_t *object_descriptors[OBJID_LAST]; +extern struct list cc_api_list; typedef HRESULT (*dispex_hook_invoke_t)(DispatchEx*,WORD,DISPPARAMS*,VARIANT*, EXCEPINFO*,IServiceProvider*); @@ -1222,6 +1223,8 @@ void ConnectionPointContainer_Destroy(ConnectionPointContainer*); HRESULT create_gecko_browser(HTMLDocumentObj*,GeckoBrowser**); void detach_gecko_browser(GeckoBrowser*); +void cycle_collect(nsIDOMWindowUtils*); +void __cdecl cc_api_collect(void); DWORD get_compat_mode_version(compat_mode_t compat_mode); compat_mode_t lock_document_mode(HTMLDocumentNode*); @@ -1529,6 +1532,7 @@ typedef struct { struct list *pending_xhr_events_tail; struct wine_rb_tree session_storage_map; void *blocking_xhr; + unsigned full_cc_in_progress; unsigned tasks_locked; BOOL timer_blocked; } thread_data_t; diff --git a/dlls/mshtml/nsembed.c b/dlls/mshtml/nsembed.c index 62e1922cf6c..6422387aa1a 100644 --- a/dlls/mshtml/nsembed.c +++ b/dlls/mshtml/nsembed.c @@ -2174,6 +2174,16 @@ static const nsISupportsWeakReferenceVtbl nsSupportsWeakReferenceVtbl = { nsSupportsWeakReference_GetWeakReference }; +void cycle_collect(nsIDOMWindowUtils *window_utils) +{ + thread_data_t *thread_data = get_thread_data(TRUE); + if(thread_data) { + thread_data->full_cc_in_progress++; + nsIDOMWindowUtils_CycleCollect(window_utils, NULL, 0); + thread_data->full_cc_in_progress--; + } +} + static HRESULT init_browser(GeckoBrowser *browser) { mozIDOMWindowProxy *mozwindow; @@ -2397,7 +2407,7 @@ void detach_gecko_browser(GeckoBrowser *This) /* Force cycle collection */ if(window_utils) { - nsIDOMWindowUtils_CycleCollect(window_utils, NULL, 0); + cycle_collect(window_utils); nsIDOMWindowUtils_Release(window_utils); } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10045