/* * 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 #include #include #include #include #include #include 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>& raw_value, bool by_ref) { if (type.IsArray()) { const auto& values = std::get>(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(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) { const auto defined_type = type.GetDefinedType(); if (defined_type != nullptr) { const auto unstructured = AidlCast(*defined_type); if (unstructured != nullptr) { // Unstructured parcelable should set its rust_type. Use it. const std::string rust_type = unstructured->GetRustType(); AIDL_FATAL_IF(rust_type.empty(), unstructured) << "Parcelable " << unstructured->GetCanonicalName() << " has no rust_type defined."; return rust_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 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"; } 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>& raw_value) { return ConstantValueDecoratorInternal(type, raw_value, false); } std::string ConstantValueDecoratorRef( const AidlTypeSpecifier& type, const std::variant>& 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(*it, value), ", ") + "]"; } return value; } // Returns true if @nullable T[] should be mapped Option> 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` 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>"; } 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 => 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 => Option<&str> // &Option> => 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 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 values don't implement Default if (TypeIsInterface(type, typenames)) { return true; } return false; } } // namespace rust } // namespace aidl } // namespace android