Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Sending only first bit of patches now, the rest will come after.
The ident map is still needed for accessing it directly by DISPID (and by MEMBERIDs when the TypeInfo is implemented), but now it is simply re-created when the DISPID count changes (after the script has stuff added to it).
Note that the map can have NULL entries in very rare cases, because of dynamic vars. The code lists which have their DISPIDs stored when the functions/variables are compiled will remain even if the script is released (by decreasing its state) but not destroyed. Meanwhile, dynamic variables created during the interpreting phase will be gone, and those slots in future ident maps will not be filled.
The scenario is rare enough that I think it is not worthwhile to keep track of available DISPIDs in this case, which would be more complicated, so we just never re-use the DISPIDs at all.
dlls/vbscript/compile.c | 7 ++++ dlls/vbscript/interp.c | 1 + dlls/vbscript/vbdisp.c | 69 ++++++++++++++++------------------------ dlls/vbscript/vbscript.h | 5 ++- 4 files changed, 40 insertions(+), 42 deletions(-)
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index c129f36..a9bf96e 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -53,6 +53,8 @@ typedef struct { unsigned func_end_label; unsigned prop_end_label;
+ DISPID cur_dispid; + dim_decl_t *dim_decls; dim_decl_t *dim_decls_tail; dynamic_var_t *global_vars; @@ -1424,6 +1426,7 @@ static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *f
V_VT(&new_var->v) = VT_EMPTY; new_var->is_const = FALSE; + new_var->dispid = ++ctx->cur_dispid;
new_var->next = ctx->global_vars; ctx->global_vars = new_var; @@ -1503,6 +1506,7 @@ static HRESULT create_function(compile_ctx_t *ctx, function_decl_t *decl, functi func->code_ctx = ctx->code; func->type = decl->type; func->is_public = decl->is_public; + func->dispid = ++ctx->cur_dispid;
func->arg_cnt = 0; if(decl->args) { @@ -1852,6 +1856,7 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *deli ctx.global_consts = NULL; ctx.stat_ctx = NULL; ctx.labels_cnt = ctx.labels_size = 0; + ctx.cur_dispid = script->cur_dispid;
hres = compile_func(&ctx, ctx.parser.stats, &ctx.code->main_code); if(FAILED(hres)) { @@ -1919,6 +1924,8 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *deli if(TRACE_ON(vbscript_disas)) dump_code(&ctx);
+ script->cur_dispid = ctx.cur_dispid; + ctx.code = NULL; release_compiler(&ctx);
diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 80ecc41..80588b5 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -228,6 +228,7 @@ static HRESULT add_dynamic_var(exec_ctx_t *ctx, const WCHAR *name, V_VT(&new_var->v) = VT_EMPTY;
if(ctx->func->type == FUNC_GLOBAL) { + new_var->dispid = ++ctx->script->cur_dispid; new_var->next = ctx->script->global_vars; ctx->script->global_vars = new_var; }else { diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index 1dd6d4e..fd51935 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -527,7 +527,6 @@ HRESULT create_vbdisp(const class_desc_t *desc, vbdisp_t **ret) }
struct _ident_map_t { - const WCHAR *name; BOOL is_var; union { dynamic_var_t *var; @@ -542,31 +541,36 @@ static inline DISPID ident_to_id(ScriptDisp *This, ident_map_t *ident)
static inline ident_map_t *id_to_ident(ScriptDisp *This, DISPID id) { - return 0 < id && id <= This->ident_map_cnt ? This->ident_map+id-1 : NULL; + return 0 < id && id <= This->ident_map_cnt && This->ident_map[id - 1].u.var + ? This->ident_map + id - 1 : NULL; }
-static ident_map_t *add_ident(ScriptDisp *This, const WCHAR *name) +static BOOL update_ident_map(ScriptDisp *This) { - ident_map_t *ret; + dynamic_var_t *var; + function_t *func; + ident_map_t *map; + + if (This->ident_map_cnt == This->ctx->cur_dispid) + return TRUE;
- if(!This->ident_map_size) { - This->ident_map = heap_alloc(4 * sizeof(*This->ident_map)); - if(!This->ident_map) - return NULL; - This->ident_map_size = 4; - }else if(This->ident_map_cnt == This->ident_map_size) { - ident_map_t *new_map; + map = heap_alloc_zero(This->ctx->cur_dispid * sizeof(*map)); + if (!map) return FALSE;
- new_map = heap_realloc(This->ident_map, 2*This->ident_map_size*sizeof(*new_map)); - if(!new_map) - return NULL; - This->ident_map = new_map; - This->ident_map_size *= 2; + heap_free(This->ident_map); + This->ident_map_cnt = This->ctx->cur_dispid; + This->ident_map = map; + + for (var = This->ctx->global_vars; var; var = var->next) + { + map[var->dispid - 1].is_var = TRUE; + map[var->dispid - 1].u.var = var; }
- ret = This->ident_map + This->ident_map_cnt++; - ret->name = name; - return ret; + for (func = This->ctx->global_funcs; func; func = func->next) + map[func->dispid - 1].u.func = func; + + return TRUE; }
static inline ScriptDisp *ScriptDisp_from_IDispatchEx(IDispatchEx *iface) @@ -676,7 +680,6 @@ static HRESULT WINAPI ScriptDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DW { ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); dynamic_var_t *var; - ident_map_t *ident; function_t *func;
TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); @@ -684,35 +687,16 @@ static HRESULT WINAPI ScriptDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DW if(!This->ctx) return E_UNEXPECTED;
- for(ident = This->ident_map; ident < This->ident_map+This->ident_map_cnt; ident++) { - if(!wcsicmp(ident->name, bstrName)) { - *pid = ident_to_id(This, ident); - return S_OK; - } - } - for(var = This->ctx->global_vars; var; var = var->next) { if(!wcsicmp(var->name, bstrName)) { - ident = add_ident(This, var->name); - if(!ident) - return E_OUTOFMEMORY; - - ident->is_var = TRUE; - ident->u.var = var; - *pid = ident_to_id(This, ident); + *pid = var->dispid; return S_OK; } }
for(func = This->ctx->global_funcs; func; func = func->next) { if(!wcsicmp(func->name, bstrName)) { - ident = add_ident(This, func->name); - if(!ident) - return E_OUTOFMEMORY; - - ident->is_var = FALSE; - ident->u.func = func; - *pid = ident_to_id(This, ident); + *pid = func->dispid; return S_OK; } } @@ -730,6 +714,9 @@ static HRESULT WINAPI ScriptDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc
TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
+ if(!update_ident_map(This)) + return E_OUTOFMEMORY; + ident = id_to_ident(This, id); if(!ident) return DISP_E_MEMBERNOTFOUND; diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 71f42aa..9d41632 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -128,7 +128,6 @@ typedef struct {
ident_map_t *ident_map; unsigned ident_map_cnt; - unsigned ident_map_size;
script_ctx_t *ctx; } ScriptDisp; @@ -169,6 +168,7 @@ typedef struct _dynamic_var_t { VARIANT v; const WCHAR *name; BOOL is_const; + DISPID dispid; } dynamic_var_t;
struct _script_ctx_t { @@ -178,6 +178,8 @@ struct _script_ctx_t { IInternetHostSecurityManager *secmgr; DWORD safeopt;
+ DISPID cur_dispid; + IDispatch *host_global;
ScriptDisp *script_obj; @@ -314,6 +316,7 @@ typedef struct { } var_desc_t;
struct _function_t { + DISPID dispid; function_type_t type; const WCHAR *name; BOOL is_public;
The heap stores dynamic global variables added during the execution phase. We'll need to reference them in the TypeInfo, and so we have to reference count the heap itself, and the simplest way is to use the script dispatch object to store it.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/interp.c | 2 +- dlls/vbscript/vbdisp.c | 2 ++ dlls/vbscript/vbscript.c | 3 --- dlls/vbscript/vbscript.h | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 80588b5..69f2c8c 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -212,7 +212,7 @@ static HRESULT add_dynamic_var(exec_ctx_t *ctx, const WCHAR *name, WCHAR *str; unsigned size;
- heap = ctx->func->type == FUNC_GLOBAL ? &ctx->script->heap : &ctx->heap; + heap = ctx->func->type == FUNC_GLOBAL ? &ctx->script->script_obj->heap : &ctx->heap;
new_var = heap_pool_alloc(heap, sizeof(*new_var)); if(!new_var) diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index fd51935..0f8fb55 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -620,6 +620,7 @@ static ULONG WINAPI ScriptDisp_Release(IDispatchEx *iface)
if(!ref) { assert(!This->ctx); + heap_pool_free(&This->heap); heap_free(This->ident_map); heap_free(This); } @@ -814,6 +815,7 @@ HRESULT create_script_disp(script_ctx_t *ctx, ScriptDisp **ret) script_disp->IDispatchEx_iface.lpVtbl = &ScriptDispVtbl; script_disp->ref = 1; script_disp->ctx = ctx; + heap_pool_init(&script_disp->heap);
*ret = script_disp; return S_OK; diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index 56c53b4..da5c3dd 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -179,8 +179,6 @@ static void release_script(script_ctx_t *ctx) }
detach_global_objects(ctx); - heap_pool_free(&ctx->heap); - heap_pool_init(&ctx->heap); }
static void destroy_script(script_ctx_t *ctx) @@ -955,7 +953,6 @@ HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory *iface, IUnknown *pU }
ctx->safeopt = INTERFACE_USES_DISPEX; - heap_pool_init(&ctx->heap); list_init(&ctx->objects); list_init(&ctx->code_list); list_init(&ctx->named_items); diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 9d41632..9933329 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -130,6 +130,7 @@ typedef struct { unsigned ident_map_cnt;
script_ctx_t *ctx; + heap_pool_t heap; } ScriptDisp;
typedef struct _builtin_prop_t builtin_prop_t; @@ -194,8 +195,6 @@ struct _script_ctx_t { class_desc_t *classes; class_desc_t *procs;
- heap_pool_t heap; - struct list objects; struct list code_list; struct list named_items;
Hi Gabriel,
On 10/21/19 1:40 PM, Gabriel Ivăncescu wrote:
The heap stores dynamic global variables added during the execution phase. We'll need to reference them in the TypeInfo, and so we have to reference count the heap itself, and the simplest way is to use the script dispatch object to store it.
It seems to me that you concentrated a bit too much at avoiding copying the data rather than on root of the problem. It's possible to avoid copying in different ways. The thing is that having to represent the collection of global members in different ways for similar tasks probably means that representation of the collection is not right. Actually, we know that current global scope representation is not great. A simple list was nice to have things done and working, but it's surely not optimal. I don't expect you to rewrite it all, but it will be eventually rewritten and it would be good to know that we're changing things in the right direction instead of working on something that will need to be rewritten for future improvements.
I don't know exactly how it should look like. It's even possible that we will have to do some copies to capture the state after all. If I was looking at it, I'd start with more experimentations. Your tests seem like a good start. It would be interesting to see how things change when you add more global members later. You tested that adding new global variables doesn't change the existing ITypeInfo. How does the new one look like? Does it append new variables after previous ones? What if we added a new "dynamic" (non-explicit) variable? What if we mix it all? How about functions? What do DISPIDs look like? Is it reasonable to try to replicate native DISPIDs? Once we know more answers, we may decide how the collection should look like.
Once we know that, we should consider how to replace existing storage with something suitable for ITypeInfo. The new collection would need to be available for the interpreter and script dispatch (its GetIDsOfNames should probably share the implementation with ITypeInfo one). script_ctx_t may indeed be a good place. Another option could be ScriptDisp or maybe another new struct. Note that we currently ignore the case of closed script engine in other places (with things like if(!This->ctx) return E_UNEXPECTED; in GetDispID) and until it's a problem for an actual application, feel free to do a similar thing (preferably with a FIXME). If we have to fix it, script_ctx_t is intentionally done in a way that would be easy to make it ref-counted. For that, however, it would be nice to see how both ITypeInfo, script dispatch IDispatchEx and VBS class instance IDispatchEx behave on closed script engines. Then we can decide how much of it do we want to keep after releasing script engine itself.
Thanks,
Jacek
Hi Jacek,
On 10/23/19 9:16 PM, Jacek Caban wrote:
Hi Gabriel,
On 10/21/19 1:40 PM, Gabriel Ivăncescu wrote:
The heap stores dynamic global variables added during the execution phase. We'll need to reference them in the TypeInfo, and so we have to reference count the heap itself, and the simplest way is to use the script dispatch object to store it.
It seems to me that you concentrated a bit too much at avoiding copying the data rather than on root of the problem. It's possible to avoid copying in different ways. The thing is that having to represent the collection of global members in different ways for similar tasks probably means that representation of the collection is not right. Actually, we know that current global scope representation is not great. A simple list was nice to have things done and working, but it's surely not optimal. I don't expect you to rewrite it all, but it will be eventually rewritten and it would be good to know that we're changing things in the right direction instead of working on something that will need to be rewritten for future improvements.
Now that I've looked deeper into the vbscript implementation, I actually think the current one is wrong and don't quite understand how it even works, honestly.
It looks to me as the compilation phase that creates the vbscode_t that is then appened to the ctx->code_list adds functions and variables, and they are immediately linked to the context's global_funcs/vars respectively. This happens even *if* the code in question has "pending_exec" so it's not even executed immediately. Shouldn't those funcs/vars only be added to the global context list when they are being executed?
Furthermore, the funcs/vars during compilation are added to the vbscode_t's heap and stored there, while vars added during execution are added to the context's heap.
release_script will go through all global_vars to release their variant data, but in destroy_script, the code_list is released (and all vbscode_t heaps) *before* the script is released. Doesn't this mean some variables in the linked list will have already been freed (if they were part of some vbscode_t) and thus we're accessing freed memory when releasing the vars?
Overall I'm just slightly confused as to how it even works right now, so I've no idea how to improve it.
Here's my thought process on how to proceed, but I could be completely wrong.
First, store the global_vars/funcs in the script dispatch object, instead of the context (just like jscript does). Furthermore, when a code in the code_list is being executed, move it to the script dispatch object (for ref counting) and remove it from the context's list. (we'll essentially just have another list in the script dispatch object for code that has been executed).
This will allow to release the script by decreasing its state, releasing the code that was executed already, while *keeping* the code that is still pending, which I believe was the intent of the whole thing. Again, I could be misunderstanding this.
Secondly, link the compiled funcs/vars to the script dispatch object during the execution phase of that vbscode_t, instead of during compilation as we have now.
Does this seem a good way forward?
With this, the TypeInfo will just grab a reference to the script dispatch (which it has to anyway due making use of the ident_map) and possibly create arrays for vars/funcs like a snapshot, so they have the indices for the TypeInfo at the moment it is retrieved.
I don't know exactly how it should look like. It's even possible that we will have to do some copies to capture the state after all. If I was looking at it, I'd start with more experimentations. Your tests seem like a good start. It would be interesting to see how things change when you add more global members later. You tested that adding new global variables doesn't change the existing ITypeInfo. How does the new one look like? Does it append new variables after previous ones? What if we added a new "dynamic" (non-explicit) variable? What if we mix it all? How about functions? What do DISPIDs look like? Is it reasonable to try to replicate native DISPIDs? Once we know more answers, we may decide how the collection should look like.
After more fiddling here's some bits of info:
The TypeInfo is frozen at the moment it is retrieved. Whatever happens to the script dispatch after that should not affect it. To simplify the explanation I will talk about "variables" but the same applies to functions as well.
If some variables are added and a new TypeInfo is retrieved, the new TypeInfo will contain those new variables, and their indices will be after the previous ones (i.e. they're appended).
I don't think it's worth replicating DISPIDs, because those are opaque. AFAIK no application should hard-code DISPIDs since they may also change between Windows versions (from experiments it does seem Windows tends to simply order the DISPIDs as they are found in the script, though, but separates vars/funcs).
However, the *indices* of the variables should probably be the same as on Windows. This is because you can obtain variables by index using GetVarDesc (again, same for functions). But, when it adds code, explicit variables are added first, then non-explicit variables. If more code is added, it will be appended to this, so they *can* be mixed. (e.g. first code adds 2 explicit vars, then 1 implicit var, second code adds 1 explicit var and 2 implicit, it will be: e->e->i->e->i->i).
I think the cleanest approach would be for the TypeInfo to hold a ref to the script dispatch object, and all the necessary info to be stored there, just like it is done for jscript.
Once we know that, we should consider how to replace existing storage with something suitable for ITypeInfo. The new collection would need to be available for the interpreter and script dispatch (its GetIDsOfNames should probably share the implementation with ITypeInfo one). script_ctx_t may indeed be a good place. Another option could be ScriptDisp or maybe another new struct. Note that we currently ignore the case of closed script engine in other places (with things like if(!This->ctx) return E_UNEXPECTED; in GetDispID) and until it's a problem for an actual application, feel free to do a similar thing (preferably with a FIXME). If we have to fix it, script_ctx_t is intentionally done in a way that would be easy to make it ref-counted. For that, however, it would be nice to see how both ITypeInfo, script dispatch IDispatchEx and VBS class instance IDispatchEx behave on closed script engines. Then we can decide how much of it do we want to keep after releasing script engine itself.
Thanks,
Jacek
So, the TypeInfo is not affected by closing of the script engine.
The only problem I see is the separation of the code_list with the executed heap. In jscript I've done something similar, and in that case we *do* need to build an array regardless, because props can be deleted, while the TypeInfo remains and the var indices.
But in jscript a ref to the script dispatch can be held and the necessary refs to the function objects. I'd like it to be similar in vbscript, honestly, instead of having vars stored in multiple places.
Thanks, Gabriel
On 10/24/19 3:32 PM, Gabriel Ivăncescu wrote:
Hi Jacek,
On 10/23/19 9:16 PM, Jacek Caban wrote:
Hi Gabriel,
On 10/21/19 1:40 PM, Gabriel Ivăncescu wrote:
The heap stores dynamic global variables added during the execution phase. We'll need to reference them in the TypeInfo, and so we have to reference count the heap itself, and the simplest way is to use the script dispatch object to store it.
It seems to me that you concentrated a bit too much at avoiding copying the data rather than on root of the problem. It's possible to avoid copying in different ways. The thing is that having to represent the collection of global members in different ways for similar tasks probably means that representation of the collection is not right. Actually, we know that current global scope representation is not great. A simple list was nice to have things done and working, but it's surely not optimal. I don't expect you to rewrite it all, but it will be eventually rewritten and it would be good to know that we're changing things in the right direction instead of working on something that will need to be rewritten for future improvements.
Now that I've looked deeper into the vbscript implementation, I actually think the current one is wrong and don't quite understand how it even works, honestly.
It looks to me as the compilation phase that creates the vbscode_t that is then appened to the ctx->code_list adds functions and variables, and they are immediately linked to the context's global_funcs/vars respectively. This happens even *if* the code in question has "pending_exec" so it's not even executed immediately. Shouldn't those funcs/vars only be added to the global context list when they are being executed?
Maybe, I'm not sure. It's not obviously wrong. Testing that would be nice.
Furthermore, the funcs/vars during compilation are added to the vbscode_t's heap and stored there, while vars added during execution are added to the context's heap.
release_script will go through all global_vars to release their variant data, but in destroy_script, the code_list is released (and all vbscode_t heaps) *before* the script is released. Doesn't this mean some variables in the linked list will have already been freed (if they were part of some vbscode_t) and thus we're accessing freed memory when releasing the vars?
Overall I'm just slightly confused as to how it even works right now, so I've no idea how to improve it.
Well, it definitely could use a clean up. Note that before destroy_script() we always set the state to closed in the destructor, so in practice the release_script() is always called before destroy_script() and there is no problem. We should eventually get rid of destroy_script(). If we want script_ctx_t ref-counted, it could be a part of release_script() and we could have separated detach_script() that would be used for closing script engine.
Here's my thought process on how to proceed, but I could be completely wrong.
First, store the global_vars/funcs in the script dispatch object, instead of the context (just like jscript does). Furthermore, when a code in the code_list is being executed, move it to the script dispatch object (for ref counting) and remove it from the context's list. (we'll essentially just have another list in the script dispatch object for code that has been executed).
This will allow to release the script by decreasing its state, releasing the code that was executed already, while *keeping* the code that is still pending, which I believe was the intent of the whole thing. Again, I could be misunderstanding this.
I'm not sure. We don't really need the whole vbscode_t in script dispatch. For the variables, we just need just a name. For function, it's a name, public flag and argument count. The rest could be left in vbscode_t and be freed with it on script engine close. We could allocate that subset of data, for example, in a dedicated heap pool like we currently do for dynamic variables. We could do that in the compiler already.
Secondly, link the compiled funcs/vars to the script dispatch object during the execution phase of that vbscode_t, instead of during compilation as we have now.
If tests confirm that, sure. If you don't want to deal with it, just leave it as is.
Does this seem a good way forward?
With this, the TypeInfo will just grab a reference to the script dispatch (which it has to anyway due making use of the ident_map)
I'd rather get rid of ident_map, see bellow.
and possibly create arrays for vars/funcs like a snapshot, so they have the indices for the TypeInfo at the moment it is retrieved.
Once we have arrays that are don't change existing data, but only grow, then and all we need to capture the state is current element count.
I don't know exactly how it should look like. It's even possible that we will have to do some copies to capture the state after all. If I was looking at it, I'd start with more experimentations. Your tests seem like a good start. It would be interesting to see how things change when you add more global members later. You tested that adding new global variables doesn't change the existing ITypeInfo. How does the new one look like? Does it append new variables after previous ones? What if we added a new "dynamic" (non-explicit) variable? What if we mix it all? How about functions? What do DISPIDs look like? Is it reasonable to try to replicate native DISPIDs? Once we know more answers, we may decide how the collection should look like.
After more fiddling here's some bits of info:
The TypeInfo is frozen at the moment it is retrieved. Whatever happens to the script dispatch after that should not affect it. To simplify the explanation I will talk about "variables" but the same applies to functions as well.
If some variables are added and a new TypeInfo is retrieved, the new TypeInfo will contain those new variables, and their indices will be after the previous ones (i.e. they're appended).
I don't think it's worth replicating DISPIDs, because those are opaque. AFAIK no application should hard-code DISPIDs since they may also change between Windows versions (from experiments it does seem Windows tends to simply order the DISPIDs as they are found in the script, though, but separates vars/funcs).
However, the *indices* of the variables should probably be the same as on Windows. This is because you can obtain variables by index using GetVarDesc (again, same for functions). But, when it adds code, explicit variables are added first, then non-explicit variables. If more code is added, it will be appended to this, so they *can* be mixed. (e.g. first code adds 2 explicit vars, then 1 implicit var, second code adds 1 explicit var and 2 implicit, it will be: e->e->i->e->i->i).
Cool, thanks for testing. It sounds like replacing the existing lists with arrays like that is what we need.
To get rid of ident_map, I'd suggest to change how DISPIDs work. DISPIDs could be just indexes in variable or function arrays, with a flag on one of high bits indicating which one is it.
Thanks,
Jacek
On 10/24/19 11:22 PM, Jacek Caban wrote:
On 10/24/19 3:32 PM, Gabriel Ivăncescu wrote:
Hi Jacek,
On 10/23/19 9:16 PM, Jacek Caban wrote:
Hi Gabriel,
On 10/21/19 1:40 PM, Gabriel Ivăncescu wrote:
The heap stores dynamic global variables added during the execution phase. We'll need to reference them in the TypeInfo, and so we have to reference count the heap itself, and the simplest way is to use the script dispatch object to store it.
It seems to me that you concentrated a bit too much at avoiding copying the data rather than on root of the problem. It's possible to avoid copying in different ways. The thing is that having to represent the collection of global members in different ways for similar tasks probably means that representation of the collection is not right. Actually, we know that current global scope representation is not great. A simple list was nice to have things done and working, but it's surely not optimal. I don't expect you to rewrite it all, but it will be eventually rewritten and it would be good to know that we're changing things in the right direction instead of working on something that will need to be rewritten for future improvements.
Now that I've looked deeper into the vbscript implementation, I actually think the current one is wrong and don't quite understand how it even works, honestly.
It looks to me as the compilation phase that creates the vbscode_t that is then appened to the ctx->code_list adds functions and variables, and they are immediately linked to the context's global_funcs/vars respectively. This happens even *if* the code in question has "pending_exec" so it's not even executed immediately. Shouldn't those funcs/vars only be added to the global context list when they are being executed?
Maybe, I'm not sure. It's not obviously wrong. Testing that would be nice.
Furthermore, the funcs/vars during compilation are added to the vbscode_t's heap and stored there, while vars added during execution are added to the context's heap.
release_script will go through all global_vars to release their variant data, but in destroy_script, the code_list is released (and all vbscode_t heaps) *before* the script is released. Doesn't this mean some variables in the linked list will have already been freed (if they were part of some vbscode_t) and thus we're accessing freed memory when releasing the vars?
Overall I'm just slightly confused as to how it even works right now, so I've no idea how to improve it.
Well, it definitely could use a clean up. Note that before destroy_script() we always set the state to closed in the destructor, so in practice the release_script() is always called before destroy_script() and there is no problem. We should eventually get rid of destroy_script(). If we want script_ctx_t ref-counted, it could be a part of release_script() and we could have separated detach_script() that would be used for closing script engine.
Right, so then it means it isn't broken currently, just that the release_script in destroy_script is confusing and probably redundant.
Here's my thought process on how to proceed, but I could be completely wrong.
First, store the global_vars/funcs in the script dispatch object, instead of the context (just like jscript does). Furthermore, when a code in the code_list is being executed, move it to the script dispatch object (for ref counting) and remove it from the context's list. (we'll essentially just have another list in the script dispatch object for code that has been executed).
This will allow to release the script by decreasing its state, releasing the code that was executed already, while *keeping* the code that is still pending, which I believe was the intent of the whole thing. Again, I could be misunderstanding this.
I'm not sure. We don't really need the whole vbscode_t in script dispatch. For the variables, we just need just a name. For function, it's a name, public flag and argument count. The rest could be left in vbscode_t and be freed with it on script engine close. We could allocate that subset of data, for example, in a dedicated heap pool like we currently do for dynamic variables. We could do that in the compiler already.
I think keeping all of the vbscode_t is simpler and shouldn't really matter in general, since they should theoretically also be part of the script dispatch (but current code can't handle it when engine is closed), for GetIDsOfNames and so on.
Note that we also need to keep the argument names, not just the count.
To make this more straightforward, I was further thinking of getting rid of the "pending_exec" field.
Then, we'll have a pending_code_list (not sure if in context or dispatch, I'm leaning towards dispatch, see next paragraph), and a code_list in the script dispatch. Code that was executed simply gets moved to the code_list in the script dispatch (since it's part of it) from the pending_code_list.
In the future, this will probably help as well if we make use of the pstrItemName arg in GetScriptDispatch, since it should separate functions/vars added to it (and thus, they need to be part of the dispatch object itself rather than the context). I noticed this when I realized the "modules" in msscript makes use of this argument to separate functions and vars.
But of course I'll need to do more testing with this, I do believe it's more correct, at least based on intuition.
To get rid of ident_map, I'd suggest to change how DISPIDs work. DISPIDs could be just indexes in variable or function arrays, with a flag on one of high bits indicating which one is it.
Seems reasonable. I suppose we could still keep linked lists in vbscode_t since they'll have to be appended to the arrays after compilation (or during execution, if tests confirm it).
Thanks, Gabriel
Just a quick update. After further investigation and tests, it does seem like my assumptions were correct, so now I'm looking for the best way to make the relevant changes. (this does mean we'll have to move some stuff to the script dispatch though, and it will be necessary when we make use of the pstrItemName arg anyway).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/vbdisp.c | 270 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 268 insertions(+), 2 deletions(-)
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index 0f8fb55..f787dd3 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -573,6 +573,268 @@ static BOOL update_ident_map(ScriptDisp *This) return TRUE; }
+typedef struct { + ITypeInfo ITypeInfo_iface; + LONG ref; +} ScriptTypeInfo; + +static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface) +{ + return CONTAINING_RECORD(iface, ScriptTypeInfo, ITypeInfo_iface); +} + +static HRESULT WINAPI ScriptTypeInfo_QueryInterface(ITypeInfo *iface, REFIID riid, void **ppv) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_ITypeInfo, riid)) + *ppv = &This->ITypeInfo_iface; + else + { + WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI ScriptTypeInfo_AddRef(ITypeInfo *iface) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if (!ref) + { + heap_free(This); + } + return ref; +} + +static HRESULT WINAPI ScriptTypeInfo_GetTypeAttr(ITypeInfo *iface, TYPEATTR **ppTypeAttr) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, ppTypeAttr); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetTypeComp(ITypeInfo *iface, ITypeComp **ppTComp) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, ppTComp); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetFuncDesc(ITypeInfo *iface, UINT index, FUNCDESC **ppFuncDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, ppFuncDesc); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetVarDesc(ITypeInfo *iface, UINT index, VARDESC **ppVarDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, ppVarDesc); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetNames(ITypeInfo *iface, MEMBERID memid, BSTR *rgBstrNames, + UINT cMaxNames, UINT *pcNames) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %p %u %p)\n", This, memid, rgBstrNames, cMaxNames, pcNames); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetRefTypeOfImplType(ITypeInfo *iface, UINT index, HREFTYPE *pRefType) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, pRefType); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetImplTypeFlags(ITypeInfo *iface, UINT index, INT *pImplTypeFlags) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%u %p)\n", This, index, pImplTypeFlags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetIDsOfNames(ITypeInfo *iface, LPOLESTR *rgszNames, UINT cNames, + MEMBERID *pMemId) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %u %p)\n", This, rgszNames, cNames, pMemId); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_Invoke(ITypeInfo *iface, PVOID pvInstance, MEMBERID memid, WORD wFlags, + DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %d %d %p %p %p %p)\n", This, pvInstance, memid, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetDocumentation(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrName, + BSTR *pBstrDocString, DWORD *pdwHelpContext, BSTR *pBstrHelpFile) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %p %p %p %p)\n", This, memid, pBstrName, pBstrDocString, pdwHelpContext, pBstrHelpFile); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetDllEntry(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind, + BSTR *pBstrDllName, BSTR *pBstrName, WORD *pwOrdinal) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %d %p %p %p)\n", This, memid, invKind, pBstrDllName, pBstrName, pwOrdinal); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetRefTypeInfo(ITypeInfo *iface, HREFTYPE hRefType, ITypeInfo **ppTInfo) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%x %p)\n", This, hRefType, ppTInfo); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_AddressOfMember(ITypeInfo *iface, MEMBERID memid, INVOKEKIND invKind, PVOID *ppv) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %d %p)\n", This, memid, invKind, ppv); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_CreateInstance(ITypeInfo *iface, IUnknown *pUnkOuter, REFIID riid, PVOID *ppvObj) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %s %p)\n", This, pUnkOuter, debugstr_guid(riid), ppvObj); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetMops(ITypeInfo *iface, MEMBERID memid, BSTR *pBstrMops) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%d %p)\n", This, memid, pBstrMops); + + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptTypeInfo_GetContainingTypeLib(ITypeInfo *iface, ITypeLib **ppTLib, UINT *pIndex) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p %p)\n", This, ppTLib, pIndex); + + return E_NOTIMPL; +} + +static void WINAPI ScriptTypeInfo_ReleaseTypeAttr(ITypeInfo *iface, TYPEATTR *pTypeAttr) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, pTypeAttr); +} + +static void WINAPI ScriptTypeInfo_ReleaseFuncDesc(ITypeInfo *iface, FUNCDESC *pFuncDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, pFuncDesc); +} + +static void WINAPI ScriptTypeInfo_ReleaseVarDesc(ITypeInfo *iface, VARDESC *pVarDesc) +{ + ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); + + FIXME("(%p)->(%p)\n", This, pVarDesc); +} + +static const ITypeInfoVtbl ScriptTypeInfoVtbl = { + ScriptTypeInfo_QueryInterface, + ScriptTypeInfo_AddRef, + ScriptTypeInfo_Release, + ScriptTypeInfo_GetTypeAttr, + ScriptTypeInfo_GetTypeComp, + ScriptTypeInfo_GetFuncDesc, + ScriptTypeInfo_GetVarDesc, + ScriptTypeInfo_GetNames, + ScriptTypeInfo_GetRefTypeOfImplType, + ScriptTypeInfo_GetImplTypeFlags, + ScriptTypeInfo_GetIDsOfNames, + ScriptTypeInfo_Invoke, + ScriptTypeInfo_GetDocumentation, + ScriptTypeInfo_GetDllEntry, + ScriptTypeInfo_GetRefTypeInfo, + ScriptTypeInfo_AddressOfMember, + ScriptTypeInfo_CreateInstance, + ScriptTypeInfo_GetMops, + ScriptTypeInfo_GetContainingTypeLib, + ScriptTypeInfo_ReleaseTypeAttr, + ScriptTypeInfo_ReleaseFuncDesc, + ScriptTypeInfo_ReleaseVarDesc +}; + +static HRESULT create_script_typeinfo(ScriptDisp *disp, ITypeInfo **typeinfo) +{ + ScriptTypeInfo *p; + + if (!(p = heap_alloc(sizeof(*p)))) + return E_OUTOFMEMORY; + + p->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl; + p->ref = 1; + + *typeinfo = &p->ITypeInfo_iface; + return S_OK; +} + static inline ScriptDisp *ScriptDisp_from_IDispatchEx(IDispatchEx *iface) { return CONTAINING_RECORD(iface, ScriptDisp, IDispatchEx_iface); @@ -642,8 +904,12 @@ static HRESULT WINAPI ScriptDisp_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LC ITypeInfo **ppTInfo) { ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); - FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); - return E_NOTIMPL; + + TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + + if (iTInfo != 0) return DISP_E_BADINDEX; + + return create_script_typeinfo(This, ppTInfo); }
static HRESULT WINAPI ScriptDisp_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Functions and variables added during compilation are added to the code_list in the script context. These lists are not freed even if the script is released (by decreasing its state), but only if the entire script is destroyed. So we need to handle it separately, becaues the TypeInfo can hold references to it.
release_vbscode_objects was added to preserve the behavior of releasing the Dispatch objects at the time the script is released even if some TypeInfo still exists that needs functions/variables info, in case any application depends on this.
dlls/vbscript/compile.c | 10 ++++-- dlls/vbscript/vbdisp.c | 75 +++++++++++++++++++++++++++++++++++++++- dlls/vbscript/vbscript.c | 15 ++++++-- dlls/vbscript/vbscript.h | 2 ++ 4 files changed, 96 insertions(+), 6 deletions(-)
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index a9bf96e..ca1e619 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -1769,17 +1769,23 @@ static HRESULT check_script_collisions(compile_ctx_t *ctx, script_ctx_t *script) return S_OK; }
+void release_vbscode_objects(vbscode_t *code) +{ + if(code->context) + IDispatch_Release(code->context); + code->context = NULL; +} + void release_vbscode(vbscode_t *code) { unsigned i;
list_remove(&code->entry); + release_vbscode_objects(code);
for(i=0; i < code->bstr_cnt; i++) SysFreeString(code->bstr_pool[i]);
- if(code->context) - IDispatch_Release(code->context); heap_pool_free(&code->heap);
heap_free(code->bstr_pool); diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index f787dd3..8f5599f 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -576,6 +576,14 @@ static BOOL update_ident_map(ScriptDisp *This) typedef struct { ITypeInfo ITypeInfo_iface; LONG ref; + + UINT num_funcs; + UINT num_vars; + function_t **funcs; + dynamic_var_t **vars; + + ScriptDisp *disp; + script_ctx_t *ctx; } ScriptTypeInfo;
static inline ScriptTypeInfo *ScriptTypeInfo_from_ITypeInfo(ITypeInfo *iface) @@ -615,11 +623,22 @@ static ULONG WINAPI ScriptTypeInfo_Release(ITypeInfo *iface) { ScriptTypeInfo *This = ScriptTypeInfo_from_ITypeInfo(iface); LONG ref = InterlockedDecrement(&This->ref); + script_ctx_t *ctx = This->ctx;
TRACE("(%p) ref=%d\n", This, ref);
if (!ref) { + /* Release the code heaps if we are the last ones holding them */ + if (!--ctx->code_refcnt) + { + while (!list_empty(&ctx->code_list)) + release_vbscode(LIST_ENTRY(list_head(&ctx->code_list), vbscode_t, entry)); + heap_free(ctx); + } + IDispatchEx_Release(&This->disp->IDispatchEx_iface); + heap_free(This->funcs); + heap_free(This->vars); heap_free(This); } return ref; @@ -823,13 +842,67 @@ static const ITypeInfoVtbl ScriptTypeInfoVtbl = {
static HRESULT create_script_typeinfo(ScriptDisp *disp, ITypeInfo **typeinfo) { + UINT num_funcs = 0, num_vars = 0; + dynamic_var_t *var, **typevar; + function_t *func, **typefunc; ScriptTypeInfo *p; + BOOL add_const;
- if (!(p = heap_alloc(sizeof(*p)))) + if (!update_ident_map(disp) || !(p = heap_alloc(sizeof(*p)))) return E_OUTOFMEMORY;
+ for (var = disp->ctx->global_vars; var; var = var->next) + num_vars++; + + for (func = disp->ctx->global_funcs; func; func = func->next) + if (func->is_public) + num_funcs++; + p->ITypeInfo_iface.lpVtbl = &ScriptTypeInfoVtbl; p->ref = 1; + p->num_funcs = num_funcs; + p->num_vars = num_vars; + p->disp = disp; + p->ctx = disp->ctx; + + p->funcs = heap_alloc(sizeof(*p->funcs) * num_funcs); + if (!p->funcs) + { + heap_free(p); + return E_OUTOFMEMORY; + } + + p->vars = heap_alloc(sizeof(*p->vars) * num_vars); + if (!p->vars) + { + heap_free(p->funcs); + heap_free(p); + return E_OUTOFMEMORY; + } + + /* Const variables must be placed after the other variables, + and the order for all reversed compared to the original. */ + typevar = p->vars + num_vars - 1; + add_const = TRUE; + for (;;) + { + for (var = disp->ctx->global_vars; var; var = var->next) + if (var->is_const == add_const) + *typevar-- = var; + + if (!add_const) break; + add_const = FALSE; + } + + typefunc = p->funcs; + for (func = disp->ctx->global_funcs; func; func = func->next) + if (func->is_public) + *typefunc++ = func; + + /* Since the variables and functions are stored in the code heaps, + increment the reference count for the existing code heaps. */ + disp->ctx->code_refcnt++; + IDispatchEx_AddRef(&disp->IDispatchEx_iface);
*typeinfo = &p->ITypeInfo_iface; return S_OK; diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index da5c3dd..27dfe83 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -183,11 +183,19 @@ static void release_script(script_ctx_t *ctx)
static void destroy_script(script_ctx_t *ctx) { - while(!list_empty(&ctx->code_list)) - release_vbscode(LIST_ENTRY(list_head(&ctx->code_list), vbscode_t, entry)); + vbscode_t *code; + + if (--ctx->code_refcnt) + LIST_FOR_EACH_ENTRY(code, &ctx->code_list, vbscode_t, entry) + release_vbscode_objects(code); + else + while (!list_empty(&ctx->code_list)) + release_vbscode(LIST_ENTRY(list_head(&ctx->code_list), vbscode_t, entry));
release_script(ctx); - heap_free(ctx); + + if (!ctx->code_refcnt) + heap_free(ctx); }
static void decrease_state(VBScript *This, SCRIPTSTATE state) @@ -952,6 +960,7 @@ HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory *iface, IUnknown *pU return E_OUTOFMEMORY; }
+ ctx->code_refcnt = 1; ctx->safeopt = INTERFACE_USES_DISPEX; list_init(&ctx->objects); list_init(&ctx->code_list); diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 9933329..009e8de 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -174,6 +174,7 @@ typedef struct _dynamic_var_t {
struct _script_ctx_t { IActiveScriptSite *site; + unsigned code_refcnt; LCID lcid;
IInternetHostSecurityManager *secmgr; @@ -349,6 +350,7 @@ struct _vbscode_t { };
void release_vbscode(vbscode_t*) DECLSPEC_HIDDEN; +void release_vbscode_objects(vbscode_t*) DECLSPEC_HIDDEN; HRESULT compile_script(script_ctx_t*,const WCHAR*,const WCHAR*,DWORD,vbscode_t**) DECLSPEC_HIDDEN; HRESULT compile_procedure(script_ctx_t*,const WCHAR*,const WCHAR*,DWORD,class_desc_t**) DECLSPEC_HIDDEN; HRESULT exec_script(script_ctx_t*,BOOL,function_t*,vbdisp_t*,DISPPARAMS*,VARIANT*) DECLSPEC_HIDDEN;