/* * 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 #include #include #include #include #include #include #include #include #include "AidlHelper.h" #include "ArrayType.h" #include "CompoundType.h" #include "ConstantExpression.h" #include "Coordinator.h" #include "EnumType.h" #include "Interface.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 emitEnumStaticAssert(Formatter& out, const NamedType& namedType, AidlBackend backend) { CHECK(namedType.isEnum()); const auto& enumType = static_cast(namedType); std::vector values; for (const EnumType* type : enumType.typeChain()) { if (!AidlHelper::shouldBeExpanded(enumType.fqName(), type->fqName())) { break; } values.insert(values.end(), type->values().rbegin(), type->values().rend()); } for (auto it = values.rbegin(); it != values.rend(); ++it) { out << "static_assert(" << aidlTypePackage(namedType, backend) << "::" << (*it)->name() << " == static_cast<" << aidlTypePackage(namedType, backend) << ">(" << namedType.fullName() << "::" << (*it)->name() << "));\n"; }; out << "\n"; } static void emitStaticAsserts(Formatter& out, const std::set& namedTypes, AidlBackend backend) { CHECK(backend != AidlBackend::JAVA); for (const auto& namedType : namedTypes) { if (namedType->isEnum()) { emitEnumStaticAssert(out, *namedType, backend); } } } static void namedTypeTranslation(Formatter& out, const std::set& namedTypes, const FieldWithVersion& field, const CompoundType* parent, AidlBackend backend) { const NamedType* type = static_cast(field.field->get()); if (namedTypes.find(type) == namedTypes.end()) { std::optional replacedType = AidlHelper::getAidlReplacedType(type->fqName()); if (replacedType) { std::optional> 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 << "// FIXME Unknown type: " << type->fqName().string() << "\n"; out << "// That type's package needs to be converted separately and the corresponding " "translate function should be added here.\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> kSignedMaxSize{ {ScalarType::KIND_UINT8, {"std::numeric_limits::max()", std::numeric_limits::max()}}, {ScalarType::KIND_UINT16, {"", 0}}, {ScalarType::KIND_INT16, {"", 0}}, {ScalarType::KIND_UINT32, {"std::numeric_limits::max()", std::numeric_limits::max()}}, {ScalarType::KIND_UINT64, {"std::numeric_limits::max()", std::numeric_limits::max()}}}; const ScalarType* scalarType = type.resolveToScalarType(); if (scalarType != nullptr && !type.isEnum()) { const auto& it = kSignedMaxSize.find(scalarType->getKind()); // *int16_t is a special case for both HIDL and AIDL. For uint16_t, checks are only // needed in the Java backend. if (backend != AidlBackend::JAVA && scalarType->getKind() == ScalarType::KIND_UINT16) { return; } 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_UINT16 || scalarType->getKind() == ScalarType::KIND_INT16) { // HIDL uses a signed 16-bit char in Java for uint16_t and int16_t // AIDL uses an unsigned 16-bit char/char16_t, so this is signed to unsigned. out << "if (" << inputAccess << " < 0) {\n"; } else { std::string affix = (scalarType->getKind() == ScalarType::KIND_UINT64) ? "L" : ""; std::string limit = (backend == AidlBackend::JAVA) ? std::to_string(it->second.second) + affix : it->second.first; out << "if (" << inputAccess << " > " << limit << " || " << 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 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(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 if (type.isBitField()) { return wrapStaticCast(payload, *static_cast(type).getElementEnumType(), fqName, 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(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(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) { // HIDL uses short(signed) in the Java backend for uint16_t and int16_t // AIDL uses char which is unsigned. This assignment needs a cast. std::string cast; if (AidlHelper::getAidlType(field.field->type(), parent->fqName()) == "char") { cast = "(char) "; } if (parent->style() == CompoundType::STYLE_STRUCT) { h2aScalarChecks(out, field.field->type(), inputAccess, backend); out << "out." << field.field->name() << " = " << cast << inputAccess << ";\n"; } else { inputAccess += "()"; h2aScalarChecks(out, field.field->type(), inputAccess, backend); out << "out.set" << StringHelper::Capitalize(field.fullName) << "(" << cast << 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& namedTypes, const CompoundType* parent, const FieldWithVersion& field, AidlBackend backend) { if (field.field->type().isNamedType() && !field.field->type().isEnum()) { 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() || field.field->type().isBitField()) { 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 getHidlPackagePath(const NamedType* type) { return base::Join(base::Split(type->fqName().package(), "."), "/"); } static std::optional getParentInterface(const NamedType* type) { const Scope* parent = type->parent(); while (parent != nullptr) { if (parent->definesInterfaces()) { return parent->getInterface(); } parent = parent->parent(); } return std::nullopt; } static const std::string hidlIncludeFile(const NamedType* type) { std::optional parent = getParentInterface(type); if (parent) { return "#include \"" + getHidlPackagePath(type) + "/" + type->fqName().version() + "/" + parent.value()->fqName().getInterfaceName() + ".h\"\n"; } else { return "#include \"" + getHidlPackagePath(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 + AidlHelper::getAidlPackagePath(type->fqName()) + "/" + AidlHelper::getAidlType(*type, type->fqName()) + ".h\"\n"; } static void emitCppTranslateHeader( const Coordinator& coordinator, const FQName& fqName, const std::set& namedTypes, const std::map& processedTypes, AidlBackend backend) { CHECK(backend == AidlBackend::CPP || backend == AidlBackend::NDK); Formatter out = coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, "include/" + AidlHelper::translateHeaderFile(fqName, backend)); AidlHelper::emitFileHeader(out); out << "// FIXME Remove this file if you don't need to translate types in this backend.\n\n"; out << "#pragma once\n\n"; std::set includes = {"#include "}; for (const auto& type : namedTypes) { const auto& it = processedTypes.find(type); if (it == processedTypes.end() && !type->isEnum()) { 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& namedTypes, const std::map& processedTypes, AidlBackend backend) { Formatter out = coordinator.getFormatter(fqName, Coordinator::Location::DIRECT, AidlHelper::translateSourceFile(fqName, backend)); AidlHelper::emitFileHeader(out); out << "// FIXME Remove this file if you don't need to translate types in this backend.\n\n"; 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"; emitStaticAsserts(out, namedTypes, backend); } 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(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& namedTypes, const std::map& 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