xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/tools/clang_generator/types.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sandboxed_api/tools/clang_generator/types.h"
16 
17 #include <string>
18 #include <vector>
19 
20 #include "absl/container/flat_hash_set.h"
21 #include "absl/strings/str_cat.h"
22 #include "clang/AST/ASTContext.h"
23 #include "clang/AST/Decl.h"
24 #include "clang/AST/QualTypeNames.h"
25 #include "clang/AST/Type.h"
26 
27 namespace sapi {
28 namespace {
29 
IsFunctionReferenceType(clang::QualType qual)30 bool IsFunctionReferenceType(clang::QualType qual) {
31 #if LLVM_VERSION_MAJOR >= 9
32   return qual->isFunctionReferenceType();
33 #else
34   const auto* ref = qual->getAs<clang::ReferenceType>();
35   return ref && ref->getPointeeType()->isFunctionType();
36 #endif
37 }
38 
39 }  // namespace
40 
RecordOrderedDecl(clang::TypeDecl * type_decl)41 void TypeCollector::RecordOrderedDecl(clang::TypeDecl* type_decl) {
42   // This implicitly assigns a number (its source order) to each declaration.
43   ordered_decls_.push_back(type_decl);
44 }
45 
CollectRelatedTypes(clang::QualType qual)46 void TypeCollector::CollectRelatedTypes(clang::QualType qual) {
47   if (!seen_.insert(qual)) {
48     return;
49   }
50 
51   if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
52     clang::TypedefNameDecl* typedef_decl = typedef_type->getDecl();
53     if (!typedef_decl->getAnonDeclWithTypedefName()) {
54       // Do not collect anonymous enums/structs as those are handled when
55       // emitting them via their parent typedef/using declaration.
56       CollectRelatedTypes(typedef_decl->getUnderlyingType());
57     }
58     collected_.insert(qual);
59     return;
60   }
61 
62   if (qual->isFunctionPointerType() || IsFunctionReferenceType(qual) ||
63       qual->isMemberFunctionPointerType()) {
64     if (const auto* function_type = qual->getPointeeOrArrayElementType()
65                                         ->getAs<clang::FunctionProtoType>()) {
66       // Collect the return type, the parameter types as well as the function
67       // pointer type itself.
68       CollectRelatedTypes(function_type->getReturnType());
69       for (const clang::QualType& param : function_type->getParamTypes()) {
70         CollectRelatedTypes(param);
71       }
72       collected_.insert(qual);
73       return;
74     }
75   }
76 
77   if (IsPointerOrReference(qual)) {
78     CollectRelatedTypes(qual->getPointeeType());
79     return;
80   }
81 
82   // C array with specified constant size (i.e. int a[42])?
83   if (const clang::ArrayType* array_type = qual->getAsArrayTypeUnsafe()) {
84     CollectRelatedTypes(array_type->getElementType());
85     return;
86   }
87 
88   if (IsSimple(qual) || qual->isEnumeralType()) {
89     if (const clang::EnumType* enum_type = qual->getAs<clang::EnumType>()) {
90       // Collect the underlying integer type of enum classes as well, as it may
91       // be a typedef.
92       if (const clang::EnumDecl* decl = enum_type->getDecl(); decl->isFixed()) {
93         CollectRelatedTypes(decl->getIntegerType());
94       }
95     }
96     collected_.insert(qual);
97     return;
98   }
99 
100   if (const auto* record_type = qual->getAs<clang::RecordType>()) {
101     const clang::RecordDecl* decl = record_type->getDecl();
102     for (const clang::FieldDecl* field : decl->fields()) {
103       CollectRelatedTypes(field->getType());
104     }
105     // Do not collect structs/unions if they are declared within another
106     // record. The enclosing type is enough to reconstruct the AST when
107     // writing the header.
108     const clang::RecordDecl* outer = decl->getOuterLexicalRecordContext();
109     decl = outer ? outer : decl;
110     collected_.insert(clang::QualType(decl->getTypeForDecl(), 0));
111     return;
112   }
113 }
114 
115 namespace {
116 
GetQualTypeName(const clang::ASTContext & context,clang::QualType qual)117 std::string GetQualTypeName(const clang::ASTContext& context,
118                             clang::QualType qual) {
119   // Remove any "const", "volatile", etc. except for those added via typedefs.
120   clang::QualType unqual = qual.getLocalUnqualifiedType();
121 
122   // This is to get to the actual name of function pointers.
123   if (unqual->isFunctionPointerType() || IsFunctionReferenceType(unqual) ||
124       unqual->isMemberFunctionPointerType()) {
125     unqual = unqual->getPointeeType();
126   }
127   return clang::TypeName::getFullyQualifiedName(unqual, context,
128                                                 context.getPrintingPolicy());
129 }
130 
131 }  // namespace
132 
GetTypeDeclarations()133 std::vector<clang::TypeDecl*> TypeCollector::GetTypeDeclarations() {
134   if (ordered_decls_.empty()) {
135     return {};
136   }
137 
138   // The AST context is the same for all declarations in this translation unit,
139   // so use a reference here.
140   clang::ASTContext& context = ordered_decls_.front()->getASTContext();
141 
142   absl::flat_hash_set<std::string> collected_names;
143   for (clang::QualType qual : collected_) {
144     const std::string qual_name = GetQualTypeName(context, qual);
145     collected_names.insert(qual_name);
146   }
147 
148   std::vector<clang::TypeDecl*> result;
149   for (clang::TypeDecl* type_decl : ordered_decls_) {
150     clang::QualType type_decl_type = context.getTypeDeclType(type_decl);
151 
152     // Filter out problematic dependent types that we cannot emit properly.
153     // CollectRelatedTypes() cannot skip those, as it runs before this
154     // information is available.
155     if (type_decl_type->isMemberFunctionPointerType() &&
156         type_decl_type->isDependentType()) {
157       continue;
158     }
159 
160     // Ideally, collected_.contains() on the underlying QualType of the TypeDecl
161     // would work here. However, QualTypes obtained from a TypeDecl contain
162     // different Type pointers, even when referring to one of the same types
163     // from the set and thus will not be found. Instead, work around the issue
164     // by always using the fully qualified name of the type.
165     const std::string qual_name = GetQualTypeName(context, type_decl_type);
166     if (!collected_names.contains(qual_name)) {
167       continue;
168     }
169 
170     // Skip anonymous declarations that are typedef-ed. For example skip things
171     // like "typedef enum { A } SomeName". In this case, the enum is unnamed and
172     // the emitter will instead work with the complete typedef, so nothing is
173     // lost.
174     if (auto* tag_decl = llvm::dyn_cast<clang::TagDecl>(type_decl);
175         tag_decl && tag_decl->getTypedefNameForAnonDecl()) {
176       continue;
177     }
178 
179     result.push_back(type_decl);
180   }
181   return result;
182 }
183 
184 namespace {
185 
186 // Removes "const" from a qualified type if it denotes a pointer or reference
187 // type. Keeps top-level typedef types intact.
MaybeRemoveConst(const clang::ASTContext & context,clang::QualType qual)188 clang::QualType MaybeRemoveConst(const clang::ASTContext& context,
189                                  clang::QualType qual) {
190   if (
191 #if LLVM_VERSION_MAJOR < 13
192       qual->getAs<clang::TypedefType>() == nullptr
193 #else
194       !qual->isTypedefNameType()
195 #endif
196       && IsPointerOrReference(qual)) {
197     clang::QualType pointee_qual = qual->getPointeeType();
198     pointee_qual.removeLocalConst();
199     qual = context.getPointerType(pointee_qual);
200   }
201   return qual;
202 }
203 
204 }  // namespace
205 
MapQualType(const clang::ASTContext & context,clang::QualType qual)206 std::string MapQualType(const clang::ASTContext& context,
207                         clang::QualType qual) {
208   if (const auto* builtin = qual->getAs<clang::BuiltinType>()) {
209     switch (builtin->getKind()) {
210       case clang::BuiltinType::Void:
211       case clang::BuiltinType::NullPtr:
212         return "::sapi::v::Void";
213 
214       /*
215        * Unsigned types
216        */
217       case clang::BuiltinType::Bool:
218         return "::sapi::v::Bool";
219       // Unsigned character types
220       case clang::BuiltinType::Char_U:
221       case clang::BuiltinType::UChar:
222         return "::sapi::v::UChar";
223       case clang::BuiltinType::WChar_U:
224         return "::sapi::v::ULong";  // 32-bit, correct for Linux and UTF-32
225       // Added in C++20
226       case clang::BuiltinType::Char8:  // Underlying type: unsigned char
227         return "::sapi::v::UChar";
228       case clang::BuiltinType::Char16:  // Underlying type: uint_least16_t
229         return "::sapi::v::UShort";
230       case clang::BuiltinType::Char32:  // Underlying type: uint_least32_t
231         return "::sapi::v::ULong";
232       // Standard unsigned types
233       case clang::BuiltinType::UShort:
234         return "::sapi::v::UShort";
235       case clang::BuiltinType::UInt:
236         return "::sapi::v::UInt";
237       case clang::BuiltinType::ULong:
238         return "::sapi::v::ULong";
239       case clang::BuiltinType::ULongLong:
240         return "::sapi::v::ULLong";
241       // TODO(cblichmann): Add 128-bit integer support
242       // case clang::BuiltinType::UInt128:
243       //   return "::sapi::v::UInt128";
244 
245       /*
246        * Signed types
247        */
248       // Signed character types
249       case clang::BuiltinType::Char_S:
250       case clang::BuiltinType::SChar:
251         return "::sapi::v::Char";
252       case clang::BuiltinType::WChar_S:
253         return "::sapi::v::Long";  // 32-bit, correct for Linux and UTF-32
254 
255       // Standard signed types
256       case clang::BuiltinType::Short:
257         return "::sapi::v::Short";
258       case clang::BuiltinType::Int:
259         return "::sapi::v::Int";
260       case clang::BuiltinType::Long:
261         return "::sapi::v::Long";
262       case clang::BuiltinType::LongLong:
263         return "::sapi::v::LLong";
264       // TODO(cblichmann): Add 128-bit integer support
265       // case clang::BuiltinType::Int128:
266       //   return "::sapi::v::Int128";
267 
268       /*
269        * Floating-point types
270        */
271       // TODO(cblichmann): Map half/__fp16, _Float16 and __float128 types
272       case clang::BuiltinType::Float:
273         return "::sapi::v::Reg<float>";
274       case clang::BuiltinType::Double:
275         return "::sapi::v::Reg<double>";
276       case clang::BuiltinType::LongDouble:
277         return "::sapi::v::Reg<long double>";
278 
279       default:
280         break;
281     }
282   } else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
283     clang::EnumDecl* enum_decl = enum_type->getDecl();
284     std::string name;
285     if (auto* typedef_name = enum_decl->getTypedefNameForAnonDecl()) {
286       name = typedef_name->getQualifiedNameAsString();
287     } else {
288       name = enum_decl->getQualifiedNameAsString();
289     }
290     return absl::StrCat("::sapi::v::IntBase<", name, ">");
291   } else if (IsPointerOrReference(qual)) {
292     // Remove "const" qualifier from a pointer or reference type's pointee, as
293     // e.g. const pointers do not work well with SAPI.
294     return absl::StrCat("::sapi::v::Reg<",
295                         clang::TypeName::getFullyQualifiedName(
296                             MaybeRemoveConst(context, qual), context,
297                             context.getPrintingPolicy()),
298                         ">");
299   }
300   // Best-effort mapping to "int", leave a comment.
301   return absl::StrCat("::sapi::v::Int /* aka '",
302                       clang::TypeName::getFullyQualifiedName(
303                           MaybeRemoveConst(context, qual), context,
304                           context.getPrintingPolicy()),
305                       "' */");
306 }
307 
MapQualTypeParameterForCxx(const clang::ASTContext & context,clang::QualType qual)308 std::string MapQualTypeParameterForCxx(const clang::ASTContext& context,
309                                        clang::QualType qual) {
310   if (const auto* builtin = qual->getAs<clang::BuiltinType>()) {
311     if (builtin->getKind() == clang::BuiltinType::Bool) {
312       return "bool";  // _Bool -> bool
313     }
314     // We may decide to add more mappings later, depending on data model:
315     // - long long -> uint64_t
316     // - ...
317   }
318   return clang::TypeName::getFullyQualifiedName(qual, context,
319                                                 context.getPrintingPolicy());
320 }
321 
MapQualTypeParameter(const clang::ASTContext & context,clang::QualType qual)322 std::string MapQualTypeParameter(const clang::ASTContext& context,
323                                  clang::QualType qual) {
324   return IsPointerOrReference(qual) ? "::sapi::v::Ptr*"
325                                     : MapQualTypeParameterForCxx(context, qual);
326 }
327 
MapQualTypeReturn(const clang::ASTContext & context,clang::QualType qual)328 std::string MapQualTypeReturn(const clang::ASTContext& context,
329                               clang::QualType qual) {
330   if (qual->isVoidType()) {
331     return "::absl::Status";
332   }
333   // Remove const qualifier like in MapQualType().
334   // TODO(cblichmann): We should return pointers differently, as they point to
335   //                   the sandboxee's address space.
336   return absl::StrCat(
337       "::absl::StatusOr<",
338       MapQualTypeParameterForCxx(context, MaybeRemoveConst(context, qual)),
339       ">");
340 }
341 
342 }  // namespace sapi
343