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