• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020, 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 
17 #include "aidl_to_rust.h"
18 #include "aidl_language.h"
19 #include "aidl_typenames.h"
20 #include "logging.h"
21 
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 
25 #include <functional>
26 #include <iostream>
27 #include <map>
28 #include <string>
29 #include <vector>
30 
31 using android::base::Join;
32 using android::base::Split;
33 using android::base::StringPrintf;
34 
35 namespace android {
36 namespace aidl {
37 namespace rust {
38 
39 namespace {
40 std::string GetRawRustName(const AidlTypeSpecifier& type);
41 
ConstantValueDecoratorInternal(const AidlTypeSpecifier & type,const std::variant<std::string,std::vector<std::string>> & raw_value,bool by_ref)42 std::string ConstantValueDecoratorInternal(
43     const AidlTypeSpecifier& type,
44     const std::variant<std::string, std::vector<std::string>>& raw_value, bool by_ref) {
45   if (type.IsArray()) {
46     const auto& values = std::get<std::vector<std::string>>(raw_value);
47     std::string value = "[" + Join(values, ", ") + "]";
48     if (type.IsDynamicArray()) {
49       value = "vec!" + value;
50     }
51     if (!type.IsMutated() && type.IsNullable()) {
52       value = "Some(" + value + ")";
53     }
54     return value;
55   }
56 
57   std::string value = std::get<std::string>(raw_value);
58 
59   const auto& aidl_name = type.GetName();
60   if (aidl_name == "char") {
61     return value + " as u16";
62   }
63 
64   if (aidl_name == "float") {
65     // value already ends in `f`, so just add `32`
66     return value + "32";
67   }
68 
69   if (aidl_name == "double") {
70     return value + "f64";
71   }
72 
73   if (auto defined_type = type.GetDefinedType(); defined_type) {
74     auto enum_type = defined_type->AsEnumDeclaration();
75     AIDL_FATAL_IF(!enum_type, type) << "Invalid type for \"" << value << "\"";
76     return GetRawRustName(type) + "::" + value.substr(value.find_last_of('.') + 1);
77   }
78 
79   if (aidl_name == "String" && !by_ref) {
80     // The actual type might be String or &str,
81     // and .into() transparently converts into either one
82     value = value + ".into()";
83   }
84 
85   if (type.IsNullable()) {
86     value = "Some(" + value + ")";
87   }
88 
89   return value;
90 }
91 
GetRawRustName(const AidlTypeSpecifier & type)92 std::string GetRawRustName(const AidlTypeSpecifier& type) {
93   // Each Rust type is defined in a file with the same name,
94   // e.g., IFoo is in IFoo.rs
95   auto split_name = type.GetSplitName();
96   std::string rust_name{"crate::mangled::"};
97   for (const auto& component : split_name) {
98     rust_name += StringPrintf("_%zd_%s", component.size(), component.c_str());
99   }
100   return rust_name;
101 }
102 
GetRustName(const AidlTypeSpecifier & type,const AidlTypenames & typenames,StorageMode mode)103 std::string GetRustName(const AidlTypeSpecifier& type, const AidlTypenames& typenames,
104                         StorageMode mode) {
105   // map from AIDL built-in type name to the corresponding Rust type name
106   static map<string, string> m = {
107       {"void", "()"},
108       {"boolean", "bool"},
109       {"byte", "i8"},
110       {"char", "u16"},
111       {"int", "i32"},
112       {"long", "i64"},
113       {"float", "f32"},
114       {"double", "f64"},
115       {"String", "String"},
116       {"IBinder", "binder::SpIBinder"},
117       {"ParcelFileDescriptor", "binder::ParcelFileDescriptor"},
118       {"ParcelableHolder", "binder::ParcelableHolder"},
119   };
120   const bool is_vector = type.IsArray() || typenames.IsList(type);
121   // If the type is an array/List<T>, get the inner element type
122   AIDL_FATAL_IF(typenames.IsList(type) && type.GetTypeParameters().size() != 1, type);
123   const auto& element_type = type.IsGeneric() ? (*type.GetTypeParameters().at(0)) : type;
124   const string& element_type_name = element_type.GetName();
125   if (m.find(element_type_name) != m.end()) {
126     AIDL_FATAL_IF(!AidlTypenames::IsBuiltinTypename(element_type_name), type);
127     if (element_type_name == "byte" && type.IsArray()) {
128       return "u8";
129     } else if (element_type_name == "String" && mode == StorageMode::UNSIZED_ARGUMENT) {
130       return "str";
131     } else if (element_type_name == "ParcelFileDescriptor" || element_type_name == "IBinder") {
132       if (is_vector && mode == StorageMode::DEFAULT_VALUE) {
133         // Out-arguments of ParcelFileDescriptors arrays need to
134         // be Vec<Option<ParcelFileDescriptor>> so resize_out_vec
135         // can initialize all elements to None (it requires Default
136         // and ParcelFileDescriptor doesn't implement that)
137         return "Option<" + m[element_type_name] + ">";
138       } else {
139         return m[element_type_name];
140       }
141     }
142     return m[element_type_name];
143   }
144   auto name = GetRawRustName(element_type);
145   if (TypeIsInterface(element_type, typenames)) {
146     name = "binder::Strong<dyn " + name + ">";
147     if (is_vector && mode == StorageMode::DEFAULT_VALUE) {
148       // Out-arguments of interface arrays need to be Vec<Option<...>> so resize_out_vec
149       // can initialize all elements to None.
150       name = "Option<" + name + ">";
151     }
152   }
153   return name;
154 }
155 }  // namespace
156 
ConstantValueDecorator(const AidlTypeSpecifier & type,const std::variant<std::string,std::vector<std::string>> & raw_value)157 std::string ConstantValueDecorator(
158     const AidlTypeSpecifier& type,
159     const std::variant<std::string, std::vector<std::string>>& raw_value) {
160   return ConstantValueDecoratorInternal(type, raw_value, false);
161 }
162 
ConstantValueDecoratorRef(const AidlTypeSpecifier & type,const std::variant<std::string,std::vector<std::string>> & raw_value)163 std::string ConstantValueDecoratorRef(
164     const AidlTypeSpecifier& type,
165     const std::variant<std::string, std::vector<std::string>>& raw_value) {
166   return ConstantValueDecoratorInternal(type, raw_value, true);
167 }
168 
169 // Returns default value for array.
ArrayDefaultValue(const AidlTypeSpecifier & type)170 std::string ArrayDefaultValue(const AidlTypeSpecifier& type) {
171   AIDL_FATAL_IF(!type.IsFixedSizeArray(), type) << "not a fixed-size array";
172   auto dimensions = type.GetFixedSizeArrayDimensions();
173   std::string value = "Default::default()";
174   for (auto it = rbegin(dimensions), end = rend(dimensions); it != end; it++) {
175     value = "[" + Join(std::vector<std::string>(*it, value), ", ") + "]";
176   }
177   return value;
178 }
179 
180 // Returns true if @nullable T[] should be mapped Option<Vec<Option<T>>
UsesOptionInNullableVector(const AidlTypeSpecifier & type,const AidlTypenames & typenames)181 bool UsesOptionInNullableVector(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
182   AIDL_FATAL_IF(!type.IsArray() && !typenames.IsList(type), type) << "not a vector";
183   AIDL_FATAL_IF(typenames.IsList(type) && type.GetTypeParameters().size() != 1, type)
184       << "List should have a single type arg.";
185 
186   const auto& element_type = type.IsArray() ? type : *type.GetTypeParameters().at(0);
187   if (typenames.IsPrimitiveTypename(element_type.GetName())) {
188     return false;
189   }
190   if (typenames.GetEnumDeclaration(element_type)) {
191     return false;
192   }
193   return true;
194 }
195 
RustLifetimeName(Lifetime lifetime)196 std::string RustLifetimeName(Lifetime lifetime) {
197   switch (lifetime) {
198     case Lifetime::NONE:
199       return "";
200     case Lifetime::A:
201       return "'a ";
202   }
203 }
204 
RustLifetimeGeneric(Lifetime lifetime)205 std::string RustLifetimeGeneric(Lifetime lifetime) {
206   switch (lifetime) {
207     case Lifetime::NONE:
208       return "";
209     case Lifetime::A:
210       return "<'a>";
211   }
212 }
213 
RustNameOf(const AidlTypeSpecifier & type,const AidlTypenames & typenames,StorageMode mode,Lifetime lifetime)214 std::string RustNameOf(const AidlTypeSpecifier& type, const AidlTypenames& typenames,
215                        StorageMode mode, Lifetime lifetime) {
216   std::string rust_name;
217   if (type.IsArray() || typenames.IsList(type)) {
218     StorageMode element_mode;
219     if (type.IsFixedSizeArray() && mode == StorageMode::PARCELABLE_FIELD) {
220       // Elements of fixed-size array field need to have Default.
221       element_mode = StorageMode::DEFAULT_VALUE;
222     } else if (mode == StorageMode::OUT_ARGUMENT || mode == StorageMode::DEFAULT_VALUE) {
223       // Elements need to have Default for resize_out_vec()
224       element_mode = StorageMode::DEFAULT_VALUE;
225     } else {
226       element_mode = StorageMode::VALUE;
227     }
228     rust_name = GetRustName(type, typenames, element_mode);
229     if (type.IsNullable() && UsesOptionInNullableVector(type, typenames)) {
230       // The mapping for nullable string arrays is
231       // optional<vector<optional<string>>> in the NDK,
232       // so we do the same
233       // However, we don't need to when GetRustName() already wraps it with Option.
234       if (!base::StartsWith(rust_name, "Option<")) {
235         rust_name = "Option<" + rust_name + ">";
236       }
237     }
238     if (mode == StorageMode::UNSIZED_ARGUMENT) {
239       rust_name = "[" + rust_name + "]";
240     } else if (type.IsFixedSizeArray()) {
241       auto dimensions = type.GetFixedSizeArrayDimensions();
242       // T[N][M] => [[T; M]; N]
243       for (auto it = rbegin(dimensions), end = rend(dimensions); it != end; it++) {
244         rust_name = "[" + rust_name + "; " + std::to_string(*it) + "]";
245       }
246     } else {
247       rust_name = "Vec<" + rust_name + ">";
248     }
249   } else {
250     rust_name = GetRustName(type, typenames, mode);
251   }
252 
253   if (mode == StorageMode::IN_ARGUMENT || mode == StorageMode::UNSIZED_ARGUMENT) {
254     // If this is a nullable input argument, put the reference inside the option,
255     // e.g., `Option<&str>` instead of `&Option<str>`
256     rust_name = "&" + RustLifetimeName(lifetime) + rust_name;
257   }
258 
259   if (type.IsNullable() ||
260       // Some types don't implement Default, so we wrap them
261       // in Option, which defaults to None
262       (TypeNeedsOption(type, typenames) &&
263        (mode == StorageMode::DEFAULT_VALUE || mode == StorageMode::OUT_ARGUMENT ||
264         mode == StorageMode::PARCELABLE_FIELD))) {
265     if (type.IsHeapNullable()) {
266       rust_name = "Option<Box<" + rust_name + ">>";
267     } else {
268       rust_name = "Option<" + rust_name + ">";
269     }
270   }
271 
272   if (mode == StorageMode::OUT_ARGUMENT || mode == StorageMode::INOUT_ARGUMENT) {
273     rust_name = "&" + RustLifetimeName(lifetime) + "mut " + rust_name;
274   }
275 
276   return rust_name;
277 }
278 
ArgumentStorageMode(const AidlArgument & arg,const AidlTypenames & typenames)279 StorageMode ArgumentStorageMode(const AidlArgument& arg, const AidlTypenames& typenames) {
280   if (arg.IsOut()) {
281     return arg.IsIn() ? StorageMode::INOUT_ARGUMENT : StorageMode::OUT_ARGUMENT;
282   }
283 
284   const auto typeName = arg.GetType().GetName();
285   const auto definedType = typenames.TryGetDefinedType(typeName);
286 
287   const bool isEnum = definedType && definedType->AsEnumDeclaration() != nullptr;
288   const bool isPrimitive = AidlTypenames::IsPrimitiveTypename(typeName);
289   if (typeName == "String" || arg.GetType().IsDynamicArray() || typenames.IsList(arg.GetType())) {
290     return StorageMode::UNSIZED_ARGUMENT;
291   } else if (!(isPrimitive || isEnum) || arg.GetType().IsFixedSizeArray()) {
292     return StorageMode::IN_ARGUMENT;
293   } else {
294     return StorageMode::VALUE;
295   }
296 }
297 
ArgumentReferenceMode(const AidlArgument & arg,const AidlTypenames & typenames)298 ReferenceMode ArgumentReferenceMode(const AidlArgument& arg, const AidlTypenames& typenames) {
299   auto arg_mode = ArgumentStorageMode(arg, typenames);
300   switch (arg_mode) {
301     case StorageMode::IN_ARGUMENT:
302       if (arg.GetType().IsNullable()) {
303         // &Option<T> => Option<&T>
304         return ReferenceMode::AS_REF;
305       } else {
306         return ReferenceMode::REF;
307       }
308 
309     case StorageMode::OUT_ARGUMENT:
310     case StorageMode::INOUT_ARGUMENT:
311       return ReferenceMode::MUT_REF;
312 
313     case StorageMode::UNSIZED_ARGUMENT:
314       if (arg.GetType().IsNullable()) {
315         // &Option<String> => Option<&str>
316         // &Option<Vec<T>> => Option<&[T]>
317         return ReferenceMode::AS_DEREF;
318       } else {
319         return ReferenceMode::REF;
320       }
321 
322     default:
323       return ReferenceMode::VALUE;
324   }
325 }
326 
TakeReference(ReferenceMode ref_mode,const std::string & name)327 std::string TakeReference(ReferenceMode ref_mode, const std::string& name) {
328   switch (ref_mode) {
329     case ReferenceMode::REF:
330       return "&" + name;
331 
332     case ReferenceMode::MUT_REF:
333       return "&mut " + name;
334 
335     case ReferenceMode::AS_REF:
336       return name + ".as_ref()";
337 
338     case ReferenceMode::AS_DEREF:
339       return name + ".as_deref()";
340 
341     default:
342       return name;
343   }
344 }
345 
TypeIsInterface(const AidlTypeSpecifier & type,const AidlTypenames & typenames)346 bool TypeIsInterface(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
347   const auto definedType = typenames.TryGetDefinedType(type.GetName());
348   return definedType != nullptr && definedType->AsInterface() != nullptr;
349 }
350 
TypeNeedsOption(const AidlTypeSpecifier & type,const AidlTypenames & typenames)351 bool TypeNeedsOption(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
352   if (type.IsArray() || typenames.IsList(type)) {
353     return false;
354   }
355 
356   // Already an Option<T>
357   if (type.IsNullable()) {
358     return false;
359   }
360 
361   const string& aidl_name = type.GetName();
362   if (aidl_name == "IBinder") {
363     return true;
364   }
365   if (aidl_name == "ParcelFileDescriptor") {
366     return true;
367   }
368   if (aidl_name == "ParcelableHolder") {
369     // ParcelableHolder never needs an Option because we always
370     // call its new() constructor directly instead of default()
371     return false;
372   }
373 
374   // Strong<dyn IFoo> values don't implement Default
375   if (TypeIsInterface(type, typenames)) {
376     return true;
377   }
378 
379   return false;
380 }
381 
382 }  // namespace rust
383 }  // namespace aidl
384 }  // namespace android
385