ccls/src/serializer.cc

253 lines
7.7 KiB
C++
Raw Normal View History

#include "serializer.h"
2017-03-14 08:33:39 +00:00
#include "indexer.h"
#include <doctest/doctest.h>
namespace {
bool gTestOutputMode = false;
std::string GetBaseName(const std::string& path) {
size_t last_slash = path.find_last_of('/');
if (last_slash != std::string::npos && (last_slash + 1) < path.size())
return path.substr(last_slash + 1);
return path;
}
} // namespace
2017-02-24 08:39:25 +00:00
2017-05-19 07:02:01 +00:00
// int16_t
void Reflect(Reader& visitor, int16_t& value) {
if (visitor.IsInt())
value = (int16_t)visitor.GetInt();
}
2017-05-19 07:02:01 +00:00
void Reflect(Writer& visitor, int16_t& value) {
visitor.Int(value);
}
// int32_t
void Reflect(Reader& visitor, int32_t& value) {
if (visitor.IsInt())
value = visitor.GetInt();
2017-05-19 07:02:01 +00:00
}
void Reflect(Writer& visitor, int32_t& value) {
visitor.Int(value);
}
// int64_t
void Reflect(Reader& visitor, int64_t& value) {
if (visitor.IsInt64())
value = visitor.GetInt64();
}
void Reflect(Writer& visitor, int64_t& value) {
visitor.Int64(value);
}
2017-05-19 07:02:01 +00:00
// uint64_t
void Reflect(Reader& visitor, uint64_t& value) {
if (visitor.IsUint64())
value = visitor.GetUint64();
2017-05-19 07:02:01 +00:00
}
void Reflect(Writer& visitor, uint64_t& value) {
visitor.Uint64(value);
}
// bool
void Reflect(Reader& visitor, bool& value) {
if (visitor.IsBool())
value = visitor.GetBool();
}
void Reflect(Writer& visitor, bool& value) {
visitor.Bool(value);
}
// std::string
void Reflect(Reader& visitor, std::string& value) {
if (visitor.IsString())
value = visitor.GetString();
}
void Reflect(Writer& visitor, std::string& value) {
visitor.String(value.c_str(), (rapidjson::SizeType)value.size());
}
2017-03-08 08:09:15 +00:00
// ReflectMember
void ReflectMember(Writer& visitor, const char* name, std::string& value) {
if (value.empty())
return;
visitor.Key(name);
Reflect(visitor, value);
}
2017-05-19 07:02:01 +00:00
// TODO: Move this to indexer.cc
void Reflect(Reader& visitor, IndexInclude& value) {
REFLECT_MEMBER_START();
REFLECT_MEMBER(line);
REFLECT_MEMBER(resolved_path);
REFLECT_MEMBER_END();
}
void Reflect(Writer& visitor, IndexInclude& value) {
REFLECT_MEMBER_START();
REFLECT_MEMBER(line);
if (gTestOutputMode) {
std::string basename = GetBaseName(value.resolved_path);
if (!StartsWith(value.resolved_path, "&"))
basename = "&" + basename;
REFLECT_MEMBER2("resolved_path", basename);
} else {
REFLECT_MEMBER(resolved_path);
}
REFLECT_MEMBER_END();
}
2017-09-22 01:14:57 +00:00
template <typename TVisitor>
void Reflect(TVisitor& visitor, IndexType& value) {
REFLECT_MEMBER_START();
REFLECT_MEMBER2("id", value.id);
REFLECT_MEMBER2("usr", value.def.usr);
REFLECT_MEMBER2("short_name", value.def.short_name);
REFLECT_MEMBER2("detailed_name", value.def.detailed_name);
2017-04-05 08:29:15 +00:00
REFLECT_MEMBER2("definition_spelling", value.def.definition_spelling);
REFLECT_MEMBER2("definition_extent", value.def.definition_extent);
REFLECT_MEMBER2("alias_of", value.def.alias_of);
REFLECT_MEMBER2("parents", value.def.parents);
REFLECT_MEMBER2("derived", value.derived);
REFLECT_MEMBER2("types", value.def.types);
REFLECT_MEMBER2("funcs", value.def.funcs);
REFLECT_MEMBER2("vars", value.def.vars);
REFLECT_MEMBER2("instances", value.instances);
REFLECT_MEMBER2("uses", value.uses);
REFLECT_MEMBER_END();
2017-03-01 04:12:57 +00:00
}
2017-09-22 01:14:57 +00:00
template <typename TVisitor>
void Reflect(TVisitor& visitor, IndexFunc& value) {
REFLECT_MEMBER_START();
REFLECT_MEMBER2("id", value.id);
REFLECT_MEMBER2("usr", value.def.usr);
REFLECT_MEMBER2("short_name", value.def.short_name);
REFLECT_MEMBER2("detailed_name", value.def.detailed_name);
REFLECT_MEMBER2("declarations", value.declarations);
2017-04-05 08:29:15 +00:00
REFLECT_MEMBER2("definition_spelling", value.def.definition_spelling);
REFLECT_MEMBER2("definition_extent", value.def.definition_extent);
REFLECT_MEMBER2("declaring_type", value.def.declaring_type);
REFLECT_MEMBER2("base", value.def.base);
REFLECT_MEMBER2("derived", value.derived);
REFLECT_MEMBER2("locals", value.def.locals);
REFLECT_MEMBER2("callers", value.callers);
REFLECT_MEMBER2("callees", value.def.callees);
REFLECT_MEMBER_END();
2017-03-01 04:12:57 +00:00
}
2017-09-22 01:14:57 +00:00
template <typename TVisitor>
void Reflect(TVisitor& visitor, IndexVar& value) {
REFLECT_MEMBER_START();
REFLECT_MEMBER2("id", value.id);
REFLECT_MEMBER2("usr", value.def.usr);
REFLECT_MEMBER2("short_name", value.def.short_name);
REFLECT_MEMBER2("detailed_name", value.def.detailed_name);
REFLECT_MEMBER2("declaration", value.def.declaration);
2017-04-05 08:29:15 +00:00
REFLECT_MEMBER2("definition_spelling", value.def.definition_spelling);
REFLECT_MEMBER2("definition_extent", value.def.definition_extent);
REFLECT_MEMBER2("variable_type", value.def.variable_type);
REFLECT_MEMBER2("declaring_type", value.def.declaring_type);
REFLECT_MEMBER2("is_local", value.def.is_local);
REFLECT_MEMBER2("is_macro", value.def.is_macro);
REFLECT_MEMBER2("uses", value.uses);
REFLECT_MEMBER_END();
2017-02-28 08:37:20 +00:00
}
// IndexFile
bool ReflectMemberStart(Writer& visitor, IndexFile& value) {
auto it = value.id_cache.usr_to_type_id.find("");
if (it != value.id_cache.usr_to_type_id.end()) {
value.Resolve(it->second)->def.short_name = "<fundamental>";
assert(value.Resolve(it->second)->uses.size() == 0);
2017-03-01 04:12:57 +00:00
}
value.version = IndexFile::kCurrentVersion;
DefaultReflectMemberStart(visitor);
return true;
}
2017-09-22 01:14:57 +00:00
template <typename TVisitor>
void Reflect(TVisitor& visitor, IndexFile& value) {
REFLECT_MEMBER_START();
if (!gTestOutputMode) {
REFLECT_MEMBER(version);
REFLECT_MEMBER(last_modification_time);
2017-04-20 07:25:38 +00:00
REFLECT_MEMBER(import_file);
REFLECT_MEMBER(args);
}
2017-05-21 03:46:15 +00:00
REFLECT_MEMBER(includes);
if (!gTestOutputMode)
REFLECT_MEMBER(dependencies);
REFLECT_MEMBER(skipped_by_preprocessor);
2017-03-07 18:17:49 +00:00
REFLECT_MEMBER(types);
REFLECT_MEMBER(funcs);
REFLECT_MEMBER(vars);
REFLECT_MEMBER_END();
2017-03-01 04:12:57 +00:00
}
std::string Serialize(IndexFile& file) {
2017-03-01 04:12:57 +00:00
rapidjson::StringBuffer output;
2017-03-27 04:04:48 +00:00
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(output);
2017-09-22 01:14:57 +00:00
// Writer writer(output);
2017-03-01 04:12:57 +00:00
writer.SetFormatOptions(
2017-09-22 01:14:57 +00:00
rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
2017-03-01 04:12:57 +00:00
writer.SetIndent(' ', 2);
Reflect(writer, file);
2017-03-01 04:12:57 +00:00
return output.GetString();
}
2017-09-22 01:14:57 +00:00
std::unique_ptr<IndexFile> Deserialize(std::string path,
std::string serialized,
optional<int> expected_version) {
rapidjson::Document reader;
reader.Parse(serialized.c_str());
2017-04-03 01:34:15 +00:00
if (reader.HasParseError())
return nullptr;
// Do not deserialize a document with a bad version. Doing so could cause a
// crash because the file format may have changed.
if (expected_version) {
auto actual_version = reader.FindMember("version");
if (actual_version == reader.MemberEnd() ||
actual_version->value.GetInt() != expected_version) {
return nullptr;
}
}
2017-03-01 04:12:57 +00:00
auto file = MakeUnique<IndexFile>(path);
Reflect(reader, *file);
2017-04-19 00:05:14 +00:00
// Restore non-serialized state.
file->path = path;
file->id_cache.primary_file = file->path;
for (const auto& type : file->types) {
file->id_cache.type_id_to_usr[type.id] = type.def.usr;
file->id_cache.usr_to_type_id[type.def.usr] = type.id;
}
for (const auto& func : file->funcs) {
file->id_cache.func_id_to_usr[func.id] = func.def.usr;
file->id_cache.usr_to_func_id[func.def.usr] = func.id;
}
for (const auto& var : file->vars) {
file->id_cache.var_id_to_usr[var.id] = var.def.usr;
file->id_cache.usr_to_var_id[var.def.usr] = var.id;
}
2017-03-01 04:12:57 +00:00
return file;
}
void SetTestOutputMode() {
gTestOutputMode = true;
}
TEST_SUITE("Serializer utils") {
TEST_CASE("GetBaseName") {
REQUIRE(GetBaseName("foo.cc") == "foo.cc");
REQUIRE(GetBaseName("foo/foo.cc") == "foo.cc");
REQUIRE(GetBaseName("/foo.cc") == "foo.cc");
REQUIRE(GetBaseName("///foo.cc") == "foo.cc");
REQUIRE(GetBaseName("bar/") == "bar/");
REQUIRE(GetBaseName("foobar/bar/") ==
"foobar/bar/"); // TODO: Should be bar, but good enough.
}
}