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