Skip to content

Commit 974bdb1

Browse files
authored
Add the ability to load a new database file into an existing cache (#16)
* Add the ability to load a new database file into an existing cache * Add comment about add_database possibly invalidating iterators in namespace_members
1 parent 66f3fcd commit 974bdb1

File tree

3 files changed

+149
-28
lines changed

3 files changed

+149
-28
lines changed

src/impl/winmd_reader/cache.h

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,34 +35,7 @@ namespace winmd::reader
3535
{
3636
for (auto&&[name, type] : members.types)
3737
{
38-
switch (get_category(type))
39-
{
40-
case category::interface_type:
41-
members.interfaces.push_back(type);
42-
continue;
43-
case category::class_type:
44-
if (extends_type(type, "System"sv, "Attribute"sv))
45-
{
46-
members.attributes.push_back(type);
47-
continue;
48-
}
49-
members.classes.push_back(type);
50-
continue;
51-
case category::enum_type:
52-
members.enums.push_back(type);
53-
continue;
54-
case category::struct_type:
55-
if (get_attribute(type, "Windows.Foundation.Metadata"sv, "ApiContractAttribute"sv))
56-
{
57-
members.contracts.push_back(type);
58-
continue;
59-
}
60-
members.structs.push_back(type);
61-
continue;
62-
case category::delegate_type:
63-
members.delegates.push_back(type);
64-
continue;
65-
}
38+
add_type_to_members(type, members);
6639
}
6740
}
6841
}
@@ -165,6 +138,32 @@ namespace winmd::reader
165138
remove(members.delegates, name);
166139
}
167140

141+
// This won't invalidate any existing database or row_base (e.g. TypeDef) instances
142+
// However, it may invalidate iterators and references to namespace_members, because those are stored in std::vector
143+
void add_database(std::string_view const& file)
144+
{
145+
auto& db = m_databases.emplace_back(file, this);
146+
for (auto&& type : db.TypeDef)
147+
{
148+
if (type.Flags().value == 0 || is_nested(type))
149+
{
150+
continue;
151+
}
152+
153+
auto& ns = m_namespaces[type.TypeNamespace()];
154+
auto[iter, inserted] = ns.types.try_emplace(type.TypeName(), type);
155+
if (inserted)
156+
{
157+
add_type_to_members(type, ns);
158+
}
159+
}
160+
161+
for (auto&& row : db.NestedClass)
162+
{
163+
m_nested_types[row.EnclosingType()].push_back(row.NestedType());
164+
}
165+
}
166+
168167
std::vector<TypeDef> const& nested_types(TypeDef const& enclosing_type) const
169168
{
170169
auto it = m_nested_types.find(enclosing_type);
@@ -195,6 +194,38 @@ namespace winmd::reader
195194

196195
private:
197196

197+
void add_type_to_members(TypeDef const& type, namespace_members& members)
198+
{
199+
switch (get_category(type))
200+
{
201+
case category::interface_type:
202+
members.interfaces.push_back(type);
203+
return;
204+
case category::class_type:
205+
if (extends_type(type, "System"sv, "Attribute"sv))
206+
{
207+
members.attributes.push_back(type);
208+
return;
209+
}
210+
members.classes.push_back(type);
211+
return;
212+
case category::enum_type:
213+
members.enums.push_back(type);
214+
return;
215+
case category::struct_type:
216+
if (get_attribute(type, "Windows.Foundation.Metadata"sv, "ApiContractAttribute"sv))
217+
{
218+
members.contracts.push_back(type);
219+
return;
220+
}
221+
members.structs.push_back(type);
222+
return;
223+
case category::delegate_type:
224+
members.delegates.push_back(type);
225+
return;
226+
}
227+
}
228+
198229
std::list<database> m_databases;
199230
std::map<std::string_view, namespace_members> m_namespaces;
200231
std::map<TypeDef, std::vector<TypeDef>> m_nested_types;

test/cache.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "pch.h"
2+
#include <winmd_reader.h>
3+
4+
using namespace winmd::reader;
5+
6+
TEST_CASE("cache_add_invalidate")
7+
{
8+
std::array<char, 260> local{};
9+
10+
#ifdef _WIN64
11+
ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
12+
#else
13+
ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
14+
#endif
15+
16+
std::filesystem::path winmd_dir = local.data();
17+
auto file_path = winmd_dir;
18+
file_path.append("Windows.Foundation.winmd");
19+
20+
cache c(file_path.string());
21+
22+
// Get a type and a database and verify that neither are invalidated by adding a new db
23+
TypeDef IStringable = c.find("Windows.Foundation", "IStringable");
24+
auto const db = &(c.databases().front());
25+
26+
file_path = winmd_dir;
27+
file_path.append("Windows.Data.winmd");
28+
c.add_database(file_path.string());
29+
30+
TypeDef IStringable2 = c.find("Windows.Foundation", "IStringable");
31+
REQUIRE(IStringable == IStringable2);
32+
33+
auto const db2 = &(c.databases().front());
34+
REQUIRE(db == db2);
35+
}
36+
37+
TEST_CASE("cache_add")
38+
{
39+
std::array<char, 260> local{};
40+
41+
#ifdef _WIN64
42+
ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
43+
#else
44+
ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
45+
#endif
46+
47+
std::filesystem::path winmd_dir = local.data();
48+
auto file_path = winmd_dir;
49+
file_path.append("Windows.Foundation.winmd");
50+
51+
cache c(file_path.string());
52+
53+
TypeDef JsonValue = c.find("Windows.Data.Json", "JsonValue");
54+
REQUIRE(!JsonValue);
55+
56+
file_path = winmd_dir;
57+
file_path.append("Windows.Data.winmd");
58+
c.add_database(file_path.string());
59+
60+
JsonValue = c.find("Windows.Data.Json", "JsonValue");
61+
REQUIRE(!!JsonValue);
62+
REQUIRE(JsonValue.TypeName() == "JsonValue");
63+
REQUIRE(JsonValue.TypeNamespace() == "Windows.Data.Json");
64+
}
65+
66+
TEST_CASE("cache_add_duplicate")
67+
{
68+
std::array<char, 260> local{};
69+
70+
#ifdef _WIN64
71+
ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
72+
#else
73+
ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
74+
#endif
75+
76+
std::filesystem::path winmd_dir = local.data();
77+
auto file_path = winmd_dir;
78+
file_path.append("Windows.Foundation.winmd");
79+
80+
cache c(file_path.string());
81+
82+
TypeDef IStringable = c.find("Windows.Foundation", "IStringable");
83+
84+
// Add a winmd with duplicate types, and verify the original types aren't invalidated.
85+
c.add_database(file_path.string());
86+
87+
TypeDef IStringable2 = c.find("Windows.Foundation", "IStringable");
88+
REQUIRE(IStringable == IStringable2);
89+
}

test/winmd.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
</ProjectConfiguration>
2020
</ItemGroup>
2121
<ItemGroup>
22+
<ClCompile Include="cache.cpp" />
2223
<ClCompile Include="database.cpp" />
2324
<ClCompile Include="main.cpp">
2425
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>

0 commit comments

Comments
 (0)