blob: be34b2f8c251e632abff62c110341c5a6a806f02 [file] [log] [blame]
/*
* Copyright (C) 2019 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 "AidlHelper.h"
#include "CompoundType.h"
#include "Coordinator.h"
#include "EnumType.h"
#include "NamedType.h"
#include "TypeDef.h"
namespace android {
struct FieldWithVersion {
const NamedReference<Type>* field;
std::pair<size_t, size_t> version;
};
struct ProcessedCompoundType {
std::vector<FieldWithVersion> fields;
std::set<const NamedType*> subTypes;
};
static void emitConversionNotes(Formatter& out, const NamedType& namedType) {
out << "// This is the HIDL definition of " << namedType.fqName().string() << "\n";
out.pushLinePrefix("// ");
namedType.emitHidlDefinition(out);
out.popLinePrefix();
out << "\n";
}
static void emitTypeDefAidlDefinition(Formatter& out, const TypeDef& typeDef) {
out << "// Cannot convert typedef " << typeDef.referencedType()->definedName() << " "
<< typeDef.fqName().string() << " since AIDL does not support typedefs.\n";
emitConversionNotes(out, typeDef);
}
static void emitEnumAidlDefinition(Formatter& out, const EnumType& enumType) {
const ScalarType* scalar = enumType.storageType()->resolveToScalarType();
CHECK(scalar != nullptr) << enumType.typeName();
enumType.emitDocComment(out);
out << "@Backing(type=\"" << AidlHelper::getAidlType(*scalar, enumType.fqName()) << "\")\n";
out << "enum " << enumType.fqName().name() << " ";
out.block([&] {
enumType.forEachValueFromRoot([&](const EnumValue* value) {
value->emitDocComment(out);
out << value->name();
if (!value->isAutoFill()) {
out << " = " << value->constExpr()->expression();
}
out << ",\n";
});
});
}
void processCompoundType(const CompoundType& compoundType, ProcessedCompoundType* processedType) {
// Gather all of the subtypes defined in this type
for (const NamedType* subType : compoundType.getSubTypes()) {
processedType->subTypes.insert(subType);
}
std::pair<size_t, size_t> version = compoundType.fqName().hasVersion()
? compoundType.fqName().getVersion()
: std::pair<size_t, size_t>{0, 0};
for (const NamedReference<Type>* field : compoundType.getFields()) {
// Check for references to an older version of itself
if (field->get()->typeName() == compoundType.typeName()) {
processCompoundType(static_cast<const CompoundType&>(*field->get()), processedType);
} else {
// Handle duplicate field names. Keep only the most recent definitions.
auto it = std::find_if(processedType->fields.begin(), processedType->fields.end(),
[field](auto& processedField) {
return processedField.field->name() == field->name();
});
if (it != processedType->fields.end()) {
AidlHelper::notes()
<< "Found conflicting field name \"" << field->name()
<< "\" in different versions of " << compoundType.fqName().name() << ". ";
if (version.first > it->version.first ||
(version.first == it->version.first && version.second > it->version.second)) {
AidlHelper::notes()
<< "Keeping " << field->get()->typeName() << " from " << version.first
<< "." << version.second << " and discarding "
<< (it->field)->get()->typeName() << " from " << it->version.first
<< "." << it->version.second << ".\n";
it->field = field;
it->version = version;
} else {
AidlHelper::notes()
<< "Keeping " << (it->field)->get()->typeName() << " from "
<< it->version.first << "." << it->version.second << " and discarding "
<< field->get()->typeName() << " from " << version.first << "."
<< version.second << ".\n";
}
} else {
processedType->fields.push_back({field, version});
}
}
}
}
static void emitCompoundTypeAidlDefinition(Formatter& out, const CompoundType& compoundType,
const Coordinator& coordinator) {
// Get all of the subtypes and fields from this type and any older versions
// that it references.
ProcessedCompoundType processedType;
processCompoundType(compoundType, &processedType);
// Emit all of the subtypes
for (const NamedType* namedType : processedType.subTypes) {
AidlHelper::emitAidl(*namedType, coordinator);
}
// Add all of the necessary imports for types that were found in older versions and missed
// when emitting the file header.
std::set<std::string> imports;
const std::vector<const NamedReference<Type>*>& latestFields = compoundType.getFields();
const std::vector<NamedType*>& latestSubTypes = compoundType.getSubTypes();
for (auto const& fieldWithVersion : processedType.fields) {
if (std::find(latestFields.begin(), latestFields.end(), fieldWithVersion.field) ==
latestFields.end()) {
AidlHelper::importLocallyReferencedType(*fieldWithVersion.field->get(), &imports);
}
}
for (const NamedType* subType : processedType.subTypes) {
if (std::find(latestSubTypes.begin(), latestSubTypes.end(), subType) ==
latestSubTypes.end()) {
AidlHelper::importLocallyReferencedType(*subType, &imports);
}
}
for (const std::string& import : imports) {
out << "import " << import << ";\n";
}
if (imports.size() > 0) {
out << "\n";
}
compoundType.emitDocComment(out);
out << "parcelable " << AidlHelper::getAidlName(compoundType.fqName()) << " ";
if (compoundType.style() == CompoundType::STYLE_STRUCT) {
out.block([&] {
// Emit all of the fields from the processed type
for (auto const& fieldWithVersion : processedType.fields) {
fieldWithVersion.field->emitDocComment(out);
out << AidlHelper::getAidlType(*fieldWithVersion.field->get(),
compoundType.fqName())
<< " " << fieldWithVersion.field->name() << ";\n";
}
});
} else {
out << "{}\n";
out << "// Cannot convert unions/safe_unions since AIDL does not support them.\n";
emitConversionNotes(out, compoundType);
}
out << "\n\n";
}
// TODO: Enum/Typedef should just emit to hidl-error.log or similar
void AidlHelper::emitAidl(const NamedType& namedType, const Coordinator& coordinator) {
Formatter out = getFileWithHeader(namedType, coordinator);
if (namedType.isTypeDef()) {
const TypeDef& typeDef = static_cast<const TypeDef&>(namedType);
emitTypeDefAidlDefinition(out, typeDef);
} else if (namedType.isCompoundType()) {
const CompoundType& compoundType = static_cast<const CompoundType&>(namedType);
emitCompoundTypeAidlDefinition(out, compoundType, coordinator);
} else if (namedType.isEnum()) {
const EnumType& enumType = static_cast<const EnumType&>(namedType);
emitEnumAidlDefinition(out, enumType);
} else {
out << "// TODO: Fix this " << namedType.definedName() << "\n";
}
}
} // namespace android