blob: 1840bae984fa84f5cad51b9a6362340c351c5109 [file] [log] [blame]
/*
* 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 <android-base/logging.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
#include <hidl-util/Formatter.h>
#include <hidl-util/StringHelper.h>
#include <limits>
#include <set>
#include <string>
#include <vector>
#include "AidlHelper.h"
#include "ArrayType.h"
#include "CompoundType.h"
#include "ConstantExpression.h"
#include "Coordinator.h"
#include "EnumType.h"
#include "NamedType.h"
#include "ScalarType.h"
#include "Scope.h"
#include "VectorType.h"
namespace android {
std::string AidlHelper::translateHeaderFile(const FQName& fqName, AidlBackend backend) {
switch (backend) {
case AidlBackend::NDK:
return AidlHelper::getAidlPackagePath(fqName) + "/translate-ndk.h";
case AidlBackend::CPP:
return AidlHelper::getAidlPackagePath(fqName) + "/translate-cpp.h";
default:
LOG(FATAL) << "Unexpected AidlBackend value";
return "";
}
}
std::string AidlHelper::translateSourceFile(const FQName& fqName, AidlBackend backend) {
switch (backend) {
case AidlBackend::NDK:
return AidlHelper::getAidlPackagePath(fqName) + "/translate-ndk.cpp";
case AidlBackend::CPP:
return AidlHelper::getAidlPackagePath(fqName) + "/translate-cpp.cpp";
case AidlBackend::JAVA:
return AidlHelper::getAidlPackagePath(fqName) + "/Translate.java";
default:
LOG(FATAL) << "Unexpected AidlBackend value";
return "";
}
}
static const std::string aidlTypePackage(const NamedType& type, AidlBackend backend) {
const std::string prefix = (backend == AidlBackend::NDK) ? "aidl::" : std::string();
const std::string separator = (backend == AidlBackend::JAVA) ? "." : "::";
return prefix +
base::Join(base::Split(AidlHelper::getAidlPackage(type.fqName()), "."), separator) +
separator + AidlHelper::getAidlType(type, type.fqName());
}
static void namedTypeTranslation(Formatter& out, const std::set<const NamedType*>& namedTypes,
const FieldWithVersion& field, const CompoundType* parent,
AidlBackend backend) {
const NamedType* type = static_cast<const NamedType*>(field.field->get());
if (namedTypes.find(type) == namedTypes.end()) {
std::optional<const ReplacedTypeInfo> replacedType =
AidlHelper::getAidlReplacedType(type->fqName());
if (replacedType) {
std::optional<std::function<void(Formatter&)>> translateField =
replacedType.value().translateField;
if (translateField) {
translateField.value()(out);
}
} else {
AidlHelper::notes() << "An unknown named type was found in translation: "
<< type->fqName().string() + "\n";
out << "#error FIXME Unknown type: " << type->fqName().string() << "\n";
}
} else {
if (parent->style() == CompoundType::STYLE_STRUCT) {
if (backend == AidlBackend::JAVA) {
out << "out." << field.field->name() << " = h2aTranslate(in." << field.fullName
<< ");\n";
} else {
out << "if (!translate(in." << field.fullName << ", &out->" << field.field->name()
<< ")) return false;\n";
}
} else {
if (backend == AidlBackend::JAVA) {
out << "out.set" << StringHelper::Capitalize(field.field->name())
<< "(h2aTranslate(in." << field.fullName << "()));\n";
} else {
out << "{\n";
out << aidlTypePackage(*type, backend) << " " << field.field->name() << ";\n";
out << "if (!translate(in." << field.fullName + "(), &" << field.field->name()
<< ")) return false;\n";
out << "out->set<" << aidlTypePackage(*parent, backend) << "::" << field.fullName
<< ">(" << field.field->name() << ");\n";
out << "}\n";
}
}
}
}
static void h2aScalarChecks(Formatter& out, const Type& type, const std::string& inputAccess,
AidlBackend backend) {
static const std::map<ScalarType::Kind, size_t> kSignedMaxSize{
{ScalarType::KIND_UINT8, std::numeric_limits<int8_t>::max()},
{ScalarType::KIND_INT16, std::numeric_limits<int32_t>::max()},
{ScalarType::KIND_UINT32, std::numeric_limits<int32_t>::max()},
{ScalarType::KIND_UINT64, std::numeric_limits<int64_t>::max()}};
const ScalarType* scalarType = type.resolveToScalarType();
if (scalarType != nullptr && !type.isEnum()) {
const auto& it = kSignedMaxSize.find(scalarType->getKind());
if (it != kSignedMaxSize.end()) {
out << "// FIXME This requires conversion between signed and unsigned. Change this if "
"it doesn't suit your needs.\n";
if (scalarType->getKind() == ScalarType::KIND_INT16) {
// AIDL uses an unsigned 16-bit integer(char16_t), so this is signed to unsigned.
out << "if (" << inputAccess << " < 0) {\n";
} else {
std::string affix = (scalarType->getKind() == ScalarType::KIND_UINT64) ? "L" : "";
out << "if (" << inputAccess << " > " << it->second << affix << " || "
<< inputAccess << " < 0) {\n";
}
if (backend == AidlBackend::JAVA) {
out.indent([&] {
out << "throw new RuntimeException(\"Unsafe conversion between signed and "
"unsigned scalars for field: "
<< inputAccess << "\");\n";
});
} else {
out.indent([&] { out << "return false;\n"; });
}
out << "}\n";
}
}
}
static std::string wrapToString16(const std::string& payload, AidlBackend backend) {
if (backend == AidlBackend::CPP) {
return "String16(" + payload + ".c_str())";
} else {
return payload;
}
}
static std::string wrapStaticCast(const std::string& payload, const Type& type,
const FQName& fqName, AidlBackend backend) {
static const std::map<std::string, std::string> kAidlBackendScalarTypes{
{"boolean", "bool"}, {"byte", "int8_t"}, {"char", "char16_t"}, {"int", "int32_t"},
{"long", "int64_t"}, {"float", "float"}, {"double", "double"}};
if (type.isEnum()) {
return "static_cast<" +
aidlTypePackage(static_cast<const android::NamedType&>(type), backend) + ">(" +
payload + ")";
}
const auto& it = kAidlBackendScalarTypes.find(AidlHelper::getAidlType(type, fqName));
if (it != kAidlBackendScalarTypes.end()) {
return "static_cast<" + it->second + ">(" + payload + ")";
} else {
return payload;
}
}
static std::string wrapCppSource(const std::string& payload, const Type& type, const FQName& fqName,
AidlBackend backend) {
if (type.isString()) {
return wrapToString16(payload, backend);
} else {
return wrapStaticCast(payload, type, fqName, backend);
}
}
static void containerTranslation(Formatter& out, const FieldWithVersion& field,
const CompoundType* parent, AidlBackend backend) {
const Type* elementType;
std::string javaSizeAccess;
std::string javaElementAccess;
std::string cppSize;
if (field.field->type().isArray()) {
elementType = static_cast<const ArrayType*>(field.field->get())->getElementType();
javaSizeAccess = ".length";
javaElementAccess = "[i]";
cppSize = "sizeof(in." + field.fullName + ")/sizeof(in." + field.fullName + "[0])";
} else if (field.field->type().isVector()) {
elementType = static_cast<const VectorType*>(field.field->get())->getElementType();
javaSizeAccess = ".size()";
javaElementAccess = ".get(i)";
cppSize = "in." + field.fullName + ".size()";
} else {
LOG(FATAL) << "Unexpected container type for field: " << field.field->name();
return;
}
if (elementType->isArray() || elementType->isVector()) {
out << "#error Nested arrays and vectors are currently not supported. Needs implementation "
"for field: "
<< field.field->name() << "\n";
return;
}
if (elementType->isNamedType() && !elementType->isEnum()) {
out << "#error Arrays of NamedTypes are not currently not supported. Needs implementation "
"for field: "
<< field.field->name() << "\n";
return;
}
if (backend == AidlBackend::JAVA) {
const std::string inputAccess = "in." + field.fullName;
out << "if (" << inputAccess << " != null) {\n";
out.indent([&] {
out << "out." << field.field->name() << " = new " << elementType->getJavaType(true)
<< "[" << inputAccess << javaSizeAccess << "];\n";
out << "for (int i = 0; i < " << inputAccess << javaSizeAccess << "; i++) {\n";
out.indent([&] {
h2aScalarChecks(out, *elementType, inputAccess + javaElementAccess, backend);
out << "out." << field.field->name() << "[i] = " << inputAccess << javaElementAccess
<< ";\n";
});
out << "}\n";
});
out << "}\n";
} else {
const std::string inputAccessElement = "in." + field.fullName + "[i]";
out << "{\n";
out.indent([&] {
out << "size_t size = " << cppSize << ";\n";
out << "for (size_t i = 0; i < size; i++) {\n";
out.indent([&] {
h2aScalarChecks(out, *elementType, inputAccessElement, backend);
out << "out->" << field.field->name() << ".push_back("
<< wrapCppSource(inputAccessElement, *elementType, parent->fqName(), backend)
<< ");\n";
});
out << "}\n";
});
out << "}\n";
}
}
static void simpleTranslation(Formatter& out, const FieldWithVersion& field,
const CompoundType* parent, AidlBackend backend) {
std::string inputAccess = "in." + field.fullName;
if (backend == AidlBackend::JAVA) {
if (parent->style() == CompoundType::STYLE_STRUCT) {
h2aScalarChecks(out, field.field->type(), inputAccess, backend);
out << "out." << field.field->name() << " = " << inputAccess << ";\n";
} else {
inputAccess += "()";
h2aScalarChecks(out, field.field->type(), inputAccess, backend);
out << "out.set" << StringHelper::Capitalize(field.fullName) << "(" << inputAccess
<< ");\n";
}
} else {
if (parent->style() == CompoundType::STYLE_STRUCT) {
h2aScalarChecks(out, field.field->type(), inputAccess, backend);
out << "out->" << field.field->name() << " = "
<< wrapCppSource("in." + field.fullName, field.field->type(), parent->fqName(),
backend)
<< ";\n";
} else {
inputAccess += "()";
h2aScalarChecks(out, field.field->type(), inputAccess, backend);
out << "*out = "
<< wrapCppSource(inputAccess, field.field->type(), parent->fqName(), backend)
<< ";\n";
}
}
}
static void h2aFieldTranslation(Formatter& out, const std::set<const NamedType*>& namedTypes,
const CompoundType* parent, const FieldWithVersion& field,
AidlBackend backend) {
// TODO(b/158489355) Need to support and validate more types like arrays/vectors.
if (field.field->type().isNamedType()) {
namedTypeTranslation(out, namedTypes, field, parent, backend);
} else if (field.field->type().isArray() || field.field->type().isVector()) {
containerTranslation(out, field, parent, backend);
} else if (field.field->type().isEnum() || field.field->type().isScalar() ||
field.field->type().isString()) {
simpleTranslation(out, field, parent, backend);
} else {
AidlHelper::notes() << "An unhandled type was found in translation: "
<< field.field->type().typeName() << "\n";
out << "#error FIXME Unhandled type: " << field.field->type().typeName() << "\n";
}
}
static const std::string declareAidlFunctionSignature(const NamedType* type, AidlBackend backend) {
if (backend == AidlBackend::JAVA) {
return "static public " + aidlTypePackage(*type, backend) + " h2aTranslate(" +
type->fullJavaName() + " in)";
} else {
return "__attribute__((warn_unused_result)) bool translate(const " + type->fullName() +
"& in, " + aidlTypePackage(*type, backend) + "* out)";
}
}
static const std::string getPackageFilePath(const NamedType* type) {
return base::Join(base::Split(type->fqName().package(), "."), "/");
}
static bool typeComesFromInterface(const NamedType* type) {
const Scope* parent = type->parent();
while (parent != nullptr) {
if (parent->isInterface()) {
return true;
}
parent = parent->parent();
}
return false;
}
static const std::string hidlIncludeFile(const NamedType* type) {
if (typeComesFromInterface(type)) {
return "#include \"" + getPackageFilePath(type) + "/" + type->fqName().version() + "/" +
type->parent()->fqName().getInterfaceName() + ".h\"\n";
} else {
return "#include \"" + getPackageFilePath(type) + "/" + type->fqName().version() +
"/types.h\"\n";
}
}
static const std::string aidlIncludeFile(const NamedType* type, AidlBackend backend) {
const std::string prefix = (backend == AidlBackend::NDK) ? "aidl/" : std::string();
return "#include \"" + prefix + getPackageFilePath(type) + "/" +
AidlHelper::getAidlType(*type, type->fqName()) + ".h\"\n";
}
static void emitCppTranslateHeader(
const Coordinator& coordinator, const FQName& fqName,
const std::set<const NamedType*>& namedTypes,
const std::map<const NamedType*, const ProcessedCompoundType>& processedTypes,
AidlBackend backend) {
CHECK(backend == AidlBackend::CPP || backend == AidlBackend::NDK);
std::set<std::string> includes;
Formatter out =
coordinator.getFormatter(fqName, Coordinator::Location::DIRECT,
"include/" + AidlHelper::translateHeaderFile(fqName, backend));
AidlHelper::emitFileHeader(out);
out << "#pragma once\n\n";
for (const auto& type : namedTypes) {
const auto& it = processedTypes.find(type);
if (it == processedTypes.end()) {
continue;
}
includes.insert(aidlIncludeFile(type, backend));
includes.insert(hidlIncludeFile(type));
}
out << base::Join(includes, "") << "\n\n";
out << "namespace android::h2a {\n\n";
for (const auto& type : namedTypes) {
const auto& it = processedTypes.find(type);
if (it == processedTypes.end()) {
continue;
}
out << declareAidlFunctionSignature(type, backend) << ";\n";
}
out << "\n} // namespace android::h2a\n";
}
static void emitTranslateSource(
const Coordinator& coordinator, const FQName& fqName,
const std::set<const NamedType*>& namedTypes,
const std::map<const NamedType*, const ProcessedCompoundType>& processedTypes,
AidlBackend backend) {
Formatter out = coordinator.getFormatter(fqName, Coordinator::Location::DIRECT,
AidlHelper::translateSourceFile(fqName, backend));
AidlHelper::emitFileHeader(out);
if (backend == AidlBackend::JAVA) {
out << "package " << AidlHelper::getAidlPackage(fqName) + ";\n\n";
out << "public class Translate {\n";
} else {
out << "#include \""
<< AidlHelper::translateHeaderFile((*namedTypes.begin())->fqName(), backend) + "\"\n\n";
out << "namespace android::h2a {\n\n";
}
for (const auto& type : namedTypes) {
const auto& it = processedTypes.find(type);
if (it == processedTypes.end()) {
continue;
}
CHECK(type->isCompoundType()) << "Unexpected type: " << type->fqName().string();
const CompoundType* compound = static_cast<const CompoundType*>(type);
if (compound->style() == CompoundType::STYLE_UNION) {
// HIDL Java backend doesn't support union so don't add a comment.
if (backend != AidlBackend::JAVA) {
out << "// FIXME not enough information to safely convert. Remove this function or "
"fill it out using the custom discriminators.\n";
out << "// " << declareAidlFunctionSignature(type, backend) << "\n\n";
}
continue;
}
out << declareAidlFunctionSignature(type, backend) << " {\n";
if (compound->style() == CompoundType::STYLE_SAFE_UNION) {
out.indent([&] {
if (backend == AidlBackend::JAVA) {
out << aidlTypePackage(*type, backend) << " out = new "
<< aidlTypePackage(*type, backend) << "();\n";
}
out << "switch (in.getDiscriminator()) {\n";
out.indent([&] {
const ProcessedCompoundType& processedType = it->second;
for (const auto& field : processedType.fields) {
if (backend == AidlBackend::JAVA) {
out << "case " << compound->fullJavaName() << ".hidl_discriminator."
<< field.field->name() << ":\n";
} else {
out << "case " << compound->fullName()
<< "::hidl_discriminator::" << field.field->name() << ":\n";
}
out.indent([&] {
h2aFieldTranslation(out, namedTypes, compound, field, backend);
out << "break;\n";
});
}
out << "default:\n";
if (backend == AidlBackend::JAVA) {
out.indent([&] {
out << "throw new RuntimeException(\"Unknown discriminator value: \" + "
"Integer.toString(in.getDiscriminator()));\n";
});
} else {
out.indent([&] { out << "return false;\n"; });
}
});
out << "}\n";
});
} else {
out.indent([&] {
if (backend == AidlBackend::JAVA) {
out << aidlTypePackage(*type, backend) << " out = new "
<< aidlTypePackage(*type, backend) << "();\n";
}
const ProcessedCompoundType& processedType = it->second;
for (const auto& field : processedType.fields) {
h2aFieldTranslation(out, namedTypes, compound, field, backend);
}
});
}
if (backend == AidlBackend::JAVA) {
out.indent([&] { out << "return out;\n"; });
} else {
out.indent([&] { out << "return true;\n"; });
}
out << "}\n\n";
}
if (backend == AidlBackend::JAVA) {
out << "}";
} else {
out << "} // namespace android::h2a";
}
}
void AidlHelper::emitTranslation(
const Coordinator& coordinator, const FQName& fqName,
const std::set<const NamedType*>& namedTypes,
const std::map<const NamedType*, const ProcessedCompoundType>& processedTypes) {
if (processedTypes.empty()) return;
for (auto backend : {AidlBackend::NDK, AidlBackend::CPP, AidlBackend::JAVA}) {
if (backend != AidlBackend::JAVA) {
emitCppTranslateHeader(coordinator, fqName, namedTypes, processedTypes, backend);
}
emitTranslateSource(coordinator, fqName, namedTypes, processedTypes, backend);
}
}
} // namespace android