blob: 5e491cf00a47a2a53cd04283e8c3459a009cc843 [file] [log] [blame]
/*
* 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 "ArrayType.h"
#include <android-base/logging.h>
#include <hidl-util/Formatter.h>
#include <iostream>
#include "ConstantExpression.h"
namespace android {
ArrayType::ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent)
: Type(parent, elementType.localName()), mElementType(elementType) {
CHECK(!elementType.isEmptyReference());
appendDimension(size);
}
void ArrayType::appendDimension(ConstantExpression *size) {
mSizes.push_back(size);
mDefinedName = mDefinedName + "[" + size->expression() + "]";
}
size_t ArrayType::countDimensions() const {
return mSizes.size();
}
bool ArrayType::isArray() const {
return true;
}
bool ArrayType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
return mElementType->canCheckEquality(visited);
}
const Type* ArrayType::getElementType() const {
return mElementType.get();
}
std::string ArrayType::typeName() const {
if (dimension() == 1) {
return "array of " + mElementType->typeName();
}
return std::to_string(dimension()) + "d array of " + mElementType->typeName();
}
std::vector<const Reference<Type>*> ArrayType::getReferences() const {
return {&mElementType};
}
std::vector<const ConstantExpression*> ArrayType::getConstantExpressions() const {
std::vector<const ConstantExpression*> ret;
ret.insert(ret.end(), mSizes.begin(), mSizes.end());
return ret;
}
status_t ArrayType::resolveInheritance() {
// Resolve for typedefs
while (mElementType->isArray()) {
ArrayType* innerArray = static_cast<ArrayType*>(mElementType.get());
mSizes.insert(mSizes.end(), innerArray->mSizes.begin(), innerArray->mSizes.end());
mElementType = innerArray->mElementType;
}
return Type::resolveInheritance();
}
status_t ArrayType::validate() const {
CHECK(!mElementType->isArray());
if (mElementType->isInterface()) {
std::cerr << "ERROR: Arrays of interface types are not supported"
<< " at " << mElementType.location() << "\n";
return UNKNOWN_ERROR;
}
return Type::validate();
}
std::string ArrayType::getCppType(StorageMode mode,
bool specifyNamespaces) const {
const std::string base = mElementType->getCppStackType(specifyNamespaces);
std::string space = specifyNamespaces ? "::android::hardware::" : "";
std::string arrayType = space + "hidl_array<" + base;
for (size_t i = 0; i < mSizes.size(); ++i) {
arrayType += ", " + mSizes[i]->cppValue();
}
arrayType += ">";
switch (mode) {
case StorageMode_Stack:
return arrayType;
case StorageMode_Argument:
return "const " + arrayType + "&";
case StorageMode_Result:
return "const " + arrayType + "*";
}
CHECK(!"Should not be here");
}
std::string ArrayType::getInternalDataCppType() const {
std::string result = mElementType->getCppStackType();
for (size_t i = 0; i < mSizes.size(); ++i) {
result += "[";
result += mSizes[i]->cppValue();
result += "]";
}
return result;
}
std::string ArrayType::getJavaType(bool forInitializer) const {
std::string base =
mElementType->getJavaType(forInitializer);
for (size_t i = 0; i < mSizes.size(); ++i) {
base += "[";
if (forInitializer) {
base += mSizes[i]->javaValue();
} else {
base += "/* " + mSizes[i]->expression() + " */";
}
base += "]";
}
return base;
}
std::string ArrayType::getVtsType() const {
return "TYPE_ARRAY";
}
void ArrayType::emitReaderWriter(
Formatter &out,
const std::string &name,
const std::string &parcelObj,
bool parcelObjIsPointer,
bool isReader,
ErrorMode mode) const {
std::string baseType = mElementType->getCppStackType();
const std::string parentName = "_hidl_" + name + "_parent";
out << "size_t " << parentName << ";\n\n";
const std::string parcelObjDeref =
parcelObj + (parcelObjIsPointer ? "->" : ".");
size_t numArrayElements = 1;
for (auto size : mSizes) {
numArrayElements *= size->castSizeT();
}
if (isReader) {
out << "_hidl_err = "
<< parcelObjDeref
<< "readBuffer("
<< numArrayElements
<< " * sizeof("
<< baseType
<< "), &"
<< parentName
<< ", "
<< " reinterpret_cast<const void **>("
<< "&" << name
<< "));\n\n";
handleError(out, mode);
} else {
out << "_hidl_err = "
<< parcelObjDeref
<< "writeBuffer("
<< name
<< ".data(), "
<< numArrayElements
<< " * sizeof("
<< baseType
<< "), &"
<< parentName
<< ");\n";
handleError(out, mode);
}
emitReaderWriterEmbedded(
out,
0 /* depth */,
name,
name /* sanitizedName */,
isReader /* nameIsPointer */,
parcelObj,
parcelObjIsPointer,
isReader,
mode,
parentName,
"0 /* parentOffset */");
}
void ArrayType::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 {
if (!mElementType->needsEmbeddedReadWrite()) {
return;
}
const std::string nameDeref = name + (nameIsPointer ? "->" : ".");
std::string baseType = mElementType->getCppStackType();
std::string iteratorName = "_hidl_index_" + std::to_string(depth);
out << "for (size_t "
<< iteratorName
<< " = 0; "
<< iteratorName
<< " < "
<< dimension()
<< "; ++"
<< iteratorName
<< ") {\n";
out.indent();
mElementType->emitReaderWriterEmbedded(
out,
depth + 1,
nameDeref + "data()[" + iteratorName + "]",
sanitizedName + "_indexed",
false /* nameIsPointer */,
parcelObj,
parcelObjIsPointer,
isReader,
mode,
parentName,
offsetText
+ " + " + iteratorName + " * sizeof("
+ baseType
+ ")");
out.unindent();
out << "}\n\n";
}
void ArrayType::emitJavaDump(
Formatter &out,
const std::string &streamName,
const std::string &name) const {
out << streamName << ".append(java.util.Arrays."
<< (countDimensions() > 1 ? "deepToString" : "toString")
<< "("
<< name
<< "));\n";
}
bool ArrayType::needsEmbeddedReadWrite() const {
return mElementType->needsEmbeddedReadWrite();
}
bool ArrayType::resultNeedsDeref() const {
return true;
}
void ArrayType::emitJavaReaderWriter(
Formatter &out,
const std::string &parcelObj,
const std::string &argName,
bool isReader) const {
size_t align, size;
getAlignmentAndSize(&align, &size);
if (isReader) {
out << "new "
<< getJavaType(true /* forInitializer */)
<< ";\n";
}
out << "{\n";
out.indent();
out << "android.os.HwBlob _hidl_blob = ";
if (isReader) {
out << parcelObj
<< ".readBuffer("
<< size
<< " /* size */);\n";
} else {
out << "new android.os.HwBlob("
<< size
<< " /* size */);\n";
}
emitJavaFieldReaderWriter(
out,
0 /* depth */,
parcelObj,
"_hidl_blob",
argName,
"0 /* offset */",
isReader);
if (!isReader) {
out << parcelObj << ".writeBuffer(_hidl_blob);\n";
}
out.unindent();
out << "}\n";
}
void ArrayType::emitJavaFieldInitializer(
Formatter &out, const std::string &fieldName) const {
const std::string typeName = getJavaType(false /* forInitializer */);
const std::string fieldDeclaration = typeName + " " + fieldName;
emitJavaFieldDefaultInitialValue(out, fieldDeclaration);
}
void ArrayType::emitJavaFieldDefaultInitialValue(
Formatter &out, const std::string &declaredFieldName) const {
out << declaredFieldName
<< " = new "
<< getJavaType(true /* forInitializer */)
<< ";\n";
}
void ArrayType::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 {
out << "{\n";
out.indent();
std::string offsetName = "_hidl_array_offset_" + std::to_string(depth);
out << "long " << offsetName << " = " << offset << ";\n";
const bool isPrimitiveArray = mElementType->isScalar();
/* If the element type corresponds to a Java primitive type we can optimize
the innermost loop by copying a linear range of memory instead of doing
a per-element copy. As a result the outer nested loop does not include
the final dimension. */
const size_t loopDimensions = mSizes.size() - (isPrimitiveArray ? 1 : 0);
std::string indexString;
for (size_t dim = 0; dim < loopDimensions; ++dim) {
std::string iteratorName =
"_hidl_index_" + std::to_string(depth) + "_" + std::to_string(dim);
out << "for (int "
<< iteratorName
<< " = 0; "
<< iteratorName
<< " < "
<< mSizes[dim]->javaValue()
<< "; ++"
<< iteratorName
<< ") {\n";
out.indent();
indexString += "[" + iteratorName + "]";
}
const bool isIndexed = (loopDimensions > 0);
const std::string fieldNameWithCast = isIndexed
? "(" + getJavaTypeCast(fieldName) + ")" + indexString
: getJavaTypeCast(fieldName);
if (isReader && mElementType->isCompoundType()) {
mElementType->emitJavaFieldDefaultInitialValue(out, fieldNameWithCast);
}
if (!isPrimitiveArray) {
mElementType->emitJavaFieldReaderWriter(
out,
depth + 1,
parcelName,
blobName,
fieldNameWithCast,
offsetName,
isReader);
size_t elementAlign, elementSize;
mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
out << offsetName << " += " << std::to_string(elementSize) << ";\n";
} else {
if (isReader) {
out << blobName
<< ".copyTo"
<< mElementType->getJavaSuffix()
<< "Array("
<< offsetName
<< ", "
<< fieldNameWithCast
<< ", "
<< mSizes.back()->javaValue()
<< " /* size */);\n";
} else {
std::string elemName = "_hidl_array_item_" + std::to_string(depth);
out << mElementType->getJavaType(false /* forInitializer */)
<< "[] "
<< elemName
<< " = "
<< fieldNameWithCast
<< ";\n\n";
out << "if ("
<< elemName
<< " == null || "
<< elemName
<< ".length != "
<< mSizes.back()->javaValue()
<< ") {\n";
out.indent();
out << "throw new IllegalArgumentException("
<< "\"Array element is not of the expected length\");\n";
out.unindent();
out << "}\n\n";
out << blobName
<< ".put"
<< mElementType->getJavaSuffix()
<< "Array("
<< offsetName
<< ", "
<< elemName
<< ");\n";
}
size_t elementAlign, elementSize;
mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
out << offsetName
<< " += "
<< mSizes.back()->javaValue()
<< " * "
<< elementSize
<< ";\n";
}
for (size_t dim = 0; dim < loopDimensions; ++dim) {
out.unindent();
out << "}\n";
}
out.unindent();
out << "}\n";
}
void ArrayType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
out << "vector_size: " << mSizes[0]->rawValue() << "\n";
out << "vector_value: {\n";
out.indent();
// Simple array case.
if (mSizes.size() == 1) {
mElementType->emitVtsTypeDeclarations(out);
} else { // Multi-dimension array case.
for (size_t index = 1; index < mSizes.size(); index++) {
out << "type: " << getVtsType() << "\n";
out << "vector_size: " << mSizes[index]->rawValue() << "\n";
out << "vector_value: {\n";
out.indent();
if (index == mSizes.size() - 1) {
mElementType->emitVtsTypeDeclarations(out);
}
}
}
for (size_t index = 0; index < mSizes.size(); index++) {
out.unindent();
out << "}\n";
}
}
bool ArrayType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
if (!mElementType->isJavaCompatible(visited)) {
return false;
}
return Type::deepIsJavaCompatible(visited);
}
bool ArrayType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
if (mElementType->containsPointer(visited)) {
return true;
}
return Type::deepContainsPointer(visited);
}
void ArrayType::getAlignmentAndSize(size_t *align, size_t *size) const {
mElementType->getAlignmentAndSize(align, size);
for (auto sizeInDimension : mSizes) {
(*size) *= sizeInDimension->castSizeT();
}
}
size_t ArrayType::dimension() const {
size_t numArrayElements = 1;
for (auto size : mSizes) {
numArrayElements *= size->castSizeT();
}
return numArrayElements;
}
} // namespace android