| /* |
| * 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 "VectorType.h" |
| |
| #include <android-base/logging.h> |
| #include <hidl-util/Formatter.h> |
| #include <iostream> |
| #include <unordered_set> |
| |
| namespace android { |
| |
| CompoundType::CompoundType(Style style, const char* localName, const Location& location, |
| Scope* parent) |
| : Scope(localName, location, parent), mStyle(style), mFields(NULL) {} |
| |
| CompoundType::Style CompoundType::style() const { |
| return mStyle; |
| } |
| |
| void CompoundType::setFields(std::vector<NamedReference<Type>*>* fields) { |
| mFields = fields; |
| } |
| |
| 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.isBinder() |
| || (type.isVector() |
| && static_cast<const VectorType *>( |
| &type)->isVectorOfBinders())) { |
| std::cerr << "ERROR: Struct/Union must not contain references to interfaces at " |
| << field->location() << "\n"; |
| return UNKNOWN_ERROR; |
| } |
| |
| if (mStyle == STYLE_UNION) { |
| if (type.needsEmbeddedReadWrite()) { |
| std::cerr << "ERROR: Union must not contain any types that need fixup at " |
| << field->location() << "\n"; |
| return UNKNOWN_ERROR; |
| } |
| } |
| } |
| |
| status_t err = validateUniqueNames(); |
| 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; |
| } |
| |
| bool CompoundType::isCompoundType() const { |
| return true; |
| } |
| |
| bool CompoundType::canCheckEquality() const { |
| if (mStyle == STYLE_UNION) { |
| return false; |
| } |
| for (const auto &field : *mFields) { |
| if (!field->type().canCheckEquality()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| std::string CompoundType::typeName() const { |
| switch (mStyle) { |
| case STYLE_STRUCT: { |
| return "struct " + localName(); |
| } |
| case STYLE_UNION: { |
| return "union " + localName(); |
| } |
| } |
| 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 "const " + base + "*"; |
| } |
| } |
| |
| 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"; |
| } |
| } |
| CHECK(!"Should not be here"); |
| } |
| |
| void CompoundType::emitReaderWriter( |
| Formatter &out, |
| const std::string &name, |
| const std::string &parcelObj, |
| bool parcelObjIsPointer, |
| bool isReader, |
| ErrorMode mode) const { |
| const std::string parentName = "_hidl_" + name + "_parent"; |
| |
| out << "size_t " << parentName << ";\n\n"; |
| |
| const std::string parcelObjDeref = |
| parcelObj + (parcelObjIsPointer ? "->" : "."); |
| |
| if (isReader) { |
| out << "_hidl_err = " |
| << parcelObjDeref |
| << "readBuffer(" |
| << "sizeof(*" |
| << name |
| << "), &" |
| << parentName |
| << ", " |
| << " reinterpret_cast<const void **>(" |
| << "&" << name |
| << "));\n"; |
| |
| handleError(out, mode); |
| } else { |
| out << "_hidl_err = " |
| << parcelObjDeref |
| << "writeBuffer(&" |
| << name |
| << ", sizeof(" |
| << name |
| << "), &" |
| << parentName |
| << ");\n"; |
| |
| handleError(out, mode); |
| } |
| |
| if (mStyle != STYLE_STRUCT || !needsEmbeddedReadWrite()) { |
| return; |
| } |
| |
| 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 << argName |
| << "." |
| << (isReader ? "readFromParcel" : "writeToParcel") |
| << "(" |
| << parcelObj |
| << ");\n"; |
| } |
| |
| void CompoundType::emitJavaFieldInitializer( |
| Formatter &out, const std::string &fieldName) const { |
| out << "final " |
| << fullJavaName() |
| << " " |
| << fieldName |
| << " = 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 << fieldName |
| << ".readEmbeddedFromParcel(" |
| << parcelName |
| << ", " |
| << blobName |
| << ", " |
| << offset |
| << ");\n"; |
| |
| return; |
| } |
| |
| out << fieldName |
| << ".writeEmbeddedToBlob(" |
| << blobName |
| << ", " |
| << offset |
| << ");\n"; |
| } |
| void CompoundType::emitResolveReferences( |
| Formatter &out, |
| const std::string &name, |
| bool nameIsPointer, |
| const std::string &parcelObj, |
| bool parcelObjIsPointer, |
| bool isReader, |
| ErrorMode mode) const { |
| emitResolveReferencesEmbedded( |
| out, |
| 0 /* depth */, |
| name, |
| name /* sanitizedName */, |
| nameIsPointer, |
| parcelObj, |
| parcelObjIsPointer, |
| isReader, |
| mode, |
| "_hidl_" + name + "_parent", |
| "0 /* parentOffset */"); |
| } |
| |
| void CompoundType::emitResolveReferencesEmbedded( |
| 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 { |
| CHECK(needsResolveReferences()); |
| |
| const std::string parcelObjDeref = |
| parcelObjIsPointer ? ("*" + parcelObj) : parcelObj; |
| |
| const std::string parcelObjPointer = |
| parcelObjIsPointer ? parcelObj : ("&" + parcelObj); |
| |
| const std::string nameDerefed = nameIsPointer ? ("*" + name) : name; |
| const std::string namePointer = nameIsPointer ? name : ("&" + name); |
| |
| out << "_hidl_err = "; |
| |
| if (isReader) { |
| out << "readEmbeddedReferenceFromParcel(\n"; |
| } else { |
| out << "writeEmbeddedReferenceToParcel(\n"; |
| } |
| |
| out.indent(2, [&]{ |
| if (isReader) { |
| out << "const_cast<" |
| << fullName() |
| << " *" |
| << ">(" |
| << namePointer |
| << "),\n" |
| << parcelObjDeref; |
| } else { |
| out << nameDerefed |
| << ",\n" |
| << parcelObjPointer; |
| } |
| |
| out << ",\n" |
| << parentName |
| << ",\n" |
| << offsetText |
| << ");\n\n"; |
| }); |
| |
| handleError(out, mode); |
| } |
| |
| status_t CompoundType::emitTypeDeclarations(Formatter &out) const { |
| out << ((mStyle == STYLE_STRUCT) ? "struct" : "union") |
| << " " |
| << localName() |
| << " final {\n"; |
| |
| out.indent(); |
| |
| Scope::emitTypeDeclarations(out); |
| |
| if (containsPointer()) { |
| for (const auto &field : *mFields) { |
| out << field->type().getCppStackType() |
| << " " |
| << field->name() |
| << ";\n"; |
| } |
| |
| out.unindent(); |
| out << "};\n\n"; |
| |
| return OK; |
| } |
| |
| 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); |
| |
| size_t pad = offset % fieldAlign; |
| if (pad > 0) { |
| offset += fieldAlign - pad; |
| } |
| |
| if (pass == 0) { |
| 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"; |
| } |
| } |
| |
| size_t structAlign, structSize; |
| getAlignmentAndSize(&structAlign, &structSize); |
| |
| out << "static_assert(sizeof(" |
| << fullName() |
| << ") == " |
| << structSize |
| << ", \"wrong size\");\n"; |
| |
| out << "static_assert(__alignof(" |
| << fullName() |
| << ") == " |
| << structAlign |
| << ", \"wrong alignment\");\n\n"; |
| |
| return OK; |
| } |
| |
| void CompoundType::emitTypeForwardDeclaration(Formatter& out) const { |
| out << ((mStyle == STYLE_STRUCT) ? "struct" : "union") << " " << localName() << ";\n"; |
| } |
| |
| status_t CompoundType::emitGlobalTypeDeclarations(Formatter &out) const { |
| Scope::emitGlobalTypeDeclarations(out); |
| |
| out << "std::string toString(" |
| << getCppArgumentType() |
| << ");\n\n"; |
| |
| if (canCheckEquality()) { |
| out << "bool operator==(" |
| << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n"; |
| |
| out << "bool operator!=(" |
| << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n"; |
| } else { |
| out << "// operator== and operator!= are not generated for " << localName() << "\n\n"; |
| } |
| |
| return OK; |
| } |
| |
| status_t CompoundType::emitGlobalHwDeclarations(Formatter &out) const { |
| 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); |
| } |
| |
| if(needsResolveReferences()) { |
| out << "::android::status_t readEmbeddedReferenceFromParcel(\n"; |
| out.indent(2); |
| out << fullName() << " *obj,\n" |
| << "const ::android::hardware::Parcel &parcel,\n" |
| << "size_t parentHandle, size_t parentOffset);\n\n"; |
| out.unindent(2); |
| out << "::android::status_t writeEmbeddedReferenceToParcel(\n"; |
| out.indent(2); |
| out << "const " << fullName() << " &obj,\n" |
| << "::android::hardware::Parcel *,\n" |
| << "size_t parentHandle, size_t parentOffset);\n\n"; |
| out.unindent(2); |
| } |
| |
| return OK; |
| } |
| |
| status_t CompoundType::emitTypeDefinitions(Formatter& out, const std::string& prefix) const { |
| std::string space = prefix.empty() ? "" : (prefix + "::"); |
| status_t err = Scope::emitTypeDefinitions(out, space + localName()); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (needsEmbeddedReadWrite()) { |
| emitStructReaderWriter(out, prefix, true /* isReader */); |
| emitStructReaderWriter(out, prefix, false /* isReader */); |
| } |
| |
| if (needsResolveReferences()) { |
| emitResolveReferenceDef(out, prefix, true /* isReader */); |
| emitResolveReferenceDef(out, prefix, false /* isReader */); |
| } |
| |
| out << "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"; |
| |
| for (const NamedReference<Type>* field : *mFields) { |
| out << "os += \""; |
| if (field != *(mFields->begin())) { |
| out << ", "; |
| } |
| out << "." << field->name() << " = \";\n"; |
| field->type().emitDump(out, "os", "o." + field->name()); |
| } |
| |
| out << "os += \"}\"; return os;\n"; |
| }).endl().endl(); |
| |
| if (canCheckEquality()) { |
| out << "bool operator==(" |
| << getCppArgumentType() << " " << (mFields->empty() ? "/* lhs */" : "lhs") << ", " |
| << getCppArgumentType() << " " << (mFields->empty() ? "/* rhs */" : "rhs") << ") "; |
| out.block([&] { |
| for (const auto &field : *mFields) { |
| out.sIf("lhs." + field->name() + " != rhs." + field->name(), [&] { |
| out << "return false;\n"; |
| }).endl(); |
| } |
| out << "return true;\n"; |
| }).endl().endl(); |
| |
| out << "bool operator!=(" |
| << getCppArgumentType() << " lhs," << getCppArgumentType() << " rhs)"; |
| out.block([&] { |
| out << "return !(lhs == rhs);\n"; |
| }).endl().endl(); |
| } else { |
| out << "// operator== and operator!= are not generated for " << localName() << "\n"; |
| } |
| |
| return OK; |
| } |
| |
| status_t CompoundType::emitJavaTypeDeclarations( |
| Formatter &out, bool atTopLevel) const { |
| out << "public final "; |
| |
| if (!atTopLevel) { |
| out << "static "; |
| } |
| |
| out << "class " |
| << localName() |
| << " {\n"; |
| |
| out.indent(); |
| |
| Scope::emitJavaTypeDeclarations(out, false /* atTopLevel */); |
| |
| for (const auto &field : *mFields) { |
| out << "public "; |
| |
| field->type().emitJavaFieldInitializer(out, field->name()); |
| } |
| |
| if (!mFields->empty()) { |
| out << "\n"; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| 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"; |
| 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, [&] { |
| 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 " << localName() << "\n"; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| out << "@Override\npublic final String toString() "; |
| out.block([&] { |
| out << "java.lang.StringBuilder builder = new java.lang.StringBuilder();\n" |
| << "builder.append(\"{\");\n"; |
| for (const auto &field : *mFields) { |
| out << "builder.append(\""; |
| if (field != *(mFields->begin())) { |
| out << ", "; |
| } |
| out << "." << field->name() << " = \");\n"; |
| field->type().emitJavaDump(out, "builder", "this." + field->name()); |
| } |
| out << "builder.append(\"}\");\nreturn builder.toString();\n"; |
| }).endl().endl(); |
| |
| size_t structAlign, structSize; |
| getAlignmentAndSize(&structAlign, &structSize); |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| out << "public final void readFromParcel(android.os.HwParcel parcel) {\n"; |
| out.indent(); |
| out << "android.os.HwBlob blob = parcel.readBuffer("; |
| out << structSize << "/* 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<" |
| << localName() |
| << "> readVectorFromParcel(android.os.HwParcel parcel) {\n"; |
| out.indent(); |
| |
| out << "java.util.ArrayList<" |
| << localName() |
| << "> _hidl_vec = new java.util.ArrayList();\n"; |
| |
| 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"; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| 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(); |
| |
| size_t offset = 0; |
| for (const auto &field : *mFields) { |
| size_t fieldAlign, fieldSize; |
| field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); |
| |
| size_t pad = offset % fieldAlign; |
| if (pad > 0) { |
| offset += fieldAlign - pad; |
| } |
| |
| field->type().emitJavaFieldReaderWriter( |
| out, |
| 0 /* depth */, |
| "parcel", |
| "_hidl_blob", |
| field->name(), |
| "_hidl_offset + " + std::to_string(offset), |
| true /* isReader */); |
| |
| offset += fieldSize; |
| } |
| |
| out.unindent(); |
| out << "}\n\n"; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| out << "public final void writeToParcel(android.os.HwParcel parcel) {\n"; |
| out.indent(); |
| |
| out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" |
| << structSize |
| << " /* 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<" |
| << localName() |
| << "> _hidl_vec) {\n"; |
| out.unindent(); |
| |
| 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"; |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| out << "public final void writeEmbeddedToBlob(\n"; |
| out.indent(2); |
| out << "android.os.HwBlob _hidl_blob, long _hidl_offset) {\n"; |
| out.unindent(); |
| |
| offset = 0; |
| for (const auto &field : *mFields) { |
| size_t fieldAlign, fieldSize; |
| field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); |
| |
| size_t pad = offset % fieldAlign; |
| if (pad > 0) { |
| offset += fieldAlign - pad; |
| } |
| |
| field->type().emitJavaFieldReaderWriter( |
| out, |
| 0 /* depth */, |
| "parcel", |
| "_hidl_blob", |
| field->name(), |
| "_hidl_offset + " + std::to_string(offset), |
| false /* isReader */); |
| |
| offset += fieldSize; |
| } |
| |
| out.unindent(); |
| out << "}\n"; |
| |
| out.unindent(); |
| out << "};\n\n"; |
| |
| return OK; |
| } |
| |
| 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); |
| |
| bool useName = false; |
| for (const auto &field : *mFields) { |
| if (field->type().useNameInEmitReaderWriterEmbedded(isReader)) { |
| useName = true; |
| break; |
| } |
| } |
| std::string name = useName ? "obj" : "/* obj */"; |
| // if not useName, then obj should not be used at all, |
| // then the #error should not be emitted. |
| std::string error = useName ? "" : "\n#error\n"; |
| |
| if (isReader) { |
| out << "const " << space << localName() << " &" << name << ",\n"; |
| out << "const ::android::hardware::Parcel &parcel,\n"; |
| } else { |
| out << "const " << space << localName() << " &" << 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"; |
| |
| for (const auto &field : *mFields) { |
| if (!field->type().needsEmbeddedReadWrite()) { |
| continue; |
| } |
| |
| field->type().emitReaderWriterEmbedded( |
| out, |
| 0 /* depth */, |
| name + "." + field->name() + error, |
| field->name() /* sanitizedName */, |
| false /* nameIsPointer */, |
| "parcel", |
| !isReader /* parcelObjIsPointer */, |
| isReader, |
| ErrorMode_Return, |
| "parentHandle", |
| "parentOffset + offsetof(" |
| + fullName() |
| + ", " |
| + field->name() |
| + ")"); |
| } |
| |
| out << "return _hidl_err;\n"; |
| |
| out.unindent(); |
| out << "}\n\n"; |
| } |
| |
| void CompoundType::emitResolveReferenceDef(Formatter& out, const std::string& prefix, |
| bool isReader) const { |
| out << "::android::status_t "; |
| const std::string space(prefix.empty() ? "" : (prefix + "::")); |
| |
| bool useParent = false; |
| for (const auto &field : *mFields) { |
| if (field->type().useParentInEmitResolveReferencesEmbedded()) { |
| useParent = true; |
| break; |
| } |
| } |
| |
| std::string parentHandleName = useParent ? "parentHandle" : "/* parentHandle */"; |
| std::string parentOffsetName = useParent ? "parentOffset" : "/* parentOffset */"; |
| |
| if (isReader) { |
| out << "readEmbeddedReferenceFromParcel(\n"; |
| out.indent(2); |
| out << space + localName() + " *obj,\n" |
| << "const ::android::hardware::Parcel &parcel,\n" |
| << "size_t " << parentHandleName << ", " |
| << "size_t " << parentOffsetName << ")\n"; |
| out.unindent(2); |
| } else { |
| out << "writeEmbeddedReferenceToParcel(\n"; |
| out.indent(2); |
| out << "const " << space + localName() + " &obj,\n" |
| << "::android::hardware::Parcel *parcel,\n" |
| << "size_t " << parentHandleName << ", " |
| << "size_t " << parentOffsetName << ")\n"; |
| out.unindent(2); |
| } |
| |
| out << " {\n"; |
| |
| out.indent(); |
| |
| out << "::android::status_t _hidl_err = ::android::OK;\n\n"; |
| |
| const std::string nameDeref(isReader ? "obj->" : "obj."); |
| // if not useParent, then parentName and offsetText |
| // should not be used at all, then the #error should not be emitted. |
| std::string error = useParent ? "" : "\n#error\n"; |
| |
| for (const auto &field : *mFields) { |
| if (!field->type().needsResolveReferences()) { |
| continue; |
| } |
| |
| field->type().emitResolveReferencesEmbedded( |
| out, |
| 0 /* depth */, |
| nameDeref + field->name(), |
| field->name() /* sanitizedName */, |
| false, // nameIsPointer |
| "parcel", // const std::string &parcelObj, |
| !isReader, // bool parcelObjIsPointer, |
| isReader, // bool isReader, |
| ErrorMode_Return, |
| parentHandleName + error, |
| parentOffsetName |
| + " + offsetof(" |
| + fullName() |
| + ", " |
| + field->name() |
| + ")" |
| + error); |
| } |
| |
| out << "return _hidl_err;\n"; |
| |
| out.unindent(); |
| out << "}\n\n"; |
| } |
| |
| bool CompoundType::needsEmbeddedReadWrite() const { |
| if (mStyle != STYLE_STRUCT) { |
| return false; |
| } |
| |
| for (const auto &field : *mFields) { |
| if (field->type().needsEmbeddedReadWrite()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool CompoundType::needsResolveReferences() const { |
| if (mStyle != STYLE_STRUCT) { |
| return false; |
| } |
| |
| for (const auto &field : *mFields) { |
| if (field->type().needsResolveReferences()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool CompoundType::resultNeedsDeref() const { |
| return true; |
| } |
| |
| status_t 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; |
| } |
| } |
| out.indent(); |
| status_t status(type->emitVtsTypeDeclarations(out)); |
| if (status != OK) { |
| return status; |
| } |
| 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; |
| } |
| } |
| out.indent(); |
| out << "name: \"" << field->name() << "\"\n"; |
| status_t status = field->type().emitVtsAttributeType(out); |
| if (status != OK) { |
| return status; |
| } |
| out.unindent(); |
| out << "}\n"; |
| } |
| |
| return OK; |
| } |
| |
| status_t CompoundType::emitVtsAttributeType(Formatter &out) const { |
| out << "type: " << getVtsType() << "\n"; |
| out << "predefined_type: \"" << fullName() << "\"\n"; |
| return OK; |
| } |
| |
| bool CompoundType::isJavaCompatible() const { |
| if (mStyle != STYLE_STRUCT || !Scope::isJavaCompatible()) { |
| return false; |
| } |
| |
| for (const auto &field : *mFields) { |
| if (!field->type().isJavaCompatible()) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool CompoundType::containsPointer() const { |
| if (Scope::containsPointer()) { |
| return true; |
| } |
| |
| for (const auto &field : *mFields) { |
| if (field->type().containsPointer()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void CompoundType::getAlignmentAndSize(size_t *align, size_t *size) const { |
| *align = 1; |
| *size = 0; |
| |
| size_t offset = 0; |
| 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 pad = offset % fieldAlign; |
| if (pad > 0) { |
| offset += fieldAlign - pad; |
| } |
| |
| if (mStyle == STYLE_STRUCT) { |
| offset += fieldSize; |
| } else { |
| *size = std::max(*size, fieldSize); |
| } |
| |
| if (fieldAlign > (*align)) { |
| *align = fieldAlign; |
| } |
| } |
| |
| if (mStyle == STYLE_STRUCT) { |
| *size = offset; |
| } |
| |
| // Final padding to account for the structure's alignment. |
| size_t pad = (*size) % (*align); |
| if (pad > 0) { |
| (*size) += (*align) - pad; |
| } |
| |
| if (*size == 0) { |
| // An empty struct still occupies a byte of space in C++. |
| *size = 1; |
| } |
| } |
| |
| } // namespace android |
| |