blob: 90ce1202eb5a17797b2d718dd6be4e069a95b3d2 [file] [log] [blame]
Andrei Homescub62afd92020-05-11 19:24:59 -07001/*
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
31using android::base::Join;
32using android::base::Split;
33using android::base::StringPrintf;
34
35namespace android {
36namespace aidl {
37namespace rust {
38
39namespace {
40std::string ConstantValueDecoratorInternal(const AidlTypeSpecifier& type,
41 const std::string& raw_value, bool by_ref) {
42 if (type.IsArray()) {
43 // Convert `{ ... }` to `vec!{ ... }`
44 return "vec!" + raw_value;
45 }
46
47 const auto& aidl_name = type.GetName();
48 if (aidl_name == "char") {
49 return raw_value + " as u16";
50 }
51
52 if (aidl_name == "float") {
53 // raw_value already ends in `f`, so just add `32`
54 return raw_value + "32";
55 }
56
57 if (aidl_name == "double") {
58 return raw_value + "f64";
59 }
60
61 if (aidl_name == "String" && !by_ref) {
62 // The actual type might be String or &str,
63 // and .into() transparently converts into either one
64 return raw_value + ".into()";
65 }
66
67 return raw_value;
68}
69
70std::string GetRawRustName(const AidlTypeSpecifier& type) {
71 // Each Rust type is defined in a file with the same name,
72 // e.g., IFoo is in IFoo.rs
73 auto split_name = type.GetSplitName();
74 std::string rust_name{"crate::mangled::"};
75 for (const auto& component : split_name) {
76 rust_name += StringPrintf("_%zd_%s", component.size(), component.c_str());
77 }
78 return rust_name;
79}
80
81std::string GetRustName(const AidlTypeSpecifier& type, const AidlTypenames& typenames,
82 StorageMode mode) {
83 // map from AIDL built-in type name to the corresponding Rust type name
84 static map<string, string> m = {
85 {"void", "()"},
86 {"boolean", "bool"},
87 {"byte", "i8"},
88 {"char", "u16"},
89 {"int", "i32"},
90 {"long", "i64"},
91 {"float", "f32"},
92 {"double", "f64"},
93 {"String", "String"},
94 {"IBinder", "binder::SpIBinder"},
95 {"ParcelFileDescriptor", "binder::parcel::ParcelFileDescriptor"},
96 };
97
98 // If the type is an array/List<T>, get the inner element type
99 CHECK(!type.IsGeneric() || (type.GetName() == "List" && type.GetTypeParameters().size() == 1));
100 const auto& element_type = type.IsGeneric() ? (*type.GetTypeParameters().at(0)) : type;
101 const string& element_type_name = element_type.GetName();
102 if (m.find(element_type_name) != m.end()) {
103 CHECK(AidlTypenames::IsBuiltinTypename(element_type_name));
104 if (element_type_name == "byte" && type.IsArray()) {
105 return "u8";
106 } else if (element_type_name == "String" && mode == StorageMode::UNSIZED_ARGUMENT) {
107 return "str";
108 } else if (element_type_name == "ParcelFileDescriptor") {
109 if (type.IsArray() && mode == StorageMode::DEFAULT_VALUE) {
110 // Out-arguments of ParcelFileDescriptors arrays need to
111 // be Vec<Option<ParcelFileDescriptor>> so resize_out_vec
112 // can initialize all elements to None (it requires Default
113 // and ParcelFileDescriptor doesn't implement that)
114 return "Option<" + m[element_type_name] + ">";
115 } else {
116 return m[element_type_name];
117 }
118 }
119 return m[element_type_name];
120 }
121 if (TypeIsInterface(element_type, typenames)) {
122 if (mode == StorageMode::INTERFACE_ARGUMENT) {
123 return "dyn " + GetRawRustName(element_type);
124 } else {
125 return "Box<dyn " + GetRawRustName(element_type) + ">";
126 }
127 }
128
129 return GetRawRustName(element_type);
130}
131} // namespace
132
133std::string ConstantValueDecorator(const AidlTypeSpecifier& type, const std::string& raw_value) {
134 auto rust_value = ConstantValueDecoratorInternal(type, raw_value, false);
135 if (type.IsNullable()) {
136 return "Some(" + rust_value + ")";
137 }
138 return rust_value;
139}
140
141std::string ConstantValueDecoratorRef(const AidlTypeSpecifier& type, const std::string& raw_value) {
142 auto rust_value = ConstantValueDecoratorInternal(type, raw_value, true);
143 if (type.IsNullable()) {
144 return "Some(" + rust_value + ")";
145 }
146 return rust_value;
147}
148
149std::string RustNameOf(const AidlTypeSpecifier& type, const AidlTypenames& typenames,
150 StorageMode mode) {
151 std::string rust_name;
152 if (type.IsArray() || type.IsGeneric()) {
153 StorageMode element_mode;
154 if (mode == StorageMode::OUT_ARGUMENT || mode == StorageMode::DEFAULT_VALUE) {
155 // Elements need to have Default for resize_out_vec()
156 element_mode = StorageMode::DEFAULT_VALUE;
157 } else {
158 element_mode = StorageMode::VALUE;
159 }
160 rust_name = GetRustName(type, typenames, element_mode);
161 if (type.IsNullable() && rust_name == "String") {
162 // The mapping for nullable string arrays is
163 // optional<vector<optional<string>>> in the NDK,
164 // so we do the same
165 rust_name = "Option<" + rust_name + ">";
166 }
167 if (mode == StorageMode::UNSIZED_ARGUMENT) {
168 rust_name = "[" + rust_name + "]";
169 } else {
170 rust_name = "Vec<" + rust_name + ">";
171 }
172 } else {
173 rust_name = GetRustName(type, typenames, mode);
174 }
175
176 if (mode == StorageMode::IN_ARGUMENT || mode == StorageMode::UNSIZED_ARGUMENT ||
177 mode == StorageMode::INTERFACE_ARGUMENT) {
178 // If this is a nullable input argument, put the reference inside the option,
179 // e.g., `Option<&str>` instead of `&Option<str>`
180 rust_name = "&" + rust_name;
181 }
182
183 if (type.IsNullable() ||
184 // Some types don't implement Default, so we wrap them
185 // in Option, which defaults to None
186 (!TypeHasDefault(type, typenames) &&
187 (mode == StorageMode::DEFAULT_VALUE || mode == StorageMode::OUT_ARGUMENT ||
188 mode == StorageMode::PARCELABLE_FIELD))) {
189 rust_name = "Option<" + rust_name + ">";
190 }
191
192 if (mode == StorageMode::OUT_ARGUMENT || mode == StorageMode::INOUT_ARGUMENT) {
193 rust_name = "&mut " + rust_name;
194 }
195
196 return rust_name;
197}
198
199StorageMode ArgumentStorageMode(const AidlArgument& arg, const AidlTypenames& typenames) {
200 if (arg.IsOut()) {
201 return arg.IsIn() ? StorageMode::INOUT_ARGUMENT : StorageMode::OUT_ARGUMENT;
202 }
203
204 const auto typeName = arg.GetType().GetName();
205 const auto definedType = typenames.TryGetDefinedType(typeName);
206 if (definedType != nullptr && definedType->AsInterface() != nullptr) {
207 return StorageMode::INTERFACE_ARGUMENT;
208 }
209
210 const bool isEnum = definedType && definedType->AsEnumDeclaration() != nullptr;
211 const bool isPrimitive = AidlTypenames::IsPrimitiveTypename(typeName);
212 if (typeName == "String" || arg.GetType().IsArray() || arg.GetType().IsGeneric()) {
213 return StorageMode::UNSIZED_ARGUMENT;
214 } else if (!(isPrimitive || isEnum)) {
215 return StorageMode::IN_ARGUMENT;
216 } else {
217 return StorageMode::VALUE;
218 }
219}
220
221ReferenceMode ArgumentReferenceMode(const AidlArgument& arg, const AidlTypenames& typenames) {
222 auto arg_mode = ArgumentStorageMode(arg, typenames);
223 switch (arg_mode) {
224 case StorageMode::IN_ARGUMENT:
225 if (arg.GetType().IsNullable()) {
226 // &Option<T> => Option<&T>
227 return ReferenceMode::AS_REF;
228 } else {
229 return ReferenceMode::REF;
230 }
231
232 case StorageMode::OUT_ARGUMENT:
233 case StorageMode::INOUT_ARGUMENT:
234 return ReferenceMode::MUT_REF;
235
236 case StorageMode::UNSIZED_ARGUMENT:
237 if (arg.GetType().IsNullable()) {
238 // &Option<String> => Option<&str>
239 // &Option<Vec<T>> => Option<&[T]>
240 return ReferenceMode::AS_DEREF;
241 } else {
242 return ReferenceMode::REF;
243 }
244
245 case StorageMode::INTERFACE_ARGUMENT:
246 if (arg.GetType().IsNullable()) {
247 // &Option<Box<dyn IFoo>> => Option<&dyn IFoo>
248 return ReferenceMode::AS_DEREF;
249 } else {
250 // &Box<dyn IFoo> => &dyn IFoo
251 // Needs inner dereference to pierce the Box: &*arg
252 return ReferenceMode::REF_DEREF;
253 }
254
255 default:
256 return ReferenceMode::VALUE;
257 }
258}
259
260std::string TakeReference(ReferenceMode ref_mode, const std::string& name) {
261 switch (ref_mode) {
262 case ReferenceMode::REF:
263 return "&" + name;
264
265 case ReferenceMode::REF_DEREF:
266 return "&*" + name;
267
268 case ReferenceMode::MUT_REF:
269 return "&mut " + name;
270
271 case ReferenceMode::AS_REF:
272 return name + ".as_ref()";
273
274 case ReferenceMode::AS_DEREF:
275 return name + ".as_deref()";
276
277 default:
278 return name;
279 }
280}
281
282bool TypeIsInterface(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
283 const auto definedType = typenames.TryGetDefinedType(type.GetName());
284 return definedType != nullptr && definedType->AsInterface() != nullptr;
285}
286
287bool TypeHasDefault(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
288 if (type.IsArray() || type.IsGeneric()) {
289 return true;
290 }
291
292 // Already an Option<T>
293 if (type.IsNullable()) {
294 return true;
295 }
296
297 const string& aidl_name = type.GetName();
298 if (aidl_name == "IBinder") {
299 return false;
300 }
301 if (aidl_name == "ParcelFileDescriptor") {
302 return false;
303 }
304
305 // dyn IFoo values don't implement Default
306 if (TypeIsInterface(type, typenames)) {
307 return false;
308 }
309
310 return true;
311}
312
313} // namespace rust
314} // namespace aidl
315} // namespace android