mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
clang_generator: Use fully qualified names
Use locally unqualified types to filter ordered type declarations in `TypeCollector::GetTypeDeclarations()`. This is necessary, as `clang::TypeName::getFullyQualifiedName()` and `TypeDecl::getQualifiedNameAsString()` have different ideas which qualifiers belong to the name. The former works on `QualType`s, while the latter deals with the declaration directly. This change decays a `TypeDecl` into its locally unqualified `QualType`. PiperOrigin-RevId: 490500091 Change-Id: Ie2f4eece4e673f8b06ab6661d7b6611daf34fba9
This commit is contained in:
parent
37ca6d0fc6
commit
d7fe6cd334
|
@ -25,9 +25,11 @@
|
||||||
#include "absl/strings/str_split.h"
|
#include "absl/strings/str_split.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "absl/strings/strip.h"
|
#include "absl/strings/strip.h"
|
||||||
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/Decl.h"
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/AST/DeclCXX.h"
|
#include "clang/AST/DeclCXX.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
|
#include "clang/AST/QualTypeNames.h"
|
||||||
#include "clang/AST/Type.h"
|
#include "clang/AST/Type.h"
|
||||||
#include "clang/Format/Format.h"
|
#include "clang/Format/Format.h"
|
||||||
#include "sandboxed_api/tools/clang_generator/diagnostics.h"
|
#include "sandboxed_api/tools/clang_generator/diagnostics.h"
|
||||||
|
@ -200,15 +202,16 @@ std::string PrintRecordTemplateArguments(const clang::CXXRecordDecl* record) {
|
||||||
if (!template_params) {
|
if (!template_params) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
clang::ASTContext& context = record->getASTContext();
|
||||||
std::vector<std::string> params;
|
std::vector<std::string> params;
|
||||||
params.reserve(template_params->size());
|
params.reserve(template_params->size());
|
||||||
for (const auto& template_param : *template_params) {
|
for (const auto& template_param : *template_params) {
|
||||||
if (const auto* p =
|
if (const auto* p =
|
||||||
llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_param)) {
|
llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_param)) {
|
||||||
// TODO(cblichmann): These types should be included by
|
// TODO(cblichmann): Should be included by CollectRelatedTypes().
|
||||||
// CollectRelatedTypes().
|
params.push_back(clang::TypeName::getFullyQualifiedName(
|
||||||
params.push_back(
|
p->getType().getDesugaredType(context), context,
|
||||||
p->getType().getDesugaredType(record->getASTContext()).getAsString());
|
context.getPrintingPolicy()));
|
||||||
} else { // Also covers template template parameters
|
} else { // Also covers template template parameters
|
||||||
params.push_back("typename");
|
params.push_back("typename");
|
||||||
}
|
}
|
||||||
|
@ -294,6 +297,12 @@ absl::StatusOr<std::string> PrintFunctionPrototypeComment(
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
|
absl::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
|
||||||
|
const clang::QualType return_type = decl->getDeclaredReturnType();
|
||||||
|
if (return_type->isRecordType()) {
|
||||||
|
return MakeStatusWithDiagnostic(
|
||||||
|
decl->getBeginLoc(), absl::StatusCode::kCancelled,
|
||||||
|
"returning record by value, skipping function");
|
||||||
|
}
|
||||||
std::string out;
|
std::string out;
|
||||||
|
|
||||||
SAPI_ASSIGN_OR_RETURN(std::string prototype,
|
SAPI_ASSIGN_OR_RETURN(std::string prototype,
|
||||||
|
@ -301,12 +310,6 @@ absl::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
|
||||||
absl::StrAppend(&out, "\n", prototype);
|
absl::StrAppend(&out, "\n", prototype);
|
||||||
|
|
||||||
auto function_name = ToStringView(decl->getName());
|
auto function_name = ToStringView(decl->getName());
|
||||||
const clang::QualType return_type = decl->getDeclaredReturnType();
|
|
||||||
if (return_type->isRecordType()) {
|
|
||||||
return MakeStatusWithDiagnostic(
|
|
||||||
decl->getBeginLoc(), absl::StatusCode::kCancelled,
|
|
||||||
"returning record by value, skipping function");
|
|
||||||
}
|
|
||||||
const bool returns_void = return_type->isVoidType();
|
const bool returns_void = return_type->isVoidType();
|
||||||
|
|
||||||
const clang::ASTContext& context = decl->getASTContext();
|
const clang::ASTContext& context = decl->getASTContext();
|
||||||
|
@ -368,7 +371,6 @@ absl::StatusOr<std::string> EmitHeader(
|
||||||
std::string out;
|
std::string out;
|
||||||
const std::string include_guard = GetIncludeGuard(options.out_file);
|
const std::string include_guard = GetIncludeGuard(options.out_file);
|
||||||
absl::StrAppendFormat(&out, kHeaderProlog, include_guard);
|
absl::StrAppendFormat(&out, kHeaderProlog, include_guard);
|
||||||
|
|
||||||
// When embedding the sandboxee, add embed header include
|
// When embedding the sandboxee, add embed header include
|
||||||
if (!options.embed_name.empty()) {
|
if (!options.embed_name.empty()) {
|
||||||
// Not using JoinPath() because even on Windows include paths use plain
|
// Not using JoinPath() because even on Windows include paths use plain
|
||||||
|
@ -441,8 +443,8 @@ void Emitter::EmitType(clang::TypeDecl* type_decl) {
|
||||||
|
|
||||||
// Skip types defined in system headers.
|
// Skip types defined in system headers.
|
||||||
// TODO(cblichmann): Instead of this and the hard-coded entities below, we
|
// TODO(cblichmann): Instead of this and the hard-coded entities below, we
|
||||||
// should map add the correct (system) headers to the
|
// should map types and add the correct (system) headers to
|
||||||
// generated output.
|
// the generated output.
|
||||||
if (type_decl->getASTContext().getSourceManager().isInSystemHeader(
|
if (type_decl->getASTContext().getSourceManager().isInSystemHeader(
|
||||||
type_decl->getBeginLoc())) {
|
type_decl->getBeginLoc())) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "absl/status/statusor.h"
|
#include "absl/status/statusor.h"
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "sandboxed_api/testing.h"
|
|
||||||
#include "sandboxed_api/tools/clang_generator/frontend_action_test_util.h"
|
#include "sandboxed_api/tools/clang_generator/frontend_action_test_util.h"
|
||||||
#include "sandboxed_api/tools/clang_generator/generator.h"
|
#include "sandboxed_api/tools/clang_generator/generator.h"
|
||||||
#include "sandboxed_api/util/status_matchers.h"
|
#include "sandboxed_api/util/status_matchers.h"
|
||||||
|
@ -121,7 +120,8 @@ TEST_F(EmitterTest, CollectFunctionPointer) {
|
||||||
|
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
UglifyAll(emitter.SpellingsForNS("")),
|
UglifyAll(emitter.SpellingsForNS("")),
|
||||||
ElementsAre("struct HandlerData { int member; callback_t *cb; }"));
|
ElementsAre("typedef void (callback_t)(void *)",
|
||||||
|
"struct HandlerData { int member; callback_t *cb; }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(EmitterTest, TypedefNames) {
|
TEST_F(EmitterTest, TypedefNames) {
|
||||||
|
@ -247,6 +247,25 @@ TEST_F(EmitterTest, TypedefStructByValueSkipsFunction) {
|
||||||
EXPECT_THAT(emitter.GetRenderedFunctions(), IsEmpty());
|
EXPECT_THAT(emitter.GetRenderedFunctions(), IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(EmitterTest, CollectTypedefPointerType) {
|
||||||
|
EmitterForTesting emitter;
|
||||||
|
EXPECT_THAT(
|
||||||
|
RunFrontendAction(
|
||||||
|
R"(typedef struct _KernelProfileRecord {
|
||||||
|
int member;
|
||||||
|
}* KernelProfileRecord;
|
||||||
|
extern "C" const KernelProfileRecord*
|
||||||
|
GetOpenCLKernelProfileRecords(const int, long long int*);)",
|
||||||
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
||||||
|
IsOk());
|
||||||
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(1));
|
||||||
|
|
||||||
|
EXPECT_THAT(
|
||||||
|
UglifyAll(emitter.SpellingsForNS("")),
|
||||||
|
ElementsAre("struct _KernelProfileRecord { int member; }",
|
||||||
|
"typedef struct _KernelProfileRecord *KernelProfileRecord"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(EmitterTest, TypedefTypeDependencies) {
|
TEST_F(EmitterTest, TypedefTypeDependencies) {
|
||||||
EmitterForTesting emitter;
|
EmitterForTesting emitter;
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
|
@ -268,6 +287,7 @@ TEST_F(EmitterTest, TypedefTypeDependencies) {
|
||||||
|
|
||||||
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
||||||
ElementsAre("using size_t = long long", "struct _Image",
|
ElementsAre("using size_t = long long", "struct _Image",
|
||||||
|
"typedef struct _Image Image",
|
||||||
"typedef size_t (*StreamHandler)(const Image *, "
|
"typedef size_t (*StreamHandler)(const Image *, "
|
||||||
"const void *, const size_t)",
|
"const void *, const size_t)",
|
||||||
"struct _Image {"
|
"struct _Image {"
|
||||||
|
|
|
@ -110,6 +110,24 @@ void TypeCollector::CollectRelatedTypes(clang::QualType qual) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string GetQualTypeName(const clang::ASTContext& context,
|
||||||
|
clang::QualType qual) {
|
||||||
|
// Remove any "const", "volatile", etc. except for those added via typedefs.
|
||||||
|
clang::QualType unqual = qual.getLocalUnqualifiedType();
|
||||||
|
|
||||||
|
// This is to get to the actual name of function pointers.
|
||||||
|
if (unqual->isFunctionPointerType() || IsFunctionReferenceType(unqual) ||
|
||||||
|
unqual->isMemberFunctionPointerType()) {
|
||||||
|
unqual = unqual->getPointeeType();
|
||||||
|
}
|
||||||
|
return clang::TypeName::getFullyQualifiedName(unqual, context,
|
||||||
|
context.getPrintingPolicy());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
std::vector<clang::TypeDecl*> TypeCollector::GetTypeDeclarations() {
|
std::vector<clang::TypeDecl*> TypeCollector::GetTypeDeclarations() {
|
||||||
if (ordered_decls_.empty()) {
|
if (ordered_decls_.empty()) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -121,28 +139,33 @@ std::vector<clang::TypeDecl*> TypeCollector::GetTypeDeclarations() {
|
||||||
|
|
||||||
absl::flat_hash_set<std::string> collected_names;
|
absl::flat_hash_set<std::string> collected_names;
|
||||||
for (clang::QualType qual : collected_) {
|
for (clang::QualType qual : collected_) {
|
||||||
collected_names.insert(clang::TypeName::getFullyQualifiedName(
|
const std::string qual_name = GetQualTypeName(context, qual);
|
||||||
qual, context, context.getPrintingPolicy()));
|
collected_names.insert(qual_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<clang::TypeDecl*> result;
|
std::vector<clang::TypeDecl*> result;
|
||||||
for (clang::TypeDecl* type_decl : ordered_decls_) {
|
for (clang::TypeDecl* type_decl : ordered_decls_) {
|
||||||
|
clang::QualType type_decl_type = context.getTypeDeclType(type_decl);
|
||||||
|
|
||||||
// Ideally, collected_.contains() on the underlying QualType of the TypeDecl
|
// Ideally, collected_.contains() on the underlying QualType of the TypeDecl
|
||||||
// would work here. However, QualTypes obtained from a TypeDecl contain
|
// would work here. However, QualTypes obtained from a TypeDecl contain
|
||||||
// different Type pointers, even when referring to one of the same types
|
// different Type pointers, even when referring to one of the same types
|
||||||
// from the set and thus will not be found. Instead, work around the issue
|
// from the set and thus will not be found. Instead, work around the issue
|
||||||
// by always using the fully qualified name of the type.
|
// by always using the fully qualified name of the type.
|
||||||
if (!collected_names.contains(type_decl->getQualifiedNameAsString())) {
|
const std::string qual_name = GetQualTypeName(context, type_decl_type);
|
||||||
|
if (!collected_names.contains(qual_name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip anonymous declarations that are typedef-ed. For example skip things
|
||||||
|
// like "typedef enum { A } SomeName". In this case, the enum is unnamed and
|
||||||
|
// the emitter will instead work with the complete typedef, so nothing is
|
||||||
|
// lost.
|
||||||
if (auto* tag_decl = llvm::dyn_cast<clang::TagDecl>(type_decl);
|
if (auto* tag_decl = llvm::dyn_cast<clang::TagDecl>(type_decl);
|
||||||
tag_decl && tag_decl->getTypedefNameForAnonDecl()) {
|
tag_decl && tag_decl->getTypedefNameForAnonDecl()) {
|
||||||
// Skip anonymous declarations that are typedef-ed. For example skip
|
|
||||||
// things like "typedef enum { A } SomeName". In this case, the enum is
|
|
||||||
// unnamed and the emitter will instead work with the complete typedef, so
|
|
||||||
// nothing is lost.
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push_back(type_decl);
|
result.push_back(type_decl);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -151,10 +174,16 @@ std::vector<clang::TypeDecl*> TypeCollector::GetTypeDeclarations() {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Removes "const" from a qualified type if it denotes a pointer or reference
|
// Removes "const" from a qualified type if it denotes a pointer or reference
|
||||||
// type.
|
// type. Keeps top-level typedef types intact.
|
||||||
clang::QualType MaybeRemoveConst(const clang::ASTContext& context,
|
clang::QualType MaybeRemoveConst(const clang::ASTContext& context,
|
||||||
clang::QualType qual) {
|
clang::QualType qual) {
|
||||||
if (IsPointerOrReference(qual)) {
|
if (
|
||||||
|
#if LLVM_VERSION_MAJOR < 13
|
||||||
|
qual->getAs<clang::TypedefType>() == nullptr
|
||||||
|
#else
|
||||||
|
!qual->isTypedefNameType()
|
||||||
|
#endif
|
||||||
|
&& IsPointerOrReference(qual)) {
|
||||||
clang::QualType pointee_qual = qual->getPointeeType();
|
clang::QualType pointee_qual = qual->getPointeeType();
|
||||||
pointee_qual.removeLocalConst();
|
pointee_qual.removeLocalConst();
|
||||||
qual = context.getPointerType(pointee_qual);
|
qual = context.getPointerType(pointee_qual);
|
||||||
|
@ -253,18 +282,21 @@ std::string MapQualType(const clang::ASTContext& context,
|
||||||
// Remove "const" qualifier from a pointer or reference type's pointee, as
|
// Remove "const" qualifier from a pointer or reference type's pointee, as
|
||||||
// e.g. const pointers do not work well with SAPI.
|
// e.g. const pointers do not work well with SAPI.
|
||||||
return absl::StrCat("::sapi::v::Reg<",
|
return absl::StrCat("::sapi::v::Reg<",
|
||||||
MaybeRemoveConst(context, qual).getAsString(), ">");
|
clang::TypeName::getFullyQualifiedName(
|
||||||
|
MaybeRemoveConst(context, qual), context,
|
||||||
|
context.getPrintingPolicy()),
|
||||||
|
">");
|
||||||
}
|
}
|
||||||
// Best-effort mapping to "int", leave a comment.
|
// Best-effort mapping to "int", leave a comment.
|
||||||
return absl::StrCat("::sapi::v::Int /* aka '", qual.getAsString(), "' */");
|
return absl::StrCat("::sapi::v::Int /* aka '",
|
||||||
|
clang::TypeName::getFullyQualifiedName(
|
||||||
|
MaybeRemoveConst(context, qual), context,
|
||||||
|
context.getPrintingPolicy()),
|
||||||
|
"' */");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MapQualTypeParameterForCxx(const clang::ASTContext& context,
|
std::string MapQualTypeParameterForCxx(const clang::ASTContext& context,
|
||||||
clang::QualType qual) {
|
clang::QualType qual) {
|
||||||
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
|
|
||||||
clang::TypedefNameDecl* typedef_decl = typedef_type->getDecl();
|
|
||||||
return typedef_decl->getQualifiedNameAsString();
|
|
||||||
}
|
|
||||||
if (const auto* builtin = qual->getAs<clang::BuiltinType>()) {
|
if (const auto* builtin = qual->getAs<clang::BuiltinType>()) {
|
||||||
if (builtin->getKind() == clang::BuiltinType::Bool) {
|
if (builtin->getKind() == clang::BuiltinType::Bool) {
|
||||||
return "bool"; // _Bool -> bool
|
return "bool"; // _Bool -> bool
|
||||||
|
|
Loading…
Reference in New Issue
Block a user