/* * 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 #include #include #include "ConstantExpression.h" namespace android { ArrayType::ArrayType(const Reference& 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* 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*> ArrayType::getReferences() const { return {&mElementType}; } std::vector ArrayType::getConstantExpressions() const { std::vector 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(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(" << "&" << 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* visited) const { if (!mElementType->isJavaCompatible(visited)) { return false; } return Type::deepIsJavaCompatible(visited); } bool ArrayType::deepContainsPointer(std::unordered_set* 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