| /* |
| * 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 <android-base/logging.h> |
| #include <hidl-util/Formatter.h> |
| #include <iostream> |
| #include <set> |
| #include <string> |
| #include <unordered_set> |
| #include <vector> |
| |
| 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<Type>* field) { |
| mFields.push_back(field); |
| } |
| |
| std::vector<const NamedReference<Type>*> CompoundType::getFields() const { |
| return std::vector<const NamedReference<Type>*>(mFields.begin(), mFields.end()); |
| } |
| |
| std::vector<const Reference<Type>*> CompoundType::getReferences() const { |
| std::vector<const Reference<Type>*> 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<const VectorType*>(&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<std::string> 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<const Type*>* 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<const CompoundType*>(&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<const void**>(reinterpret_cast<void **>(" |
| << "&" << 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<Type>& 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<const CompoundType*>(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<FQName> namesDeclaredInScope; |
| for (const NamedReference<Type>* ref : mFields) { |
| if (ref->definedInline()) { |
| const Type* type = ref->get(); |
| CHECK(type->isCompoundType()) << " only compound types can be defined inline"; |
| namesDeclaredInScope.insert(static_cast<const CompoundType*>(type)->fqName()); |
| } |
| } |
| |
| std::vector<const NamedType*> 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<Type>* 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<Type>* field, |
| const std::string& initializerObjectName) { |
| out << "new (&hidl_u." |
| << field->name() |
| << ") " |
| << field->type().getCppStackType() |
| << "(" |
| << initializerObjectName |
| << ");\n"; |
| } |
| |
| static void emitSafeUnionSetterDefinition(Formatter& out, |
| const NamedReference<Type>* 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<uint64_t>(hidl_d), " |
| << "static_cast<uint64_t>(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<Type>* 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<T> */);\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<T>) */);\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<const Type*>* 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<const Type*>* 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<uint8_t*>(this) + " << offset << ", 0, " << size |
| << ");\n"; |
| } else { |
| out << "// no padding to zero starting at offset " << offset << "\n"; |
| } |
| } |
| |
| std::unique_ptr<ScalarType> CompoundType::getUnionDiscriminatorType() const { |
| static const std::vector<std::pair<int, ScalarType::Kind> > 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<ScalarType>(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 |
| |