/* * 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 "CompoundType.h" #include "ArrayType.h" #include "Reference.h" #include "ScalarType.h" #include "VectorType.h" #include #include #include #include #include #include #include namespace android { CompoundType::CompoundType(Style style, const std::string& localName, const FQName& fullName, const Location& location, Scope* parent) : Scope(localName, fullName, location, parent), mStyle(style) {} CompoundType::Style CompoundType::style() const { return mStyle; } void CompoundType::addField(NamedReference* field) { mFields.push_back(field); } std::vector*> CompoundType::getFields() const { return std::vector*>(mFields.begin(), mFields.end()); } std::vector*> CompoundType::getReferences() const { std::vector*> ret; ret.insert(ret.begin(), mFields.begin(), mFields.end()); return ret; } status_t CompoundType::validate() const { for (const auto* field : mFields) { const Type& type = field->type(); if ((type.isVector() && static_cast(&type)->isVectorOfBinders())) { std::cerr << "ERROR: Struct/union cannot contain vectors of interfaces at " << field->location() << "\n"; return UNKNOWN_ERROR; } if (mStyle == STYLE_UNION) { if (type.isInterface()) { std::cerr << "ERROR: Union cannot contain interfaces at " << field->location() << "\n"; return UNKNOWN_ERROR; } if (type.needsEmbeddedReadWrite()) { std::cerr << "ERROR: Union must not contain any types that need fixup at " << field->location() << "\n"; return UNKNOWN_ERROR; } } } if (mStyle == STYLE_SAFE_UNION && mFields.size() < 2) { std::cerr << "ERROR: Safe union must contain at least two types to be useful at " << location() << "\n"; return UNKNOWN_ERROR; } status_t err = validateUniqueNames(); if (err != OK) return err; err = validateSubTypeNames(); if (err != OK) return err; return Scope::validate(); } status_t CompoundType::validateUniqueNames() const { std::unordered_set names; for (const auto* field : mFields) { if (names.find(field->name()) != names.end()) { std::cerr << "ERROR: Redefinition of field '" << field->name() << "' at " << field->location() << "\n"; return UNKNOWN_ERROR; } names.insert(field->name()); } return OK; } void CompoundType::emitInvalidSubTypeNamesError(const std::string& subTypeName, const Location& location) const { std::cerr << "ERROR: Type name '" << subTypeName << "' defined at " << location << " conflicts with a member function of " << "safe_union " << definedName() << ". Consider renaming or " << "moving its definition outside the safe_union scope.\n"; } status_t CompoundType::validateSubTypeNames() const { if (mStyle != STYLE_SAFE_UNION) { return OK; } const auto& subTypes = Scope::getSubTypes(); for (const auto& subType : subTypes) { if (subType->definedName() == "getDiscriminator") { emitInvalidSubTypeNamesError(subType->definedName(), subType->location()); return UNKNOWN_ERROR; } } return OK; } bool CompoundType::isCompoundType() const { return true; } bool CompoundType::deepCanCheckEquality(std::unordered_set* visited) const { if (mStyle == STYLE_UNION) { return false; } for (const auto* field : mFields) { if (!field->get()->canCheckEquality(visited)) { return false; } } return true; } std::string CompoundType::typeName() const { switch (mStyle) { case STYLE_STRUCT: { return "struct " + definedName(); } case STYLE_UNION: { return "union " + definedName(); } case STYLE_SAFE_UNION: { return "safe_union " + definedName(); } } CHECK(!"Should not be here"); } std::string CompoundType::getCppType( StorageMode mode, bool /* specifyNamespaces */) const { const std::string base = fullName(); switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: return base + (containsInterface()?"":"*"); } CHECK(!"Should not be here"); } std::string CompoundType::getJavaType(bool /* forInitializer */) const { return fullJavaName(); } std::string CompoundType::getVtsType() const { switch (mStyle) { case STYLE_STRUCT: { return "TYPE_STRUCT"; } case STYLE_UNION: { return "TYPE_UNION"; } case STYLE_SAFE_UNION: { return "TYPE_SAFE_UNION"; } } CHECK(!"Should not be here"); } bool CompoundType::containsInterface() const { for (const auto& field : mFields) { if (field->type().isCompoundType()) { const Type& t = field->type(); const CompoundType* ct = static_cast(&t); if (ct->containsInterface()) { return true; } } if (field->type().isInterface()) { return true; } } return false; } void CompoundType::emitSafeUnionUnknownDiscriminatorError(Formatter& out, const std::string& value, bool fatal) const { if (fatal) { out << "::android::hardware::details::logAlwaysFatal("; } else { out << "ALOGE(\"%s\", "; } out << "(\n"; out.indent(2, [&] { out << "\"Unknown union discriminator (value: \" +\n" << "std::to_string(" << getUnionDiscriminatorType()->getCppTypeCast(value) << ") + \").\").c_str());\n"; }); } void CompoundType::emitSafeUnionReaderWriterForInterfaces( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { CHECK(mStyle == STYLE_SAFE_UNION); out.block([&] { const auto discriminatorType = getUnionDiscriminatorType(); if (isReader) { out << discriminatorType->getCppStackType() << " _hidl_d_primitive;\n"; } else { out << "const " << discriminatorType->getCppStackType() << " _hidl_d_primitive = " << discriminatorType->getCppTypeCast(name + ".getDiscriminator()") << ";\n"; } getUnionDiscriminatorType()->emitReaderWriter(out, "_hidl_d_primitive", parcelObj, parcelObjIsPointer, isReader, mode); out << "switch ((" << fullName() << "::hidl_discriminator) _hidl_d_primitive) "; out.block([&] { for (const auto& field : mFields) { out << "case " << fullName() << "::hidl_discriminator::" << field->name() << ": "; const std::string tempFieldName = "_hidl_temp_" + field->name(); out.block([&] { if (isReader) { out << field->type().getCppResultType() << " " << tempFieldName << ";\n"; field->type().emitReaderWriter(out, tempFieldName, parcelObj, parcelObjIsPointer, isReader, mode); const std::string derefOperator = field->type().resultNeedsDeref() ? "*" : ""; out << name << "." << field->name() << "(std::move(" << derefOperator << tempFieldName << "));\n"; } else { const std::string fieldValue = name + "." + field->name() + "()"; out << field->type().getCppArgumentType() << " " << tempFieldName << " = " << fieldValue << ";\n"; field->type().emitReaderWriter(out, tempFieldName, parcelObj, parcelObjIsPointer, isReader, mode); } out << "break;\n"; }).endl(); } out << "default: "; out.block([&] { emitSafeUnionUnknownDiscriminatorError(out, "_hidl_d_primitive", !isReader /*fatal*/); if (isReader) { out << "_hidl_err = ::android::BAD_VALUE;\n"; handleError(out, mode); } }).endl(); }).endl(); }).endl(); } void CompoundType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { const std::string parcelObjDeref = parcelObj + (parcelObjIsPointer ? "->" : "."); if(containsInterface()){ if (mStyle == STYLE_SAFE_UNION) { emitSafeUnionReaderWriterForInterfaces(out, name, parcelObj, parcelObjIsPointer, isReader, mode); return; } for (const auto& field : mFields) { const std::string tempFieldName = "_hidl_temp_" + field->name(); const std::string fieldValue = name + "." + field->name(); out.block([&] { if (isReader) { out << field->type().getCppResultType() << " " << tempFieldName << ";\n"; } else { out << field->type().getCppArgumentType() << " " << tempFieldName << " = " << fieldValue << ";\n"; } field->type().emitReaderWriter(out, tempFieldName, parcelObj, parcelObjIsPointer, isReader, mode); if (isReader) { const std::string derefOperator = field->type().resultNeedsDeref() ? "*" : ""; out << fieldValue << " = std::move(" << derefOperator << tempFieldName << ");\n"; } }).endl(); } } else { const std::string parentName = "_hidl_" + name + "_parent"; out << "size_t " << parentName << ";\n\n"; if (isReader) { out << "_hidl_err = " << parcelObjDeref << "readBuffer(" << "sizeof(*" << name << "), &" << parentName << ", " << " const_cast(reinterpret_cast(" << "&" << name << ")));\n"; handleError(out, mode); } else { out << "_hidl_err = " << parcelObjDeref << "writeBuffer(&" << name << ", sizeof(" << name << "), &" << parentName << ");\n"; handleError(out, mode); } bool needEmbeddedReadWrite = needsEmbeddedReadWrite(); CHECK(mStyle != STYLE_UNION || !needEmbeddedReadWrite); if (needEmbeddedReadWrite) { emitReaderWriterEmbedded(out, 0 /* depth */, name, name, /* sanitizedName */ isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, "0 /* parentOffset */"); } } } void CompoundType::emitReaderWriterEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string & /*sanitizedName */, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { emitReaderWriterEmbeddedForTypeName( out, name, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText, fullName(), "" /* childName */, "" /* namespace */); } void CompoundType::emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const { if (isReader) { out << "new " << fullJavaName() << "();\n"; } out << "(" << getJavaTypeCast(argName) << ")." << (isReader ? "readFromParcel" : "writeToParcel") << "(" << parcelObj << ");\n"; } void CompoundType::emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const { const std::string fieldDeclaration = fullJavaName() + " " + fieldName; emitJavaFieldDefaultInitialValue(out, fieldDeclaration); } void CompoundType::emitJavaFieldDefaultInitialValue( Formatter &out, const std::string &declaredFieldName) const { out << declaredFieldName << " = new " << fullJavaName() << "();\n"; } void CompoundType::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 { if (isReader) { out << "(" << getJavaTypeCast(fieldName) << ").readEmbeddedFromParcel(" << parcelName << ", " << blobName << ", " << offset << ");\n"; return; } out << fieldName << ".writeEmbeddedToBlob(" << blobName << ", " << offset << ");\n"; } void CompoundType::emitLayoutAsserts(Formatter& out, const Layout& layout, const std::string& layoutName) const { out << "static_assert(sizeof(" << fullName() << layoutName << ") == " << layout.size << ", \"wrong size\");\n"; out << "static_assert(__alignof(" << fullName() << layoutName << ") == " << layout.align << ", \"wrong alignment\");\n"; } void CompoundType::emitSafeUnionTypeDeclarations(Formatter& out) const { out << "struct " << definedName() << " final {\n"; out.indent(); Scope::emitTypeDeclarations(out); bool hasPointer = containsPointer(); CompoundLayout layout = hasPointer ? CompoundLayout() : getCompoundAlignmentAndSize(); out << "enum class hidl_discriminator : " << getUnionDiscriminatorType()->getCppType(StorageMode_Stack, false) << " "; out.block([&] { for (size_t idx = 0; idx < mFields.size(); idx++) { const auto& field = mFields.at(idx); field->emitDocComment(out); out << field->name() << " = " << idx << ", // " << field->type().getCppStackType(true /*specifyNamespaces*/) << "\n"; } }); out << ";\n\n"; out << definedName() << "();\n" // Constructor << "~" << definedName() << "();\n" // Destructor << definedName() << "(" << definedName() << "&&);\n" // Move constructor << definedName() << "(const " << definedName() << "&);\n" // Copy constructor << definedName() << "& operator=(" << definedName() << "&&);\n" // Move assignment << definedName() << "& operator=(const " << definedName() << "&);\n\n"; // Copy assignment for (const auto& field : mFields) { // Setter (copy) out << "void " << field->name() << "(" << field->type().getCppArgumentType() << ");\n"; if (field->type().resolveToScalarType() == nullptr) { // Setter (move) out << "void " << field->name() << "(" << field->type().getCppStackType() << "&&);\n"; } // Getter (mutable) out << field->type().getCppStackType() << "& " << field->name() << "();\n"; // Getter (immutable) out << field->type().getCppArgumentType() << " " << field->name() << "() const;\n\n"; } out << "// Utility methods\n"; out << "hidl_discriminator getDiscriminator() const;\n\n"; out << "constexpr size_t hidl_getUnionOffset() const "; out.block([&] { out << "return offsetof(" << fullName() << ", hidl_u);\n"; }).endl().endl(); out.unindent(); out << "private:\n"; out.indent(); out << "void hidl_destructUnion();\n\n"; out << "hidl_discriminator hidl_d"; if (!hasPointer) { out << " __attribute__ ((aligned(" << layout.discriminator.align << "))) "; } out << ";\n"; out << "union hidl_union final {\n"; out.indent(); for (const auto& field : mFields) { size_t fieldAlign, fieldSize; field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); out << field->type().getCppStackType() << " " << field->name(); if (!hasPointer) { out << " __attribute__ ((aligned(" << fieldAlign << ")))"; } out << ";\n"; } out << "\n" << "hidl_union();\n" << "~hidl_union();\n"; out.unindent(); out << "} hidl_u;\n"; if (!hasPointer) { out << "\n"; emitLayoutAsserts(out, layout.innerStruct, "::hidl_union"); emitLayoutAsserts(out, layout.discriminator, "::hidl_discriminator"); } out.unindent(); out << "};\n\n"; if (!hasPointer) { emitLayoutAsserts(out, layout.overall, ""); out << "\n"; } } void CompoundType::emitFieldHidlDefinition(Formatter& out, const NamedReference& ref) const { if (ref.getDocComment() != nullptr) ref.getDocComment()->emit(out); if (ref.definedInline()) { // Same check as above, this is for sanity CHECK(ref.get()->isCompoundType()); static_cast(ref.get())->emitInlineHidlDefinition(out); out << " " << ref.name() << ";\n"; } else { out << ref.localName() << " " << ref.name() << ";\n"; } } void CompoundType::emitInlineHidlDefinition(Formatter& out) const { if (getDocComment() != nullptr) getDocComment()->emit(out); out << typeName() << " "; std::set namesDeclaredInScope; for (const NamedReference* ref : mFields) { if (ref->definedInline()) { const Type* type = ref->get(); CHECK(type->isCompoundType()) << " only compound types can be defined inline"; namesDeclaredInScope.insert(static_cast(type)->fqName()); } } std::vector preDeclaredTypes; for (const NamedType* namedType : getSortedDefinedTypes()) { if (namesDeclaredInScope.find(namedType->fqName()) == namesDeclaredInScope.end()) { // have to predeclare it preDeclaredTypes.push_back(namedType); } } out << "{"; out.indent([&] { size_t preDeclaredTypesIdx = 0; size_t fieldIdx = 0; while (preDeclaredTypesIdx < preDeclaredTypes.size() && fieldIdx < mFields.size()) { out << "\n"; if (preDeclaredTypes.at(preDeclaredTypesIdx)->location() < mFields.at(fieldIdx)->location()) { preDeclaredTypes.at(preDeclaredTypesIdx++)->emitHidlDefinition(out); } else { emitFieldHidlDefinition(out, *mFields.at(fieldIdx++)); } } while (preDeclaredTypesIdx < preDeclaredTypes.size()) { out << "\n"; preDeclaredTypes.at(preDeclaredTypesIdx++)->emitHidlDefinition(out); } while (fieldIdx < mFields.size()) { out << "\n"; emitFieldHidlDefinition(out, *mFields.at(fieldIdx++)); } }); out << "}"; } void CompoundType::emitHidlDefinition(Formatter& out) const { emitInlineHidlDefinition(out); out << ";\n"; } void CompoundType::emitTypeDeclarations(Formatter& out) const { if (mStyle == STYLE_SAFE_UNION) { emitSafeUnionTypeDeclarations(out); return; } out << ((mStyle == STYLE_STRUCT) ? "struct" : "union") << " " << definedName() << " final {\n"; out.indent(); Scope::emitTypeDeclarations(out); if (containsPointer()) { for (const auto& field : mFields) { field->emitDocComment(out); out << field->type().getCppStackType() << " " << field->name() << ";\n"; } out.unindent(); out << "};\n\n"; return; } for (int pass = 0; pass < 2; ++pass) { size_t offset = 0; for (const auto& field : mFields) { size_t fieldAlign, fieldSize; field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); offset += Layout::getPad(offset, fieldAlign); if (pass == 0) { field->emitDocComment(out); out << field->type().getCppStackType() << " " << field->name() << " __attribute__ ((aligned(" << fieldAlign << ")));\n"; } else { out << "static_assert(offsetof(" << fullName() << ", " << field->name() << ") == " << offset << ", \"wrong offset\");\n"; } if (mStyle == STYLE_STRUCT) { offset += fieldSize; } } if (pass == 0) { out.unindent(); out << "};\n\n"; } } CompoundLayout layout = getCompoundAlignmentAndSize(); emitLayoutAsserts(out, layout.overall, ""); out << "\n"; } void CompoundType::emitTypeForwardDeclaration(Formatter& out) const { switch (mStyle) { case STYLE_UNION: { out << "union"; break; } case STYLE_STRUCT: case STYLE_SAFE_UNION: { out << "struct"; break; } default: { CHECK(!"Should not be here"); } } out << " " << definedName() << ";\n"; } void CompoundType::emitPackageTypeDeclarations(Formatter& out) const { Scope::emitPackageTypeDeclarations(out); out << "static inline std::string toString(" << getCppArgumentType() << " o);\n"; out << "static inline void PrintTo(" << getCppArgumentType() << " o, ::std::ostream*);\n"; if (canCheckEquality()) { out << "static inline bool operator==(" << getCppArgumentType() << " lhs, " << getCppArgumentType() << " rhs);\n"; out << "static inline bool operator!=(" << getCppArgumentType() << " lhs, " << getCppArgumentType() << " rhs);\n"; } else { out << "// operator== and operator!= are not generated for " << definedName() << "\n"; } out.endl(); } void CompoundType::emitPackageTypeHeaderDefinitions(Formatter& out) const { Scope::emitPackageTypeHeaderDefinitions(out); out << "static inline std::string toString(" << getCppArgumentType() << (mFields.empty() ? "" : " o") << ") "; out.block([&] { // include toString for scalar types out << "using ::android::hardware::toString;\n" << "std::string os;\n"; out << "os += \"{\";\n"; if (mStyle == STYLE_SAFE_UNION) { out << "\nswitch (o.getDiscriminator()) {\n"; out.indent(); } for (const NamedReference* field : mFields) { if (mStyle == STYLE_SAFE_UNION) { out << "case " << fullName() << "::hidl_discriminator::" << field->name() << ": "; out.block([&] { out << "os += \"." << field->name() << " = \";\n" << "os += toString(o." << field->name() << "());\n" << "break;\n"; }).endl(); } else { out << "os += \""; if (field != *(mFields.begin())) { out << ", "; } out << "." << field->name() << " = \";\n"; field->type().emitDump(out, "os", "o." + field->name()); } } if (mStyle == STYLE_SAFE_UNION) { out << "default: "; out.block([&] { emitSafeUnionUnknownDiscriminatorError(out, "o.getDiscriminator()", true /*fatal*/); }) .endl(); out.unindent(); out << "}\n"; } out << "os += \"}\"; return os;\n"; }).endl().endl(); out << "static inline void PrintTo(" << getCppArgumentType() << " o, ::std::ostream* os) "; out.block([&] { out << "*os << toString(o);\n"; }).endl().endl(); if (canCheckEquality()) { out << "static inline bool operator==(" << getCppArgumentType() << " " << (mFields.empty() ? "/* lhs */" : "lhs") << ", " << getCppArgumentType() << " " << (mFields.empty() ? "/* rhs */" : "rhs") << ") "; out.block([&] { if (mStyle == STYLE_SAFE_UNION) { out.sIf("lhs.getDiscriminator() != rhs.getDiscriminator()", [&] { out << "return false;\n"; }).endl(); out << "switch (lhs.getDiscriminator()) {\n"; out.indent(); } for (const auto& field : mFields) { if (mStyle == STYLE_SAFE_UNION) { out << "case " << fullName() << "::hidl_discriminator::" << field->name() << ": "; out.block([&] { out << "return (lhs." << field->name() << "() == rhs." << field->name() << "());\n"; }).endl(); } else { out.sIf("lhs." + field->name() + " != rhs." + field->name(), [&] { out << "return false;\n"; }).endl(); } } if (mStyle == STYLE_SAFE_UNION) { out << "default: "; out.block([&] { emitSafeUnionUnknownDiscriminatorError(out, "lhs.getDiscriminator()", true /*fatal*/); }) .endl(); out.unindent(); out << "}\n"; } out << "return true;\n"; }).endl().endl(); out << "static inline bool operator!=(" << getCppArgumentType() << " lhs, " << getCppArgumentType() << " rhs)"; out.block([&] { out << "return !(lhs == rhs);\n"; }).endl().endl(); } else { out << "// operator== and operator!= are not generated for " << definedName() << "\n\n"; } } void CompoundType::emitPackageHwDeclarations(Formatter& out) const { Scope::emitPackageHwDeclarations(out); if (needsEmbeddedReadWrite()) { out << "::android::status_t readEmbeddedFromParcel(\n"; out.indent(2); out << "const " << fullName() << " &obj,\n" << "const ::android::hardware::Parcel &parcel,\n" << "size_t parentHandle,\n" << "size_t parentOffset);\n\n"; out.unindent(2); out << "::android::status_t writeEmbeddedToParcel(\n"; out.indent(2); out << "const " << fullName() << " &obj,\n" << "::android::hardware::Parcel *parcel,\n" << "size_t parentHandle,\n" << "size_t parentOffset);\n\n"; out.unindent(2); } } static void emitSafeUnionFieldConstructor(Formatter& out, const NamedReference* field, const std::string& initializerObjectName) { out << "new (&hidl_u." << field->name() << ") " << field->type().getCppStackType() << "(" << initializerObjectName << ");\n"; } static void emitSafeUnionSetterDefinition(Formatter& out, const NamedReference* field, const std::string& parameterName, bool usesMoveSemantics) { out.block([&] { out << "if (hidl_d != hidl_discriminator::" << field->name() << ") "; const std::string argumentName = usesMoveSemantics ? ("std::move(" + parameterName + ")") : parameterName; out.block([&] { out << "hidl_destructUnion();\n" << "::std::memset(&hidl_u, 0, sizeof(hidl_u));\n\n"; emitSafeUnionFieldConstructor(out, field, argumentName); out << "hidl_d = hidl_discriminator::" << field->name() << ";\n"; }).endl(); out << "else if (&(hidl_u." << field->name() << ") != &" << parameterName << ") "; out.block([&] { out << "hidl_u." << field->name() << " = " << argumentName << ";\n"; }).endl(); }).endl().endl(); } static void emitSafeUnionGetterDefinition(Formatter& out, const std::string& fieldName) { out.block([&] { out << "if (CC_UNLIKELY(hidl_d != hidl_discriminator::" << fieldName << ")) "; out.block([&] { out << "LOG_ALWAYS_FATAL(\"Bad safe_union access: safe_union has discriminator %\" " << "PRIu64 \" but discriminator %\" PRIu64 \" was accessed.\",\n"; out.indent(2, [&] { out << "static_cast(hidl_d), " << "static_cast(hidl_discriminator::" << fieldName << "));"; }).endl(); }).endl().endl(); out << "return hidl_u." << fieldName << ";\n"; }).endl().endl(); } void CompoundType::emitSafeUnionAssignDefinition(Formatter& out, const std::string& parameterName, bool usesMoveSemantics) const { out.block([&] { out << "if (this == &" << parameterName << ") { return *this; }\n\n"; out << "switch (" << parameterName << ".hidl_d) "; out.block([&] { for (const auto& field : mFields) { const std::string parameterFieldName = (parameterName + ".hidl_u." + field->name()); const std::string argumentName = usesMoveSemantics ? ("std::move(" + parameterFieldName + ")") : parameterFieldName; out << "case hidl_discriminator::" << field->name() << ": "; out.block([&] { out << field->name() << "(" << argumentName << ");\n" << "break;\n"; }).endl(); } out << "default: "; out.block([&] { emitSafeUnionUnknownDiscriminatorError(out, parameterName + ".hidl_d", true /*fatal*/); }).endl(); }).endl(); out << "return *this;\n"; }) .endl() .endl(); } void CompoundType::emitSafeUnionTypeConstructors(Formatter& out) const { // Default constructor out << fullName() << "::" << definedName() << "() "; out.block([&] { out << "static_assert(offsetof(" << fullName() << ", hidl_d) == 0, \"wrong offset\");\n"; const CompoundLayout layout = getCompoundAlignmentAndSize(); if (!containsPointer()) { out << "static_assert(offsetof(" << fullName() << ", hidl_u) == " << layout.innerStruct.offset << ", \"wrong offset\");\n"; } out.endl(); out << "::std::memset(&hidl_u, 0, sizeof(hidl_u));\n"; // union itself is zero'd when set // padding after descriminator size_t dpad = layout.innerStruct.offset - layout.discriminator.size; emitPaddingZero(out, layout.discriminator.size /*offset*/, dpad /*size*/); size_t innerStructEnd = layout.innerStruct.offset + layout.innerStruct.size; // final padding of the struct size_t fpad = layout.overall.size - innerStructEnd; emitPaddingZero(out, innerStructEnd /*offset*/, fpad /*size*/); out.endl(); CHECK(!mFields.empty()); out << "hidl_d = hidl_discriminator::" << mFields.at(0)->name() << ";\n"; emitSafeUnionFieldConstructor(out, mFields.at(0), ""); }).endl().endl(); // Destructor out << fullName() << "::~" << definedName() << "() "; out.block([&] { out << "hidl_destructUnion();\n"; }).endl().endl(); // Move constructor out << fullName() << "::" << definedName() << "(" << definedName() << "&& other) : " << fullName() << "() "; out.block([&] { out << "*this = std::move(other);\n"; }).endl().endl(); // Copy constructor out << fullName() << "::" << definedName() << "(const " << definedName() << "& other) : " << fullName() << "() "; out.block([&] { out << "*this = other;\n"; }).endl().endl(); // Move assignment operator out << fullName() << "& (" << fullName() << "::operator=)(" << definedName() << "&& other) "; emitSafeUnionAssignDefinition(out, "other", true /* usesMoveSemantics */); // Copy assignment operator out << fullName() << "& (" << fullName() << "::operator=)(const " << definedName() << "& other) "; emitSafeUnionAssignDefinition(out, "other", false /* usesMoveSemantics */); } void CompoundType::emitSafeUnionTypeDefinitions(Formatter& out) const { emitSafeUnionTypeConstructors(out); out << "void " << fullName() << "::hidl_destructUnion() "; out.block([&] { out << "switch (hidl_d) "; out.block([&] { for (const auto& field : mFields) { out << "case hidl_discriminator::" << field->name() << ": "; out.block([&] { out << "::android::hardware::details::destructElement(&(hidl_u." << field->name() << "));\n" << "break;\n"; }).endl(); } out << "default: "; out.block([&] { emitSafeUnionUnknownDiscriminatorError(out, "hidl_d", true /*fatal*/); }).endl(); }) .endl() .endl(); }).endl().endl(); for (const NamedReference* field : mFields) { // Setter (copy) out << "void " << fullName() << "::" << field->name() << "(" << field->type().getCppArgumentType() << " o) "; emitSafeUnionSetterDefinition(out, field, "o", false /* usesMoveSemantics */); if (field->type().resolveToScalarType() == nullptr) { // Setter (move) out << "void " << fullName() << "::" << field->name() << "(" << field->type().getCppStackType() << "&& o) "; emitSafeUnionSetterDefinition(out, field, "o", true /* usesMoveSemantics */); } // Getter (mutable) out << field->type().getCppStackType() << "& (" << fullName() << "::" << field->name() << ")() "; emitSafeUnionGetterDefinition(out, field->name()); // Getter (immutable) out << field->type().getCppArgumentType() << " (" << fullName() << "::" << field->name() << ")() const "; emitSafeUnionGetterDefinition(out, field->name()); } // Trivial constructor/destructor for internal union out << fullName() << "::hidl_union::hidl_union() {}\n\n" << fullName() << "::hidl_union::~hidl_union() {}\n\n"; // Utility method out << fullName() << "::hidl_discriminator (" << fullName() << "::getDiscriminator)() const "; out.block([&] { out << "return hidl_d;\n"; }).endl().endl(); } void CompoundType::emitTypeDefinitions(Formatter& out, const std::string& prefix) const { std::string space = prefix.empty() ? "" : (prefix + "::"); Scope::emitTypeDefinitions(out, space + definedName()); if (needsEmbeddedReadWrite()) { emitStructReaderWriter(out, prefix, true /* isReader */); emitStructReaderWriter(out, prefix, false /* isReader */); } if (mStyle == STYLE_SAFE_UNION) { emitSafeUnionTypeDefinitions(out); } } static void emitJavaSafeUnionUnknownDiscriminatorError(Formatter& out, bool fatal) { out << "throw new "; if (fatal) { out << "Error"; } else { out << "IllegalStateException"; } out << "(\"Unknown union discriminator (value: \" + hidl_d + \").\");\n"; } void CompoundType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const { out << "public final "; if (!atTopLevel) { out << "static "; } out << "class " << definedName() << " {\n"; out.indent(); Scope::emitJavaTypeDeclarations(out, false /* atTopLevel */); if (mStyle == STYLE_SAFE_UNION) { out << "public " << definedName() << "() "; out.block([&] { CHECK(!mFields.empty()); mFields.at(0)->type().emitJavaFieldDefaultInitialValue(out, "hidl_o"); }) .endl() .endl(); const std::string discriminatorStorageType = ( getUnionDiscriminatorType()->getJavaType(false)); out << "public static final class hidl_discriminator "; out.block([&] { for (size_t idx = 0; idx < mFields.size(); idx++) { const auto& field = mFields.at(idx); field->emitDocComment(out); out << "public static final " << discriminatorStorageType << " " << field->name() << " = " << idx << "; // " << field->type().getJavaType(true /* forInitializer */) << "\n"; } out << "\n" << "public static final String getName(" << discriminatorStorageType << " value) "; out.block([&] { out << "switch (value) "; out.block([&] { for (size_t idx = 0; idx < mFields.size(); idx++) { const auto& field = mFields.at(idx); out << "case " << idx << ": { return \"" << field->name() << "\"; }\n"; } out << "default: { return \"Unknown\"; }\n"; }).endl(); }) .endl() .endl(); out << "private hidl_discriminator() {}\n"; }) .endl() .endl(); out << "private " << discriminatorStorageType << " hidl_d = 0;\n"; out << "private Object hidl_o = null;\n"; for (const auto& field : mFields) { // Setter out << "public void " << field->name() << "(" << field->type().getJavaType(false) << " " << field->name() << ") "; out.block([&] { out << "hidl_d = hidl_discriminator." << field->name() << ";\n"; out << "hidl_o = " << field->name() << ";\n"; }).endl().endl(); // Getter out << "public " << field->type().getJavaType(false) << " " << field->name() << "() "; out.block([&] { out << "if (hidl_d != hidl_discriminator." << field->name() << ") "; out.block([&] { out << "String className = (hidl_o != null) ? " << "hidl_o.getClass().getName() : \"null\";\n"; out << "throw new IllegalStateException(\n"; out.indent(2, [&] { out << "\"Read access to inactive union components is disallowed. \" +\n" << "\"Discriminator value is \" + hidl_d + \" (corresponding \" +\n" << "\"to \" + hidl_discriminator.getName(hidl_d) + \"), and \" +\n" << "\"hidl_o is of type \" + className + \".\");\n"; }); }).endl(); out << "if (hidl_o != null && !" << field->type().getJavaTypeClass() << ".class.isInstance(hidl_o)) "; out.block([&] { out << "throw new Error(\"Union is in a corrupted state.\");\n"; }).endl(); out << "return (" << field->type().getJavaTypeCast("hidl_o") << ");\n"; }).endl().endl(); } out << "// Utility method\n" << "public " << discriminatorStorageType << " getDiscriminator() { return hidl_d; }\n\n"; } else if (mStyle == STYLE_STRUCT) { for (const auto& field : mFields) { field->emitDocComment(out); out << "public "; field->type().emitJavaFieldInitializer(out, field->name()); } out << "\n"; } else { LOG(FATAL) << "Java output doesn't support " << mStyle; } //////////////////////////////////////////////////////////////////////////// if (canCheckEquality()) { out << "@Override\npublic final boolean equals(Object otherObject) "; out.block([&] { out.sIf("this == otherObject", [&] { out << "return true;\n"; }).endl(); out.sIf("otherObject == null", [&] { out << "return false;\n"; }).endl(); // Though class is final, we use getClass instead of instanceof to be explicit. out.sIf("otherObject.getClass() != " + fullJavaName() + ".class", [&] { out << "return false;\n"; }).endl(); out << fullJavaName() << " other = (" << fullJavaName() << ")otherObject;\n"; if (mStyle == STYLE_SAFE_UNION) { out.sIf("this.hidl_d != other.hidl_d", [&] { out << "return false;\n"; }).endl(); out.sIf("!android.os.HidlSupport.deepEquals(this.hidl_o, other.hidl_o)", [&] { out << "return false;\n"; }).endl(); } else { for (const auto& field : mFields) { std::string condition = (field->type().isScalar() || field->type().isEnum()) ? "this." + field->name() + " != other." + field->name() : ("!android.os.HidlSupport.deepEquals(this." + field->name() + ", other." + field->name() + ")"); out.sIf(condition, [&] { out << "return false;\n"; }).endl(); } } out << "return true;\n"; }).endl().endl(); out << "@Override\npublic final int hashCode() "; out.block([&] { out << "return java.util.Objects.hash(\n"; out.indent(2, [&] { if (mStyle == STYLE_SAFE_UNION) { out << "android.os.HidlSupport.deepHashCode(this.hidl_o),\n" << "java.util.Objects.hashCode(this.hidl_d)"; } else { out.join(mFields.begin(), mFields.end(), ", \n", [&](const auto& field) { out << "android.os.HidlSupport.deepHashCode(this." << field->name() << ")"; }); } }); out << ");\n"; }).endl().endl(); } else { out << "// equals() is not generated for " << definedName() << "\n"; } //////////////////////////////////////////////////////////////////////////// out << "@Override\npublic final String toString() "; out.block([&] { out << "java.lang.StringBuilder builder = new java.lang.StringBuilder();\n" << "builder.append(\"{\");\n"; if (mStyle == STYLE_SAFE_UNION) { out << "switch (this.hidl_d) {\n"; out.indent(); } for (const auto& field : mFields) { if (mStyle == STYLE_SAFE_UNION) { out << "case hidl_discriminator." << field->name() << ": "; out.block([&] { out << "builder.append(\"" << "." << field->name() << " = \");\n"; field->type().emitJavaDump(out, "builder", "this." + field->name() + "()"); out << "break;\n"; }).endl(); } else { out << "builder.append(\""; if (field != *(mFields.begin())) { out << ", "; } out << "." << field->name() << " = \");\n"; field->type().emitJavaDump(out, "builder", "this." + field->name()); } } if (mStyle == STYLE_SAFE_UNION) { out << "default: "; out.block([&] { emitJavaSafeUnionUnknownDiscriminatorError(out, true /*fatal*/); }) .endl(); out.unindent(); out << "}\n"; } out << "builder.append(\"}\");\nreturn builder.toString();\n"; }).endl().endl(); CompoundLayout layout = getCompoundAlignmentAndSize(); //////////////////////////////////////////////////////////////////////////// out << "public final void readFromParcel(android.os.HwParcel parcel) {\n"; out.indent(); if (containsInterface()) { if (mStyle == STYLE_SAFE_UNION) { out << "hidl_d = "; getUnionDiscriminatorType()->emitJavaReaderWriter( out, "parcel", "hidl_d", true); out << "switch (hidl_d) {\n"; out.indent(); } for (const auto& field : mFields) { if (mStyle == STYLE_SAFE_UNION) { out << "case hidl_discriminator." << field->name() << ": "; out.block([&] { out << "hidl_o = "; field->type().emitJavaReaderWriter(out, "parcel", "hidl_o", true); out << "break;\n"; }).endl(); } else { out << field->name() << " = "; field->type().emitJavaReaderWriter(out, "parcel", field->name(), true); } } if (mStyle == STYLE_SAFE_UNION) { out << "default: "; out.block([&] { emitJavaSafeUnionUnknownDiscriminatorError(out, false /*fatal*/); }) .endl(); out.unindent(); out << "}\n"; } } else { out << "android.os.HwBlob blob = parcel.readBuffer("; out << layout.overall.size << " /* size */);\n"; out << "readEmbeddedFromParcel(parcel, blob, 0 /* parentOffset */);\n"; } out.unindent(); out << "}\n\n"; //////////////////////////////////////////////////////////////////////////// size_t vecAlign, vecSize; VectorType::getAlignmentAndSizeStatic(&vecAlign, &vecSize); out << "public static final java.util.ArrayList<" << definedName() << "> readVectorFromParcel(android.os.HwParcel parcel) {\n"; out.indent(); out << "java.util.ArrayList<" << definedName() << "> _hidl_vec = new java.util.ArrayList();\n"; if (containsInterface()) { out << "int size = parcel.readInt32();\n"; out << "for(int i = 0 ; i < size; i ++) {\n"; out.indent(); out << fullJavaName() << " tmp = "; emitJavaReaderWriter(out, "parcel", "tmp", true); out << "_hidl_vec.add(tmp);\n"; out.unindent(); out << "}\n"; } else { out << "android.os.HwBlob _hidl_blob = parcel.readBuffer("; out << vecSize << " /* sizeof hidl_vec */);\n\n"; VectorType::EmitJavaFieldReaderWriterForElementType(out, 0 /* depth */, this, "parcel", "_hidl_blob", "_hidl_vec", "0", true /* isReader */); } out << "\nreturn _hidl_vec;\n"; out.unindent(); out << "}\n\n"; //////////////////////////////////////////////////////////////////////////// if (containsInterface()) { out << "// readEmbeddedFromParcel is not generated()\n"; } else { out << "public final void readEmbeddedFromParcel(\n"; out.indent(2); out << "android.os.HwParcel parcel, android.os.HwBlob _hidl_blob, long _hidl_offset) {\n"; out.unindent(); if (mStyle == STYLE_SAFE_UNION) { getUnionDiscriminatorType()->emitJavaFieldReaderWriter( out, 0 /* depth */, "parcel", "_hidl_blob", "hidl_d", "_hidl_offset + " + std::to_string(layout.discriminator.offset), true /* isReader */); out << "switch (this.hidl_d) {\n"; out.indent(); } size_t offset = layout.innerStruct.offset; for (const auto& field : mFields) { if (mStyle == STYLE_SAFE_UNION) { out << "case hidl_discriminator." << field->name() << ": "; out.block([&] { field->type().emitJavaFieldDefaultInitialValue(out, "hidl_o"); field->type().emitJavaFieldReaderWriter( out, 0 /* depth */, "parcel", "_hidl_blob", "hidl_o", "_hidl_offset + " + std::to_string(offset), true /* isReader */); out << "break;\n"; }).endl(); } else { size_t fieldAlign, fieldSize; field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); offset += Layout::getPad(offset, fieldAlign); field->type().emitJavaFieldReaderWriter( out, 0 /* depth */, "parcel", "_hidl_blob", field->name(), "_hidl_offset + " + std::to_string(offset), true /* isReader */); offset += fieldSize; } } if (mStyle == STYLE_SAFE_UNION) { out << "default: "; out.block([&] { emitJavaSafeUnionUnknownDiscriminatorError(out, false /*fatal*/); }) .endl(); out.unindent(); out << "}\n"; } out.unindent(); out << "}\n\n"; } //////////////////////////////////////////////////////////////////////////// out << "public final void writeToParcel(android.os.HwParcel parcel) {\n"; out.indent(); if (containsInterface()) { if (mStyle == STYLE_SAFE_UNION) { getUnionDiscriminatorType()->emitJavaReaderWriter( out, "parcel", "hidl_d", false); out << "switch (this.hidl_d) {\n"; out.indent(); } for (const auto& field : mFields) { if (mStyle == STYLE_SAFE_UNION) { out << "case hidl_discriminator." << field->name() << ": "; out.block([&] { field->type().emitJavaReaderWriter(out, "parcel", field->name() + "()", false); out << "break;\n"; }).endl(); } else { field->type().emitJavaReaderWriter(out, "parcel", field->name(), false); } } if (mStyle == STYLE_SAFE_UNION) { out << "default: "; out.block([&] { emitJavaSafeUnionUnknownDiscriminatorError(out, true /*fatal*/); }) .endl(); out.unindent(); out << "}\n"; } } else { out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << layout.overall.size << " /* size */);\n"; out << "writeEmbeddedToBlob(_hidl_blob, 0 /* parentOffset */);\n" << "parcel.writeBuffer(_hidl_blob);\n"; } out.unindent(); out << "}\n\n"; //////////////////////////////////////////////////////////////////////////// out << "public static final void writeVectorToParcel(\n"; out.indent(2); out << "android.os.HwParcel parcel, java.util.ArrayList<" << definedName() << "> _hidl_vec) {\n"; out.unindent(); if (containsInterface()) { out << "parcel.writeInt32(_hidl_vec.size());\n"; out << "for(" << fullJavaName() << " tmp: _hidl_vec) "; out.block([&] { emitJavaReaderWriter(out, "parcel", "tmp", false); }).endl(); } else { out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << vecSize << " /* sizeof(hidl_vec) */);\n"; VectorType::EmitJavaFieldReaderWriterForElementType(out, 0 /* depth */, this, "parcel", "_hidl_blob", "_hidl_vec", "0", false /* isReader */); out << "\nparcel.writeBuffer(_hidl_blob);\n"; } out.unindent(); out << "}\n\n"; //////////////////////////////////////////////////////////////////////////// if (containsInterface()) { out << "// writeEmbeddedToBlob() is not generated\n"; } else { out << "public final void writeEmbeddedToBlob(\n"; out.indent(2); out << "android.os.HwBlob _hidl_blob, long _hidl_offset) {\n"; out.unindent(); if (mStyle == STYLE_SAFE_UNION) { getUnionDiscriminatorType()->emitJavaFieldReaderWriter( out, 0 /* depth */, "parcel", "_hidl_blob", "hidl_d", "_hidl_offset + " + std::to_string(layout.discriminator.offset), false /* isReader */); out << "switch (this.hidl_d) {\n"; out.indent(); } size_t offset = layout.innerStruct.offset; for (const auto& field : mFields) { if (mStyle == STYLE_SAFE_UNION) { out << "case hidl_discriminator." << field->name() << ": "; out.block([&] { field->type().emitJavaFieldReaderWriter( out, 0 /* depth */, "parcel", "_hidl_blob", field->name() + "()", "_hidl_offset + " + std::to_string(offset), false /* isReader */); out << "break;\n"; }).endl(); } else { size_t fieldAlign, fieldSize; field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); offset += Layout::getPad(offset, fieldAlign); field->type().emitJavaFieldReaderWriter( out, 0 /* depth */, "parcel", "_hidl_blob", field->name(), "_hidl_offset + " + std::to_string(offset), false /* isReader */); offset += fieldSize; } } if (mStyle == STYLE_SAFE_UNION) { out << "default: "; out.block([&] { emitJavaSafeUnionUnknownDiscriminatorError(out, true /*fatal*/); }) .endl(); out.unindent(); out << "}\n"; } out.unindent(); out << "}\n"; } out.unindent(); out << "};\n\n"; } void CompoundType::emitStructReaderWriter( Formatter &out, const std::string &prefix, bool isReader) const { std::string space = prefix.empty() ? "" : (prefix + "::"); out << "::android::status_t " << (isReader ? "readEmbeddedFromParcel" : "writeEmbeddedToParcel") << "(\n"; out.indent(2); const std::string name = "obj"; if (isReader) { out << "const " << space << definedName() << " &" << name << ",\n"; out << "const ::android::hardware::Parcel &parcel,\n"; } else { out << "const " << space << definedName() << " &" << name << ",\n"; out << "::android::hardware::Parcel *parcel,\n"; } out << "size_t parentHandle,\n" << "size_t parentOffset)"; out << " {\n"; out.unindent(2); out.indent(); out << "::android::status_t _hidl_err = ::android::OK;\n\n"; if (mStyle == STYLE_SAFE_UNION) { out << "switch (" << name << ".getDiscriminator()) {\n"; out.indent(); } for (const auto& field : mFields) { if (!field->type().needsEmbeddedReadWrite()) { continue; } if (mStyle == STYLE_SAFE_UNION) { out << "case " << fullName() << "::hidl_discriminator::" << field->name() << ": {\n"; out.indent(); } const std::string fieldName = (mStyle == STYLE_SAFE_UNION) ? (name + "." + field->name() + "()") : (name + "." + field->name()); const std::string fieldOffset = (mStyle == STYLE_SAFE_UNION) ? (name + ".hidl_getUnionOffset() " + "/* safe_union: union offset into struct */") : ("offsetof(" + fullName() + ", " + field->name() + ")"); field->type().emitReaderWriterEmbedded( out, 0 /* depth */, fieldName, field->name() /* sanitizedName */, false /* nameIsPointer */, "parcel", !isReader /* parcelObjIsPointer */, isReader, ErrorMode_Return, "parentHandle", "parentOffset + " + fieldOffset); if (mStyle == STYLE_SAFE_UNION) { out << "break;\n"; out.unindent(); out << "}\n"; } } if (mStyle == STYLE_SAFE_UNION) { out << "default: { break; }\n"; out.unindent(); out << "}\n"; } out << "return _hidl_err;\n"; out.unindent(); out << "}\n\n"; } bool CompoundType::needsEmbeddedReadWrite() const { if (mStyle == STYLE_UNION) { return false; } for (const auto& field : mFields) { if (field->type().needsEmbeddedReadWrite()) { return true; } } return false; } bool CompoundType::resultNeedsDeref() const { return !containsInterface() ; } void CompoundType::emitVtsTypeDeclarations(Formatter& out) const { out << "name: \"" << fullName() << "\"\n"; out << "type: " << getVtsType() << "\n"; // Emit declaration for each subtype. for (const auto &type : getSubTypes()) { switch (mStyle) { case STYLE_STRUCT: { out << "sub_struct: {\n"; break; } case STYLE_UNION: { out << "sub_union: {\n"; break; } case STYLE_SAFE_UNION: { out << "sub_safe_union: {\n"; break; } default: { CHECK(!"Should not be here"); } } out.indent(); type->emitVtsTypeDeclarations(out); out.unindent(); out << "}\n"; } // Emit declaration for each field. for (const auto& field : mFields) { switch (mStyle) { case STYLE_STRUCT: { out << "struct_value: {\n"; break; } case STYLE_UNION: { out << "union_value: {\n"; break; } case STYLE_SAFE_UNION: { out << "safe_union_value: {\n"; break; } default: { CHECK(!"Should not be here"); } } out.indent(); out << "name: \"" << field->name() << "\"\n"; field->type().emitVtsAttributeType(out); out.unindent(); out << "}\n"; } } void CompoundType::emitVtsAttributeType(Formatter& out) const { out << "type: " << getVtsType() << "\n"; out << "predefined_type: \"" << fullName() << "\"\n"; } bool CompoundType::deepIsJavaCompatible(std::unordered_set* visited) const { if (mStyle == STYLE_UNION) { return false; } for (const auto* field : mFields) { if (!field->get()->isJavaCompatible(visited)) { return false; } } return Scope::deepIsJavaCompatible(visited); } bool CompoundType::deepContainsPointer(std::unordered_set* visited) const { for (const auto* field : mFields) { if (field->get()->containsPointer(visited)) { return true; } } return Scope::deepContainsPointer(visited); } void CompoundType::getAlignmentAndSize(size_t *align, size_t *size) const { CompoundLayout layout = getCompoundAlignmentAndSize(); *align = layout.overall.align; *size = layout.overall.size; } CompoundType::CompoundLayout CompoundType::getCompoundAlignmentAndSize() const { CompoundLayout compoundLayout; // Local aliases for convenience Layout& overall = compoundLayout.overall; Layout& innerStruct = compoundLayout.innerStruct; Layout& discriminator = compoundLayout.discriminator; if (mStyle == STYLE_SAFE_UNION) { getUnionDiscriminatorType()->getAlignmentAndSize( &(discriminator.align), &(discriminator.size)); innerStruct.offset = discriminator.size; } for (const auto& field : mFields) { // Each field is aligned according to its alignment requirement. // The surrounding structure's alignment is the maximum of its // fields' aligments. size_t fieldAlign, fieldSize; field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); size_t lPad = Layout::getPad(innerStruct.size, fieldAlign); innerStruct.size = (mStyle == STYLE_STRUCT) ? (innerStruct.size + lPad + fieldSize) : std::max(innerStruct.size, fieldSize); innerStruct.align = std::max(innerStruct.align, fieldAlign); } // Pad the inner structure's size innerStruct.size += Layout::getPad(innerStruct.size, innerStruct.align); // Compute its final offset innerStruct.offset += Layout::getPad(innerStruct.offset, innerStruct.align); // An empty struct/union still occupies a byte of space in C++. if (innerStruct.size == 0) { innerStruct.size = 1; } overall.size = innerStruct.offset + innerStruct.size; // Pad the overall structure's size overall.align = std::max(innerStruct.align, discriminator.align); overall.size += Layout::getPad(overall.size, overall.align); if (mStyle != STYLE_SAFE_UNION) { CHECK(overall.offset == innerStruct.offset) << overall.offset << " " << innerStruct.offset; CHECK(overall.align == innerStruct.align) << overall.align << " " << innerStruct.align; CHECK(overall.size == innerStruct.size) << overall.size << " " << innerStruct.size; } return compoundLayout; } void CompoundType::emitPaddingZero(Formatter& out, size_t offset, size_t size) const { if (size > 0) { out << "::std::memset(reinterpret_cast(this) + " << offset << ", 0, " << size << ");\n"; } else { out << "// no padding to zero starting at offset " << offset << "\n"; } } std::unique_ptr CompoundType::getUnionDiscriminatorType() const { static const std::vector > scalars { {8, ScalarType::Kind::KIND_UINT8}, {16, ScalarType::Kind::KIND_UINT16}, {32, ScalarType::Kind::KIND_UINT32}, }; size_t numFields = mFields.size(); auto kind = ScalarType::Kind::KIND_UINT64; for (const auto& scalar : scalars) { if (numFields <= (1ULL << scalar.first)) { kind = scalar.second; break; } } return std::unique_ptr(new ScalarType(kind, nullptr)); } size_t CompoundType::Layout::getPad(size_t offset, size_t align) { size_t remainder = offset % align; return (remainder > 0) ? (align - remainder) : 0; } } // namespace android