385 lines
12 KiB
C++
385 lines
12 KiB
C++
/*
|
|
* 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;
|
|
}
|
|
|
|
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 bool is_vector = type.IsArray() || typenames.IsList(type);
|
|
// If the type is an array/List<T>, get the inner element type
|
|
AIDL_FATAL_IF(typenames.IsList(type) && type.GetTypeParameters().size() != 1, type);
|
|
const auto& element_type = type.IsGeneric() ? (*type.GetTypeParameters().at(0)) : type;
|
|
const string& element_type_name = element_type.GetName();
|
|
if (m.find(element_type_name) != m.end()) {
|
|
AIDL_FATAL_IF(!AidlTypenames::IsBuiltinTypename(element_type_name), type);
|
|
if (element_type_name == "byte" && type.IsArray()) {
|
|
return "u8";
|
|
} else if (element_type_name == "String" && mode == StorageMode::UNSIZED_ARGUMENT) {
|
|
return "str";
|
|
} else if (element_type_name == "ParcelFileDescriptor" || element_type_name == "IBinder") {
|
|
if (is_vector && mode == StorageMode::DEFAULT_VALUE) {
|
|
// Out-arguments of ParcelFileDescriptors arrays need to
|
|
// be Vec<Option<ParcelFileDescriptor>> so resize_out_vec
|
|
// can initialize all elements to None (it requires Default
|
|
// and ParcelFileDescriptor doesn't implement that)
|
|
return "Option<" + m[element_type_name] + ">";
|
|
} else {
|
|
return m[element_type_name];
|
|
}
|
|
}
|
|
return m[element_type_name];
|
|
}
|
|
auto name = GetRawRustName(element_type);
|
|
if (TypeIsInterface(element_type, typenames)) {
|
|
name = "binder::Strong<dyn " + name + ">";
|
|
if (is_vector && mode == StorageMode::DEFAULT_VALUE) {
|
|
// Out-arguments of interface arrays need to be Vec<Option<...>> so resize_out_vec
|
|
// can initialize all elements to None.
|
|
name = "Option<" + 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)) {
|
|
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;
|
|
}
|
|
rust_name = GetRustName(type, typenames, element_mode);
|
|
if (type.IsNullable() && UsesOptionInNullableVector(type, typenames)) {
|
|
// The mapping for nullable string arrays is
|
|
// optional<vector<optional<string>>> in the NDK,
|
|
// so we do the same
|
|
// However, we don't need to when GetRustName() already wraps it with Option.
|
|
if (!base::StartsWith(rust_name, "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
|