/*
* 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 <hidl-util/Formatter.h>
#include <android-base/logging.h>
namespace android {
CompoundType::CompoundType(Style style, const char *localName, const Location &location)
: Scope(localName, location),
mStyle(style),
mFields(NULL) {
}
CompoundType::Style CompoundType::style() const {
return mStyle;
}
bool CompoundType::setFields(
std::vector<CompoundField *> *fields, std::string *errorMsg) {
mFields = fields;
for (const auto &field : *fields) {
const Type &type = field->type();
if (type.isBinder()
|| (type.isVector()
&& static_cast<const VectorType *>(
&type)->isVectorOfBinders())) {
*errorMsg =
"Structs/Unions must not contain references to interfaces.";
return false;
}
if (mStyle == STYLE_UNION) {
if (type.needsEmbeddedReadWrite()) {
// Can't have those in a union.
*errorMsg =
"Unions must not contain any types that need fixup.";
return false;
}
}
}
return true;
}
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::getCppType(
StorageMode mode,
bool specifyNamespaces) const {
const std::string base =
specifyNamespaces ? fullName() : partialCppName();
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";
}
}
}
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;
}
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 CompoundField *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;
}
}
////////////////////////////////////////////////////////////////////////////////
CompoundField::CompoundField(const char *name, Type *type)
: mName(name),
mType(type) {
}
std::string CompoundField::name() const {
return mName;
}
const Type &CompoundField::type() const {
return *mType;
}
} // namespace android