From: Francis De Brabandere <francisdb@gmail.com> Eval compiles the string as an expression and returns the result. ExecuteGlobal compiles and executes the string at global scope. Execute compiles and executes the string in the calling scope. When called from within a Sub or Function, Eval and Execute use the calling scope for variable resolution, accessing local variables and arguments. When called from global scope, they fall back to exec_global_code. ExecuteGlobal always executes in global scope. The calling scope is passed through script_ctx_t.caller_exec, which exec_script picks up and stores on exec_ctx_t.caller. The interpreter's lookup_identifier checks the caller's locals, arguments, and dynamic vars when running FUNC_GLOBAL code with a caller context set. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49908 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53962 --- dlls/vbscript/global.c | 62 ++++++++++++++++++++++++++++++++---- dlls/vbscript/interp.c | 39 +++++++++++++++++++++++ dlls/vbscript/tests/lang.vbs | 62 ++++++++++++++++++++++++++++++++++++ dlls/vbscript/vbscript.c | 2 +- dlls/vbscript/vbscript.h | 5 +++ 5 files changed, 163 insertions(+), 7 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index f38ff895ef7..941991196b2 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -3333,20 +3333,70 @@ static HRESULT Global_Unescape(BuiltinDisp *This, VARIANT *arg, unsigned args_cn static HRESULT Global_Eval(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + vbscode_t *code; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) != VT_BSTR) { + FIXME("arg %s not supported\n", debugstr_variant(arg)); + return E_NOTIMPL; + } + + hres = compile_script(This->ctx, V_BSTR(arg), NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &code); + if(FAILED(hres)) + return hres; + + if(is_exec_local_scope(This->ctx->current_exec)) { + This->ctx->caller_exec = This->ctx->current_exec; + return exec_script(This->ctx, TRUE, &code->main_code, NULL, NULL, res); + } + + return exec_global_code(This->ctx, code, res); } static HRESULT Global_Execute(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + vbscode_t *code; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) != VT_BSTR) { + FIXME("arg %s not supported\n", debugstr_variant(arg)); + return E_NOTIMPL; + } + + hres = compile_script(This->ctx, V_BSTR(arg), NULL, NULL, 0, 0, 0, &code); + if(FAILED(hres)) + return hres; + + if(is_exec_local_scope(This->ctx->current_exec)) { + This->ctx->caller_exec = This->ctx->current_exec; + return exec_script(This->ctx, TRUE, &code->main_code, NULL, NULL, res); + } + + return exec_global_code(This->ctx, code, res); } + static HRESULT Global_ExecuteGlobal(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + vbscode_t *code; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) != VT_BSTR) { + FIXME("arg %s not supported\n", debugstr_variant(arg)); + return E_NOTIMPL; + } + + hres = compile_script(This->ctx, V_BSTR(arg), NULL, NULL, 0, 0, 0, &code); + if(FAILED(hres)) + return hres; + + return exec_global_code(This->ctx, code, res); } static HRESULT Global_GetRef(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 127fcc2c05e..d432b0ef488 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -47,6 +47,8 @@ typedef struct { VARIANT *stack; VARIANT ret_val; + + void *caller; } exec_ctx_t; typedef HRESULT (*instr_func_t)(exec_ctx_t*); @@ -182,6 +184,29 @@ static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, vbdisp_invoke_type_ } } + if(ctx->func->type == FUNC_GLOBAL && ctx->caller) { + exec_ctx_t *caller = ctx->caller; + + for(i=0; i < caller->func->var_cnt; i++) { + if(!wcsicmp(caller->func->vars[i].name, name)) { + ref->type = REF_VAR; + ref->u.v = caller->vars+i; + return S_OK; + } + } + + for(i=0; i < caller->func->arg_cnt; i++) { + if(!wcsicmp(caller->func->args[i].name, name)) { + ref->type = REF_VAR; + ref->u.v = caller->args+i; + return S_OK; + } + } + + if(lookup_dynamic_vars(caller->dynamic_vars, name, ref)) + return S_OK; + } + if(ctx->code->named_item) { if(lookup_global_vars(ctx->code->named_item->script_obj, name, ref)) return S_OK; @@ -2456,13 +2481,22 @@ static void release_exec(exec_ctx_t *ctx) free(ctx->stack); } +BOOL is_exec_local_scope(void *exec) +{ + exec_ctx_t *ctx = exec; + return ctx && ctx->func->type != FUNC_GLOBAL; +} + HRESULT exec_script(script_ctx_t *ctx, BOOL extern_caller, function_t *func, vbdisp_t *vbthis, DISPPARAMS *dp, VARIANT *res) { exec_ctx_t exec = {func->code_ctx}; + void *prev_exec; vbsop_t op; HRESULT hres = S_OK; exec.code = func->code_ctx; + exec.caller = ctx->caller_exec; + ctx->caller_exec = NULL; if(dp ? func->arg_cnt != arg_cnt(dp) : func->arg_cnt) { FIXME("wrong arg_cnt %d, expected %d\n", dp ? arg_cnt(dp) : 0, func->arg_cnt); @@ -2532,6 +2566,9 @@ HRESULT exec_script(script_ctx_t *ctx, BOOL extern_caller, function_t *func, vbd exec.script = ctx; exec.func = func; + prev_exec = ctx->current_exec; + ctx->current_exec = &exec; + while(exec.instr) { op = exec.instr->op; hres = op_funcs[op](&exec); @@ -2592,6 +2629,8 @@ HRESULT exec_script(script_ctx_t *ctx, BOOL extern_caller, function_t *func, vbd exec.instr += op_move[op]; } + ctx->current_exec = prev_exec; + assert(!exec.top); if(extern_caller) { diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index c364f12c00b..dbe12ad9222 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -2298,4 +2298,66 @@ f1 not 1 = 0 arr (0) = 2 xor -2 +' Eval tests +Call ok(Eval("1 + 2") = 3, "Eval(""1 + 2"") = " & Eval("1 + 2")) +Call ok(Eval("""test""") = "test", "Eval(""""""test"""""") = " & Eval("""test""")) +Call ok(Eval("true") = true, "Eval(""true"") = " & Eval("true")) + +x = 5 +Call ok(Eval("x + 1") = 6, "Eval(""x + 1"") = " & Eval("x + 1")) +Call ok(Eval("x * x") = 25, "Eval(""x * x"") = " & Eval("x * x")) + +Sub TestEvalLocalScope + Dim a + a = 10 + Call ok(Eval("a") = 10, "Eval(""a"") in local scope = " & Eval("a")) + Call ok(Eval("a + 5") = 15, "Eval(""a + 5"") in local scope = " & Eval("a + 5")) +End Sub +Call TestEvalLocalScope + +Function TestEvalLocalArgs(x) + TestEvalLocalArgs = Eval("x * 2") +End Function +Call ok(TestEvalLocalArgs(7) = 14, "TestEvalLocalArgs(7) = " & TestEvalLocalArgs(7)) + +Dim evalLocal +Sub TestEvalNoLeak + Dim evalLocal + evalLocal = 77 + Call ok(Eval("evalLocal") = 77, "Eval evalLocal = " & Eval("evalLocal")) +End Sub +Call TestEvalNoLeak +Call ok(getVT(evalLocal) = "VT_EMPTY*", "evalLocal leaked from Eval scope: " & getVT(evalLocal)) + +' ExecuteGlobal tests +x = 0 +ExecuteGlobal "x = 42" +Call ok(x = 42, "ExecuteGlobal x = " & x) + +ExecuteGlobal "Function evalTestFunc : evalTestFunc = 7 : End Function" +Call ok(evalTestFunc() = 7, "evalTestFunc() = " & evalTestFunc()) + +' Execute tests +x = 0 +Execute "x = 99" +Call ok(x = 99, "Execute x = " & x) + +Sub TestExecuteLocalScope + Dim a + a = 10 + Execute "a = 20" + Call ok(a = 20, "Execute local assign: a = " & a) +End Sub +Call TestExecuteLocalScope + +Dim executeLocal +Sub TestExecuteNoLeak + Dim executeLocal + executeLocal = 10 + Execute "executeLocal = 55" + Call ok(executeLocal = 55, "executeLocal = " & executeLocal) +End Sub +Call TestExecuteNoLeak +Call ok(getVT(executeLocal) = "VT_EMPTY*", "executeLocal leaked from Execute scope: " & getVT(executeLocal)) + reportSuccess() diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index 715c98d32bb..8685900fa5c 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -91,7 +91,7 @@ static inline BOOL is_started(VBScript *This) || This->state == SCRIPTSTATE_DISCONNECTED; } -static HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res) +HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res) { ScriptDisp *obj = ctx->script_obj; function_t *func_iter, **new_funcs; diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 6b67c8b1bc5..7a68f11c0ea 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -205,6 +205,9 @@ struct _script_ctx_t { BuiltinDisp *global_obj; BuiltinDisp *err_obj; + void *current_exec; + void *caller_exec; + EXCEPINFO ei; vbscode_t *error_loc_code; unsigned error_loc_offset; @@ -390,6 +393,8 @@ void release_vbscode(vbscode_t*); HRESULT compile_script(script_ctx_t*,const WCHAR*,const WCHAR*,const WCHAR*,DWORD_PTR,unsigned,DWORD,vbscode_t**); HRESULT compile_procedure(script_ctx_t*,const WCHAR*,const WCHAR*,const WCHAR*,DWORD_PTR,unsigned,DWORD,class_desc_t**); HRESULT exec_script(script_ctx_t*,BOOL,function_t*,vbdisp_t*,DISPPARAMS*,VARIANT*); +HRESULT exec_global_code(script_ctx_t*,vbscode_t*,VARIANT*); +BOOL is_exec_local_scope(void*); void release_dynamic_var(dynamic_var_t*); named_item_t *lookup_named_item(script_ctx_t*,const WCHAR*,unsigned); void release_named_item(named_item_t*); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10368