1 /*
2 * Copyright (C) 2018, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "aidl_to_cpp_common.h"
17
18 #include <android-base/format.h>
19 #include <android-base/stringprintf.h>
20 #include <android-base/strings.h>
21
22 #include <limits>
23 #include <set>
24 #include <unordered_map>
25
26 #include "comments.h"
27 #include "logging.h"
28 #include "os.h"
29
30 using ::android::base::Join;
31 using ::android::base::Split;
32
33 namespace android {
34 namespace aidl {
35 namespace cpp {
36
37 char kTransactionLogStruct[] = R"(struct TransactionLog {
38 double duration_ms;
39 std::string interface_name;
40 std::string method_name;
41 const void* proxy_address;
42 const void* stub_address;
43 std::vector<std::pair<std::string, std::string>> input_args;
44 std::vector<std::pair<std::string, std::string>> output_args;
45 std::string result;
46 std::string exception_message;
47 int32_t exception_code;
48 int32_t transaction_error;
49 int32_t service_specific_error_code;
50 };
51 )";
52
ClassName(const AidlDefinedType & defined_type,ClassNames type)53 string ClassName(const AidlDefinedType& defined_type, ClassNames type) {
54 string base_name = defined_type.GetName();
55 if (base_name.length() >= 2 && base_name[0] == 'I' && isupper(base_name[1])) {
56 base_name = base_name.substr(1);
57 }
58
59 switch (type) {
60 case ClassNames::CLIENT:
61 return "Bp" + base_name;
62 case ClassNames::SERVER:
63 return "Bn" + base_name;
64 case ClassNames::INTERFACE:
65 return "I" + base_name;
66 case ClassNames::DEFAULT_IMPL:
67 return "I" + base_name + "Default";
68 case ClassNames::BASE:
69 return base_name;
70 case ClassNames::DELEGATOR_IMPL:
71 return "I" + base_name + "Delegator";
72 case ClassNames::RAW:
73 [[fallthrough]];
74 default:
75 return defined_type.GetName();
76 }
77 }
78
HeaderFile(const AidlDefinedType & defined_type,ClassNames class_type,bool use_os_sep)79 std::string HeaderFile(const AidlDefinedType& defined_type, ClassNames class_type,
80 bool use_os_sep) {
81 // For a nested type, we need to include its top-most parent type's header.
82 const AidlDefinedType* toplevel = &defined_type;
83 for (auto parent = toplevel->GetParentType(); parent;) {
84 // When including the parent's header, it should be always RAW
85 class_type = ClassNames::RAW;
86 toplevel = parent;
87 parent = toplevel->GetParentType();
88 }
89 AIDL_FATAL_IF(toplevel->GetParentType() != nullptr, defined_type)
90 << "Can't find a top-level decl";
91
92 char separator = (use_os_sep) ? OS_PATH_SEPARATOR : '/';
93 vector<string> paths = toplevel->GetSplitPackage();
94 paths.push_back(ClassName(*toplevel, class_type));
95 return Join(paths, separator) + ".h";
96 }
97
EnterNamespace(CodeWriter & out,const AidlDefinedType & defined_type)98 void EnterNamespace(CodeWriter& out, const AidlDefinedType& defined_type) {
99 const std::vector<std::string> packages = defined_type.GetSplitPackage();
100 for (const std::string& package : packages) {
101 out << "namespace " << package << " {\n";
102 }
103 }
LeaveNamespace(CodeWriter & out,const AidlDefinedType & defined_type)104 void LeaveNamespace(CodeWriter& out, const AidlDefinedType& defined_type) {
105 const std::vector<std::string> packages = defined_type.GetSplitPackage();
106 for (auto it = packages.rbegin(); it != packages.rend(); ++it) {
107 out << "} // namespace " << *it << "\n";
108 }
109 }
110
BuildVarName(const AidlArgument & a)111 string BuildVarName(const AidlArgument& a) {
112 string prefix = "out_";
113 if (a.GetDirection() & AidlArgument::IN_DIR) {
114 prefix = "in_";
115 }
116 return prefix + a.GetName();
117 }
118
WriteLogForArgument(CodeWriter & w,const AidlArgument & a,bool is_server,const string & log_var,bool is_ndk)119 void WriteLogForArgument(CodeWriter& w, const AidlArgument& a, bool is_server,
120 const string& log_var, bool is_ndk) {
121 const string var_name = is_server || is_ndk ? BuildVarName(a) : a.GetName();
122 const bool is_pointer = a.IsOut() && !is_server;
123 const string value_expr = (is_pointer ? "*" : "") + var_name;
124 w << log_var
125 << ".emplace_back(\"" + var_name + "\", ::android::internal::ToString(" + value_expr + "));\n";
126 }
127
GenLogBeforeExecute(const string className,const AidlMethod & method,bool isServer,bool isNdk)128 const string GenLogBeforeExecute(const string className, const AidlMethod& method, bool isServer,
129 bool isNdk) {
130 string code;
131 CodeWriterPtr writer = CodeWriter::ForString(&code);
132 (*writer) << className << "::TransactionLog _transaction_log;\n";
133
134 (*writer) << "if (" << className << "::logFunc != nullptr) {\n";
135 (*writer).Indent();
136
137 for (const auto& a : method.GetInArguments()) {
138 WriteLogForArgument(*writer, *a, isServer, "_transaction_log.input_args", isNdk);
139 }
140
141 (*writer).Dedent();
142 (*writer) << "}\n";
143
144 (*writer) << "auto _log_start = std::chrono::steady_clock::now();\n";
145 writer->Close();
146 return code;
147 }
148
GenLogAfterExecute(const string className,const AidlInterface & interface,const AidlMethod & method,const string & statusVarName,const string & returnVarName,bool isServer,bool isNdk)149 const string GenLogAfterExecute(const string className, const AidlInterface& interface,
150 const AidlMethod& method, const string& statusVarName,
151 const string& returnVarName, bool isServer, bool isNdk) {
152 string code;
153 CodeWriterPtr writer = CodeWriter::ForString(&code);
154
155 (*writer) << "if (" << className << "::logFunc != nullptr) {\n";
156 (*writer).Indent();
157 const auto address = (isNdk && isServer) ? "_aidl_impl.get()" : "static_cast<const void*>(this)";
158 (*writer) << "auto _log_end = std::chrono::steady_clock::now();\n";
159 (*writer) << "_transaction_log.duration_ms = std::chrono::duration<double, std::milli>(_log_end "
160 "- _log_start).count();\n";
161 (*writer) << "_transaction_log.interface_name = \"" << interface.GetCanonicalName() << "\";\n";
162 (*writer) << "_transaction_log.method_name = \"" << method.GetName() << "\";\n";
163 (*writer) << "_transaction_log.stub_address = " << (isServer ? address : "nullptr") << ";\n";
164 (*writer) << "_transaction_log.proxy_address = " << (isServer ? "nullptr" : address) << ";\n";
165
166 if (isNdk) {
167 (*writer) << "_transaction_log.exception_code = AStatus_getExceptionCode(" << statusVarName
168 << ".get());\n";
169 (*writer) << "_transaction_log.exception_message = AStatus_getMessage(" << statusVarName
170 << ".get());\n";
171 (*writer) << "_transaction_log.transaction_error = AStatus_getStatus(" << statusVarName
172 << ".get());\n";
173 (*writer) << "_transaction_log.service_specific_error_code = AStatus_getServiceSpecificError("
174 << statusVarName << ".get());\n";
175 } else {
176 (*writer) << "_transaction_log.exception_code = " << statusVarName << ".exceptionCode();\n";
177 (*writer) << "_transaction_log.exception_message = " << statusVarName
178 << ".exceptionMessage();\n";
179 (*writer) << "_transaction_log.transaction_error = " << statusVarName
180 << ".transactionError();\n";
181 (*writer) << "_transaction_log.service_specific_error_code = " << statusVarName
182 << ".serviceSpecificErrorCode();\n";
183 }
184
185 for (const auto& a : method.GetOutArguments()) {
186 WriteLogForArgument(*writer, *a, isServer, "_transaction_log.output_args", isNdk);
187 }
188
189 if (method.GetType().GetName() != "void") {
190 const string expr = (isServer ? "" : "*") + returnVarName;
191 (*writer) << "_transaction_log.result = ::android::internal::ToString(" << expr << ");\n";
192 }
193
194 // call the user-provided function with the transaction log object
195 (*writer) << className << "::logFunc(_transaction_log);\n";
196
197 (*writer).Dedent();
198 (*writer) << "}\n";
199
200 writer->Close();
201 return code;
202 }
203
204 // Returns Parent1::Parent2::Self. Namespaces are not included.
GetQualifiedName(const AidlDefinedType & type,ClassNames class_names)205 string GetQualifiedName(const AidlDefinedType& type, ClassNames class_names) {
206 string q_name = ClassName(type, class_names);
207 for (auto parent = type.GetParentType(); parent; parent = parent->GetParentType()) {
208 q_name = parent->GetName() + "::" + q_name;
209 }
210 return q_name;
211 }
212
213 // Generates enum's class declaration. This should be called in a proper scope. For example, in its
214 // namespace or parent type.
GenerateEnumClassDecl(CodeWriter & out,const AidlEnumDeclaration & enum_decl,const std::string & backing_type,ConstantValueDecorator decorator)215 void GenerateEnumClassDecl(CodeWriter& out, const AidlEnumDeclaration& enum_decl,
216 const std::string& backing_type, ConstantValueDecorator decorator) {
217 out << "enum class";
218 GenerateDeprecated(out, enum_decl);
219 out << " " << enum_decl.GetName() << " : " << backing_type << " {\n";
220 out.Indent();
221 for (const auto& enumerator : enum_decl.GetEnumerators()) {
222 out << enumerator->GetName();
223 GenerateDeprecated(out, *enumerator);
224 out << " = " << enumerator->ValueString(enum_decl.GetBackingType(), decorator) << ",\n";
225 }
226 out.Dedent();
227 out << "};\n";
228 }
229
IsEnumDeprecated(const AidlEnumDeclaration & enum_decl)230 static bool IsEnumDeprecated(const AidlEnumDeclaration& enum_decl) {
231 if (enum_decl.IsDeprecated()) {
232 return true;
233 }
234 for (const auto& e : enum_decl.GetEnumerators()) {
235 if (e->IsDeprecated()) {
236 return true;
237 }
238 }
239 return false;
240 }
241
242 // enum_values template value is defined in its own namespace (android::internal or ndk::internal),
243 // so the enum_decl type should be fully qualified.
GenerateEnumValues(const AidlEnumDeclaration & enum_decl,const std::vector<std::string> & enclosing_namespaces_of_enum_decl)244 std::string GenerateEnumValues(const AidlEnumDeclaration& enum_decl,
245 const std::vector<std::string>& enclosing_namespaces_of_enum_decl) {
246 const auto fq_name =
247 Join(Append(enclosing_namespaces_of_enum_decl, enum_decl.GetSplitPackage()), "::") +
248 "::" + GetQualifiedName(enum_decl);
249 const auto size = enum_decl.GetEnumerators().size();
250 std::ostringstream code;
251 code << "#pragma clang diagnostic push\n";
252 code << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
253 if (IsEnumDeprecated(enum_decl)) {
254 code << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
255 }
256 code << "template <>\n";
257 code << "constexpr inline std::array<" << fq_name << ", " << size << ">";
258 code << " enum_values<" << fq_name << "> = {\n";
259 for (const auto& enumerator : enum_decl.GetEnumerators()) {
260 code << " " << fq_name << "::" << enumerator->GetName() << ",\n";
261 }
262 code << "};\n";
263 code << "#pragma clang diagnostic pop\n";
264 return code.str();
265 }
266
267 // toString(enum_type) is defined in the same namespace of the type.
268 // So, if enum_decl is nested in parent type(s), it should be qualified with parent type(s).
GenerateEnumToString(const AidlEnumDeclaration & enum_decl,const std::string & backing_type)269 std::string GenerateEnumToString(const AidlEnumDeclaration& enum_decl,
270 const std::string& backing_type) {
271 const auto q_name = GetQualifiedName(enum_decl);
272 std::ostringstream code;
273 bool is_enum_deprecated = IsEnumDeprecated(enum_decl);
274 if (is_enum_deprecated) {
275 code << "#pragma clang diagnostic push\n";
276 code << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
277 }
278 code << "[[nodiscard]] static inline std::string toString(" + q_name + " val) {\n";
279 code << " switch(val) {\n";
280 std::set<std::string> unique_cases;
281 for (const auto& enumerator : enum_decl.GetEnumerators()) {
282 std::string c = enumerator->ValueString(enum_decl.GetBackingType(), AidlConstantValueDecorator);
283 // Only add a case if its value has not yet been used in the switch
284 // statement. C++ does not allow multiple cases with the same value, but
285 // enums does allow this. In this scenario, the first declared
286 // enumerator with the given value is printed.
287 if (unique_cases.count(c) == 0) {
288 unique_cases.insert(c);
289 code << " case " << q_name << "::" << enumerator->GetName() << ":\n";
290 code << " return \"" << enumerator->GetName() << "\";\n";
291 }
292 }
293 code << " default:\n";
294 code << " return std::to_string(static_cast<" << backing_type << ">(val));\n";
295 code << " }\n";
296 code << "}\n";
297 if (is_enum_deprecated) {
298 code << "#pragma clang diagnostic pop\n";
299 }
300 return code.str();
301 }
302
TemplateDecl(const AidlParcelable & defined_type)303 std::string TemplateDecl(const AidlParcelable& defined_type) {
304 std::string decl = "";
305 if (defined_type.IsGeneric()) {
306 std::vector<std::string> template_params;
307 for (const auto& parameter : defined_type.GetTypeParameters()) {
308 template_params.push_back(parameter);
309 }
310 decl = base::StringPrintf("template <typename %s>\n",
311 base::Join(template_params, ", typename ").c_str());
312 }
313 return decl;
314 }
315
GenerateParcelableComparisonOperators(CodeWriter & out,const AidlParcelable & parcelable)316 void GenerateParcelableComparisonOperators(CodeWriter& out, const AidlParcelable& parcelable) {
317 std::set<string> operators{"<", ">", "==", ">=", "<=", "!="};
318
319 if (parcelable.AsUnionDeclaration() && parcelable.IsFixedSize()) {
320 auto name = parcelable.GetName();
321 auto max_tag = parcelable.GetFields().back()->GetName();
322 auto min_tag = parcelable.GetFields().front()->GetName();
323 auto tmpl = R"--(static int _cmp(const {name}& _lhs, const {name}& _rhs) {{
324 return _cmp_value(_lhs.getTag(), _rhs.getTag()) || _cmp_value_at<{max_tag}>(_lhs, _rhs);
325 }}
326 template <Tag _Tag>
327 static int _cmp_value_at(const {name}& _lhs, const {name}& _rhs) {{
328 if constexpr (_Tag == {min_tag}) {{
329 return _cmp_value(_lhs.get<_Tag>(), _rhs.get<_Tag>());
330 }} else {{
331 return (_lhs.getTag() == _Tag)
332 ? _cmp_value(_lhs.get<_Tag>(), _rhs.get<_Tag>())
333 : _cmp_value_at<static_cast<Tag>(static_cast<size_t>(_Tag)-1)>(_lhs, _rhs);
334 }}
335 }}
336 template <typename _Type>
337 static int _cmp_value(const _Type& _lhs, const _Type& _rhs) {{
338 return (_lhs == _rhs) ? 0 : (_lhs < _rhs) ? -1 : 1;
339 }}
340 )--";
341 out << fmt::format(tmpl, fmt::arg("name", name), fmt::arg("min_tag", min_tag),
342 fmt::arg("max_tag", max_tag));
343 for (const auto& op : operators) {
344 out << "inline bool operator" << op << "(const " << name << "&_rhs) const {\n";
345 out << " return _cmp(*this, _rhs) " << op << " 0;\n";
346 out << "}\n";
347 }
348 return;
349 }
350
351 bool is_empty = false;
352
353 auto comparable = [&](const string& prefix) {
354 vector<string> fields;
355 if (auto p = parcelable.AsStructuredParcelable(); p != nullptr) {
356 is_empty = p->GetFields().empty();
357 for (const auto& f : p->GetFields()) {
358 fields.push_back(prefix + f->GetName());
359 }
360 return "std::tie(" + Join(fields, ", ") + ")";
361 } else if (auto p = parcelable.AsUnionDeclaration(); p != nullptr) {
362 return prefix + "_value";
363 } else {
364 AIDL_FATAL(parcelable) << "Unknown paracelable type";
365 }
366 };
367
368 string lhs = comparable("");
369 string rhs = comparable("rhs.");
370 for (const auto& op : operators) {
371 out << "inline bool operator" << op << "(const " << parcelable.GetName() << "&"
372 << (is_empty ? "" : " rhs") << ") const {\n"
373 << " return " << lhs << " " << op << " " << rhs << ";\n"
374 << "}\n";
375 }
376 out << "\n";
377 }
378
379 // Output may look like:
380 // inline std::string toString() const {
381 // std::ostringstream os;
382 // os << "MyData{";
383 // os << "field1: " << field1;
384 // os << ", field2: " << v.field2;
385 // ...
386 // os << "}";
387 // return os.str();
388 // }
GenerateToString(CodeWriter & out,const AidlStructuredParcelable & parcelable)389 void GenerateToString(CodeWriter& out, const AidlStructuredParcelable& parcelable) {
390 out << "inline std::string toString() const {\n";
391 out.Indent();
392 out << "std::ostringstream os;\n";
393 out << "os << \"" << parcelable.GetName() << "{\";\n";
394 bool first = true;
395 for (const auto& f : parcelable.GetFields()) {
396 if (first) {
397 out << "os << \"";
398 first = false;
399 } else {
400 out << "os << \", ";
401 }
402 out << f->GetName() << ": \" << ::android::internal::ToString(" << f->GetName() << ");\n";
403 }
404 out << "os << \"}\";\n";
405 out << "return os.str();\n";
406 out.Dedent();
407 out << "}\n";
408 }
409
410 // Output may look like:
411 // inline std::string toString() const {
412 // std::ostringstream os;
413 // os << "MyData{";
414 // switch (v.getTag()) {
415 // case MyData::field: os << "field: " << v.get<MyData::field>(); break;
416 // ...
417 // }
418 // os << "}";
419 // return os.str();
420 // }
GenerateToString(CodeWriter & out,const AidlUnionDecl & parcelable)421 void GenerateToString(CodeWriter& out, const AidlUnionDecl& parcelable) {
422 out << "inline std::string toString() const {\n";
423 out.Indent();
424 out << "std::ostringstream os;\n";
425 out << "os << \"" + parcelable.GetName() + "{\";\n";
426 out << "switch (getTag()) {\n";
427 for (const auto& f : parcelable.GetFields()) {
428 if (f->IsDeprecated()) {
429 out << "#pragma clang diagnostic push\n";
430 out << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
431 }
432 const string tag = f->GetName();
433 out << "case " << tag << ": os << \"" << tag << ": \" << "
434 << "::android::internal::ToString(get<" + tag + ">()); break;\n";
435 if (f->IsDeprecated()) {
436 out << "#pragma clang diagnostic pop\n";
437 }
438 }
439 out << "}\n";
440 out << "os << \"}\";\n";
441 out << "return os.str();\n";
442 out.Dedent();
443 out << "}\n";
444 }
445
GetDeprecatedAttribute(const AidlCommentable & type)446 std::string GetDeprecatedAttribute(const AidlCommentable& type) {
447 if (auto deprecated = FindDeprecated(type.GetComments()); deprecated.has_value()) {
448 if (deprecated->note.empty()) {
449 return "__attribute__((deprecated))";
450 }
451 return "__attribute__((deprecated(" + QuotedEscape(deprecated->note) + ")))";
452 }
453 return "";
454 }
455
AlignmentOf(const AidlTypeSpecifier & type,const AidlTypenames & typenames)456 size_t AlignmentOf(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
457 static map<string, size_t> alignment = {
458 {"boolean", 1}, {"byte", 1}, {"char", 2}, {"double", 8},
459 {"float", 4}, {"int", 4}, {"long", 8},
460 };
461
462 string name = type.GetName();
463 if (auto enum_decl = typenames.GetEnumDeclaration(type); enum_decl) {
464 name = enum_decl->GetBackingType().GetName();
465 }
466 // default to 0 for parcelable types
467 return alignment[name];
468 }
469
GetHeaders(const AidlUnionDecl & decl)470 std::set<std::string> UnionWriter::GetHeaders(const AidlUnionDecl& decl) {
471 std::set<std::string> union_headers = {
472 "cassert", // __assert for logging
473 "type_traits", // std::is_same_v
474 "utility", // std::mode/forward for value
475 "variant", // union's impl
476 };
477 if (decl.IsFixedSize()) {
478 union_headers.insert("tuple"); // fixed-sized union's typelist
479 }
480 return union_headers;
481 }
482
483 // fixed-sized union class looks like:
484 // class Union {
485 // public:
486 // enum Tag : uint8_t {
487 // field1 = 0,
488 // field2,
489 // };
490 // ... methods ...
491 // private:
492 // Tag _tag;
493 // union {
494 // type1 field1;
495 // type2 field2;
496 // } _value;
497 // };
498
PrivateFields(CodeWriter & out) const499 void UnionWriter::PrivateFields(CodeWriter& out) const {
500 if (decl.IsFixedSize()) {
501 AIDL_FATAL_IF(decl.GetFields().empty(), decl) << "Union '" << decl.GetName() << "' is empty.";
502 const auto& first_field = decl.GetFields()[0];
503 const auto& default_name = first_field->GetName();
504 const auto& default_value = name_of(first_field->GetType(), typenames) + "(" +
505 first_field->ValueString(decorator) + ")";
506
507 out << "Tag _tag = " << default_name << ";\n";
508 out << "union _value_t {\n";
509 out.Indent();
510 out << "_value_t() {}\n";
511 out << "~_value_t() {}\n";
512 for (const auto& f : decl.GetFields()) {
513 const auto& fn = f->GetName();
514 out << name_of(f->GetType(), typenames) << " " << fn;
515 if (decl.IsFixedSize()) {
516 int alignment = AlignmentOf(f->GetType(), typenames);
517 if (alignment > 0) {
518 out << " __attribute__((aligned (" << std::to_string(alignment) << ")))";
519 }
520 }
521 if (fn == default_name) {
522 out << " = " << default_value;
523 }
524 out << ";\n";
525 }
526 out.Dedent();
527 out << "} _value;\n";
528 } else {
529 vector<string> field_types;
530 for (const auto& f : decl.GetFields()) {
531 field_types.push_back(name_of(f->GetType(), typenames));
532 }
533 out << "std::variant<" + Join(field_types, ", ") + "> _value;\n";
534 }
535 }
536
PublicFields(CodeWriter & out) const537 void UnionWriter::PublicFields(CodeWriter& out) const {
538 out << "// Expose tag symbols for legacy code\n";
539 for (const auto& f : decl.GetFields()) {
540 out << "static const inline Tag";
541 GenerateDeprecated(out, *f);
542 out << " " << f->GetName() << " = Tag::" << f->GetName() << ";\n";
543 }
544
545 const auto& name = decl.GetName();
546
547 if (decl.IsFixedSize()) {
548 vector<string> field_types;
549 for (const auto& f : decl.GetFields()) {
550 field_types.push_back(name_of(f->GetType(), typenames));
551 }
552 auto typelist = Join(field_types, ", ");
553 auto tmpl = R"--(
554 template <Tag _Tag>
555 using _at = typename std::tuple_element<static_cast<size_t>(_Tag), std::tuple<{typelist}>>::type;
556 template <Tag _Tag, typename _Type>
557 static {name} make(_Type&& _arg) {{
558 {name} _inst;
559 _inst.set<_Tag>(std::forward<_Type>(_arg));
560 return _inst;
561 }}
562 constexpr Tag getTag() const {{
563 return _tag;
564 }}
565 template <Tag _Tag>
566 const _at<_Tag>& get() const {{
567 if (_Tag != _tag) {{ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }}
568 return *(_at<_Tag>*)(&_value);
569 }}
570 template <Tag _Tag>
571 _at<_Tag>& get() {{
572 if (_Tag != _tag) {{ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }}
573 return *(_at<_Tag>*)(&_value);
574 }}
575 template <Tag _Tag, typename _Type>
576 void set(_Type&& _arg) {{
577 _tag = _Tag;
578 get<_Tag>() = std::forward<_Type>(_arg);
579 }}
580 )--";
581 out << fmt::format(tmpl, fmt::arg("name", name), fmt::arg("typelist", typelist));
582 } else {
583 AIDL_FATAL_IF(decl.GetFields().empty(), decl) << "Union '" << name << "' is empty.";
584 const auto& first_field = decl.GetFields()[0];
585 const auto& default_name = first_field->GetName();
586 const auto& default_value = name_of(first_field->GetType(), typenames) + "(" +
587 first_field->ValueString(decorator) + ")";
588
589 auto tmpl = R"--(
590 template<typename _Tp>
591 static constexpr bool _not_self = !std::is_same_v<std::remove_cv_t<std::remove_reference_t<_Tp>>, {name}>;
592
593 {name}() : _value(std::in_place_index<static_cast<size_t>({default_name})>, {default_value}) {{ }}
594
595 template <typename _Tp, typename = std::enable_if_t<_not_self<_Tp>>>
596 // NOLINTNEXTLINE(google-explicit-constructor)
597 constexpr {name}(_Tp&& _arg)
598 : _value(std::forward<_Tp>(_arg)) {{}}
599
600 template <size_t _Np, typename... _Tp>
601 constexpr explicit {name}(std::in_place_index_t<_Np>, _Tp&&... _args)
602 : _value(std::in_place_index<_Np>, std::forward<_Tp>(_args)...) {{}}
603
604 template <Tag _tag, typename... _Tp>
605 static {name} make(_Tp&&... _args) {{
606 return {name}(std::in_place_index<static_cast<size_t>(_tag)>, std::forward<_Tp>(_args)...);
607 }}
608
609 template <Tag _tag, typename _Tp, typename... _Up>
610 static {name} make(std::initializer_list<_Tp> _il, _Up&&... _args) {{
611 return {name}(std::in_place_index<static_cast<size_t>(_tag)>, std::move(_il), std::forward<_Up>(_args)...);
612 }}
613
614 Tag getTag() const {{
615 return static_cast<Tag>(_value.index());
616 }}
617
618 template <Tag _tag>
619 const auto& get() const {{
620 if (getTag() != _tag) {{ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }}
621 return std::get<static_cast<size_t>(_tag)>(_value);
622 }}
623
624 template <Tag _tag>
625 auto& get() {{
626 if (getTag() != _tag) {{ __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, "bad access: a wrong tag"); }}
627 return std::get<static_cast<size_t>(_tag)>(_value);
628 }}
629
630 template <Tag _tag, typename... _Tp>
631 void set(_Tp&&... _args) {{
632 _value.emplace<static_cast<size_t>(_tag)>(std::forward<_Tp>(_args)...);
633 }}
634
635 )--";
636 out << fmt::format(tmpl, fmt::arg("name", name), fmt::arg("default_name", default_name),
637 fmt::arg("default_value", default_value));
638 }
639 }
640
ReadFromParcel(CodeWriter & out,const ParcelWriterContext & ctx) const641 void UnionWriter::ReadFromParcel(CodeWriter& out, const ParcelWriterContext& ctx) const {
642 // Even though @FixedSize union may use a smaller type than int32_t, we need to read/write it
643 // as if it is int32_t for compatibility with other bckends.
644 auto tag_type = typenames.MakeResolvedType(AIDL_LOCATION_HERE, "int", /* is_array= */ false);
645
646 const string tag = "_aidl_tag";
647 const string value = "_aidl_value";
648 const string status = "_aidl_ret_status";
649
650 auto read_var = [&](const string& var, const AidlTypeSpecifier& type) {
651 out << fmt::format("{} {};\n", name_of(type, typenames), var);
652 out << fmt::format("if (({} = ", status);
653 ctx.read_func(out, var, type);
654 out << fmt::format(") != {}) return {};\n", ctx.status_ok, status);
655 };
656
657 out << fmt::format("{} {};\n", ctx.status_type, status);
658 read_var(tag, *tag_type);
659 out << fmt::format("switch (static_cast<Tag>({})) {{\n", tag);
660 for (const auto& variable : decl.GetFields()) {
661 if (variable->IsDeprecated()) {
662 out << "#pragma clang diagnostic push\n";
663 out << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
664 }
665 out << fmt::format("case {}: {{\n", variable->GetName());
666 out.Indent();
667 const auto& type = variable->GetType();
668 read_var(value, type);
669 out << fmt::format("if constexpr (std::is_trivially_copyable_v<{}>) {{\n",
670 name_of(type, typenames));
671 out.Indent();
672 out << fmt::format("set<{}>({});\n", variable->GetName(), value);
673 out.Dedent();
674 out << "} else {\n";
675 out.Indent();
676 // Even when the `if constexpr` is false, the compiler runs the tidy check for the
677 // next line, which doesn't make sense. Silence the check for the unreachable code.
678 out << "// NOLINTNEXTLINE(performance-move-const-arg)\n";
679 out << fmt::format("set<{}>(std::move({}));\n", variable->GetName(), value);
680 out.Dedent();
681 out << "}\n";
682 out << fmt::format("return {}; }}\n", ctx.status_ok);
683 out.Dedent();
684 if (variable->IsDeprecated()) {
685 out << "#pragma clang diagnostic pop\n";
686 }
687 }
688 out << "}\n";
689 out << fmt::format("return {};\n", ctx.status_bad);
690 }
691
WriteToParcel(CodeWriter & out,const ParcelWriterContext & ctx) const692 void UnionWriter::WriteToParcel(CodeWriter& out, const ParcelWriterContext& ctx) const {
693 // Even though @FixedSize union may use a smaller type than int32_t, we need to read/write it
694 // as if it is int32_t for compatibility with other bckends.
695 auto tag_type = typenames.MakeResolvedType(AIDL_LOCATION_HERE, "int", /* is_array= */ false);
696
697 const string tag = "_aidl_tag";
698 const string value = "_aidl_value";
699 const string status = "_aidl_ret_status";
700
701 out << fmt::format("{} {} = ", ctx.status_type, status);
702 ctx.write_func(out, "static_cast<int32_t>(getTag())", *tag_type);
703 out << ";\n";
704 out << fmt::format("if ({} != {}) return {};\n", status, ctx.status_ok, status);
705 out << "switch (getTag()) {\n";
706 for (const auto& variable : decl.GetFields()) {
707 if (variable->IsDeprecated()) {
708 out << "#pragma clang diagnostic push\n";
709 out << "#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n";
710 }
711 out << fmt::format("case {}: return ", variable->GetName());
712 ctx.write_func(out, "get<" + variable->GetName() + ">()", variable->GetType());
713 out << ";\n";
714 if (variable->IsDeprecated()) {
715 out << "#pragma clang diagnostic pop\n";
716 }
717 }
718 out << "}\n";
719 out << "__assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__, \"can't reach here\");\n";
720 }
721
CppConstantValueDecorator(const AidlTypeSpecifier & type,const std::variant<std::string,std::vector<std::string>> & raw_value,bool is_ndk)722 std::string CppConstantValueDecorator(
723 const AidlTypeSpecifier& type,
724 const std::variant<std::string, std::vector<std::string>>& raw_value, bool is_ndk) {
725 if (type.IsArray()) {
726 auto values = std::get<std::vector<std::string>>(raw_value);
727 // Hexadecimal literals for byte arrays should be casted to uint8_t
728 if (type.GetName() == "byte" &&
729 std::any_of(values.begin(), values.end(),
730 [](const auto& value) { return !value.empty() && value[0] == '-'; })) {
731 for (auto& value : values) {
732 // cast only if necessary
733 if (value[0] == '-') {
734 value = "uint8_t(" + value + ")";
735 }
736 }
737 }
738 std::string value = "{" + Join(values, ", ") + "}";
739
740 if (type.IsFixedSizeArray()) {
741 // For arrays, use double braces because arrays can be nested.
742 // e.g.) array<array<int, 2>, 3> ints = {{ {{1,2}}, {{3,4}}, {{5,6}} }};
743 // Vectors might need double braces, but since we don't have nested vectors (yet)
744 // single brace would work even for optional vectors.
745 value = "{" + value + "}";
746 }
747
748 if (!type.IsMutated() && type.IsNullable()) {
749 // For outermost std::optional<>, we need an additional brace pair to initialize its value.
750 value = "{" + value + "}";
751 }
752 return value;
753 }
754
755 const std::string& value = std::get<std::string>(raw_value);
756 if (AidlTypenames::IsBuiltinTypename(type.GetName())) {
757 if (type.GetName() == "boolean") {
758 return value;
759 } else if (type.GetName() == "byte") {
760 return value;
761 } else if (type.GetName() == "char") {
762 // TODO: consider 'L'-prefix for wide char literal
763 return value;
764 } else if (type.GetName() == "double") {
765 return value;
766 } else if (type.GetName() == "float") {
767 return value; // value has 'f' suffix
768 } else if (type.GetName() == "int") {
769 return value;
770 } else if (type.GetName() == "long") {
771 return value + "L";
772 } else if (type.GetName() == "String") {
773 if (is_ndk || type.IsUtf8InCpp()) {
774 return value;
775 } else {
776 return "::android::String16(" + value + ")";
777 }
778 }
779 AIDL_FATAL(type) << "Unknown built-in type: " << type.GetName();
780 }
781
782 auto defined_type = type.GetDefinedType();
783 AIDL_FATAL_IF(!defined_type, type) << "Invalid type for \"" << value << "\"";
784 auto enum_type = defined_type->AsEnumDeclaration();
785 AIDL_FATAL_IF(!enum_type, type) << "Invalid type for \"" << value << "\"";
786
787 auto cpp_type_name = "::" + Join(Split(enum_type->GetCanonicalName(), "."), "::");
788 if (is_ndk) {
789 cpp_type_name = "::aidl" + cpp_type_name;
790 }
791 return cpp_type_name + "::" + value.substr(value.find_last_of('.') + 1);
792 }
793 } // namespace cpp
794 } // namespace aidl
795 } // namespace android
796