From: Francis De Brabandere <francisdb@gmail.com> An uninitialized VBScript variable is Empty, so scripts can end up using Empty as a dictionary key alongside a numeric 0 or "". Native treats those as the same key; Wine kept Empty distinct from every other type. Match an Empty key against any numeric zero or the empty string, while keeping it distinct from Null and from non-zero/non-empty keys. --- dlls/scrrun/dictionary.c | 35 +++++++++++++++++++++++++++++++--- dlls/scrrun/tests/dictionary.c | 8 ++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/dlls/scrrun/dictionary.c b/dlls/scrrun/dictionary.c index 1b618865360..8137855b592 100644 --- a/dlls/scrrun/dictionary.c +++ b/dlls/scrrun/dictionary.c @@ -162,9 +162,38 @@ static inline BOOL numeric_key_eq(const VARIANT *key1, const VARIANT *key2) return V_R4(&v1) == V_R4(&v2); } +/* Empty matches the zero/default value of the other key's type: any numeric + * zero, the empty string, or another Empty. */ +static BOOL empty_matches_key(const VARIANT *key) +{ + VARIANT v; + + switch (V_VT(key)) + { + case VT_EMPTY: + return TRUE; + case VT_BSTR: + { + const WCHAR *str = get_key_strptr(key); + return !str || !*str; + } + default: + if (!is_numeric_key(key)) + return FALSE; + VariantInit(&v); + if (FAILED(VariantChangeType(&v, key, 0, VT_R4))) + return FALSE; + return V_R4(&v) == 0.0f; + } +} + static BOOL is_matching_key(const struct dictionary *dict, const struct keyitem_pair *pair, const VARIANT *key, DWORD hash) { - if (is_string_key(key) != is_string_key(&pair->key)) + if (V_VT(key) == VT_EMPTY || V_VT(&pair->key) == VT_EMPTY) + { + return empty_matches_key(V_VT(key) == VT_EMPTY ? &pair->key : key); + } + else if (is_string_key(key) != is_string_key(&pair->key)) { return FALSE; } @@ -188,9 +217,9 @@ static BOOL is_matching_key(const struct dictionary *dict, const struct keyitem_ { return hash == pair->hash && numeric_key_eq(key, &pair->key); } - else if (V_VT(&pair->key) == VT_EMPTY || V_VT(&pair->key) == VT_NULL) + else if (V_VT(&pair->key) == VT_NULL) { - return V_VT(&pair->key) == V_VT(key); + return V_VT(key) == VT_NULL; } else { diff --git a/dlls/scrrun/tests/dictionary.c b/dlls/scrrun/tests/dictionary.c index 8878b528381..bc5fbd878a3 100644 --- a/dlls/scrrun/tests/dictionary.c +++ b/dlls/scrrun/tests/dictionary.c @@ -1181,14 +1181,14 @@ static void test_empty_key(void) V_R4(&b) = 0.0f; else V_I4(&b) = 0; - todo_wine ok(keys_match(&a, &b), "Empty should match zero key vt %d\n", zero_keys[i]); + ok(keys_match(&a, &b), "Empty should match zero key vt %d\n", zero_keys[i]); } /* Empty matches the empty string, but not a non-empty one. */ V_VT(&a) = VT_EMPTY; V_VT(&b) = VT_BSTR; V_BSTR(&b) = SysAllocString(L""); - todo_wine ok(keys_match(&a, &b), "Empty should match empty string\n"); + ok(keys_match(&a, &b), "Empty should match empty string\n"); VariantClear(&b); V_VT(&b) = VT_BSTR; @@ -1232,10 +1232,10 @@ static void test_empty_key(void) exists = VARIANT_FALSE; hr = IDictionary_Exists(dict, &b, &exists); ok(hr == S_OK, "Exists with Empty key: %#lx.\n", hr); - todo_wine ok(exists == VARIANT_TRUE, "Empty should find I2 0, got %x\n", exists); + ok(exists == VARIANT_TRUE, "Empty should find I2 0, got %x\n", exists); hr = IDictionary_Remove(dict, &b); - todo_wine ok(hr == S_OK, "Remove with Empty key: %#lx.\n", hr); + ok(hr == S_OK, "Remove with Empty key: %#lx.\n", hr); IDictionary_Release(dict); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10960