Andrei Homescu | b62afd9 | 2020-05-11 19:24:59 -0700 | [diff] [blame] | 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 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 | |
| 70 | std::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 | |
| 81 | std::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 | |
| 133 | std::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 | |
| 141 | std::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 | |
| 149 | std::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 | |
| 199 | StorageMode 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 | |
| 221 | ReferenceMode 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 | |
| 260 | std::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 | |
| 282 | bool TypeIsInterface(const AidlTypeSpecifier& type, const AidlTypenames& typenames) { |
| 283 | const auto definedType = typenames.TryGetDefinedType(type.GetName()); |
| 284 | return definedType != nullptr && definedType->AsInterface() != nullptr; |
| 285 | } |
| 286 | |
| 287 | bool 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 |