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