mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 01:20:25 +00:00
Lagom: Enforce GC-allocated fields are visited in LibJSGCVerifier
This commit is contained in:
parent
82eeee2008
commit
48d92eecac
Notes:
sideshowbarker
2024-07-17 05:18:58 +09:00
Author: https://github.com/mattco98 Commit: https://github.com/SerenityOS/serenity/commit/48d92eecac Pull-request: https://github.com/SerenityOS/serenity/pull/17979 Reviewed-by: https://github.com/awesomekling
1 changed files with 43 additions and 0 deletions
|
@ -108,6 +108,7 @@ std::vector<clang::QualType> get_all_qualified_types(clang::QualType const& type
|
|||
struct FieldValidationResult {
|
||||
bool is_valid { false };
|
||||
bool is_wrapped_in_gcptr { false };
|
||||
bool needs_visiting { false };
|
||||
};
|
||||
|
||||
FieldValidationResult validate_field(clang::FieldDecl const* field_decl)
|
||||
|
@ -124,6 +125,7 @@ FieldValidationResult validate_field(clang::FieldDecl const* field_decl)
|
|||
if (record_inherits_from_cell(*pointee)) {
|
||||
result.is_valid = false;
|
||||
result.is_wrapped_in_gcptr = false;
|
||||
result.needs_visiting = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +134,7 @@ FieldValidationResult validate_field(clang::FieldDecl const* field_decl)
|
|||
if (record_inherits_from_cell(*pointee)) {
|
||||
result.is_valid = false;
|
||||
result.is_wrapped_in_gcptr = false;
|
||||
result.needs_visiting = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +157,7 @@ FieldValidationResult validate_field(clang::FieldDecl const* field_decl)
|
|||
|
||||
result.is_wrapped_in_gcptr = true;
|
||||
result.is_valid = record_inherits_from_cell(*record_decl);
|
||||
result.needs_visiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +172,13 @@ void CollectCellsHandler::run(clang::ast_matchers::MatchFinder::MatchResult cons
|
|||
if (!record || !record->isCompleteDefinition() || (!record->isClass() && !record->isStruct()))
|
||||
return;
|
||||
|
||||
// Cell triggers a bunch of warnings for its empty visit_edges implementation, but
|
||||
// it doesn't have any members anyways so it's fine to just ignore.
|
||||
if (record->getQualifiedNameAsString() == "JS::Cell")
|
||||
return;
|
||||
|
||||
auto& diag_engine = result.Context->getDiagnostics();
|
||||
std::vector<clang::FieldDecl const*> fields_that_need_visiting;
|
||||
|
||||
for (clang::FieldDecl const* field : record->fields()) {
|
||||
auto validation_results = validate_field(field);
|
||||
|
@ -187,6 +197,8 @@ void CollectCellsHandler::run(clang::ast_matchers::MatchFinder::MatchResult cons
|
|||
<< "JS::GCPtr";
|
||||
}
|
||||
}
|
||||
} else if (validation_results.needs_visiting) {
|
||||
fields_that_need_visiting.push_back(field);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,4 +238,35 @@ void CollectCellsHandler::run(clang::ast_matchers::MatchFinder::MatchResult cons
|
|||
auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Warning, "Missing call to Base::visit_edges");
|
||||
diag_engine.Report(visit_edges_method->getBeginLoc(), diag_id);
|
||||
}
|
||||
|
||||
// Search for uses of all fields that need visiting. We don't ensure they are _actually_ visited
|
||||
// with a call to visitor.visit(...), as that is too complex. Instead, we just assume that if the
|
||||
// field is accessed at all, then it is visited.
|
||||
|
||||
if (fields_that_need_visiting.empty())
|
||||
return;
|
||||
|
||||
MatchFinder field_access_finder;
|
||||
SimpleCollectMatchesCallback<clang::MemberExpr> field_access_callback("member-expr");
|
||||
|
||||
auto field_access_matcher = memberExpr(
|
||||
hasAncestor(cxxMethodDecl(hasName("visit_edges"))),
|
||||
hasObjectExpression(hasType(pointsTo(cxxRecordDecl(hasName(record->getName()))))))
|
||||
.bind("member-expr");
|
||||
|
||||
field_access_finder.addMatcher(field_access_matcher, &field_access_callback);
|
||||
field_access_finder.matchAST(visit_edges_method->getASTContext());
|
||||
|
||||
std::unordered_set<std::string> fields_that_are_visited;
|
||||
for (auto const* member_expr : field_access_callback.matches())
|
||||
fields_that_are_visited.insert(member_expr->getMemberNameInfo().getAsString());
|
||||
|
||||
auto diag_id = diag_engine.getCustomDiagID(clang::DiagnosticsEngine::Warning, "GC-allocated member is not visited in %0::visit_edges");
|
||||
|
||||
for (auto const* field : fields_that_need_visiting) {
|
||||
if (!fields_that_are_visited.contains(field->getNameAsString())) {
|
||||
auto builder = diag_engine.Report(field->getBeginLoc(), diag_id);
|
||||
builder << record->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue