Split global usr lookup into separate lookups for file/type/func/var.

This eliminates problems when actively editing code, ie, a USR can change from being a variable to being a type.
This commit is contained in:
Jacob Dufault 2017-05-08 21:20:28 -07:00
parent ea5400a633
commit ad0a03b0da
3 changed files with 73 additions and 100 deletions

View File

@ -181,11 +181,11 @@ void PushBack(NonElidedVector<lsLocation>* result, optional<lsLocation> location
} }
QueryFile* FindFile(QueryDatabase* db, const std::string& filename, QueryFileId* file_id) { QueryFile* FindFile(QueryDatabase* db, const std::string& filename, QueryFileId* file_id) {
auto it = db->usr_to_symbol.find(filename); auto it = db->usr_to_file.find(filename);
if (it != db->usr_to_symbol.end()) { if (it != db->usr_to_file.end()) {
optional<QueryFile>& file = db->files[it->second.idx]; optional<QueryFile>& file = db->files[it->second.id];
if (file) { if (file) {
*file_id = QueryFileId(it->second.idx); *file_id = QueryFileId(it->second.id);
return &file.value(); return &file.value();
} }
} }
@ -197,9 +197,9 @@ QueryFile* FindFile(QueryDatabase* db, const std::string& filename, QueryFileId*
QueryFile* FindFile(QueryDatabase* db, const std::string& filename) { QueryFile* FindFile(QueryDatabase* db, const std::string& filename) {
// TODO: consider calling NormalizePath here. It might add too much latency though. // TODO: consider calling NormalizePath here. It might add too much latency though.
auto it = db->usr_to_symbol.find(filename); auto it = db->usr_to_file.find(filename);
if (it != db->usr_to_symbol.end()) { if (it != db->usr_to_file.end()) {
optional<QueryFile>& file = db->files[it->second.idx]; optional<QueryFile>& file = db->files[it->second.id];
if (file) if (file)
return &file.value(); return &file.value();
} }

View File

@ -242,60 +242,46 @@ QueryFile::Def BuildFileDef(const IdMap& id_map, const IndexedFile& indexed) {
// TODO: consider having separate lookup maps so they are smaller (maybe
// lookups will go faster).
QueryFileId GetQueryFileIdFromPath(QueryDatabase* query_db, const std::string& path) { QueryFileId GetQueryFileIdFromPath(QueryDatabase* query_db, const std::string& path) {
auto it = query_db->usr_to_symbol.find(path); auto it = query_db->usr_to_file.find(path);
if (it != query_db->usr_to_symbol.end() && it->second.kind != SymbolKind::Invalid) { if (it != query_db->usr_to_file.end())
// TODO: should this be an assert? return QueryFileId(it->second.id);
if (it->second.kind == SymbolKind::File)
return QueryFileId(it->second.idx);
}
size_t idx = query_db->files.size(); size_t idx = query_db->files.size();
query_db->usr_to_symbol[path] = SymbolIdx(SymbolKind::File, idx); query_db->usr_to_file[path] = QueryFileId(idx);
query_db->files.push_back(QueryFile(path)); query_db->files.push_back(QueryFile(path));
return QueryFileId(idx); return QueryFileId(idx);
} }
QueryTypeId GetQueryTypeIdFromUsr(QueryDatabase* query_db, const Usr& usr) { QueryTypeId GetQueryTypeIdFromUsr(QueryDatabase* query_db, const Usr& usr) {
auto it = query_db->usr_to_symbol.find(usr); auto it = query_db->usr_to_type.find(usr);
if (it != query_db->usr_to_symbol.end() && it->second.kind != SymbolKind::Invalid) { if (it != query_db->usr_to_type.end())
// TODO: should this be an assert? return QueryTypeId(it->second.id);
if (it->second.kind == SymbolKind::Type)
return QueryTypeId(it->second.idx);
}
size_t idx = query_db->types.size(); size_t idx = query_db->types.size();
query_db->usr_to_symbol[usr] = SymbolIdx(SymbolKind::Type, idx); query_db->usr_to_type[usr] = QueryTypeId(idx);
query_db->types.push_back(QueryType(usr)); query_db->types.push_back(QueryType(usr));
return QueryTypeId(idx); return QueryTypeId(idx);
} }
QueryFuncId GetQueryFuncIdFromUsr(QueryDatabase* query_db, const Usr& usr) { QueryFuncId GetQueryFuncIdFromUsr(QueryDatabase* query_db, const Usr& usr) {
auto it = query_db->usr_to_symbol.find(usr); auto it = query_db->usr_to_func.find(usr);
if (it != query_db->usr_to_symbol.end() && it->second.kind != SymbolKind::Invalid) { if (it != query_db->usr_to_func.end())
// TODO: should this be an assert? return QueryFuncId(it->second.id);
if (it->second.kind == SymbolKind::Func)
return QueryFuncId(it->second.idx);
}
size_t idx = query_db->funcs.size(); size_t idx = query_db->funcs.size();
query_db->usr_to_symbol[usr] = SymbolIdx(SymbolKind::Func, idx); query_db->usr_to_func[usr] = QueryFuncId(idx);
query_db->funcs.push_back(QueryFunc(usr)); query_db->funcs.push_back(QueryFunc(usr));
return QueryFuncId(idx); return QueryFuncId(idx);
} }
QueryVarId GetQueryVarIdFromUsr(QueryDatabase* query_db, const Usr& usr) { QueryVarId GetQueryVarIdFromUsr(QueryDatabase* query_db, const Usr& usr) {
auto it = query_db->usr_to_symbol.find(usr); auto it = query_db->usr_to_var.find(usr);
if (it != query_db->usr_to_symbol.end() && it->second.kind != SymbolKind::Invalid) { if (it != query_db->usr_to_var.end())
// TODO: should this be an assert? return QueryVarId(it->second.id);
if (it->second.kind == SymbolKind::Var)
return QueryVarId(it->second.idx);
}
size_t idx = query_db->vars.size(); size_t idx = query_db->vars.size();
query_db->usr_to_symbol[usr] = SymbolIdx(SymbolKind::Var, idx); query_db->usr_to_var[usr] = QueryVarId(idx);
query_db->vars.push_back(QueryVar(usr)); query_db->vars.push_back(QueryVar(usr));
return QueryVarId(idx); return QueryVarId(idx);
} }
@ -595,7 +581,7 @@ void IndexUpdate::Merge(const IndexUpdate& update) {
// QUERYDB THREAD FUNCTIONS // QUERYDB THREAD FUNCTIONS
// ------------------------ // ------------------------
void QueryDatabase::RemoveUsrs(const std::vector<Usr>& to_remove) { void QueryDatabase::RemoveUsrs(SymbolKind usr_kind, const std::vector<Usr>& to_remove) {
// This function runs on the querydb thread. // This function runs on the querydb thread.
// When we remove an element, we just erase the state from the storage. We do // When we remove an element, we just erase the state from the storage. We do
@ -609,24 +595,27 @@ void QueryDatabase::RemoveUsrs(const std::vector<Usr>& to_remove) {
// TODO: Add "cquery: Reload Index" command which unloads all querydb state // TODO: Add "cquery: Reload Index" command which unloads all querydb state
// and fully reloads from cache. This will address the memory leak above. // and fully reloads from cache. This will address the memory leak above.
for (Usr usr : to_remove) { switch (usr_kind) {
SymbolIdx& symbol = usr_to_symbol[usr]; case SymbolKind::File: {
switch (symbol.kind) { for (const Usr& usr : to_remove)
case SymbolKind::File: files[usr_to_file[usr].id] = nullopt;
files[symbol.idx] = nullopt; break;
break; }
case SymbolKind::Type: case SymbolKind::Type: {
types[symbol.idx] = nullopt; for (const Usr& usr : to_remove)
break; types[usr_to_type[usr].id] = nullopt;
case SymbolKind::Func: break;
funcs[symbol.idx] = nullopt; }
break; case SymbolKind::Func: {
case SymbolKind::Var: for (const Usr& usr : to_remove)
vars[symbol.idx] = nullopt; funcs[usr_to_func[usr].id] = nullopt;
break;
}
case SymbolKind::Var: {
for (const Usr& usr : to_remove)
vars[usr_to_var[usr].id] = nullopt;
break; break;
} }
symbol.kind = SymbolKind::Invalid;
} }
} }
@ -647,22 +636,22 @@ void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
RemoveRange(&def->def_var_name, merge_update.to_remove); \ RemoveRange(&def->def_var_name, merge_update.to_remove); \
} }
RemoveUsrs(update->files_removed); RemoveUsrs(SymbolKind::File, update->files_removed);
ImportOrUpdate(update->files_def_update); ImportOrUpdate(update->files_def_update);
RemoveUsrs(update->types_removed); RemoveUsrs(SymbolKind::Type, update->types_removed);
ImportOrUpdate(update->types_def_update); ImportOrUpdate(update->types_def_update);
HANDLE_MERGEABLE(types_derived, derived, types); HANDLE_MERGEABLE(types_derived, derived, types);
HANDLE_MERGEABLE(types_instances, instances, types); HANDLE_MERGEABLE(types_instances, instances, types);
HANDLE_MERGEABLE(types_uses, uses, types); HANDLE_MERGEABLE(types_uses, uses, types);
RemoveUsrs(update->funcs_removed); RemoveUsrs(SymbolKind::Func, update->funcs_removed);
ImportOrUpdate(update->funcs_def_update); ImportOrUpdate(update->funcs_def_update);
HANDLE_MERGEABLE(funcs_declarations, declarations, funcs); HANDLE_MERGEABLE(funcs_declarations, declarations, funcs);
HANDLE_MERGEABLE(funcs_derived, derived, funcs); HANDLE_MERGEABLE(funcs_derived, derived, funcs);
HANDLE_MERGEABLE(funcs_callers, callers, funcs); HANDLE_MERGEABLE(funcs_callers, callers, funcs);
RemoveUsrs(update->vars_removed); RemoveUsrs(SymbolKind::Var, update->vars_removed);
ImportOrUpdate(update->vars_def_update); ImportOrUpdate(update->vars_def_update);
HANDLE_MERGEABLE(vars_uses, uses, vars); HANDLE_MERGEABLE(vars_uses, uses, vars);
@ -673,15 +662,15 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& upda
// This function runs on the querydb thread. // This function runs on the querydb thread.
for (auto& def : updates) { for (auto& def : updates) {
auto it = usr_to_symbol.find(def.path); auto it = usr_to_file.find(def.path);
assert(it != usr_to_symbol.end()); assert(it != usr_to_file.end());
optional<QueryFile>& existing = files[it->second.idx]; optional<QueryFile>& existing = files[it->second.id];
if (!existing) if (!existing)
existing = QueryFile(def.path); existing = QueryFile(def.path);
existing->def = def; existing->def = def;
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::File, it->second.idx, def.path); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::File, it->second.id, def.path);
} }
} }
@ -691,17 +680,11 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryType::DefUpdate>& upda
for (auto& def : updates) { for (auto& def : updates) {
assert(!def.detailed_name.empty()); assert(!def.detailed_name.empty());
auto it = usr_to_symbol.find(def.usr); auto it = usr_to_type.find(def.usr);
assert(it != usr_to_symbol.end()); assert(it != usr_to_type.end());
if (it->second.kind != SymbolKind::Type) { assert(it->second.id >= 0 && it->second.id < types.size());
std::cerr << "!! Import/update got symbol kind " << (int)(it->second.kind) << ", expected SymbolKind::Type for usr " << def.usr << std::endl; optional<QueryType>& existing = types[it->second.id];
continue;
}
assert(it->second.idx >= 0 && it->second.idx < types.size());
optional<QueryType>& existing = types[it->second.idx];
if (!existing) if (!existing)
existing = QueryType(def.usr); existing = QueryType(def.usr);
@ -710,7 +693,7 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryType::DefUpdate>& upda
continue; continue;
existing->def = def; existing->def = def;
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Type, it->second.idx, def.detailed_name); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Type, it->second.id, def.detailed_name);
} }
} }
@ -720,17 +703,11 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& upda
for (auto& def : updates) { for (auto& def : updates) {
assert(!def.detailed_name.empty()); assert(!def.detailed_name.empty());
auto it = usr_to_symbol.find(def.usr); auto it = usr_to_func.find(def.usr);
assert(it != usr_to_symbol.end()); assert(it != usr_to_func.end());
if (it->second.kind != SymbolKind::Func) { assert(it->second.id >= 0 && it->second.id < funcs.size());
std::cerr << "!! Import/update got symbol kind " << (int)(it->second.kind) << ", expected SymbolKind::Func for usr " << def.usr << std::endl; optional<QueryFunc>& existing = funcs[it->second.id];
continue;
}
assert(it->second.idx >= 0 && it->second.idx < funcs.size());
optional<QueryFunc>& existing = funcs[it->second.idx];
if (!existing) if (!existing)
existing = QueryFunc(def.usr); existing = QueryFunc(def.usr);
@ -739,7 +716,7 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& upda
continue; continue;
existing->def = def; existing->def = def;
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Func, it->second.idx, def.detailed_name); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Func, it->second.id, def.detailed_name);
} }
} }
@ -749,17 +726,11 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updat
for (auto& def : updates) { for (auto& def : updates) {
assert(!def.detailed_name.empty()); assert(!def.detailed_name.empty());
auto it = usr_to_symbol.find(def.usr); auto it = usr_to_var.find(def.usr);
assert(it != usr_to_symbol.end()); assert(it != usr_to_var.end());
if (it->second.kind != SymbolKind::Var) { assert(it->second.id >= 0 && it->second.id < vars.size());
std::cerr << "!! Import/update got symbol kind " << (int)(it->second.kind) << ", expected SymbolKind::Var for usr " << def.usr << std::endl; optional<QueryVar>& existing = vars[it->second.id];
continue;
}
assert(it->second.idx >= 0 && it->second.idx < vars.size());
optional<QueryVar>& existing = vars[it->second.idx];
if (!existing) if (!existing)
existing = QueryVar(def.usr); existing = QueryVar(def.usr);
@ -769,7 +740,7 @@ void QueryDatabase::ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updat
existing->def = def; existing->def = def;
if (def.declaring_type) if (def.declaring_type)
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Var, it->second.idx, def.detailed_name); UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Var, it->second.id, def.detailed_name);
} }
} }

View File

@ -251,11 +251,13 @@ struct QueryDatabase {
std::vector<optional<QueryVar>> vars; std::vector<optional<QueryVar>> vars;
// Lookup symbol based on a usr. // Lookup symbol based on a usr.
// TODO: consider splitting this into type/func/var spp::sparse_hash_map<Usr, QueryFileId> usr_to_file;
spp::sparse_hash_map<Usr, SymbolIdx> usr_to_symbol; spp::sparse_hash_map<Usr, QueryTypeId> usr_to_type;
spp::sparse_hash_map<Usr, QueryFuncId> usr_to_func;
spp::sparse_hash_map<Usr, QueryVarId> usr_to_var;
// Marks the given Usrs as invalid. // Marks the given Usrs as invalid.
void RemoveUsrs(const std::vector<Usr>& to_remove); void RemoveUsrs(SymbolKind usr_kind, const std::vector<Usr>& to_remove);
// Insert the contents of |update| into |db|. // Insert the contents of |update| into |db|.
void ApplyIndexUpdate(IndexUpdate* update); void ApplyIndexUpdate(IndexUpdate* update);
void ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& updates); void ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& updates);