[PATCH v31 0/2] MR10393: Draft: vbscript: Probe top-level declared names against the host dispatch.
Match native VBScript's pattern of probing top-level declared names against an `ISVISIBLE` named item's host `IDispatch` at parse time, and keep the probe IDispatch slot independent of the runtime cache. Why - **Wire-level compatibility.** Hosts that observe `GetIDsOfNames` calls see the same pattern they get on native. The probe result is discarded - it doesn't change shadowing - but the host gets notified of declared names. - **Correctness with rotating hosts.** Wine previously had a single `IDispatch` slot per named item; a Dim parse overwrote the runtime cache, misrouting subsequent qualified access. The patch keeps the probe slot (`dim_probe_disp`) separate. What's in it - Probe pass in `exec_global_code` for visible items, in native order: Subs/Functions -> Classes -> explicit Dims -> implicit declarations from bare top-level assignments. - Cross-parse deduplication: a name is probed at most once per named item across the script lifetime. - Two-slot independence; probe slot reused as runtime cache when no separate runtime fetch has happened yet. - Lazy runtime fetch via `ensure_named_item_disp`. Known divergence (follow-up) When an implicit-decl name is later read as RHS (`a = 1; b = a`), Wine still triggers one extra runtime `GetIDsOfNames` that native avoids. Native pre-creates implicit decls during parse; matching that needs compiler changes. Verified against native VBScript 5.812 with a raw-IDispatch probe harness. -- v31: vbscript: Probe top-level declared names against the host dispatch. vbscript/tests: Test probing of top-level declared names against the host dispatch. https://gitlab.winehq.org/wine/wine/-/merge_requests/10393
From: Francis De Brabandere <francisdb@gmail.com> When a visible named item's code runs, native probes the host IDispatch (GetItemInfo and GetIDsOfNames) for each top-level declared name (Subs and Functions, Classes, Dims), in that order, and caches the result, so a host that returns a different dispatch on a later GetItemInfo still routes qualified access correctly. The probe happens when the code executes, not at parse: a Dim parsed while the engine is only initialized is probed only once the deferred code runs on connect. A name assigned only in a never-taken branch is not probed. Mark with todo_wine the probing Wine does not do yet. --- dlls/vbscript/tests/vbscript.c | 483 +++++++++++++++++++++++++++++++++ 1 file changed, 483 insertions(+) diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index 6fe7893d3b0..a9f4ba8d611 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -108,6 +108,12 @@ DEFINE_EXPECT(testCall); DEFINE_EXPECT(host_shadow_method); DEFINE_EXPECT(host_shadow_propput); DEFINE_EXPECT(host_shadow_propget); +DEFINE_EXPECT(GetItemInfo_rotating); +DEFINE_EXPECT(GetIDsOfNames_rotating); +DEFINE_EXPECT(testCall_rotating_a); +DEFINE_EXPECT(testCall_rotating_b); + +static int rotating_get_item_idx; DEFINE_GUID(CLSID_VBScript, 0xb54f3741, 0x5b07, 0x11cf, 0xa4,0xb0, 0x00,0xaa,0x00,0x4a,0x55,0xe8); DEFINE_GUID(CLSID_VBScriptRegExp, 0x3f4daca4, 0x160d, 0x11d2, 0xa8,0xe9, 0x00,0x10,0x4b,0x36,0x5c,0x9f); @@ -351,6 +357,80 @@ static const IDispatchVtbl shadowing_named_item_vtbl = { static IDispatch shadowing_named_item = { &shadowing_named_item_vtbl }; +static int deadcode_probe_count; + +static HRESULT WINAPI deadcode_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *names, UINT name_cnt, + LCID lcid, DISPID *ids) +{ + ok(name_cnt == 1, "name_cnt = %u\n", name_cnt); + if(!wcscmp(names[0], L"deadVar")) + deadcode_probe_count++; + return DISP_E_UNKNOWNNAME; +} + +static const IDispatchVtbl deadcode_named_item_vtbl = { + Dispatch_QueryInterface, + Dispatch_AddRef, + Dispatch_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + deadcode_GetIDsOfNames, + shadowing_Invoke +}; + +static IDispatch deadcode_named_item = { &deadcode_named_item_vtbl }; + +static HRESULT WINAPI rotating_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *names, UINT name_cnt, + LCID lcid, DISPID *ids) +{ + ok(name_cnt == 1, "name_cnt = %u\n", name_cnt); + if(!wcscmp(names[0], L"testCall")) { + *ids = 1; + return S_OK; + } + CHECK_EXPECT2(GetIDsOfNames_rotating); + return DISP_E_UNKNOWNNAME; +} + +static HRESULT WINAPI rotating_a_Invoke(IDispatch *iface, DISPID id, REFIID riid, LCID lcid, WORD flags, + DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, UINT *err) +{ + CHECK_EXPECT(testCall_rotating_a); + ok(id == 1, "id = %lu\n", id); + return S_OK; +} + +static HRESULT WINAPI rotating_b_Invoke(IDispatch *iface, DISPID id, REFIID riid, LCID lcid, WORD flags, + DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, UINT *err) +{ + CHECK_EXPECT(testCall_rotating_b); + ok(id == 1, "id = %lu\n", id); + return S_OK; +} + +static const IDispatchVtbl rotating_a_vtbl = { + Dispatch_QueryInterface, + Dispatch_AddRef, + Dispatch_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + rotating_GetIDsOfNames, + rotating_a_Invoke +}; + +static const IDispatchVtbl rotating_b_vtbl = { + Dispatch_QueryInterface, + Dispatch_AddRef, + Dispatch_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + rotating_GetIDsOfNames, + rotating_b_Invoke +}; + +static IDispatch rotating_a_named_item = { &rotating_a_vtbl }; +static IDispatch rotating_b_named_item = { &rotating_b_vtbl }; + static HRESULT WINAPI ActiveScriptSite_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **ppv) { *ppv = NULL; @@ -421,6 +501,22 @@ static HRESULT WINAPI ActiveScriptSite_GetItemInfo(IActiveScriptSite *iface, LPC *item_unk = (IUnknown*)&shadowing_named_item; return S_OK; } + if(!wcscmp(name, L"deadcodeItem")) { + IDispatch_AddRef(&deadcode_named_item); + *item_unk = (IUnknown*)&deadcode_named_item; + return S_OK; + } + if(!wcscmp(name, L"rotatingItem")) { + CHECK_EXPECT2(GetItemInfo_rotating); + if(rotating_get_item_idx++ == 0) { + IDispatch_AddRef(&rotating_a_named_item); + *item_unk = (IUnknown*)&rotating_a_named_item; + } else { + IDispatch_AddRef(&rotating_b_named_item); + *item_unk = (IUnknown*)&rotating_b_named_item; + } + return S_OK; + } ok(0, "unexpected call %s\n", wine_dbgstr_w(name)); return E_NOTIMPL; } @@ -2439,6 +2535,80 @@ static void test_named_items(void) CHECK_CALLED(OnEnterScript); CHECK_CALLED(OnLeaveScript); + /* Probe: multiple names in a single Dim statement. */ + SET_EXPECT(OnEnterScript); + SET_EXPECT_MULTI(GetIDsOfNames_visible, 3); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_a, probe_b, probe_c\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED_MULTI(GetIDsOfNames_visible, 3); + CHECK_CALLED(OnLeaveScript); + + /* Probe: Dim with explicit array bounds. */ + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames_visible); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_arr(5)\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames_visible); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"sub probe_sub\ndim probe_local\nend sub\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames_visible); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"class probe_cls\npublic probe_prop\nend class\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames_visible); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"sub probe_named_sub\nend sub\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames_visible); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"function probe_fn\nend function\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT_MULTI(GetIDsOfNames_visible, 2); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_top\nsub probe_s3\nend sub\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED_MULTI(GetIDsOfNames_visible, 2); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames_visible); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_second\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(OnLeaveScript); + SET_EXPECT(OnEnterScript); SET_EXPECT(OnLeaveScript); hres = IActiveScriptParse_ParseScriptText(parse, L"set global_me = me\n", L"globalItem", NULL, NULL, 0, 0, SCRIPTTEXT_ISPERSISTENT, NULL, NULL); @@ -3080,6 +3250,122 @@ static void test_named_item_no_dim_routes_to_host(void) ok(!ref, "ref = %ld\n", ref); } +static void test_named_item_dead_code_probe(void) +{ + IActiveScriptParse *parse; + IActiveScript *script; + HRESULT hres; + LONG ref; + + /* An undeclared name assigned only in a never-taken branch is not probed + * against the host: native does not consult the host dispatch for an + * assignment opcode that never executes. */ + + script = create_vbscript(); + hres = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parse); + ok(hres == S_OK, "Could not get IActiveScriptParse: %08lx\n", hres); + + SET_EXPECT(GetLCID); + hres = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hres == S_OK, "SetScriptSite failed: %08lx\n", hres); + CHECK_CALLED(GetLCID); + + hres = IActiveScript_AddNamedItem(script, L"deadcodeItem", SCRIPTITEM_ISVISIBLE); + ok(hres == S_OK, "AddNamedItem failed: %08lx\n", hres); + + SET_EXPECT(OnStateChange_INITIALIZED); + hres = IActiveScriptParse_InitNew(parse); + ok(hres == S_OK, "InitNew failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hres == S_OK, "SetScriptState failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_CONNECTED); + + deadcode_probe_count = 0; + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"If False Then\ndeadVar = 1\nEnd If\n", + L"deadcodeItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + trace("deadcode_probe_count = %d\n", deadcode_probe_count); + ok(deadcode_probe_count == 0, "host probed for deadVar assigned in dead code (count=%d)\n", + deadcode_probe_count); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hres = IActiveScript_Close(script); + ok(hres == S_OK, "Close failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScriptParse_Release(parse); + ref = IActiveScript_Release(script); + ok(!ref, "ref = %ld\n", ref); +} + +static void test_named_item_deferred_probe(void) +{ + IActiveScriptParse *parse; + IActiveScript *script; + HRESULT hres; + LONG ref; + + /* The host is probed for a declared name when the named-item code runs, + * not at parse: a Dim parsed while the engine is only initialized is not + * probed until the deferred code executes on connect. */ + + script = create_vbscript(); + hres = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parse); + ok(hres == S_OK, "Could not get IActiveScriptParse: %08lx\n", hres); + + SET_EXPECT(GetLCID); + hres = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hres == S_OK, "SetScriptSite failed: %08lx\n", hres); + CHECK_CALLED(GetLCID); + + hres = IActiveScript_AddNamedItem(script, L"deadcodeItem", SCRIPTITEM_ISVISIBLE); + ok(hres == S_OK, "AddNamedItem failed: %08lx\n", hres); + + SET_EXPECT(OnStateChange_INITIALIZED); + hres = IActiveScriptParse_InitNew(parse); + ok(hres == S_OK, "InitNew failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_INITIALIZED); + + deadcode_probe_count = 0; + hres = IActiveScriptParse_ParseScriptText(parse, L"Dim deadVar\n", L"deadcodeItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + ok(deadcode_probe_count == 0, "host probed at parse before execution (count=%d)\n", deadcode_probe_count); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + SET_EXPECT(OnStateChange_CONNECTED); + hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hres == S_OK, "SetScriptState failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + CHECK_CALLED(OnStateChange_CONNECTED); + todo_wine ok(deadcode_probe_count == 1, "host not probed when the deferred code ran (count=%d)\n", deadcode_probe_count); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hres = IActiveScript_Close(script); + ok(hres == S_OK, "Close failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScriptParse_Release(parse); + ref = IActiveScript_Release(script); + ok(!ref, "ref = %ld\n", ref); +} + static void test_const_at_top_level(void) { IActiveScriptParse *parse; @@ -3296,6 +3582,198 @@ static void test_cross_parse_name_redef(void) cross_parse_err (func, cls, 1041); } +static void test_named_item_dim_first_use_no_double_fetch(void) +{ + IActiveScriptParse *parse; + IActiveScript *script; + HRESULT hres; + LONG ref; + + script = create_vbscript(); + + hres = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parse); + ok(hres == S_OK, "Could not get IActiveScriptParse: %08lx\n", hres); + + SET_EXPECT(GetLCID); + hres = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hres == S_OK, "SetScriptSite failed: %08lx\n", hres); + CHECK_CALLED(GetLCID); + + hres = IActiveScript_AddNamedItem(script, L"visibleItem", SCRIPTITEM_ISVISIBLE); + ok(hres == S_OK, "AddNamedItem failed: %08lx\n", hres); + + SET_EXPECT(OnStateChange_INITIALIZED); + hres = IActiveScriptParse_InitNew(parse); + ok(hres == S_OK, "InitNew failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hres == S_OK, "SetScriptState(SCRIPTSTATE_CONNECTED) failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_CONNECTED); + + SET_EXPECT_MULTI(GetItemInfo_visible, 2); + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetIDsOfNames_visible); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"dim foo\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(OnLeaveScript); + ok(called_GetItemInfo_visible == 1, + "GetItemInfo_visible called %d times, expected 1\n", called_GetItemInfo_visible); + expect_GetItemInfo_visible = 0; + called_GetItemInfo_visible = 0; + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hres = IActiveScript_Close(script); + ok(hres == S_OK, "Close failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScriptParse_Release(parse); + ref = IActiveScript_Release(script); + ok(!ref, "ref = %ld\n", ref); +} + +static void test_named_item_dim_two_slot_rotating(void) +{ + IActiveScriptParse *parse; + IActiveScript *script; + HRESULT hres; + LONG ref; + + rotating_get_item_idx = 0; + + script = create_vbscript(); + hres = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parse); + ok(hres == S_OK, "Could not get IActiveScriptParse: %08lx\n", hres); + + SET_EXPECT(GetLCID); + hres = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hres == S_OK, "SetScriptSite failed: %08lx\n", hres); + CHECK_CALLED(GetLCID); + + hres = IActiveScript_AddNamedItem(script, L"rotatingItem", SCRIPTITEM_ISVISIBLE); + ok(hres == S_OK, "AddNamedItem failed: %08lx\n", hres); + + SET_EXPECT(OnStateChange_INITIALIZED); + hres = IActiveScriptParse_InitNew(parse); + ok(hres == S_OK, "InitNew failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hres == S_OK, "SetScriptState failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_CONNECTED); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetItemInfo_rotating); + SET_EXPECT(testCall_rotating_a); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"rotatingItem.testCall\n", NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(GetItemInfo_rotating); + CHECK_CALLED(testCall_rotating_a); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(GetItemInfo_rotating); + SET_EXPECT(GetIDsOfNames_rotating); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"dim foo\n", L"rotatingItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + todo_wine CHECK_CALLED(GetItemInfo_rotating); + todo_wine CHECK_CALLED(GetIDsOfNames_rotating); + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(testCall_rotating_a); + SET_EXPECT(testCall_rotating_b); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"rotatingItem.testCall\n", NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + ok(called_testCall_rotating_a, "expected testCall on the runtime-cached (first) dispatch\n"); + ok(!called_testCall_rotating_b, "wine routed testCall to the dim-refetched dispatch\n"); + expect_testCall_rotating_a = 0; called_testCall_rotating_a = 0; + expect_testCall_rotating_b = 0; called_testCall_rotating_b = 0; + CHECK_CALLED(OnLeaveScript); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hres = IActiveScript_Close(script); + ok(hres == S_OK, "Close failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScriptParse_Release(parse); + ref = IActiveScript_Release(script); + ok(!ref, "ref = %ld\n", ref); +} + +static void test_named_item_globalmembers_dim_no_refetch(void) +{ + IActiveScriptParse *parse; + IActiveScript *script; + HRESULT hres; + LONG ref; + + script = create_vbscript(); + + hres = IActiveScript_QueryInterface(script, &IID_IActiveScriptParse, (void**)&parse); + ok(hres == S_OK, "Could not get IActiveScriptParse: %08lx\n", hres); + + SET_EXPECT(GetLCID); + hres = IActiveScript_SetScriptSite(script, &ActiveScriptSite); + ok(hres == S_OK, "SetScriptSite failed: %08lx\n", hres); + CHECK_CALLED(GetLCID); + + SET_EXPECT(GetItemInfo_global); + hres = IActiveScript_AddNamedItem(script, L"globalItem", SCRIPTITEM_GLOBALMEMBERS); + ok(hres == S_OK, "AddNamedItem failed: %08lx\n", hres); + CHECK_CALLED(GetItemInfo_global); + + SET_EXPECT(OnStateChange_INITIALIZED); + hres = IActiveScriptParse_InitNew(parse); + ok(hres == S_OK, "InitNew failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_INITIALIZED); + + SET_EXPECT(OnStateChange_CONNECTED); + hres = IActiveScript_SetScriptState(script, SCRIPTSTATE_CONNECTED); + ok(hres == S_OK, "SetScriptState failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_CONNECTED); + + SET_EXPECT(OnEnterScript); + SET_EXPECT(OnLeaveScript); + hres = IActiveScriptParse_ParseScriptText(parse, L"dim foo\n", L"globalItem", NULL, NULL, 0, 0, 0, NULL, NULL); + ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); + CHECK_CALLED(OnEnterScript); + CHECK_CALLED(OnLeaveScript); + ok(!called_GetItemInfo_global, "GetItemInfo refetched on GLOBALMEMBERS dim parse\n"); + + SET_EXPECT(OnStateChange_DISCONNECTED); + SET_EXPECT(OnStateChange_INITIALIZED); + SET_EXPECT(OnStateChange_CLOSED); + hres = IActiveScript_Close(script); + ok(hres == S_OK, "Close failed: %08lx\n", hres); + CHECK_CALLED(OnStateChange_DISCONNECTED); + CHECK_CALLED(OnStateChange_INITIALIZED); + CHECK_CALLED(OnStateChange_CLOSED); + + IActiveScriptParse_Release(parse); + ref = IActiveScript_Release(script); + ok(!ref, "ref = %ld\n", ref); +} + static void test_RegExp(void) { IRegExp2 *regexp; @@ -3521,8 +3999,13 @@ START_TEST(vbscript) test_named_item_sub_shadowing(); test_named_item_dim_shadowing(); test_named_item_no_dim_routes_to_host(); + test_named_item_dead_code_probe(); + test_named_item_deferred_probe(); test_const_at_top_level(); test_cross_parse_name_redef(); + test_named_item_dim_first_use_no_double_fetch(); + test_named_item_dim_two_slot_rotating(); + test_named_item_globalmembers_dim_no_refetch(); test_scriptdisp(); test_code_persistence(); test_script_typeinfo(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10393
From: Francis De Brabandere <francisdb@gmail.com> When a visible named item's code runs, native probes the host IDispatch (GetItemInfo and GetIDsOfNames) for each top-level declared name (Subs and Functions, Classes, Dims), in that order, and caches the result. Do the same in exec_global_code, and keep the probe dispatch slot separate from the runtime cache so a Dim no longer overwrites it and misroutes later qualified access against a host that returns a different dispatch on a later GetItemInfo. Implicit names from bare assignments are not probed here: native probes those only when the assignment executes, which the runtime identifier lookup already handles, so an assignment that never runs is not probed. --- dlls/vbscript/interp.c | 38 +++++-- dlls/vbscript/tests/vbscript.c | 28 +++--- dlls/vbscript/vbscript.c | 175 +++++++++++++++++++++++++++++++-- dlls/vbscript/vbscript.h | 18 ++++ 4 files changed, 230 insertions(+), 29 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 8f1688229a1..29526e68edf 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -208,15 +208,37 @@ static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, vbdisp_invoke_type_ return S_OK; } - if(ctx->func->code_ctx->named_item && ctx->func->code_ctx->named_item->disp && + if(ctx->func->code_ctx->named_item && !(ctx->func->code_ctx->named_item->flags & SCRIPTITEM_CODEONLY)) { - hres = disp_get_id(ctx->func->code_ctx->named_item->disp, name, invoke_type, TRUE, &id); - if(SUCCEEDED(hres)) { - ref->type = REF_DISP; - ref->u.d.disp = ctx->func->code_ctx->named_item->disp; - ref->u.d.id = id; - return S_OK; + named_item_t *ni = ctx->func->code_ctx->named_item; + BOOL cached = FALSE; + DISPID cached_id = lookup_probed_name(ni, name, &cached); + + if(cached) { + /* Probe ran for this name during parse. Reuse the cached + * dispid (or skip the host route if the host didn't claim + * it) instead of issuing another GetIDsOfNames. */ + if(cached_id != DISPID_UNKNOWN) { + ensure_named_item_disp(ctx->script, ni); + if(ni->disp) { + ref->type = REF_DISP; + ref->u.d.disp = ni->disp; + ref->u.d.id = cached_id; + return S_OK; + } + } + }else { + ensure_named_item_disp(ctx->script, ni); + if(ni->disp) { + hres = disp_get_id(ni->disp, name, invoke_type, TRUE, &id); + if(SUCCEEDED(hres)) { + ref->type = REF_DISP; + ref->u.d.disp = ni->disp; + ref->u.d.id = id; + return S_OK; + } + } } } @@ -1930,6 +1952,8 @@ static HRESULT interp_me(exec_ctx_t *ctx) if(ctx->vbthis) { disp = (IDispatch*)&ctx->vbthis->IDispatchEx_iface; }else if(ctx->code->named_item) { + if(!(ctx->code->named_item->flags & SCRIPTITEM_CODEONLY)) + ensure_named_item_disp(ctx->script, ctx->code->named_item); disp = (ctx->code->named_item->flags & SCRIPTITEM_CODEONLY) ? (IDispatch*)&ctx->code->named_item->script_obj->IDispatchEx_iface : ctx->code->named_item->disp; diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index a9f4ba8d611..850555ec818 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -2510,8 +2510,8 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"dim abc\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetItemInfo_visible); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetItemInfo_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); SET_EXPECT(OnLeaveScript); @@ -2542,7 +2542,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_a, probe_b, probe_c\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED_MULTI(GetIDsOfNames_visible, 3); + CHECK_CALLED_MULTI(GetIDsOfNames_visible, 3); CHECK_CALLED(OnLeaveScript); /* Probe: Dim with explicit array bounds. */ @@ -2552,7 +2552,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_arr(5)\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); @@ -2561,7 +2561,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"sub probe_sub\ndim probe_local\nend sub\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); @@ -2570,7 +2570,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"class probe_cls\npublic probe_prop\nend class\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); @@ -2579,7 +2579,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"sub probe_named_sub\nend sub\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); @@ -2588,7 +2588,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"function probe_fn\nend function\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); @@ -2597,7 +2597,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_top\nsub probe_s3\nend sub\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED_MULTI(GetIDsOfNames_visible, 2); + CHECK_CALLED_MULTI(GetIDsOfNames_visible, 2); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); @@ -2606,7 +2606,7 @@ static void test_named_items(void) hres = IActiveScriptParse_ParseScriptText(parse, L"dim probe_second\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); @@ -3350,7 +3350,7 @@ static void test_named_item_deferred_probe(void) CHECK_CALLED(OnEnterScript); CHECK_CALLED(OnLeaveScript); CHECK_CALLED(OnStateChange_CONNECTED); - todo_wine ok(deadcode_probe_count == 1, "host not probed when the deferred code ran (count=%d)\n", deadcode_probe_count); + ok(deadcode_probe_count == 1, "host not probed when the deferred code ran (count=%d)\n", deadcode_probe_count); SET_EXPECT(OnStateChange_DISCONNECTED); SET_EXPECT(OnStateChange_INITIALIZED); @@ -3619,7 +3619,7 @@ static void test_named_item_dim_first_use_no_double_fetch(void) hres = IActiveScriptParse_ParseScriptText(parse, L"dim foo\n", L"visibleItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetIDsOfNames_visible); + CHECK_CALLED(GetIDsOfNames_visible); CHECK_CALLED(OnLeaveScript); ok(called_GetItemInfo_visible == 1, "GetItemInfo_visible called %d times, expected 1\n", called_GetItemInfo_visible); @@ -3689,8 +3689,8 @@ static void test_named_item_dim_two_slot_rotating(void) hres = IActiveScriptParse_ParseScriptText(parse, L"dim foo\n", L"rotatingItem", NULL, NULL, 0, 0, 0, NULL, NULL); ok(hres == S_OK, "ParseScriptText failed: %08lx\n", hres); CHECK_CALLED(OnEnterScript); - todo_wine CHECK_CALLED(GetItemInfo_rotating); - todo_wine CHECK_CALLED(GetIDsOfNames_rotating); + CHECK_CALLED(GetItemInfo_rotating); + CHECK_CALLED(GetIDsOfNames_rotating); CHECK_CALLED(OnLeaveScript); SET_EXPECT(OnEnterScript); diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index ff28b3b17ef..db5cc774bae 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -93,6 +93,83 @@ static inline BOOL is_started(VBScript *This) || This->state == SCRIPTSTATE_DISCONNECTED; } +static HRESULT retrieve_named_item_disp(IActiveScriptSite *site, named_item_t *item); +static HRESULT fetch_named_item_disp(IActiveScriptSite *site, const WCHAR *name, IDispatch **out); + +HRESULT ensure_named_item_disp(script_ctx_t *ctx, named_item_t *item) +{ + if(item->disp || (item->flags & SCRIPTITEM_CODEONLY)) + return S_OK; + /* If the dim-probe already fetched a host dispatch for this item, reuse + * it as the runtime cache instead of issuing a second GetItemInfo. + * Native VBScript shares the dispatch when the probe ran first; only + * when runtime use precedes the probe do the two slots diverge (the + * rotating-dispatch case). */ + if(item->dim_probe_disp) { + IDispatch_AddRef(item->dim_probe_disp); + item->disp = item->dim_probe_disp; + return S_OK; + } + return retrieve_named_item_disp(ctx->site, item); +} + +static probed_name_t *find_probed_name(named_item_t *item, const WCHAR *name) +{ + unsigned i; + for(i = 0; i < item->probed_names_cnt; i++) + if(!vbs_wcsicmp(item->probed_names[i].name, name)) + return &item->probed_names[i]; + return NULL; +} + +/* Probe `name` against the named-item's probe IDispatch unless we've + * already probed it. Native VBScript only probes a given name once per + * named item across the script lifetime; the dispid is cached so later + * runtime lookups don't reissue GetIDsOfNames. */ +static void probe_name_once(named_item_t *item, const WCHAR *name) +{ + probed_name_t *new_arr; + BSTR bstr; + DISPID id; + HRESULT hres; + + if(find_probed_name(item, name)) return; + if(item->probed_names_cnt == item->probed_names_size) { + unsigned new_size = item->probed_names_size ? item->probed_names_size * 2 : 8; + new_arr = realloc(item->probed_names, new_size * sizeof(*new_arr)); + if(!new_arr) return; + item->probed_names = new_arr; + item->probed_names_size = new_size; + } + item->probed_names[item->probed_names_cnt].name = wcsdup(name); + if(!item->probed_names[item->probed_names_cnt].name) return; + item->probed_names[item->probed_names_cnt].dispid = DISPID_UNKNOWN; + + bstr = SysAllocString(name); + if(bstr) { + hres = disp_get_id(item->dim_probe_disp, bstr, VBDISP_CALLGET, TRUE, &id); + if(SUCCEEDED(hres)) + item->probed_names[item->probed_names_cnt].dispid = id; + SysFreeString(bstr); + } + item->probed_names_cnt++; +} + +/* Look up `name` in the named-item's probe cache. Returns the cached + * dispid, or DISPID_UNKNOWN if the name was probed and the host did not + * claim it. Returns DISPID_UNKNOWN and sets *cached to FALSE if the name + * has not been probed. */ +DISPID lookup_probed_name(named_item_t *item, const WCHAR *name, BOOL *cached) +{ + probed_name_t *p = find_probed_name(item, name); + if(!p) { + *cached = FALSE; + return DISPID_UNKNOWN; + } + *cached = TRUE; + return p->dispid; +} + HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res, BOOL extern_caller) { ScriptDisp *obj = ctx->script_obj; @@ -138,6 +215,57 @@ HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res, BOOL obj->global_funcs_size = cnt; } + /* For visible named items, native VBScript fetches a dedicated host + IDispatch via GetItemInfo on the first top-level declaration parse + and probes each top-level name on it. Native order is: + (1) Subs/Functions in declaration order + (2) Classes in declaration order + (3) Explicit Dim variables in declaration order + (4) Implicit declarations from top-level assignments (LHS), in + source order, deduplicated against the names above. + The probe IDispatch is independent of the runtime cache + (named_item->disp) so subsequent qualified access keeps using the + runtime cache. Probe results are never used to skip name creation; + declarations always succeed. */ + if(code->named_item + && (code->named_item->flags & SCRIPTITEM_ISVISIBLE) + && !(code->named_item->flags & (SCRIPTITEM_CODEONLY | SCRIPTITEM_GLOBALMEMBERS))) + { + if(code->main_code.var_cnt || code->funcs || code->classes) + { + if(!code->named_item->dim_disp_probed) { + fetch_named_item_disp(ctx->site, code->named_item->name, &code->named_item->dim_probe_disp); + code->named_item->dim_disp_probed = TRUE; + } + + if(code->named_item->dim_probe_disp) { + class_desc_t *class_iter; + class_desc_t **classes_ordered = NULL; + unsigned class_n = 0, k; + + /* code->funcs is in declaration order (head-first). */ + for (func_iter = code->funcs; func_iter; func_iter = func_iter->next) + probe_name_once(code->named_item, func_iter->name); + /* code->classes is built by prepend, so iterating head-first + * gives reverse-declaration order. Reverse via local array. */ + for (class_iter = code->classes; class_iter; class_iter = class_iter->next) class_n++; + if (class_n) { + classes_ordered = malloc(class_n * sizeof(*classes_ordered)); + if (classes_ordered) { + k = class_n; + for (class_iter = code->classes; class_iter; class_iter = class_iter->next) + classes_ordered[--k] = class_iter; + for (k = 0; k < class_n; k++) + probe_name_once(code->named_item, classes_ordered[k]->name); + } + } + for (i = 0; i < code->main_code.var_cnt; i++) + probe_name_once(code->named_item, code->main_code.vars[i].name); + free(classes_ordered); + } + } + } + for (i = 0; i < code->main_code.var_cnt; i++) { if (script_disp_find_var(obj, code->main_code.vars[i].name)) @@ -216,25 +344,32 @@ static void exec_queued_code(script_ctx_t *ctx) } } -static HRESULT retrieve_named_item_disp(IActiveScriptSite *site, named_item_t *item) +static HRESULT fetch_named_item_disp(IActiveScriptSite *site, const WCHAR *name, IDispatch **out) { IUnknown *unk; HRESULT hres; - hres = IActiveScriptSite_GetItemInfo(site, item->name, SCRIPTINFO_IUNKNOWN, &unk, NULL); + *out = NULL; + if(!site) + return E_UNEXPECTED; + + hres = IActiveScriptSite_GetItemInfo(site, name, SCRIPTINFO_IUNKNOWN, &unk, NULL); if(FAILED(hres)) { WARN("GetItemInfo failed: %08lx\n", hres); return hres; } - hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&item->disp); + hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)out); IUnknown_Release(unk); - if(FAILED(hres)) { + if(FAILED(hres)) WARN("object does not implement IDispatch\n"); - return hres; - } - return S_OK; + return hres; +} + +static HRESULT retrieve_named_item_disp(IActiveScriptSite *site, named_item_t *item) +{ + return fetch_named_item_disp(site, item->name, &item->disp); } named_item_t *lookup_named_item(script_ctx_t *ctx, const WCHAR *name, unsigned flags) @@ -249,7 +384,7 @@ named_item_t *lookup_named_item(script_ctx_t *ctx, const WCHAR *name, unsigned f if(FAILED(hres)) return NULL; } - if(!item->disp && (flags || !(item->flags & SCRIPTITEM_CODEONLY))) { + if(!item->disp && flags) { hres = retrieve_named_item_disp(ctx->site, item); if(FAILED(hres)) continue; } @@ -272,8 +407,12 @@ static void release_named_item_script_obj(named_item_t *item) void release_named_item(named_item_t *item) { + unsigned i; if(--item->ref) return; + for(i = 0; i < item->probed_names_cnt; i++) + free(item->probed_names[i].name); + free(item->probed_names); free(item->name); free(item); } @@ -308,6 +447,21 @@ static void release_script(script_ctx_t *ctx) IDispatch_Release(item->disp); item->disp = NULL; } + if(item->dim_probe_disp) + { + IDispatch_Release(item->dim_probe_disp); + item->dim_probe_disp = NULL; + } + item->dim_disp_probed = FALSE; + { + unsigned pi; + for(pi = 0; pi < item->probed_names_cnt; pi++) + free(item->probed_names[pi].name); + free(item->probed_names); + item->probed_names = NULL; + item->probed_names_cnt = 0; + item->probed_names_size = 0; + } release_named_item_script_obj(item); if(!(item->flags & SCRIPTITEM_ISPERSISTENT)) { @@ -867,8 +1021,13 @@ static HRESULT WINAPI VBScript_AddNamedItem(IActiveScript *iface, LPCOLESTR pstr item->ref = 1; item->disp = disp; + item->dim_probe_disp = NULL; item->flags = dwFlags; item->script_obj = NULL; + item->dim_disp_probed = FALSE; + item->probed_names = NULL; + item->probed_names_cnt = 0; + item->probed_names_size = 0; item->name = wcsdup(pstrName); if(!item->name) { if(disp) diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 7e902badb0f..c51a0adffc8 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -157,12 +157,28 @@ typedef struct { script_ctx_t *ctx; } BuiltinDisp; +typedef struct { + WCHAR *name; + DISPID dispid; /* DISPID_UNKNOWN when the host didn't claim this name */ +} probed_name_t; + typedef struct named_item_t { ScriptDisp *script_obj; IDispatch *disp; + IDispatch *dim_probe_disp; unsigned ref; DWORD flags; LPWSTR name; + BOOL dim_disp_probed; + + /* Names already probed against dim_probe_disp, with the dispid the + * host returned (or DISPID_UNKNOWN if it didn't claim the name). + * Native VBScript only probes a given name once per named item + * across the script lifetime; later runtime lookups reuse the + * cached result instead of issuing another GetIDsOfNames. */ + probed_name_t *probed_names; + unsigned probed_names_cnt; + unsigned probed_names_size; struct list entry; } named_item_t; @@ -427,6 +443,8 @@ BOOL is_exec_local_scope(exec_ctx_t*); HRESULT exec_add_caller_dynamic_var(script_ctx_t*,exec_ctx_t*,const WCHAR*); void release_dynamic_var(dynamic_var_t*); named_item_t *lookup_named_item(script_ctx_t*,const WCHAR*,unsigned); +HRESULT ensure_named_item_disp(script_ctx_t*,named_item_t*); +DISPID lookup_probed_name(named_item_t*,const WCHAR*,BOOL*); void release_named_item(named_item_t*); void clear_error_loc(script_ctx_t*); void clear_ei(EXCEPINFO*); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10393
On Wed May 27 12:47:15 2026 +0000, Francis De Brabandere wrote:
changed this line in [version 30 of the diff](/wine/wine/-/merge_requests/10393/diffs?diff_id=270704&start_sha=441d96c7bc12cf3a7d7f21a88545edad3fe7badc#afa4d099c115eb14fae6d341ac23582cd814375f_278_264) Dropped the bytecode scan: the probe pass now only walks declared names (Subs/Functions, Classes, Dims), and implicit names from bare assignments are left to the runtime identifier lookup, which probes them only when they actually execute. I also added the test you asked for - a name assigned only in a never-taken branch isn't probed (native doesn't either), plus a deferred-parse test confirming the probe fires when the code runs, not at parse.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10393#note_141429
@jacek I have a feeling this whole MR is not going anywhere useful. Agree to close it? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10393#note_142433
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)