Skip to content

Commit

Permalink
Add the ability to load a new database file into an existing cache (#16)
Browse files Browse the repository at this point in the history
* Add the ability to load a new database file into an existing cache

* Add comment about add_database possibly invalidating iterators in namespace_members
  • Loading branch information
DefaultRyan authored Feb 25, 2021
1 parent 66f3fcd commit 974bdb1
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 28 deletions.
87 changes: 59 additions & 28 deletions src/impl/winmd_reader/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,7 @@ namespace winmd::reader
{
for (auto&&[name, type] : members.types)
{
switch (get_category(type))
{
case category::interface_type:
members.interfaces.push_back(type);
continue;
case category::class_type:
if (extends_type(type, "System"sv, "Attribute"sv))
{
members.attributes.push_back(type);
continue;
}
members.classes.push_back(type);
continue;
case category::enum_type:
members.enums.push_back(type);
continue;
case category::struct_type:
if (get_attribute(type, "Windows.Foundation.Metadata"sv, "ApiContractAttribute"sv))
{
members.contracts.push_back(type);
continue;
}
members.structs.push_back(type);
continue;
case category::delegate_type:
members.delegates.push_back(type);
continue;
}
add_type_to_members(type, members);
}
}
}
Expand Down Expand Up @@ -165,6 +138,32 @@ namespace winmd::reader
remove(members.delegates, name);
}

// This won't invalidate any existing database or row_base (e.g. TypeDef) instances
// However, it may invalidate iterators and references to namespace_members, because those are stored in std::vector
void add_database(std::string_view const& file)
{
auto& db = m_databases.emplace_back(file, this);
for (auto&& type : db.TypeDef)
{
if (type.Flags().value == 0 || is_nested(type))
{
continue;
}

auto& ns = m_namespaces[type.TypeNamespace()];
auto[iter, inserted] = ns.types.try_emplace(type.TypeName(), type);
if (inserted)
{
add_type_to_members(type, ns);
}
}

for (auto&& row : db.NestedClass)
{
m_nested_types[row.EnclosingType()].push_back(row.NestedType());
}
}

std::vector<TypeDef> const& nested_types(TypeDef const& enclosing_type) const
{
auto it = m_nested_types.find(enclosing_type);
Expand Down Expand Up @@ -195,6 +194,38 @@ namespace winmd::reader

private:

void add_type_to_members(TypeDef const& type, namespace_members& members)
{
switch (get_category(type))
{
case category::interface_type:
members.interfaces.push_back(type);
return;
case category::class_type:
if (extends_type(type, "System"sv, "Attribute"sv))
{
members.attributes.push_back(type);
return;
}
members.classes.push_back(type);
return;
case category::enum_type:
members.enums.push_back(type);
return;
case category::struct_type:
if (get_attribute(type, "Windows.Foundation.Metadata"sv, "ApiContractAttribute"sv))
{
members.contracts.push_back(type);
return;
}
members.structs.push_back(type);
return;
case category::delegate_type:
members.delegates.push_back(type);
return;
}
}

std::list<database> m_databases;
std::map<std::string_view, namespace_members> m_namespaces;
std::map<TypeDef, std::vector<TypeDef>> m_nested_types;
Expand Down
89 changes: 89 additions & 0 deletions test/cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include "pch.h"
#include <winmd_reader.h>

using namespace winmd::reader;

TEST_CASE("cache_add_invalidate")
{
std::array<char, 260> local{};

#ifdef _WIN64
ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
#else
ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
#endif

std::filesystem::path winmd_dir = local.data();
auto file_path = winmd_dir;
file_path.append("Windows.Foundation.winmd");

cache c(file_path.string());

// Get a type and a database and verify that neither are invalidated by adding a new db
TypeDef IStringable = c.find("Windows.Foundation", "IStringable");
auto const db = &(c.databases().front());

file_path = winmd_dir;
file_path.append("Windows.Data.winmd");
c.add_database(file_path.string());

TypeDef IStringable2 = c.find("Windows.Foundation", "IStringable");
REQUIRE(IStringable == IStringable2);

auto const db2 = &(c.databases().front());
REQUIRE(db == db2);
}

TEST_CASE("cache_add")
{
std::array<char, 260> local{};

#ifdef _WIN64
ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
#else
ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
#endif

std::filesystem::path winmd_dir = local.data();
auto file_path = winmd_dir;
file_path.append("Windows.Foundation.winmd");

cache c(file_path.string());

TypeDef JsonValue = c.find("Windows.Data.Json", "JsonValue");
REQUIRE(!JsonValue);

file_path = winmd_dir;
file_path.append("Windows.Data.winmd");
c.add_database(file_path.string());

JsonValue = c.find("Windows.Data.Json", "JsonValue");
REQUIRE(!!JsonValue);
REQUIRE(JsonValue.TypeName() == "JsonValue");
REQUIRE(JsonValue.TypeNamespace() == "Windows.Data.Json");
}

TEST_CASE("cache_add_duplicate")
{
std::array<char, 260> local{};

#ifdef _WIN64
ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
#else
ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast<uint32_t>(local.size()));
#endif

std::filesystem::path winmd_dir = local.data();
auto file_path = winmd_dir;
file_path.append("Windows.Foundation.winmd");

cache c(file_path.string());

TypeDef IStringable = c.find("Windows.Foundation", "IStringable");

// Add a winmd with duplicate types, and verify the original types aren't invalidated.
c.add_database(file_path.string());

TypeDef IStringable2 = c.find("Windows.Foundation", "IStringable");
REQUIRE(IStringable == IStringable2);
}
1 change: 1 addition & 0 deletions test/winmd.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="cache.cpp" />
<ClCompile Include="database.cpp" />
<ClCompile Include="main.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
Expand Down

0 comments on commit 974bdb1

Please sign in to comment.