[PATCH v22 0/2] MR10393: Draft: vbscript: Check named item dispatch for Dim variable names.
When declaring variables with Dim in a visible named item context, check the named item's IDispatch for each variable name via GetIDsOfNames. This matches Windows VBScript behavior where Dim probes the host object before creating the variable. -- v22: vbscript: Probe top-level declared names against the host dispatch. vbscript/tests: Cover named-item dim-probe IDispatch fetching. https://gitlab.winehq.org/wine/wine/-/merge_requests/10393
From: Francis De Brabandere <francisdb@gmail.com> --- dlls/vbscript/tests/vbscript.c | 372 +++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index 7500ec30a8d..f0092a6603f 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -105,6 +105,12 @@ DEFINE_EXPECT(GetItemInfo_visible); DEFINE_EXPECT(GetItemInfo_visible_code); DEFINE_EXPECT(GetItemInfo_persistent); DEFINE_EXPECT(testCall); +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); @@ -301,6 +307,57 @@ static const IDispatchVtbl persistent_named_item_vtbl = { static IDispatch persistent_named_item = { &persistent_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; @@ -366,6 +423,17 @@ static HRESULT WINAPI ActiveScriptSite_GetItemInfo(IActiveScriptSite *iface, LPC *item_unk = (IUnknown*)&persistent_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; } @@ -2113,6 +2181,90 @@ static void test_named_items(void) CHECK_CALLED(OnEnterScript); CHECK_CALLED(OnLeaveScript); + /* Probe: multiple names in a single Dim statement. Each name should + * be probed against the host dispatch via GetIDsOfNames. The host + * dispatch is already cached from the earlier "dim abc" parse, so + * GetItemInfo is not refetched. */ + 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); + + /* Native probes the host with GetIDsOfNames once per top-level + * declared name, which includes bare Subs, Functions, and Classes + * (in addition to Dim variables). Inner Dims inside a Sub body are + * not probed. */ + 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); + + /* Probe: a second top-level Dim parse on the same named item. The + * host dispatch is already cached, so GetItemInfo should not fire + * a second time. */ + 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); @@ -2520,6 +2672,223 @@ static void test_named_items(void) ok(!ref, "ref = %ld\n", ref); } +static void test_named_item_dim_first_use_no_double_fetch(void) +{ + IActiveScriptParse *parse; + IActiveScript *script; + HRESULT hres; + LONG ref; + + /* When a top-level dim parse is the FIRST use of an ISVISIBLE named item, + * native VBScript fetches the host IDispatch via GetItemInfo exactly once + * (the probe-time fetch). Wine currently fetches twice: once at compile + * time via lookup_named_item and again in exec_global_code's release- + * and-refetch. */ + + 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 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; + + /* Native VBScript maintains separate IDispatch references for the dim + * probe and runtime access on an ISVISIBLE item. With a host that + * returns a different IDispatch on each GetItemInfo call, native uses + * the first returned dispatch for runtime (cached on first runtime use) + * and a second dispatch for the dim probe; subsequent qualified access + * keeps using the original runtime dispatch. Wine currently has a + * single slot, so the dim refetch overwrites the runtime cache. */ + + 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); + + /* First runtime use: GetItemInfo returns rotating_a (idx 0). */ + 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); + + /* First dim parse on the same item: native fetches a separate probe + * dispatch (rotating_b, idx 1) and probes "foo" on it. */ + 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); + + /* Second runtime access: native uses cached rotating_a (the runtime + * slot is independent of the dim probe slot). Wine routes to + * rotating_b because the dim refetch overwrote the single cached + * dispatch. */ + 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; + + /* Native VBScript does not refetch the host dispatch via GetItemInfo on + * a dim parse with a SCRIPTITEM_GLOBALMEMBERS item: the eager fetch at + * AddNamedItem time is the only GetItemInfo call. */ + + 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; @@ -2742,6 +3111,9 @@ START_TEST(vbscript) test_vbscript_initializing(); test_param_ids(); test_named_items(); + 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> --- dlls/vbscript/tests/vbscript.c | 26 +++++------ dlls/vbscript/vbscript.c | 80 ++++++++++++++++++++++++++++++---- dlls/vbscript/vbscript.h | 2 + 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/dlls/vbscript/tests/vbscript.c b/dlls/vbscript/tests/vbscript.c index f0092a6603f..1dd39d39338 100644 --- a/dlls/vbscript/tests/vbscript.c +++ b/dlls/vbscript/tests/vbscript.c @@ -2156,8 +2156,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); @@ -2191,7 +2191,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. */ @@ -2201,7 +2201,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); /* Native probes the host with GetIDsOfNames once per top-level @@ -2214,7 +2214,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); @@ -2223,7 +2223,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); @@ -2232,7 +2232,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); @@ -2241,7 +2241,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); @@ -2250,7 +2250,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); /* Probe: a second top-level Dim parse on the same named item. The @@ -2262,7 +2262,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); @@ -2715,7 +2715,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); @@ -2796,8 +2796,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); /* Second runtime access: native uses cached rotating_a (the runtime diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index ff28b3b17ef..41338ea96de 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -93,6 +93,9 @@ 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 exec_global_code(script_ctx_t *ctx, vbscode_t *code, VARIANT *res, BOOL extern_caller) { ScriptDisp *obj = ctx->script_obj; @@ -138,6 +141,52 @@ 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 declared name (Dim variables, Subs, Functions, + Classes) via GetIDsOfNames on it. The probe IDispatch is independent + of the runtime cache (named_item->disp) so subsequent qualified + access keeps using the runtime cache. The probe results are never + used to skip name creation; declarations always succeed. */ + if(code->named_item && !(code->named_item->flags & SCRIPTITEM_CODEONLY) + && (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; + IDispatch *probe = code->named_item->dim_probe_disp; + + for (i = 0; i < code->main_code.var_cnt; i++) { + BSTR name = SysAllocString(code->main_code.vars[i].name); + if(name) { + DISPID id; + disp_get_id(probe, name, VBDISP_CALLGET, TRUE, &id); + SysFreeString(name); + } + } + for (func_iter = code->funcs; func_iter; func_iter = func_iter->next) { + BSTR name = SysAllocString(func_iter->name); + if(name) { + DISPID id; + disp_get_id(probe, name, VBDISP_CALLGET, TRUE, &id); + SysFreeString(name); + } + } + for (class_iter = code->classes; class_iter; class_iter = class_iter->next) { + BSTR name = SysAllocString(class_iter->name); + if(name) { + DISPID id; + disp_get_id(probe, name, VBDISP_CALLGET, TRUE, &id); + SysFreeString(name); + } + } + } + } + for (i = 0; i < code->main_code.var_cnt; i++) { if (script_disp_find_var(obj, code->main_code.vars[i].name)) @@ -216,25 +265,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 +305,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; } @@ -308,6 +364,12 @@ 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; release_named_item_script_obj(item); if(!(item->flags & SCRIPTITEM_ISPERSISTENT)) { @@ -867,8 +929,10 @@ 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->name = wcsdup(pstrName); if(!item->name) { if(disp) diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 1420e2da0d1..c8aa84d5562 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -160,9 +160,11 @@ typedef struct { typedef struct named_item_t { ScriptDisp *script_obj; IDispatch *disp; + IDispatch *dim_probe_disp; unsigned ref; DWORD flags; LPWSTR name; + BOOL dim_disp_probed; struct list entry; } named_item_t; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10393
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)