• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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