| /* |
| * Copyright (C) 2020, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "aidl_to_rust.h" |
| #include "aidl_language.h" |
| #include "aidl_typenames.h" |
| #include "logging.h" |
| |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| |
| #include <functional> |
| #include <iostream> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| using android::base::Join; |
| using android::base::Split; |
| using android::base::StringPrintf; |
| |
| namespace android { |
| namespace aidl { |
| namespace rust { |
| |
| namespace { |
| std::string GetRawRustName(const AidlTypeSpecifier& type); |
| |
| std::string ConstantValueDecoratorInternal( |
| const AidlTypeSpecifier& type, |
| const std::variant<std::string, std::vector<std::string>>& raw_value, bool by_ref) { |
| if (type.IsArray()) { |
| const auto& values = std::get<std::vector<std::string>>(raw_value); |
| std::string value = "[" + Join(values, ", ") + "]"; |
| if (type.IsDynamicArray()) { |
| value = "vec!" + value; |
| } |
| if (!type.IsMutated() && type.IsNullable()) { |
| value = "Some(" + value + ")"; |
| } |
| return value; |
| } |
| |
| std::string value = std::get<std::string>(raw_value); |
| |
| const auto& aidl_name = type.GetName(); |
| if (aidl_name == "char") { |
| return value + " as u16"; |
| } |
| |
| if (aidl_name == "float") { |
| // value already ends in `f`, so just add `32` |
| return value + "32"; |
| } |
| |
| if (aidl_name == "double") { |
| return value + "f64"; |
| } |
| |
| if (auto defined_type = type.GetDefinedType(); defined_type) { |
| auto enum_type = defined_type->AsEnumDeclaration(); |
| AIDL_FATAL_IF(!enum_type, type) << "Invalid type for \"" << value << "\""; |
| return GetRawRustName(type) + "::" + value.substr(value.find_last_of('.') + 1); |
| } |
| |
| if (aidl_name == "String" && !by_ref) { |
| // The actual type might be String or &str, |
| // and .into() transparently converts into either one |
| value = value + ".into()"; |
| } |
| |
| if (type.IsNullable()) { |
| value = "Some(" + value + ")"; |
| } |
| |
| return value; |
| } |
| |
| std::string GetRawRustName(const AidlTypeSpecifier& type) { |
| // Each Rust type is defined in a file with the same name, |
| // e.g., IFoo is in IFoo.rs |
| auto split_name = type.GetSplitName(); |
| std::string rust_name{"crate::mangled::"}; |
| for (const auto& component : split_name) { |
| rust_name += StringPrintf("_%zd_%s", component.size(), component.c_str()); |
| } |
| return rust_name; |
| } |
| |
| // Usually, this means that the type implements `Default`, however `ParcelableHolder` is also |
| // included in this list because the code generator knows how to call `::new(stability)`. |
| bool AutoConstructor(const AidlTypeSpecifier& type, const AidlTypenames& typenames) { |
| return !(type.GetName() == "ParcelFileDescriptor" || type.GetName() == "IBinder" || |
| TypeIsInterface(type, typenames)); |
| } |
| |
| std::string GetRustName(const AidlTypeSpecifier& type, const AidlTypenames& typenames, |
| StorageMode mode) { |
| // map from AIDL built-in type name to the corresponding Rust type name |
| static map<string, string> m = { |
| {"void", "()"}, |
| {"boolean", "bool"}, |
| {"byte", "i8"}, |
| {"char", "u16"}, |
| {"int", "i32"}, |
| {"long", "i64"}, |
| {"float", "f32"}, |
| {"double", "f64"}, |
| {"String", "String"}, |
| {"IBinder", "binder::SpIBinder"}, |
| {"ParcelFileDescriptor", "binder::ParcelFileDescriptor"}, |
| {"ParcelableHolder", "binder::ParcelableHolder"}, |
| }; |
| const string& type_name = type.GetName(); |
| if (m.find(type_name) != m.end()) { |
| AIDL_FATAL_IF(!AidlTypenames::IsBuiltinTypename(type_name), type); |
| if (type_name == "String" && mode == StorageMode::UNSIZED_ARGUMENT) { |
| return "str"; |
| } else { |
| return m[type_name]; |
| } |
| } |
| auto name = GetRawRustName(type); |
| if (TypeIsInterface(type, typenames)) { |
| name = "binder::Strong<dyn " + name + ">"; |
| } |
| if (type.IsGeneric()) { |
| name += "<"; |
| for (const auto& param : type.GetTypeParameters()) { |
| name += GetRustName(*param, typenames, mode); |
| name += ","; |
| } |
| name += ">"; |
| } |
| return name; |
| } |
| } // namespace |
| |
| std::string ConstantValueDecorator( |
| const AidlTypeSpecifier& type, |
| const std::variant<std::string, std::vector<std::string>>& raw_value) { |
| return ConstantValueDecoratorInternal(type, raw_value, false); |
| } |
| |
| std::string ConstantValueDecoratorRef( |
| const AidlTypeSpecifier& type, |
| const std::variant<std::string, std::vector<std::string>>& raw_value) { |
| return ConstantValueDecoratorInternal(type, raw_value, true); |
| } |
| |
| // Returns default value for array. |
| std::string ArrayDefaultValue(const AidlTypeSpecifier& type) { |
| AIDL_FATAL_IF(!type.IsFixedSizeArray(), type) << "not a fixed-size array"; |
| auto dimensions = type.GetFixedSizeArrayDimensions(); |
| std::string value = "Default::default()"; |
| for (auto it = rbegin(dimensions), end = rend(dimensions); it != end; it++) { |
| value = "[" + Join(std::vector<std::string>(*it, value), ", ") + "]"; |
| } |
| return value; |
| } |
| |
| // Returns true if @nullable T[] should be mapped Option<Vec<Option<T>> |
| bool UsesOptionInNullableVector(const AidlTypeSpecifier& type, const AidlTypenames& typenames) { |
| AIDL_FATAL_IF(!type.IsArray() && !typenames.IsList(type), type) << "not a vector"; |
| AIDL_FATAL_IF(typenames.IsList(type) && type.GetTypeParameters().size() != 1, type) |
| << "List should have a single type arg."; |
| |
| const auto& element_type = type.IsArray() ? type : *type.GetTypeParameters().at(0); |
| if (typenames.IsPrimitiveTypename(element_type.GetName())) { |
| return false; |
| } |
| if (typenames.GetEnumDeclaration(element_type)) { |
| return false; |
| } |
| return true; |
| } |
| |
| std::string RustLifetimeName(Lifetime lifetime) { |
| switch (lifetime) { |
| case Lifetime::NONE: |
| return ""; |
| case Lifetime::A: |
| return "'a "; |
| } |
| } |
| |
| std::string RustLifetimeGeneric(Lifetime lifetime) { |
| switch (lifetime) { |
| case Lifetime::NONE: |
| return ""; |
| case Lifetime::A: |
| return "<'a>"; |
| } |
| } |
| |
| std::string RustNameOf(const AidlTypeSpecifier& type, const AidlTypenames& typenames, |
| StorageMode mode, Lifetime lifetime) { |
| std::string rust_name; |
| if (type.IsArray() || typenames.IsList(type)) { |
| const auto& element_type = type.IsGeneric() ? (*type.GetTypeParameters().at(0)) : type; |
| StorageMode element_mode; |
| if (type.IsFixedSizeArray() && mode == StorageMode::PARCELABLE_FIELD) { |
| // Elements of fixed-size array field need to have Default. |
| element_mode = StorageMode::DEFAULT_VALUE; |
| } else if (mode == StorageMode::OUT_ARGUMENT || mode == StorageMode::DEFAULT_VALUE) { |
| // Elements need to have Default for resize_out_vec() |
| element_mode = StorageMode::DEFAULT_VALUE; |
| } else { |
| element_mode = StorageMode::VALUE; |
| } |
| if (type.IsArray() && element_type.GetName() == "byte") { |
| rust_name = "u8"; |
| } else { |
| rust_name = GetRustName(element_type, typenames, element_mode); |
| } |
| |
| // Needs `Option` wrapping because type is not default constructible |
| const bool default_option = |
| element_mode == StorageMode::DEFAULT_VALUE && !AutoConstructor(element_type, typenames); |
| // Needs `Option` wrapping due to being a nullable, non-primitive, non-enum type in a vector. |
| const bool nullable_option = type.IsNullable() && UsesOptionInNullableVector(type, typenames); |
| if (default_option || nullable_option) { |
| rust_name = "Option<" + rust_name + ">"; |
| } |
| |
| if (mode == StorageMode::UNSIZED_ARGUMENT) { |
| rust_name = "[" + rust_name + "]"; |
| } else if (type.IsFixedSizeArray()) { |
| auto dimensions = type.GetFixedSizeArrayDimensions(); |
| // T[N][M] => [[T; M]; N] |
| for (auto it = rbegin(dimensions), end = rend(dimensions); it != end; it++) { |
| rust_name = "[" + rust_name + "; " + std::to_string(*it) + "]"; |
| } |
| } else { |
| rust_name = "Vec<" + rust_name + ">"; |
| } |
| } else { |
| rust_name = GetRustName(type, typenames, mode); |
| } |
| |
| if (mode == StorageMode::IN_ARGUMENT || mode == StorageMode::UNSIZED_ARGUMENT) { |
| // If this is a nullable input argument, put the reference inside the option, |
| // e.g., `Option<&str>` instead of `&Option<str>` |
| rust_name = "&" + RustLifetimeName(lifetime) + rust_name; |
| } |
| |
| if (type.IsNullable() || |
| // Some types don't implement Default, so we wrap them |
| // in Option, which defaults to None |
| (TypeNeedsOption(type, typenames) && |
| (mode == StorageMode::DEFAULT_VALUE || mode == StorageMode::OUT_ARGUMENT || |
| mode == StorageMode::PARCELABLE_FIELD))) { |
| if (type.IsHeapNullable()) { |
| rust_name = "Option<Box<" + rust_name + ">>"; |
| } else { |
| rust_name = "Option<" + rust_name + ">"; |
| } |
| } |
| |
| if (mode == StorageMode::OUT_ARGUMENT || mode == StorageMode::INOUT_ARGUMENT) { |
| rust_name = "&" + RustLifetimeName(lifetime) + "mut " + rust_name; |
| } |
| |
| return rust_name; |
| } |
| |
| StorageMode ArgumentStorageMode(const AidlArgument& arg, const AidlTypenames& typenames) { |
| if (arg.IsOut()) { |
| return arg.IsIn() ? StorageMode::INOUT_ARGUMENT : StorageMode::OUT_ARGUMENT; |
| } |
| |
| const auto typeName = arg.GetType().GetName(); |
| const auto definedType = typenames.TryGetDefinedType(typeName); |
| |
| const bool isEnum = definedType && definedType->AsEnumDeclaration() != nullptr; |
| const bool isPrimitive = AidlTypenames::IsPrimitiveTypename(typeName); |
| if (typeName == "String" || arg.GetType().IsDynamicArray() || typenames.IsList(arg.GetType())) { |
| return StorageMode::UNSIZED_ARGUMENT; |
| } else if (!(isPrimitive || isEnum) || arg.GetType().IsFixedSizeArray()) { |
| return StorageMode::IN_ARGUMENT; |
| } else { |
| return StorageMode::VALUE; |
| } |
| } |
| |
| ReferenceMode ArgumentReferenceMode(const AidlArgument& arg, const AidlTypenames& typenames) { |
| auto arg_mode = ArgumentStorageMode(arg, typenames); |
| switch (arg_mode) { |
| case StorageMode::IN_ARGUMENT: |
| if (arg.GetType().IsNullable()) { |
| // &Option<T> => Option<&T> |
| return ReferenceMode::AS_REF; |
| } else { |
| return ReferenceMode::REF; |
| } |
| |
| case StorageMode::OUT_ARGUMENT: |
| case StorageMode::INOUT_ARGUMENT: |
| return ReferenceMode::MUT_REF; |
| |
| case StorageMode::UNSIZED_ARGUMENT: |
| if (arg.GetType().IsNullable()) { |
| // &Option<String> => Option<&str> |
| // &Option<Vec<T>> => Option<&[T]> |
| return ReferenceMode::AS_DEREF; |
| } else { |
| return ReferenceMode::REF; |
| } |
| |
| default: |
| return ReferenceMode::VALUE; |
| } |
| } |
| |
| std::string TakeReference(ReferenceMode ref_mode, const std::string& name) { |
| switch (ref_mode) { |
| case ReferenceMode::REF: |
| return "&" + name; |
| |
| case ReferenceMode::MUT_REF: |
| return "&mut " + name; |
| |
| case ReferenceMode::AS_REF: |
| return name + ".as_ref()"; |
| |
| case ReferenceMode::AS_DEREF: |
| return name + ".as_deref()"; |
| |
| default: |
| return name; |
| } |
| } |
| |
| bool TypeIsInterface(const AidlTypeSpecifier& type, const AidlTypenames& typenames) { |
| const auto definedType = typenames.TryGetDefinedType(type.GetName()); |
| return definedType != nullptr && definedType->AsInterface() != nullptr; |
| } |
| |
| bool TypeNeedsOption(const AidlTypeSpecifier& type, const AidlTypenames& typenames) { |
| if (type.IsArray() || typenames.IsList(type)) { |
| return false; |
| } |
| |
| // Already an Option<T> |
| if (type.IsNullable()) { |
| return false; |
| } |
| |
| const string& aidl_name = type.GetName(); |
| if (aidl_name == "IBinder") { |
| return true; |
| } |
| if (aidl_name == "ParcelFileDescriptor") { |
| return true; |
| } |
| if (aidl_name == "ParcelableHolder") { |
| // ParcelableHolder never needs an Option because we always |
| // call its new() constructor directly instead of default() |
| return false; |
| } |
| |
| // Strong<dyn IFoo> values don't implement Default |
| if (TypeIsInterface(type, typenames)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace rust |
| } // namespace aidl |
| } // namespace android |