Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/vbscript.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index 6d2bb54..938e517 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -193,13 +193,10 @@ static void release_script(script_ctx_t *ctx) heap_pool_init(&ctx->heap); }
-static void destroy_script(script_ctx_t *ctx) +static void release_code_list(script_ctx_t *ctx) { while(!list_empty(&ctx->code_list)) release_vbscode(LIST_ENTRY(list_head(&ctx->code_list), vbscode_t, entry)); - - release_script(ctx); - heap_free(ctx); }
static void decrease_state(VBScript *This, SCRIPTSTATE state) @@ -221,6 +218,8 @@ static void decrease_state(VBScript *This, SCRIPTSTATE state) break; release_script(This->ctx); This->thread_id = 0; + if(state == SCRIPTSTATE_CLOSED) + release_code_list(This->ctx); break; case SCRIPTSTATE_CLOSED: break; @@ -396,7 +395,7 @@ static ULONG WINAPI VBScript_Release(IActiveScript *iface)
if(!ref) { decrease_state(This, SCRIPTSTATE_CLOSED); - destroy_script(This->ctx); + heap_free(This->ctx); heap_free(This); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This is needed for the next patch to work properly, else it will crash when script is re-initialized.
dlls/vbscript/vbscript.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index 938e517..3848f9a 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -419,6 +419,9 @@ static HRESULT WINAPI VBScript_SetScriptSite(IActiveScript *iface, IActiveScript if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0)) return E_UNEXPECTED;
+ if(!This->ctx->global_obj && FAILED(hres = init_global(This->ctx))) + return hres; + hres = create_script_disp(This->ctx, &This->ctx->script_obj); if(FAILED(hres)) return hres;
On 11/7/19 2:28 PM, Gabriel Ivăncescu wrote:
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com
This is needed for the next patch to work properly, else it will crash when script is re-initialized.
dlls/vbscript/vbscript.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index 938e517..3848f9a 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -419,6 +419,9 @@ static HRESULT WINAPI VBScript_SetScriptSite(IActiveScript *iface, IActiveScript if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0)) return E_UNEXPECTED;
- if(!This->ctx->global_obj && FAILED(hres = init_global(This->ctx)))
return hres;
We could just keep existing global object alive as long context is alive. With the patch I just sent, this should not be needed.
Jacek
Persistent code has to be re-executed if the script is uninitialized and then reinitialized and restarted.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/compile.c | 6 +++++- dlls/vbscript/vbscript.c | 12 ++++++++++++ dlls/vbscript/vbscript.h | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 7b6649e..40e8091 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -2010,6 +2010,7 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *deli
class->next = script->classes; script->classes = ctx.classes; + code->last_class = class; }
if(TRACE_ON(vbscript_disas)) @@ -2018,6 +2019,9 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *deli ctx.code = NULL; release_compiler(&ctx);
+ if(flags & SCRIPTTEXT_ISPERSISTENT) + code->is_persistent = TRUE; + list_add_tail(&script->code_list, &code->entry); *ret = code; return S_OK; @@ -2029,7 +2033,7 @@ HRESULT compile_procedure(script_ctx_t *script, const WCHAR *src, const WCHAR *d vbscode_t *code; HRESULT hres;
- hres = compile_script(script, src, delimiter, flags, &code); + hres = compile_script(script, src, delimiter, flags & ~SCRIPTTEXT_ISPERSISTENT, &code); if(FAILED(hres)) return hres;
diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index 3848f9a..6c3da44 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -130,6 +130,7 @@ IDispatch *lookup_named_item(script_ctx_t *ctx, const WCHAR *name, unsigned flag
static void release_script(script_ctx_t *ctx) { + vbscode_t *code, *code_next; class_desc_t *class_desc; unsigned i;
@@ -148,6 +149,17 @@ static void release_script(script_ctx_t *ctx) ctx->global_funcs_cnt = 0; ctx->global_funcs_size = 0;
+ LIST_FOR_EACH_ENTRY_SAFE(code, code_next, &ctx->code_list, vbscode_t, entry) + { + if(code->is_persistent) + { + code->pending_exec = TRUE; + if(code->last_class) code->last_class->next = NULL; + } + else + release_vbscode(code); + } + while(!list_empty(&ctx->named_items)) { named_item_t *iter = LIST_ENTRY(list_head(&ctx->named_items), named_item_t, entry);
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 9f14e14..04a9b83 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -341,6 +341,7 @@ struct _vbscode_t { BOOL option_explicit;
BOOL pending_exec; + BOOL is_persistent; function_t main_code; IDispatch *context;
@@ -349,6 +350,8 @@ struct _vbscode_t { unsigned bstr_cnt; heap_pool_t heap;
+ class_desc_t *last_class; + struct list entry; };
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/compile.c | 91 +++++++++++++--------------------------- dlls/vbscript/vbscript.c | 69 ++++++++++++++++++++++++++++++ dlls/vbscript/vbscript.h | 3 ++ 3 files changed, 100 insertions(+), 63 deletions(-)
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 40e8091..fd46af8 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -1787,6 +1787,7 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl) static BOOL lookup_script_identifier(script_ctx_t *script, const WCHAR *identifier) { class_desc_t *class; + vbscode_t *code; unsigned i;
for(i = 0; i < script->global_vars_cnt; i++) { @@ -1804,6 +1805,29 @@ static BOOL lookup_script_identifier(script_ctx_t *script, const WCHAR *identifi return TRUE; }
+ LIST_FOR_EACH_ENTRY(code, &script->code_list, vbscode_t, entry) { + dynamic_var_t *var; + function_t *func; + + if(!code->pending_exec) + continue; + + for(var = code->global_vars; var; var = var->next) { + if(!wcsicmp(var->name, identifier)) + return TRUE; + } + + for(func = code->funcs; func; func = func->next) { + if(!wcsicmp(func->name, identifier)) + return TRUE; + } + + for(class = code->classes; class; class = class->next) { + if(!wcsicmp(class->name, identifier)) + return TRUE; + } + } + return FALSE; }
@@ -1891,13 +1915,11 @@ static void release_compiler(compile_ctx_t *ctx)
HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *delimiter, DWORD flags, vbscode_t **ret) { - function_t *new_func, *func_iter; function_decl_t *func_decl; class_decl_t *class_decl; - dynamic_var_t *var_iter; + function_t *new_func; compile_ctx_t ctx; vbscode_t *code; - size_t cnt; HRESULT hres;
if (!src) src = L""; @@ -1952,66 +1974,9 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *deli return compile_error(script, hres); }
- cnt = script->global_vars_cnt; - for(var_iter = ctx.global_vars; var_iter; var_iter = var_iter->next) - cnt++; - if(cnt > script->global_vars_size) { - dynamic_var_t **new_vars; - if(script->global_vars) - new_vars = heap_realloc(script->global_vars, cnt * sizeof(*new_vars)); - else - new_vars = heap_alloc(cnt * sizeof(*new_vars)); - if(!new_vars) - return compile_error(script, E_OUTOFMEMORY); - script->global_vars = new_vars; - script->global_vars_size = cnt; - } - - cnt = script->global_funcs_cnt; - for(func_iter = ctx.funcs; func_iter; func_iter = func_iter->next) - cnt++; - if(cnt > script->global_funcs_size) { - function_t **new_funcs; - if(script->global_funcs) - new_funcs = heap_realloc(script->global_funcs, cnt * sizeof(*new_funcs)); - else - new_funcs = heap_alloc(cnt * sizeof(*new_funcs)); - if(!new_funcs) - return compile_error(script, E_OUTOFMEMORY); - script->global_funcs = new_funcs; - script->global_funcs_size = cnt; - } - - for(var_iter = ctx.global_vars; var_iter; var_iter = var_iter->next) - script->global_vars[script->global_vars_cnt++] = var_iter; - - for(func_iter = ctx.funcs; func_iter; func_iter = func_iter->next) { - unsigned i; - for(i = 0; i < script->global_funcs_cnt; i++) { - if(!wcsicmp(script->global_funcs[i]->name, func_iter->name)) { - /* global function already exists, replace it */ - script->global_funcs[i] = func_iter; - break; - } - } - if(i == script->global_funcs_cnt) - script->global_funcs[script->global_funcs_cnt++] = func_iter; - } - - if(ctx.classes) { - class_desc_t *class = ctx.classes; - - while(1) { - class->ctx = script; - if(!class->next) - break; - class = class->next; - } - - class->next = script->classes; - script->classes = ctx.classes; - code->last_class = class; - } + code->global_vars = ctx.global_vars; + code->funcs = ctx.funcs; + code->classes = ctx.classes;
if(TRACE_ON(vbscript_disas)) dump_code(&ctx); diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index 6c3da44..f55cd5b 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -82,6 +82,75 @@ static inline BOOL is_started(VBScript *This)
static HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res) { + dynamic_var_t *var_iter, **new_vars; + function_t *func_iter, **new_funcs; + size_t cnt, i; + + cnt = ctx->global_vars_cnt; + for (var_iter = code->global_vars; var_iter; var_iter = var_iter->next) + cnt++; + if (cnt > ctx->global_vars_size) + { + if (ctx->global_vars) + new_vars = heap_realloc(ctx->global_vars, cnt * sizeof(*new_vars)); + else + new_vars = heap_alloc(cnt * sizeof(*new_vars)); + if (!new_vars) + return E_OUTOFMEMORY; + ctx->global_vars = new_vars; + ctx->global_vars_size = cnt; + } + + cnt = ctx->global_funcs_cnt; + for (func_iter = code->funcs; func_iter; func_iter = func_iter->next) + cnt++; + if (cnt > ctx->global_funcs_size) + { + if (ctx->global_funcs) + new_funcs = heap_realloc(ctx->global_funcs, cnt * sizeof(*new_funcs)); + else + new_funcs = heap_alloc(cnt * sizeof(*new_funcs)); + if (!new_funcs) + return E_OUTOFMEMORY; + ctx->global_funcs = new_funcs; + ctx->global_funcs_size = cnt; + } + + for (var_iter = code->global_vars; var_iter; var_iter = var_iter->next) + ctx->global_vars[ctx->global_vars_cnt++] = var_iter; + + for (func_iter = code->funcs; func_iter; func_iter = func_iter->next) + { + for (i = 0; i < ctx->global_funcs_cnt; i++) + { + if (!wcsicmp(ctx->global_funcs[i]->name, func_iter->name)) + { + /* global function already exists, replace it */ + ctx->global_funcs[i] = func_iter; + break; + } + } + if (i == ctx->global_funcs_cnt) + ctx->global_funcs[ctx->global_funcs_cnt++] = func_iter; + } + + if (code->classes) + { + class_desc_t *class = code->classes; + + while (1) + { + class->ctx = ctx; + if (!class->next) + break; + class = class->next; + } + + class->next = ctx->classes; + ctx->classes = code->classes; + code->last_class = class; + } + code->pending_exec = FALSE; return exec_script(ctx, TRUE, &code->main_code, NULL, NULL, res); } diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 04a9b83..efc3585 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -350,6 +350,9 @@ struct _vbscode_t { unsigned bstr_cnt; heap_pool_t heap;
+ dynamic_var_t *global_vars; + function_t *funcs; + class_desc_t *classes; class_desc_t *last_class;
struct list entry;
Hi Gabriel,
On 11/7/19 2:28 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 04a9b83..efc3585 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -350,6 +350,9 @@ struct _vbscode_t { unsigned bstr_cnt; heap_pool_t heap;
- dynamic_var_t *global_vars;
It seems to me that we don't need it. Now that we post-process variables to append to global variable set anyway, we could just get rid of special-case for global code in compiler and use variables stored in vbscode_t::main_code when we need to add them to global variables set.
Jacek
On 11/7/19 8:28 PM, Jacek Caban wrote:
Hi Gabriel,
On 11/7/19 2:28 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 04a9b83..efc3585 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -350,6 +350,9 @@ struct _vbscode_t { unsigned bstr_cnt; heap_pool_t heap; + dynamic_var_t *global_vars;
It seems to me that we don't need it. Now that we post-process variables to append to global variable set anyway, we could just get rid of special-case for global code in compiler and use variables stored in vbscode_t::main_code when we need to add them to global variables set.
Jacek
I've been looking at the code a bit and I'm afraid I'm missing something. What exactly do you mean by post-process variables?
As it is now, the code is wrong and will not pass the tests I wrote. When the code is executed (and it can be executed several times) it should add its vars/functions to the global lists so they get DISPIDs and so on.
Or do you have a better place to add its functions and variables to the global lists? It has to be a place where they get added if the code gets re-executed (pending code), though. And only after the script is started.
Sorry for the confusion.
On 11/8/19 1:08 PM, Gabriel Ivăncescu wrote:
On 11/7/19 8:28 PM, Jacek Caban wrote:
Hi Gabriel,
On 11/7/19 2:28 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 04a9b83..efc3585 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -350,6 +350,9 @@ struct _vbscode_t { unsigned bstr_cnt; heap_pool_t heap; + dynamic_var_t *global_vars;
It seems to me that we don't need it. Now that we post-process variables to append to global variable set anyway, we could just get rid of special-case for global code in compiler and use variables stored in vbscode_t::main_code when we need to add them to global variables set.
Jacek
I've been looking at the code a bit and I'm afraid I'm missing something. What exactly do you mean by post-process variables?
As it is now, the code is wrong and will not pass the tests I wrote. When the code is executed (and it can be executed several times) it should add its vars/functions to the global lists so they get DISPIDs and so on.
Or do you have a better place to add its functions and variables to the global lists? It has to be a place where they get added if the code gets re-executed (pending code), though. And only after the script is started.
Sorry for the confusion.
See how compile_func allocates variables. It handles global variables differently from variables inside functions. The reason for that is that in the past, global variables were added straight to the context. Now that we have an extra step anyway, I wonder if we still need that. Maybe, instead of exposing this special case outside compiler, we could just use single representation in all cases. It's already exposed in function_t (although we'd need to add is_const to var_desc_t so that we have all needed informations exposed). In exec_script, you could iterate code->main_code.vars instead of newly exposed global_vars. The only downside is that we'd need to allocate dynamic_var_t later anyway, like we do for implicitly created variables (we could eventually delay that through, but that's additional work not necessary for what you need).
Jacek
On 11/8/19 3:16 PM, Jacek Caban wrote:
On 11/8/19 1:08 PM, Gabriel Ivăncescu wrote:
On 11/7/19 8:28 PM, Jacek Caban wrote:
Hi Gabriel,
On 11/7/19 2:28 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 04a9b83..efc3585 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -350,6 +350,9 @@ struct _vbscode_t { unsigned bstr_cnt; heap_pool_t heap; + dynamic_var_t *global_vars;
It seems to me that we don't need it. Now that we post-process variables to append to global variable set anyway, we could just get rid of special-case for global code in compiler and use variables stored in vbscode_t::main_code when we need to add them to global variables set.
Jacek
I've been looking at the code a bit and I'm afraid I'm missing something. What exactly do you mean by post-process variables?
As it is now, the code is wrong and will not pass the tests I wrote. When the code is executed (and it can be executed several times) it should add its vars/functions to the global lists so they get DISPIDs and so on.
Or do you have a better place to add its functions and variables to the global lists? It has to be a place where they get added if the code gets re-executed (pending code), though. And only after the script is started.
Sorry for the confusion.
See how compile_func allocates variables. It handles global variables differently from variables inside functions. The reason for that is that in the past, global variables were added straight to the context. Now that we have an extra step anyway, I wonder if we still need that. Maybe, instead of exposing this special case outside compiler, we could just use single representation in all cases. It's already exposed in function_t (although we'd need to add is_const to var_desc_t so that we have all needed informations exposed). In exec_script, you could iterate code->main_code.vars instead of newly exposed global_vars. The only downside is that we'd need to allocate dynamic_var_t later anyway, like we do for implicitly created variables (we could eventually delay that through, but that's additional work not necessary for what you need).
Jacek
Thanks for the explanation.
So, I've been fiddling with some more tests, it seems that the explicit vars (the ones compiled) need to be added to DISPID as soon as script starts executing, even if they are never set.
I've updated the tests I wrote (from "dim z\nz=3\n" to just "dim z") and they still pass on Windows, but fail on Wine if I remove that special case.
On a side note, I don't think `is_const' is needed in var_desc_t because to reconstruct the global vars in exec_global_script, they are always set to FALSE currently. So there's no need to add it to var_desc_t.
So let me summarize and see if I got this right:
Get rid of the special case for global function in compile_func.
Then, in exec_global_code, walk through the code->main_code.vars and allocate the variables there instead of in compile_func, the same way.
This second step is required because those vars need to exist as soon as the script is executed (even if they are never set), and besides, they need to be added *before* the dynamic (implicit) vars, for the TypeInfo anyway.
Thanks, Gabriel
On 11/8/19 2:28 PM, Gabriel Ivăncescu wrote:
On 11/8/19 3:16 PM, Jacek Caban wrote:
On 11/8/19 1:08 PM, Gabriel Ivăncescu wrote:
On 11/7/19 8:28 PM, Jacek Caban wrote:
Hi Gabriel,
On 11/7/19 2:28 PM, Gabriel Ivăncescu wrote:
diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 04a9b83..efc3585 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -350,6 +350,9 @@ struct _vbscode_t { unsigned bstr_cnt; heap_pool_t heap; + dynamic_var_t *global_vars;
It seems to me that we don't need it. Now that we post-process variables to append to global variable set anyway, we could just get rid of special-case for global code in compiler and use variables stored in vbscode_t::main_code when we need to add them to global variables set.
Jacek
I've been looking at the code a bit and I'm afraid I'm missing something. What exactly do you mean by post-process variables?
As it is now, the code is wrong and will not pass the tests I wrote. When the code is executed (and it can be executed several times) it should add its vars/functions to the global lists so they get DISPIDs and so on.
Or do you have a better place to add its functions and variables to the global lists? It has to be a place where they get added if the code gets re-executed (pending code), though. And only after the script is started.
Sorry for the confusion.
See how compile_func allocates variables. It handles global variables differently from variables inside functions. The reason for that is that in the past, global variables were added straight to the context. Now that we have an extra step anyway, I wonder if we still need that. Maybe, instead of exposing this special case outside compiler, we could just use single representation in all cases. It's already exposed in function_t (although we'd need to add is_const to var_desc_t so that we have all needed informations exposed). In exec_script, you could iterate code->main_code.vars instead of newly exposed global_vars. The only downside is that we'd need to allocate dynamic_var_t later anyway, like we do for implicitly created variables (we could eventually delay that through, but that's additional work not necessary for what you need).
Jacek
Thanks for the explanation.
So, I've been fiddling with some more tests, it seems that the explicit vars (the ones compiled) need to be added to DISPID as soon as script starts executing, even if they are never set.
I didn't mean to suggest changing that. The comment about delaying was for the storage only (at some point we could want to store VARIANT outside dynamic_var_t for different reasons), not for something that would be visible outside interpreter. Please do not bother about that.
I've updated the tests I wrote (from "dim z\nz=3\n" to just "dim z") and they still pass on Windows, but fail on Wine if I remove that special case.
On a side note, I don't think `is_const' is needed in var_desc_t because to reconstruct the global vars in exec_global_script, they are always set to FALSE currently. So there's no need to add it to var_desc_t.
So let me summarize and see if I got this right:
Get rid of the special case for global function in compile_func.
Then, in exec_global_code, walk through the code->main_code.vars and allocate the variables there instead of in compile_func, the same way.
This second step is required because those vars need to exist as soon as the script is executed (even if they are never set), and besides, they need to be added *before* the dynamic (implicit) vars, for the TypeInfo anyway.
Yes, that's what I meant.
Thanks,
Jacek
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Note that we have to move the non-persistent vbscode from the context's list, so it can be freed by the dispatch object only when it is released. Not doing so will cause a crash even with Wine's own existing tests, since the script dispatch will be held and released later when the code list no longer exists.
At the same time, the original approach wasn't quite correct either (using the dispatch's code list when compiling the code), because of ordering issues. So the easiest way is to move the entry.
dlls/vbscript/compile.c | 15 +++++---- dlls/vbscript/interp.c | 41 +++++++++++++---------- dlls/vbscript/vbdisp.c | 39 +++++++++++++++++----- dlls/vbscript/vbscript.c | 71 +++++++++++++++------------------------- dlls/vbscript/vbscript.h | 44 +++++++++++++------------ 5 files changed, 110 insertions(+), 100 deletions(-)
diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index fd46af8..4de2a18 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -1786,21 +1786,22 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl)
static BOOL lookup_script_identifier(script_ctx_t *script, const WCHAR *identifier) { + ScriptDisp *obj = script->script_obj; class_desc_t *class; vbscode_t *code; unsigned i;
- for(i = 0; i < script->global_vars_cnt; i++) { - if(!wcsicmp(script->global_vars[i]->name, identifier)) + for(i = 0; i < obj->global_vars_cnt; i++) { + if(!wcsicmp(obj->global_vars[i]->name, identifier)) return TRUE; }
- for(i = 0; i < script->global_funcs_cnt; i++) { - if(!wcsicmp(script->global_funcs[i]->name, identifier)) + for(i = 0; i < obj->global_funcs_cnt; i++) { + if(!wcsicmp(obj->global_funcs[i]->name, identifier)) return TRUE; }
- for(class = script->classes; class; class = class->next) { + for(class = obj->classes; class; class = class->next) { if(!wcsicmp(class->name, identifier)) return TRUE; } @@ -2011,8 +2012,8 @@ HRESULT compile_procedure(script_ctx_t *script, const WCHAR *src, const WCHAR *d desc->func_cnt = 1; desc->funcs->entries[VBDISP_CALLGET] = &code->main_code;
- desc->next = script->procs; - script->procs = desc; + desc->next = script->script_obj->procs; + script->script_obj->procs = desc;
*ret = desc; return S_OK; diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 3dfcf1e..6551f1a 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -94,7 +94,7 @@ static BOOL lookup_dynamic_vars(dynamic_var_t *var, const WCHAR *name, ref_t *re return FALSE; }
-static BOOL lookup_global_vars(script_ctx_t *script, const WCHAR *name, ref_t *ref) +static BOOL lookup_global_vars(ScriptDisp *script, const WCHAR *name, ref_t *ref) { dynamic_var_t **vars = script->global_vars; size_t i, cnt = script->global_vars_cnt; @@ -112,6 +112,7 @@ static BOOL lookup_global_vars(script_ctx_t *script, const WCHAR *name, ref_t *r
static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, vbdisp_invoke_type_t invoke_type, ref_t *ref) { + ScriptDisp *script_obj = ctx->script->script_obj; named_item_t *item; IDispatch *disp; unsigned i; @@ -175,11 +176,11 @@ static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, vbdisp_invoke_type_ } }
- if(lookup_global_vars(ctx->script, name, ref)) + if(lookup_global_vars(script_obj, name, ref)) return S_OK;
- for(i = 0; i < ctx->script->global_funcs_cnt; i++) { - function_t *func = ctx->script->global_funcs[i]; + for(i = 0; i < script_obj->global_funcs_cnt; i++) { + function_t *func = script_obj->global_funcs[i]; if(!wcsicmp(func->name, name)) { ref->type = REF_FUNC; ref->u.f = func; @@ -221,12 +222,13 @@ static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, vbdisp_invoke_type_ static HRESULT add_dynamic_var(exec_ctx_t *ctx, const WCHAR *name, BOOL is_const, VARIANT **out_var) { + ScriptDisp *script_obj = ctx->script->script_obj; dynamic_var_t *new_var; heap_pool_t *heap; WCHAR *str; unsigned size;
- heap = ctx->func->type == FUNC_GLOBAL ? &ctx->script->heap : &ctx->heap; + heap = ctx->func->type == FUNC_GLOBAL ? &script_obj->heap : &ctx->heap;
new_var = heap_pool_alloc(heap, sizeof(*new_var)); if(!new_var) @@ -243,19 +245,21 @@ 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) { - size_t cnt = ctx->script->global_vars_cnt + 1; - if(cnt > ctx->script->global_vars_size) { + size_t cnt = script_obj->global_vars_cnt + 1; + if(cnt > script_obj->global_vars_size) { dynamic_var_t **new_vars; - if(ctx->script->global_vars) - new_vars = heap_realloc(ctx->script->global_vars, cnt * 2 * sizeof(*new_vars)); + if(script_obj->global_vars) + new_vars = heap_realloc(script_obj->global_vars, cnt * 2 * sizeof(*new_vars)); else new_vars = heap_alloc(cnt * 2 * sizeof(*new_vars)); if(!new_vars) return E_OUTOFMEMORY; - ctx->script->global_vars = new_vars; - ctx->script->global_vars_size = cnt * 2; + script_obj->global_vars = new_vars; + script_obj->global_vars_size = cnt * 2; } - ctx->script->global_vars[ctx->script->global_vars_cnt++] = new_var; + script_obj->global_vars[script_obj->global_vars_cnt++] = new_var; + new_var->next = script_obj->dynamic_vars; + script_obj->dynamic_vars = new_var; }else { new_var->next = ctx->dynamic_vars; ctx->dynamic_vars = new_var; @@ -1113,7 +1117,7 @@ static HRESULT interp_new(exec_ctx_t *ctx) return stack_push(ctx, &v); }
- for(class_desc = ctx->script->classes; class_desc; class_desc = class_desc->next) { + for(class_desc = ctx->script->script_obj->classes; class_desc; class_desc = class_desc->next) { if(!wcsicmp(class_desc->name, arg)) break; } @@ -1133,6 +1137,7 @@ static HRESULT interp_new(exec_ctx_t *ctx)
static HRESULT interp_dim(exec_ctx_t *ctx) { + ScriptDisp *script_obj = ctx->script->script_obj; const BSTR ident = ctx->instr->arg1.bstr; const unsigned array_id = ctx->instr->arg2.uint; const array_desc_t *array_desc; @@ -1146,13 +1151,13 @@ static HRESULT interp_dim(exec_ctx_t *ctx)
if(ctx->func->type == FUNC_GLOBAL) { unsigned i; - for(i = 0; i < ctx->script->global_vars_cnt; i++) { - if(!wcsicmp(ctx->script->global_vars[i]->name, ident)) + for(i = 0; i < script_obj->global_vars_cnt; i++) { + if(!wcsicmp(script_obj->global_vars[i]->name, ident)) break; } - assert(i < ctx->script->global_vars_cnt); - v = &ctx->script->global_vars[i]->v; - array_ref = &ctx->script->global_vars[i]->array; + assert(i < script_obj->global_vars_cnt); + v = &script_obj->global_vars[i]->v; + array_ref = &script_obj->global_vars[i]->array; }else { ref_t ref;
diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index 12626e0..09bf4a8 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -569,11 +569,30 @@ static ULONG WINAPI ScriptDisp_Release(IDispatchEx *iface) { ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); LONG ref = InterlockedDecrement(&This->ref); + vbscode_t *code, *code_next;
TRACE("(%p) ref=%d\n", This, ref);
if(!ref) { assert(!This->ctx); + + while (This->procs) + { + class_desc_t *class_desc = This->procs; + This->procs = class_desc->next; + heap_free(class_desc); + } + + LIST_FOR_EACH_ENTRY_SAFE(code, code_next, &This->code_list, vbscode_t, entry) + { + release_dynamic_vars(code->global_vars); + release_vbscode(code); + } + release_dynamic_vars(This->dynamic_vars); + + heap_pool_free(&This->heap); + heap_free(This->global_vars); + heap_free(This->global_funcs); heap_free(This); }
@@ -639,15 +658,15 @@ static HRESULT WINAPI ScriptDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DW if(!This->ctx) return E_UNEXPECTED;
- for(i = 0; i < This->ctx->global_vars_cnt; i++) { - if(!wcsicmp(This->ctx->global_vars[i]->name, bstrName)) { + for(i = 0; i < This->global_vars_cnt; i++) { + if(!wcsicmp(This->global_vars[i]->name, bstrName)) { *pid = i + 1; return S_OK; } }
- for(i = 0; i < This->ctx->global_funcs_cnt; i++) { - if(!wcsicmp(This->ctx->global_funcs[i]->name, bstrName)) { + for(i = 0; i < This->global_funcs_cnt; i++) { + if(!wcsicmp(This->global_funcs[i]->name, bstrName)) { *pid = i + 1 + DISPID_FUNCTION_MASK; return S_OK; } @@ -668,14 +687,14 @@ static HRESULT WINAPI ScriptDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc if (id & DISPID_FUNCTION_MASK) { id &= ~DISPID_FUNCTION_MASK; - if (id > This->ctx->global_funcs_cnt) + if (id > This->global_funcs_cnt) return DISP_E_MEMBERNOTFOUND;
switch (wFlags) { case DISPATCH_METHOD: case DISPATCH_METHOD | DISPATCH_PROPERTYGET: - hres = exec_script(This->ctx, TRUE, This->ctx->global_funcs[id - 1], NULL, pdp, pvarRes); + hres = exec_script(This->ctx, TRUE, This->global_funcs[id - 1], NULL, pdp, pvarRes); break; default: FIXME("Unsupported flags %x\n", wFlags); @@ -685,16 +704,16 @@ static HRESULT WINAPI ScriptDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc return hres; }
- if (id > This->ctx->global_vars_cnt) + if (id > This->global_vars_cnt) return DISP_E_MEMBERNOTFOUND;
- if (This->ctx->global_vars[id - 1]->is_const) + if (This->global_vars[id - 1]->is_const) { FIXME("const not supported\n"); return E_NOTIMPL; }
- return invoke_variant_prop(This->ctx, &This->ctx->global_vars[id - 1]->v, wFlags, pdp, pvarRes); + return invoke_variant_prop(This->ctx, &This->global_vars[id - 1]->v, wFlags, pdp, pvarRes); }
static HRESULT WINAPI ScriptDisp_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) @@ -768,6 +787,8 @@ 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); + list_init(&script_disp->code_list);
*ret = script_disp; return S_OK; diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index f55cd5b..6eaa6b6 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -84,54 +84,55 @@ static HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res { dynamic_var_t *var_iter, **new_vars; function_t *func_iter, **new_funcs; + ScriptDisp *obj = ctx->script_obj; size_t cnt, i;
- cnt = ctx->global_vars_cnt; + cnt = obj->global_vars_cnt; for (var_iter = code->global_vars; var_iter; var_iter = var_iter->next) cnt++; - if (cnt > ctx->global_vars_size) + if (cnt > obj->global_vars_size) { - if (ctx->global_vars) - new_vars = heap_realloc(ctx->global_vars, cnt * sizeof(*new_vars)); + if (obj->global_vars) + new_vars = heap_realloc(obj->global_vars, cnt * sizeof(*new_vars)); else new_vars = heap_alloc(cnt * sizeof(*new_vars)); if (!new_vars) return E_OUTOFMEMORY; - ctx->global_vars = new_vars; - ctx->global_vars_size = cnt; + obj->global_vars = new_vars; + obj->global_vars_size = cnt; }
- cnt = ctx->global_funcs_cnt; + cnt = obj->global_funcs_cnt; for (func_iter = code->funcs; func_iter; func_iter = func_iter->next) cnt++; - if (cnt > ctx->global_funcs_size) + if (cnt > obj->global_funcs_size) { - if (ctx->global_funcs) - new_funcs = heap_realloc(ctx->global_funcs, cnt * sizeof(*new_funcs)); + if (obj->global_funcs) + new_funcs = heap_realloc(obj->global_funcs, cnt * sizeof(*new_funcs)); else new_funcs = heap_alloc(cnt * sizeof(*new_funcs)); if (!new_funcs) return E_OUTOFMEMORY; - ctx->global_funcs = new_funcs; - ctx->global_funcs_size = cnt; + obj->global_funcs = new_funcs; + obj->global_funcs_size = cnt; }
for (var_iter = code->global_vars; var_iter; var_iter = var_iter->next) - ctx->global_vars[ctx->global_vars_cnt++] = var_iter; + obj->global_vars[obj->global_vars_cnt++] = var_iter;
for (func_iter = code->funcs; func_iter; func_iter = func_iter->next) { - for (i = 0; i < ctx->global_funcs_cnt; i++) + for (i = 0; i < obj->global_funcs_cnt; i++) { - if (!wcsicmp(ctx->global_funcs[i]->name, func_iter->name)) + if (!wcsicmp(obj->global_funcs[i]->name, func_iter->name)) { /* global function already exists, replace it */ - ctx->global_funcs[i] = func_iter; + obj->global_funcs[i] = func_iter; break; } } - if (i == ctx->global_funcs_cnt) - ctx->global_funcs[ctx->global_funcs_cnt++] = func_iter; + if (i == obj->global_funcs_cnt) + obj->global_funcs[obj->global_funcs_cnt++] = func_iter; }
if (code->classes) @@ -146,8 +147,8 @@ static HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res class = class->next; }
- class->next = ctx->classes; - ctx->classes = code->classes; + class->next = obj->classes; + obj->classes = code->classes; code->last_class = class; }
@@ -200,33 +201,23 @@ IDispatch *lookup_named_item(script_ctx_t *ctx, const WCHAR *name, unsigned flag static void release_script(script_ctx_t *ctx) { vbscode_t *code, *code_next; - class_desc_t *class_desc; - unsigned i;
collect_objects(ctx); clear_ei(&ctx->ei);
- for(i = 0; i < ctx->global_vars_cnt; i++) - VariantClear(&ctx->global_vars[i]->v); - - heap_free(ctx->global_vars); - heap_free(ctx->global_funcs); - ctx->global_vars = NULL; - ctx->global_vars_cnt = 0; - ctx->global_vars_size = 0; - ctx->global_funcs = NULL; - ctx->global_funcs_cnt = 0; - ctx->global_funcs_size = 0; - LIST_FOR_EACH_ENTRY_SAFE(code, code_next, &ctx->code_list, vbscode_t, entry) { if(code->is_persistent) { code->pending_exec = TRUE; if(code->last_class) code->last_class->next = NULL; + release_dynamic_vars(code->global_vars); } else - release_vbscode(code); + { + list_remove(&code->entry); + list_add_tail(&ctx->script_obj->code_list, &code->entry); + } }
while(!list_empty(&ctx->named_items)) { @@ -239,13 +230,6 @@ static void release_script(script_ctx_t *ctx) heap_free(iter); }
- while(ctx->procs) { - class_desc = ctx->procs; - ctx->procs = class_desc->next; - - heap_free(class_desc); - } - if(ctx->host_global) { IDispatch_Release(ctx->host_global); ctx->host_global = NULL; @@ -270,8 +254,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 release_code_list(script_ctx_t *ctx) @@ -1047,7 +1029,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 efc3585..d4e47d7 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -120,11 +120,34 @@ struct _vbdisp_t { VARIANT props[1]; };
+typedef struct _dynamic_var_t { + struct _dynamic_var_t *next; + VARIANT v; + const WCHAR *name; + BOOL is_const; + SAFEARRAY *array; +} dynamic_var_t; + typedef struct { IDispatchEx IDispatchEx_iface; LONG ref;
+ dynamic_var_t **global_vars; + size_t global_vars_cnt; + size_t global_vars_size; + + function_t **global_funcs; + size_t global_funcs_cnt; + size_t global_funcs_size; + + dynamic_var_t *dynamic_vars; + class_desc_t *classes; + class_desc_t *procs; + script_ctx_t *ctx; + heap_pool_t heap; + + struct list code_list; } ScriptDisp;
typedef struct _builtin_prop_t builtin_prop_t; @@ -158,14 +181,6 @@ static inline VARIANT *get_arg(DISPPARAMS *dp, DWORD i) return dp->rgvarg + dp->cArgs-i-1; }
-typedef struct _dynamic_var_t { - struct _dynamic_var_t *next; - VARIANT v; - const WCHAR *name; - BOOL is_const; - SAFEARRAY *array; -} dynamic_var_t; - struct _script_ctx_t { IActiveScriptSite *site; LCID lcid; @@ -182,19 +197,6 @@ struct _script_ctx_t {
EXCEPINFO ei;
- dynamic_var_t **global_vars; - size_t global_vars_cnt; - size_t global_vars_size; - - function_t **global_funcs; - size_t global_funcs_cnt; - size_t global_funcs_size; - - class_desc_t *classes; - class_desc_t *procs; - - heap_pool_t heap; - struct list objects; struct list code_list; struct list named_items;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This will be needed for next patch, since we'll need to record two such calls at one point.
dlls/vbscript/tests/vbscript.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index c0a582c..3fe73a9 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -53,27 +53,27 @@ DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
#define DEFINE_EXPECT(func) \ - static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + static int expect_ ## func = 0, called_ ## func = 0
#define SET_EXPECT(func) \ - expect_ ## func = TRUE + expect_ ## func = 1
#define CHECK_EXPECT2(func) \ do { \ ok(expect_ ##func, "unexpected call " #func "\n"); \ - called_ ## func = TRUE; \ + called_ ## func++; \ }while(0)
#define CHECK_EXPECT(func) \ do { \ CHECK_EXPECT2(func); \ - expect_ ## func = FALSE; \ + expect_ ## func--; \ }while(0)
#define CHECK_CALLED(func) \ do { \ ok(called_ ## func, "expected " #func "\n"); \ - expect_ ## func = called_ ## func = FALSE; \ + expect_ ## func = called_ ## func = 0; \ }while(0)
DEFINE_EXPECT(GetLCID);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/vbscript/tests/vbscript.c | 252 +++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+)
diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index 3fe73a9..5cb50c5 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -58,6 +58,9 @@ DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); #define SET_EXPECT(func) \ expect_ ## func = 1
+#define SET_EXPECT_MULTI(func, num) \ + expect_ ## func = num + #define CHECK_EXPECT2(func) \ do { \ ok(expect_ ##func, "unexpected call " #func "\n"); \ @@ -76,6 +79,12 @@ DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); expect_ ## func = called_ ## func = 0; \ }while(0)
+#define CHECK_CALLED_MULTI(func, num) \ + do { \ + ok(called_ ## func == num, "expected " #func " %d times (got %d)\n", num, called_ ## func); \ + expect_ ## func = called_ ## func = 0; \ + }while(0) + DEFINE_EXPECT(GetLCID); DEFINE_EXPECT(OnStateChange_UNINITIALIZED); DEFINE_EXPECT(OnStateChange_STARTED); @@ -624,6 +633,248 @@ static void test_scriptdisp(void) ok(!ref, "ref = %d\n", ref); }
+static void test_code_persistence(void) +{ + IActiveScriptParse *parser; + IDispatchEx *script_disp; + IActiveScript *vbscript; + VARIANT var; + HRESULT hr; + DISPID id; + ULONG ref; + BSTR str; + + vbscript = create_vbscript(); + + hr = IActiveScript_QueryInterface(vbscript, &IID_IActiveScriptParse, (void**)&parser); + ok(hr == S_OK, "Could not get IActiveScriptParse iface: %08x\n", hr); + test_state(vbscript, SCRIPTSTATE_UNINITIALIZED); + test_safety(vbscript); + + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(vbscript, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScriptParse_InitNew(parser); + ok(hr == S_OK, "InitNew failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + test_state(vbscript, SCRIPTSTATE_INITIALIZED); + + str = a2bstr( + "x = 1\n" + "dim y\ny = 2\n"); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + str = a2bstr( + "dim z\nz = 3\n" + "y = 42\n" + "var = 10\n"); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISPERSISTENT, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + /* Pending code does not add identifiers to the global scope */ + script_disp = get_script_dispatch(vbscript); + id = 0; + get_disp_id(script_disp, "x", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(script_disp, "y", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(script_disp, "z", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + IDispatchEx_Release(script_disp); + + /* Uninitialized state removes code without SCRIPTTEXT_ISPERSISTENT */ + SET_EXPECT(OnStateChange_UNINITIALIZED); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_UNINITIALIZED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_UNINITIALIZED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_UNINITIALIZED); + test_no_script_dispatch(vbscript); + + SET_EXPECT(GetLCID); + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScript_SetScriptSite(vbscript, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + CHECK_CALLED(OnStateChange_INITIALIZED); + + str = a2bstr("var = 20\n"); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + SET_EXPECT(OnStateChange_CONNECTED); + SET_EXPECT_MULTI(OnEnterScript, 2); + SET_EXPECT_MULTI(OnLeaveScript, 2); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + CHECK_CALLED_MULTI(OnEnterScript, 2); + CHECK_CALLED_MULTI(OnLeaveScript, 2); + test_state(vbscript, SCRIPTSTATE_CONNECTED); + + script_disp = get_script_dispatch(vbscript); + id = 0; + get_disp_id(script_disp, "x", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(script_disp, "y", S_OK, &id); + ok(id != -1, "id = -1\n"); + id = 0; + get_disp_id(script_disp, "z", S_OK, &id); + ok(id != -1, "id = -1\n"); + IDispatchEx_Release(script_disp); + + str = a2bstr("y"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I2 && V_I2(&var) == 42, "V_VT(y) = %d, V_I2(y) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + str = a2bstr("var"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I2 && V_I2(&var) == 20, "V_VT(var) = %d, V_I2(var) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + /* Uninitialized state does not remove persistent code, even if it was executed */ + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_UNINITIALIZED); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_UNINITIALIZED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_UNINITIALIZED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_UNINITIALIZED); + test_no_script_dispatch(vbscript); + + SET_EXPECT(GetLCID); + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScript_SetScriptSite(vbscript, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + CHECK_CALLED(OnStateChange_INITIALIZED); + + script_disp = get_script_dispatch(vbscript); + id = 0; + get_disp_id(script_disp, "z", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + IDispatchEx_Release(script_disp); + + SET_EXPECT(OnStateChange_CONNECTED); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + test_state(vbscript, SCRIPTSTATE_CONNECTED); + + script_disp = get_script_dispatch(vbscript); + id = 0; + get_disp_id(script_disp, "z", S_OK, &id); + ok(id != -1, "id = -1\n"); + IDispatchEx_Release(script_disp); + + str = a2bstr("y"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I2 && V_I2(&var) == 42, "V_VT(y) = %d, V_I2(y) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + str = a2bstr("var"); + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &var, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + ok(V_VT(&var) == VT_I2 && V_I2(&var) == 10, "V_VT(var) = %d, V_I2(var) = %d\n", V_VT(&var), V_I2(&var)); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + SysFreeString(str); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_UNINITIALIZED); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_UNINITIALIZED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_UNINITIALIZED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_UNINITIALIZED); + + SET_EXPECT(GetLCID); + SET_EXPECT(OnStateChange_INITIALIZED); + hr = IActiveScript_SetScriptSite(vbscript, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(GetLCID); + CHECK_CALLED(OnStateChange_INITIALIZED); + + str = a2bstr("dim y\ny = 2\n"); + hr = IActiveScriptParse_ParseScriptText(parser, str, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISPERSISTENT, NULL, NULL); + ok(hr == S_OK, "ParseScriptText failed: %08x\n", hr); + SysFreeString(str); + + /* Closing the script engine removes all code (even if it's pending and persistent) */ + SET_EXPECT(OnStateChange_CLOSED); + hr = IActiveScript_Close(vbscript); + ok(hr == S_OK, "Close failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CLOSED); + test_state(vbscript, SCRIPTSTATE_CLOSED); + test_no_script_dispatch(vbscript); + + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(GetLCID); + hr = IActiveScript_SetScriptSite(vbscript, &ActiveScriptSite); + ok(hr == S_OK, "SetScriptSite failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(GetLCID); + test_state(vbscript, SCRIPTSTATE_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hr = IActiveScript_SetScriptState(vbscript, SCRIPTSTATE_CONNECTED); + ok(hr == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08x\n", hr); + CHECK_CALLED(OnStateChange_CONNECTED); + test_state(vbscript, SCRIPTSTATE_CONNECTED); + + script_disp = get_script_dispatch(vbscript); + id = 0; + get_disp_id(script_disp, "y", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + id = 0; + get_disp_id(script_disp, "z", DISP_E_UNKNOWNNAME, &id); + ok(id == -1, "id = %d, expected -1\n", id); + IDispatchEx_Release(script_disp); + + IActiveScriptParse_Release(parser); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + ref = IActiveScript_Release(vbscript); + ok(!ref, "ref = %d\n", ref); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); +} + static void test_vbscript(void) { IActiveScriptParseProcedure2 *parse_proc; @@ -1249,6 +1500,7 @@ START_TEST(vbscript) test_vbscript_initializing(); test_named_items(); test_scriptdisp(); + test_code_persistence(); test_RegExp(); test_RegExp_Replace(); }else {