/*
* Copyright (C) 2016 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 "EnumType.h"
#include "Annotation.h"
#include "ScalarType.h"
#include <inttypes.h>
#include <hidl-util/Formatter.h>
#include <android-base/logging.h>
namespace android {
EnumType::EnumType(
const char *localName,
const Location &location,
Type *storageType)
: Scope(localName, location),
mValues(),
mStorageType(storageType) {
mBitfieldType = new BitFieldType();
mBitfieldType->setElementType(this);
}
const Type *EnumType::storageType() const {
return mStorageType;
}
const std::vector<EnumValue *> &EnumType::values() const {
return mValues;
}
void EnumType::addValue(EnumValue *value) {
CHECK(value != nullptr);
EnumValue *prev = nullptr;
std::vector<const EnumType *> chain;
getTypeChain(&chain);
for (auto it = chain.begin(); it != chain.end(); ++it) {
const auto &type = *it;
if(!type->values().empty()) {
prev = type->values().back();
break;
}
}
value->autofill(prev, resolveToScalarType());
mValues.push_back(value);
}
bool EnumType::isElidableType() const {
return mStorageType->isElidableType();
}
const ScalarType *EnumType::resolveToScalarType() const {
return mStorageType->resolveToScalarType();
}
std::string EnumType::typeName() const {
return "enum " + localName();
}
bool EnumType::isEnum() const {
return true;
}
bool EnumType::canCheckEquality() const {
return true;
}
std::string EnumType::getCppType(StorageMode,
bool specifyNamespaces) const {
return specifyNamespaces ? fullName() : partialCppName();
}
std::string EnumType::getJavaType(bool forInitializer) const {
return mStorageType->resolveToScalarType()->getJavaType(forInitializer);
}
std::string EnumType::getJavaSuffix() const {
return mStorageType->resolveToScalarType()->getJavaSuffix();
}
std::string EnumType::getJavaWrapperType() const {
return mStorageType->resolveToScalarType()->getJavaWrapperType();
}
std::string EnumType::getVtsType() const {
return "TYPE_ENUM";
}
BitFieldType *EnumType::getBitfieldType() const {
return mBitfieldType;
}
LocalIdentifier *EnumType::lookupIdentifier(const std::string &name) const {
std::vector<const EnumType *> chain;
getTypeChain(&chain);
for (auto it = chain.begin(); it != chain.end(); ++it) {
const auto &type = *it;
for(EnumValue *v : type->values()) {
if(v->name() == name) {
return v;
}
}
}
return nullptr;
}
void EnumType::emitReaderWriter(
Formatter &out,
const std::string &name,
const std::string &parcelObj,
bool parcelObjIsPointer,
bool isReader,
ErrorMode mode) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != NULL);
scalarType->emitReaderWriterWithCast(
out,
name,
parcelObj,
parcelObjIsPointer,
isReader,
mode,
true /* needsCast */);
}
void EnumType::emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
const std::string &parcelName,
const std::string &blobName,
const std::string &fieldName,
const std::string &offset,
bool isReader) const {
return mStorageType->emitJavaFieldReaderWriter(
out, depth, parcelName, blobName, fieldName, offset, isReader);
}
status_t EnumType::emitTypeDeclarations(Formatter &out) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != nullptr);
const std::string storageType = scalarType->getCppStackType();
out << "enum class "
<< localName()
<< " : "
<< storageType
<< " {\n";
out.indent();
std::vector<const EnumType *> chain;
getTypeChain(&chain);
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
for (const auto &entry : type->values()) {
out << entry->name();
std::string value = entry->cppValue(scalarType->getKind());
CHECK(!value.empty()); // use autofilled values for c++.
out << " = " << value;
out << ",";
std::string comment = entry->comment();
if (!comment.empty() && comment != value) {
out << " // " << comment;
}
out << "\n";
}
}
out.unindent();
out << "};\n\n";
return OK;
}
void EnumType::emitEnumBitwiseOperator(
Formatter &out,
bool lhsIsEnum,
bool rhsIsEnum,
const std::string &op) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != nullptr);
const std::string storageType = scalarType->getCppStackType();
out << "constexpr "
<< storageType
<< " operator"
<< op
<< "(const "
<< (lhsIsEnum ? fullName() : storageType)
<< " lhs, const "
<< (rhsIsEnum ? fullName() : storageType)
<< " rhs) {\n";
out.indent([&] {
out << "return static_cast<"
<< storageType
<< ">(";
if (lhsIsEnum) {
out << "static_cast<"
<< storageType
<< ">(lhs)";
} else {
out << "lhs";
}
out << " " << op << " ";
if (rhsIsEnum) {
out << "static_cast<"
<< storageType
<< ">(rhs)";
} else {
out << "rhs";
}
out << ");\n";
});
out << "}\n\n";
}
void EnumType::emitBitFieldBitwiseAssignmentOperator(
Formatter &out,
const std::string &op) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != nullptr);
const std::string storageType = scalarType->getCppStackType();
out << "constexpr " << storageType << " &operator" << op << "=("
<< storageType << "& v, const " << fullName() << " e) {\n";
out.indent([&] {
out << "v " << op << "= static_cast<" << storageType << ">(e);\n";
out << "return v;\n";
});
out << "}\n\n";
}
status_t EnumType::emitGlobalTypeDeclarations(Formatter &out) const {
emitEnumBitwiseOperator(out, true /* lhsIsEnum */, true /* rhsIsEnum */, "|");
emitEnumBitwiseOperator(out, false /* lhsIsEnum */, true /* rhsIsEnum */, "|");
emitEnumBitwiseOperator(out, true /* lhsIsEnum */, false /* rhsIsEnum */, "|");
emitEnumBitwiseOperator(out, true /* lhsIsEnum */, true /* rhsIsEnum */, "&");
emitEnumBitwiseOperator(out, false /* lhsIsEnum */, true /* rhsIsEnum */, "&");
emitEnumBitwiseOperator(out, true /* lhsIsEnum */, false /* rhsIsEnum */, "&");
emitBitFieldBitwiseAssignmentOperator(out, "|");
emitBitFieldBitwiseAssignmentOperator(out, "&");
// toString for bitfields, equivalent to dumpBitfield in Java
out << "template<typename>\n"
<< "std::string toString("
<< resolveToScalarType()->getCppArgumentType()
<< " o);\n";
out << "template<>\n"
<< "std::string toString<" << getCppStackType() << ">("
<< resolveToScalarType()->getCppArgumentType()
<< " o);\n\n";
// toString for enum itself
out << "std::string toString("
<< getCppArgumentType()
<< " o);\n\n";
return OK;
}
status_t EnumType::emitTypeDefinitions(Formatter &out, const std::string /* prefix */) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != NULL);
out << "template<>\n"
<< "std::string toString<" << getCppStackType() << ">("
<< scalarType->getCppArgumentType()
<< " o) ";
out.block([&] {
// include toHexString for scalar types
out << "using ::android::hardware::details::toHexString;\n"
<< "std::string os;\n"
<< getBitfieldType()->getCppStackType() << " flipped = 0;\n"
<< "bool first = true;\n";
for (EnumValue *value : values()) {
std::string valueName = fullName() + "::" + value->name();
out.sIf("(o & " + valueName + ")" +
" == static_cast<" + scalarType->getCppStackType() +
">(" + valueName + ")", [&] {
out << "os += (first ? \"\" : \" | \");\n"
<< "os += \"" << value->name() << "\";\n"
<< "first = false;\n"
<< "flipped |= " << valueName << ";\n";
}).endl();
}
// put remaining bits
out.sIf("o != flipped", [&] {
out << "os += (first ? \"\" : \" | \");\n";
scalarType->emitHexDump(out, "os", "o & (~flipped)");
});
out << "os += \" (\";\n";
scalarType->emitHexDump(out, "os", "o");
out << "os += \")\";\n";
out << "return os;\n";
}).endl().endl();
out << "std::string toString("
<< getCppArgumentType()
<< " o) ";
out.block([&] {
out << "using ::android::hardware::details::toHexString;\n";
for (EnumValue *value : values()) {
out.sIf("o == " + fullName() + "::" + value->name(), [&] {
out << "return \"" << value->name() << "\";\n";
}).endl();
}
out << "std::string os;\n";
scalarType->emitHexDump(out, "os",
"static_cast<" + scalarType->getCppStackType() + ">(o)");
out << "return os;\n";
}).endl().endl();
return OK;
}
status_t EnumType::emitJavaTypeDeclarations(Formatter &out, bool atTopLevel) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != NULL);
out << "public "
<< (atTopLevel ? "" : "static ")
<< "final class "
<< localName()
<< " {\n";
out.indent();
const std::string typeName =
scalarType->getJavaType(false /* forInitializer */);
std::vector<const EnumType *> chain;
getTypeChain(&chain);
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
for (const auto &entry : type->values()) {
out << "public static final "
<< typeName
<< " "
<< entry->name()
<< " = ";
// javaValue will make the number signed.
std::string value = entry->javaValue(scalarType->getKind());
CHECK(!value.empty()); // use autofilled values for java.
out << value;
out << ";";
std::string comment = entry->comment();
if (!comment.empty() && comment != value) {
out << " // " << comment;
}
out << "\n";
}
}
out << "public static final String toString("
<< typeName << " o) ";
out.block([&] {
for (EnumValue *value : values()) {
out.sIf("o == " + value->name(), [&] {
out << "return \"" << value->name() << "\";\n";
}).endl();
}
out << "return \"0x\" + ";
scalarType->emitConvertToJavaHexString(out, "o");
out << ";\n";
}).endl();
auto bitfieldType = getBitfieldType()->getJavaType(false /* forInitializer */);
auto bitfieldWrapperType = getBitfieldType()->getJavaWrapperType();
out << "\n"
<< "public static final String dumpBitfield("
<< bitfieldType << " o) ";
out.block([&] {
out << "java.util.ArrayList<String> list = new java.util.ArrayList<>();\n";
out << bitfieldType << " flipped = 0;\n";
for (EnumValue *value : values()) {
out.sIf("(o & " + value->name() + ") == " + value->name(), [&] {
out << "list.add(\"" << value->name() << "\");\n";
out << "flipped |= " << value->name() << ";\n";
}).endl();
}
// put remaining bits
out.sIf("o != flipped", [&] {
out << "list.add(\"0x\" + ";
scalarType->emitConvertToJavaHexString(out, "o & (~flipped)");
out << ");\n";
}).endl();
out << "return String.join(\" | \", list);\n";
}).endl().endl();
out.unindent();
out << "};\n\n";
return OK;
}
status_t EnumType::emitVtsTypeDeclarations(Formatter &out) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
out << "name: \"" << fullName() << "\"\n";
out << "type: " << getVtsType() << "\n";
out << "enum_value: {\n";
out.indent();
out << "scalar_type: \""
<< scalarType->getVtsScalarType()
<< "\"\n\n";
std::vector<const EnumType *> chain;
getTypeChain(&chain);
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
for (const auto &entry : type->values()) {
out << "enumerator: \"" << entry->name() << "\"\n";
out << "scalar_value: {\n";
out.indent();
// use autofilled values for vts.
std::string value = entry->value(scalarType->getKind());
CHECK(!value.empty());
out << mStorageType->resolveToScalarType()->getVtsScalarType()
<< ": "
<< value
<< "\n";
out.unindent();
out << "}\n";
}
}
out.unindent();
out << "}\n";
return OK;
}
status_t EnumType::emitVtsAttributeType(Formatter &out) const {
out << "type: " << getVtsType() << "\n";
out << "predefined_type: \"" << fullName() << "\"\n";
return OK;
}
void EnumType::emitJavaDump(
Formatter &out,
const std::string &streamName,
const std::string &name) const {
out << streamName << ".append(" << fqName().javaName() << ".toString("
<< name << "));\n";
}
void EnumType::getTypeChain(std::vector<const EnumType *> *out) const {
out->clear();
const EnumType *type = this;
for (;;) {
out->push_back(type);
const Type *superType = type->storageType();
if (superType == NULL || !superType->isEnum()) {
break;
}
type = static_cast<const EnumType *>(superType);
}
}
void EnumType::getAlignmentAndSize(size_t *align, size_t *size) const {
mStorageType->getAlignmentAndSize(align, size);
}
const Annotation *EnumType::findExportAnnotation() const {
for (const auto &annotation : annotations()) {
if (annotation->name() == "export") {
return annotation;
}
}
return nullptr;
}
void EnumType::appendToExportedTypesVector(
std::vector<const Type *> *exportedTypes) const {
if (findExportAnnotation() != nullptr) {
exportedTypes->push_back(this);
}
}
status_t EnumType::emitExportedHeader(Formatter &out, bool forJava) const {
const Annotation *annotation = findExportAnnotation();
CHECK(annotation != nullptr);
std::string name = localName();
const AnnotationParam *nameParam = annotation->getParam("name");
if (nameParam != nullptr) {
name = nameParam->getSingleString();
}
bool exportParent = true;
const AnnotationParam *exportParentParam = annotation->getParam("export_parent");
if (exportParentParam != nullptr) {
exportParent = exportParentParam->getSingleBool();
}
std::string valuePrefix;
const AnnotationParam *prefixParam = annotation->getParam("value_prefix");
if (prefixParam != nullptr) {
valuePrefix = prefixParam->getSingleString();
}
std::string valueSuffix;
const AnnotationParam *suffixParam = annotation->getParam("value_suffix");
if (suffixParam != nullptr) {
valueSuffix = suffixParam->getSingleString();
}
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != nullptr);
std::vector<const EnumType *> chain;
if (exportParent) {
getTypeChain(&chain);
} else {
chain = { this };
}
if (forJava) {
if (!name.empty()) {
out << "public final class "
<< name
<< " {\n";
out.indent();
} else {
out << "// Values declared in " << localName() << " follow.\n";
}
const std::string typeName =
scalarType->getJavaType(false /* forInitializer */);
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
for (const auto &entry : type->values()) {
out << "public static final "
<< typeName
<< " "
<< valuePrefix
<< entry->name()
<< valueSuffix
<< " = ";
// javaValue will make the number signed.
std::string value = entry->javaValue(scalarType->getKind());
CHECK(!value.empty()); // use autofilled values for java.
out << value;
out << ";";
std::string comment = entry->comment();
if (!comment.empty() && comment != value) {
out << " // " << comment;
}
out << "\n";
}
}
if (!name.empty()) {
out.unindent();
out << "};\n";
}
out << "\n";
return OK;
}
if (!name.empty()) {
out << "typedef ";
}
out << "enum {\n";
out.indent();
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
for (const auto &entry : type->values()) {
out << valuePrefix << entry->name() << valueSuffix;
std::string value = entry->cppValue(scalarType->getKind());
CHECK(!value.empty()); // use autofilled values for c++.
out << " = " << value;
out << ",";
std::string comment = entry->comment();
if (!comment.empty() && comment != value) {
out << " // " << comment;
}
out << "\n";
}
}
out.unindent();
out << "}";
if (!name.empty()) {
out << " " << name;
}
out << ";\n\n";
return OK;
}
////////////////////////////////////////////////////////////////////////////////
EnumValue::EnumValue(const char *name, ConstantExpression *value)
: mName(name),
mValue(value),
mIsAutoFill(false) {
}
std::string EnumValue::name() const {
return mName;
}
std::string EnumValue::value(ScalarType::Kind castKind) const {
CHECK(mValue != nullptr);
return mValue->value(castKind);
}
std::string EnumValue::cppValue(ScalarType::Kind castKind) const {
CHECK(mValue != nullptr);
return mValue->cppValue(castKind);
}
std::string EnumValue::javaValue(ScalarType::Kind castKind) const {
CHECK(mValue != nullptr);
return mValue->javaValue(castKind);
}
std::string EnumValue::comment() const {
CHECK(mValue != nullptr);
return mValue->description();
}
ConstantExpression *EnumValue::constExpr() const {
CHECK(mValue != nullptr);
return mValue;
}
void EnumValue::autofill(const EnumValue *prev, const ScalarType *type) {
if(mValue != nullptr)
return;
mIsAutoFill = true;
ConstantExpression *value = new ConstantExpression();
if(prev == nullptr) {
*value = ConstantExpression::Zero(type->getKind());
} else {
CHECK(prev->mValue != nullptr);
*value = prev->mValue->addOne();
}
mValue = value;
}
bool EnumValue::isAutoFill() const {
return mIsAutoFill;
}
bool EnumValue::isEnumValue() const {
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool BitFieldType::isBitField() const {
return true;
}
std::string BitFieldType::typeName() const {
return "mask" + (mElementType == nullptr ? "" : (" of " + mElementType->typeName()));
}
void BitFieldType::addNamedTypesToSet(std::set<const FQName> &) const {
}
bool BitFieldType::isCompatibleElementType(Type *elementType) const {
return elementType->isEnum();
}
const ScalarType *BitFieldType::resolveToScalarType() const {
return mElementType->resolveToScalarType();
}
std::string BitFieldType::getCppType(StorageMode mode,
bool specifyNamespaces) const {
return resolveToScalarType()->getCppType(mode, specifyNamespaces);
}
std::string BitFieldType::getJavaType(bool forInitializer) const {
return resolveToScalarType()->getJavaType(forInitializer);
}
std::string BitFieldType::getJavaSuffix() const {
return resolveToScalarType()->getJavaSuffix();
}
std::string BitFieldType::getJavaWrapperType() const {
return resolveToScalarType()->getJavaWrapperType();
}
std::string BitFieldType::getVtsType() const {
return "TYPE_MASK";
}
bool BitFieldType::isElidableType() const {
return resolveToScalarType()->isElidableType();
}
bool BitFieldType::canCheckEquality() const {
return resolveToScalarType()->canCheckEquality();
}
status_t BitFieldType::emitVtsAttributeType(Formatter &out) const {
out << "type: " << getVtsType() << "\n";
out << "scalar_type: \""
<< mElementType->resolveToScalarType()->getVtsScalarType()
<< "\"\n";
out << "predefined_type: \""
<< static_cast<NamedType *>(mElementType)->fullName() << "\"\n";
return OK;
}
void BitFieldType::getAlignmentAndSize(size_t *align, size_t *size) const {
resolveToScalarType()->getAlignmentAndSize(align, size);
}
void BitFieldType::emitReaderWriter(
Formatter &out,
const std::string &name,
const std::string &parcelObj,
bool parcelObjIsPointer,
bool isReader,
ErrorMode mode) const {
resolveToScalarType()->emitReaderWriterWithCast(
out,
name,
parcelObj,
parcelObjIsPointer,
isReader,
mode,
true /* needsCast */);
}
EnumType *BitFieldType::getEnumType() const {
CHECK(mElementType->isEnum());
return static_cast<EnumType *>(mElementType);
}
// a bitfield maps to the underlying scalar type in C++, so operator<< is
// already defined. We can still emit useful information if the bitfield is
// in a struct / union by overriding emitDump as below.
void BitFieldType::emitDump(
Formatter &out,
const std::string &streamName,
const std::string &name) const {
out << streamName << " += "<< getEnumType()->fqName().cppNamespace()
<< "::toString<" << getEnumType()->getCppStackType()
<< ">(" << name << ");\n";
}
void BitFieldType::emitJavaDump(
Formatter &out,
const std::string &streamName,
const std::string &name) const {
out << streamName << ".append(" << getEnumType()->fqName().javaName() << ".dumpBitfield("
<< name << "));\n";
}
void BitFieldType::emitJavaFieldReaderWriter(
Formatter &out,
size_t depth,
const std::string &parcelName,
const std::string &blobName,
const std::string &fieldName,
const std::string &offset,
bool isReader) const {
return resolveToScalarType()->emitJavaFieldReaderWriter(
out, depth, parcelName, blobName, fieldName, offset, isReader);
}
} // namespace android