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  * limitations under the License.
13  */
14 
15 #include "aidl_to_ndk.h"
16 #include "aidl_language.h"
17 #include "aidl_to_cpp_common.h"
18 #include "logging.h"
19 #include "os.h"
20 
21 #include <android-base/stringprintf.h>
22 #include <android-base/strings.h>
23 
24 #include <functional>
25 
26 using ::android::base::Join;
27 using ::android::base::Split;
28 
29 namespace android {
30 namespace aidl {
31 namespace ndk {
32 
NdkHeaderFile(const AidlDefinedType & defined_type,cpp::ClassNames name,bool use_os_sep)33 std::string NdkHeaderFile(const AidlDefinedType& defined_type, cpp::ClassNames name,
34                           bool use_os_sep) {
35   // Unstructured parcelable should set its ndk_header. use it.
36   if (auto unstructured = AidlCast<AidlParcelable>(defined_type); unstructured) {
37     AIDL_FATAL_IF(name != cpp::ClassNames::RAW, "unstructured parcelable should only use raw name");
38     const std::string ndk_header = unstructured->GetNdkHeader();
39     AIDL_FATAL_IF(ndk_header.empty(), unstructured)
40         << "Parcelable " << unstructured->GetCanonicalName() << " has no ndk_header defined.";
41     return ndk_header;
42   }
43 
44   char seperator = (use_os_sep) ? OS_PATH_SEPARATOR : '/';
45   return std::string("aidl") + seperator + cpp::HeaderFile(defined_type, name, use_os_sep);
46 }
47 
48 // This represents a type in AIDL (e.g. 'String' which can be referenced in multiple ways)
49 struct TypeInfo {
50   // name of the type in C++ output
51   std::string cpp_name;
52   // whether to prefer 'value type' over 'const&'
53   bool value_is_cheap = false;
54 };
55 
ConstantValueDecorator(const AidlTypeSpecifier & type,const std::variant<std::string,std::vector<std::string>> & raw_value)56 std::string ConstantValueDecorator(
57     const AidlTypeSpecifier& type,
58     const std::variant<std::string, std::vector<std::string>>& raw_value) {
59   return cpp::CppConstantValueDecorator(type, raw_value, /*is_ndk=*/true);
60 };
61 
62 // map from AIDL built-in type name to the corresponding Ndk type info
63 static map<std::string, TypeInfo> kNdkTypeInfoMap = {
64     {"void", {"void", true}},
65     {"boolean", {"bool", true}},
66     {"byte", {"int8_t", true}},
67     {"char", {"char16_t", true}},
68     {"int", {"int32_t", true}},
69     {"long", {"int64_t", true}},
70     {"float", {"float", true}},
71     {"double", {"double", true}},
72     {"String", {"std::string"}},
73     // TODO(b/136048684) {"Map", ""},
74     {"IBinder", {"::ndk::SpAIBinder"}},
75     {"ParcelFileDescriptor", {"::ndk::ScopedFileDescriptor"}},
76     {"ParcelableHolder", {"::ndk::AParcelableHolder"}},
77 };
78 
GetBaseTypeInfo(const AidlTypenames & types,const AidlTypeSpecifier & aidl)79 static TypeInfo GetBaseTypeInfo(const AidlTypenames& types, const AidlTypeSpecifier& aidl) {
80   auto& aidl_name = aidl.GetName();
81 
82   if (AidlTypenames::IsBuiltinTypename(aidl_name)) {
83     auto it = kNdkTypeInfoMap.find(aidl_name);
84     AIDL_FATAL_IF(it == kNdkTypeInfoMap.end(), aidl_name);
85     return it->second;
86   }
87   const AidlDefinedType* type = types.TryGetDefinedType(aidl_name);
88   AIDL_FATAL_IF(type == nullptr, aidl_name) << "Unrecognized type.";
89 
90   if (const AidlInterface* intf = type->AsInterface(); intf != nullptr) {
91     const std::string clazz = NdkFullClassName(*intf, cpp::ClassNames::INTERFACE);
92     return TypeInfo{"std::shared_ptr<" + clazz + ">"};
93   } else if (const AidlParcelable* parcelable = type->AsParcelable(); parcelable != nullptr) {
94     std::string clazz = NdkFullClassName(*parcelable, cpp::ClassNames::RAW);
95     std::string template_params = "";
96     if (aidl.IsGeneric()) {
97       std::vector<std::string> type_params;
98       for (const auto& parameter : aidl.GetTypeParameters()) {
99         type_params.push_back(NdkNameOf(types, *parameter, StorageMode::STACK));
100       }
101       clazz += base::StringPrintf("<%s>", base::Join(type_params, ", ").c_str());
102     }
103     return TypeInfo{clazz};
104   } else if (const AidlEnumDeclaration* enum_decl = type->AsEnumDeclaration();
105              enum_decl != nullptr) {
106     const std::string clazz = NdkFullClassName(*enum_decl, cpp::ClassNames::RAW);
107     return TypeInfo{clazz, true};
108   } else {
109     AIDL_FATAL(aidl_name) << "Unrecognized type";
110   }
111 }
112 
WrapNullableType(TypeInfo info,bool is_heap)113 static TypeInfo WrapNullableType(TypeInfo info, bool is_heap) {
114   if (is_heap) {
115     info.cpp_name = "std::unique_ptr<" + info.cpp_name + ">";
116   } else {
117     info.cpp_name = "std::optional<" + info.cpp_name + ">";
118   }
119   info.value_is_cheap = false;
120   return info;
121 }
122 
WrapArrayType(TypeInfo info,const ArrayType * array)123 static TypeInfo WrapArrayType(TypeInfo info, const ArrayType* array) {
124   AIDL_FATAL_IF(!array, AIDL_LOCATION_HERE) << "not an array";
125   // When "byte"(AIDL) is used in an array, use "uint8_t" because it's more C++ idiomatic.
126   if (info.cpp_name == "int8_t") {
127     info.cpp_name = "uint8_t";
128   }
129   if (std::get_if<DynamicArray>(array)) {
130     info.cpp_name = "std::vector<" + info.cpp_name + ">";
131   } else {
132     auto dimensions = std::get<FixedSizeArray>(*array).GetDimensionInts();
133     for (auto it = rbegin(dimensions), end = rend(dimensions); it != end; it++) {
134       info.cpp_name = "std::array<" + info.cpp_name + ", " + std::to_string(*it) + ">";
135     }
136   }
137   info.value_is_cheap = false;
138   return info;
139 }
140 
ShouldWrapNullable(const AidlTypenames & types,const std::string & aidl_name)141 static bool ShouldWrapNullable(const AidlTypenames& types, const std::string& aidl_name) {
142   if (AidlTypenames::IsPrimitiveTypename(aidl_name) || aidl_name == "ParcelableHolder" ||
143       aidl_name == "IBinder" || aidl_name == "ParcelFileDescriptor") {
144     return false;
145   }
146   if (auto defined_type = types.TryGetDefinedType(aidl_name); defined_type) {
147     if (defined_type->AsEnumDeclaration() || defined_type->AsInterface()) {
148       return false;
149     }
150   }
151   return true;
152 }
153 
GetTypeInfo(const AidlTypenames & types,const AidlTypeSpecifier & aidl)154 static TypeInfo GetTypeInfo(const AidlTypenames& types, const AidlTypeSpecifier& aidl) {
155   AIDL_FATAL_IF(!aidl.IsResolved(), aidl) << aidl.ToString();
156   // Keep original @nullable to handle the case of List<T>. "@nullable" is attached to "List" not
157   // "T"
158   bool is_nullable = aidl.IsNullable();
159   const ArrayType* array = nullptr;
160   const AidlTypeSpecifier* element_type = &aidl;
161 
162   // List<T> is converted to T[].
163   if (aidl.GetName() == "List") {
164     static const ArrayType kDynamicArray{DynamicArray{}};
165 
166     AIDL_FATAL_IF(!aidl.IsGeneric(), aidl) << "List must be generic type.";
167     AIDL_FATAL_IF(aidl.GetTypeParameters().size() != 1, aidl)
168         << "List can accept only one type parameter.";
169     const auto& type_param = *aidl.GetTypeParameters()[0];
170     // TODO(b/136048684) AIDL doesn't support nested type parameter yet.
171     AIDL_FATAL_IF(type_param.IsGeneric(), aidl) << "AIDL doesn't support nested type parameter";
172     // Treat "List<T>" as an array of T.
173     array = &kDynamicArray;
174     element_type = &type_param;
175   } else if (aidl.IsArray()) {
176     array = &aidl.GetArray();
177   }
178 
179   TypeInfo info = GetBaseTypeInfo(types, *element_type);
180 
181   if (is_nullable && ShouldWrapNullable(types, element_type->GetName())) {
182     info = WrapNullableType(info, aidl.IsHeapNullable());
183   }
184   if (array) {
185     info = WrapArrayType(info, array);
186     if (is_nullable) {
187       AIDL_FATAL_IF(aidl.IsHeapNullable(), aidl) << "Array/List can't be @nullable(heap=true)";
188       info = WrapNullableType(info, /*is_heap=*/false);
189     }
190   }
191   return info;
192 }
193 
NdkFullClassName(const AidlDefinedType & type,cpp::ClassNames name)194 std::string NdkFullClassName(const AidlDefinedType& type, cpp::ClassNames name) {
195   std::vector<std::string> pieces = {"::aidl"};
196   std::vector<std::string> split_name = Split(type.GetCanonicalName(), ".");
197   pieces.insert(pieces.end(), split_name.begin(), split_name.end());
198   // Override name part with cpp::ClassName(type, name)
199   pieces.back() = cpp::ClassName(type, name);
200   return Join(pieces, "::");
201 }
202 
NdkNameOf(const AidlTypenames & types,const AidlTypeSpecifier & aidl,StorageMode mode)203 std::string NdkNameOf(const AidlTypenames& types, const AidlTypeSpecifier& aidl, StorageMode mode) {
204   TypeInfo aspect = GetTypeInfo(types, aidl);
205 
206   switch (mode) {
207     case StorageMode::STACK:
208       return aspect.cpp_name;
209     case StorageMode::ARGUMENT:
210       if (aspect.value_is_cheap) {
211         return aspect.cpp_name;
212       } else {
213         return "const " + aspect.cpp_name + "&";
214       }
215     case StorageMode::OUT_ARGUMENT:
216       return aspect.cpp_name + "*";
217     default:
218       AIDL_FATAL(aidl.GetName()) << "Unrecognized mode type: " << static_cast<int>(mode);
219   }
220 }
221 
WriteToParcelFor(const CodeGeneratorContext & c)222 void WriteToParcelFor(const CodeGeneratorContext& c) {
223   if (c.type.IsNullable()) {
224     c.writer << "::ndk::AParcel_writeNullableData(" << c.parcel << ", " << c.var << ")";
225   } else {
226     c.writer << "::ndk::AParcel_writeData(" << c.parcel << ", " << c.var << ")";
227   }
228 }
229 
ReadFromParcelFor(const CodeGeneratorContext & c)230 void ReadFromParcelFor(const CodeGeneratorContext& c) {
231   if (c.type.IsNullable()) {
232     c.writer << "::ndk::AParcel_readNullableData(" << c.parcel << ", " << c.var << ")";
233   } else {
234     c.writer << "::ndk::AParcel_readData(" << c.parcel << ", " << c.var << ")";
235   }
236 }
237 
NdkArgList(const AidlTypenames & types,const AidlMethod & method,std::function<std::string (const std::string & type,const std::string & name,bool isOut)> formatter)238 std::string NdkArgList(
239     const AidlTypenames& types, const AidlMethod& method,
240     std::function<std::string(const std::string& type, const std::string& name, bool isOut)>
241         formatter) {
242   std::vector<std::string> method_arguments;
243   for (const auto& a : method.GetArguments()) {
244     StorageMode mode = a->IsOut() ? StorageMode::OUT_ARGUMENT : StorageMode::ARGUMENT;
245     std::string type = NdkNameOf(types, a->GetType(), mode);
246     std::string name = cpp::BuildVarName(*a);
247     method_arguments.emplace_back(formatter(type, name, a->IsOut()));
248   }
249 
250   if (method.GetType().GetName() != "void") {
251     std::string type = NdkNameOf(types, method.GetType(), StorageMode::OUT_ARGUMENT);
252     std::string name = "_aidl_return";
253     method_arguments.emplace_back(formatter(type, name, true));
254   }
255 
256   return Join(method_arguments, ", ");
257 }
258 
NdkMethodDecl(const AidlTypenames & types,const AidlMethod & method,const std::string & clazz)259 std::string NdkMethodDecl(const AidlTypenames& types, const AidlMethod& method,
260                           const std::string& clazz) {
261   std::string class_prefix = clazz.empty() ? "" : (clazz + "::");
262   return "::ndk::ScopedAStatus " + class_prefix + method.GetName() + "(" +
263          NdkArgList(types, method, FormatArgForDecl) + ")";
264 }
265 
266 }  // namespace ndk
267 }  // namespace aidl
268 }  // namespace android
269