1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "ArrayType.h" 18 19 #include <android-base/logging.h> 20 #include <hidl-util/Formatter.h> 21 #include <iostream> 22 23 #include "ConstantExpression.h" 24 25 namespace android { 26 27 ArrayType::ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent) 28 : Type(parent, elementType.localName()), mElementType(elementType) { 29 CHECK(!elementType.isEmptyReference()); 30 31 appendDimension(size); 32 } 33 34 void ArrayType::appendDimension(ConstantExpression *size) { 35 mSizes.push_back(size); 36 mDefinedName = mDefinedName + "[" + size->expression() + "]"; 37 } 38 39 size_t ArrayType::countDimensions() const { 40 return mSizes.size(); 41 } 42 43 bool ArrayType::isArray() const { 44 return true; 45 } 46 47 bool ArrayType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const { 48 return mElementType->canCheckEquality(visited); 49 } 50 51 const Type* ArrayType::getElementType() const { 52 return mElementType.get(); 53 } 54 55 std::string ArrayType::typeName() const { 56 if (dimension() == 1) { 57 return "array of " + mElementType->typeName(); 58 } 59 60 return std::to_string(dimension()) + "d array of " + mElementType->typeName(); 61 } 62 63 std::vector<const Reference<Type>*> ArrayType::getReferences() const { 64 return {&mElementType}; 65 } 66 67 std::vector<const ConstantExpression*> ArrayType::getConstantExpressions() const { 68 std::vector<const ConstantExpression*> ret; 69 ret.insert(ret.end(), mSizes.begin(), mSizes.end()); 70 return ret; 71 } 72 73 status_t ArrayType::resolveInheritance() { 74 // Resolve for typedefs 75 while (mElementType->isArray()) { 76 ArrayType* innerArray = static_cast<ArrayType*>(mElementType.get()); 77 mSizes.insert(mSizes.end(), innerArray->mSizes.begin(), innerArray->mSizes.end()); 78 mElementType = innerArray->mElementType; 79 } 80 return Type::resolveInheritance(); 81 } 82 83 status_t ArrayType::validate() const { 84 CHECK(!mElementType->isArray()); 85 86 if (mElementType->isInterface()) { 87 std::cerr << "ERROR: Arrays of interface types are not supported" 88 << " at " << mElementType.location() << "\n"; 89 90 return UNKNOWN_ERROR; 91 } 92 return Type::validate(); 93 } 94 95 std::string ArrayType::getCppType(StorageMode mode, 96 bool specifyNamespaces) const { 97 const std::string base = mElementType->getCppStackType(specifyNamespaces); 98 99 std::string space = specifyNamespaces ? "::android::hardware::" : ""; 100 std::string arrayType = space + "hidl_array<" + base; 101 102 for (size_t i = 0; i < mSizes.size(); ++i) { 103 arrayType += ", " + mSizes[i]->cppValue(); 104 } 105 106 arrayType += ">"; 107 108 switch (mode) { 109 case StorageMode_Stack: 110 return arrayType; 111 112 case StorageMode_Argument: 113 return "const " + arrayType + "&"; 114 115 case StorageMode_Result: 116 return "const " + arrayType + "*"; 117 } 118 119 CHECK(!"Should not be here"); 120 } 121 122 std::string ArrayType::getInternalDataCppType() const { 123 std::string result = mElementType->getCppStackType(); 124 for (size_t i = 0; i < mSizes.size(); ++i) { 125 result += "["; 126 result += mSizes[i]->cppValue(); 127 result += "]"; 128 } 129 return result; 130 } 131 132 std::string ArrayType::getJavaType(bool forInitializer) const { 133 std::string base = 134 mElementType->getJavaType(forInitializer); 135 136 for (size_t i = 0; i < mSizes.size(); ++i) { 137 base += "["; 138 139 if (forInitializer) { 140 base += mSizes[i]->javaValue(); 141 } else { 142 base += "/* " + mSizes[i]->expression() + " */"; 143 } 144 145 base += "]"; 146 } 147 148 return base; 149 } 150 151 std::string ArrayType::getVtsType() const { 152 return "TYPE_ARRAY"; 153 } 154 155 void ArrayType::emitReaderWriter( 156 Formatter &out, 157 const std::string &name, 158 const std::string &parcelObj, 159 bool parcelObjIsPointer, 160 bool isReader, 161 ErrorMode mode) const { 162 std::string baseType = mElementType->getCppStackType(); 163 164 const std::string parentName = "_hidl_" + name + "_parent"; 165 166 out << "size_t " << parentName << ";\n\n"; 167 168 const std::string parcelObjDeref = 169 parcelObj + (parcelObjIsPointer ? "->" : "."); 170 171 size_t numArrayElements = 1; 172 for (auto size : mSizes) { 173 numArrayElements *= size->castSizeT(); 174 } 175 if (isReader) { 176 out << "_hidl_err = " 177 << parcelObjDeref 178 << "readBuffer(" 179 << numArrayElements 180 << " * sizeof(" 181 << baseType 182 << "), &" 183 << parentName 184 << ", " 185 << " reinterpret_cast<const void **>(" 186 << "&" << name 187 << "));\n\n"; 188 189 handleError(out, mode); 190 } else { 191 192 out << "_hidl_err = " 193 << parcelObjDeref 194 << "writeBuffer(" 195 << name 196 << ".data(), " 197 << numArrayElements 198 << " * sizeof(" 199 << baseType 200 << "), &" 201 << parentName 202 << ");\n"; 203 204 handleError(out, mode); 205 } 206 207 emitReaderWriterEmbedded( 208 out, 209 0 /* depth */, 210 name, 211 name /* sanitizedName */, 212 isReader /* nameIsPointer */, 213 parcelObj, 214 parcelObjIsPointer, 215 isReader, 216 mode, 217 parentName, 218 "0 /* parentOffset */"); 219 } 220 221 void ArrayType::emitReaderWriterEmbedded( 222 Formatter &out, 223 size_t depth, 224 const std::string &name, 225 const std::string &sanitizedName, 226 bool nameIsPointer, 227 const std::string &parcelObj, 228 bool parcelObjIsPointer, 229 bool isReader, 230 ErrorMode mode, 231 const std::string &parentName, 232 const std::string &offsetText) const { 233 if (!mElementType->needsEmbeddedReadWrite()) { 234 return; 235 } 236 237 const std::string nameDeref = name + (nameIsPointer ? "->" : "."); 238 239 std::string baseType = mElementType->getCppStackType(); 240 241 std::string iteratorName = "_hidl_index_" + std::to_string(depth); 242 243 out << "for (size_t " 244 << iteratorName 245 << " = 0; " 246 << iteratorName 247 << " < " 248 << dimension() 249 << "; ++" 250 << iteratorName 251 << ") {\n"; 252 253 out.indent(); 254 255 mElementType->emitReaderWriterEmbedded( 256 out, 257 depth + 1, 258 nameDeref + "data()[" + iteratorName + "]", 259 sanitizedName + "_indexed", 260 false /* nameIsPointer */, 261 parcelObj, 262 parcelObjIsPointer, 263 isReader, 264 mode, 265 parentName, 266 offsetText 267 + " + " + iteratorName + " * sizeof(" 268 + baseType 269 + ")"); 270 271 out.unindent(); 272 273 out << "}\n\n"; 274 } 275 276 void ArrayType::emitJavaDump( 277 Formatter &out, 278 const std::string &streamName, 279 const std::string &name) const { 280 out << streamName << ".append(java.util.Arrays." 281 << (countDimensions() > 1 ? "deepToString" : "toString") 282 << "(" 283 << name 284 << "));\n"; 285 } 286 287 288 bool ArrayType::needsEmbeddedReadWrite() const { 289 return mElementType->needsEmbeddedReadWrite(); 290 } 291 292 bool ArrayType::resultNeedsDeref() const { 293 return true; 294 } 295 296 void ArrayType::emitJavaReaderWriter( 297 Formatter &out, 298 const std::string &parcelObj, 299 const std::string &argName, 300 bool isReader) const { 301 size_t align, size; 302 getAlignmentAndSize(&align, &size); 303 304 if (isReader) { 305 out << "new " 306 << getJavaType(true /* forInitializer */) 307 << ";\n"; 308 } 309 310 out << "{\n"; 311 out.indent(); 312 313 out << "android.os.HwBlob _hidl_blob = "; 314 315 if (isReader) { 316 out << parcelObj 317 << ".readBuffer(" 318 << size 319 << " /* size */);\n"; 320 } else { 321 out << "new android.os.HwBlob(" 322 << size 323 << " /* size */);\n"; 324 } 325 326 emitJavaFieldReaderWriter( 327 out, 328 0 /* depth */, 329 parcelObj, 330 "_hidl_blob", 331 argName, 332 "0 /* offset */", 333 isReader); 334 335 if (!isReader) { 336 out << parcelObj << ".writeBuffer(_hidl_blob);\n"; 337 } 338 339 out.unindent(); 340 out << "}\n"; 341 } 342 343 void ArrayType::emitJavaFieldInitializer( 344 Formatter &out, const std::string &fieldName) const { 345 const std::string typeName = getJavaType(false /* forInitializer */); 346 const std::string fieldDeclaration = typeName + " " + fieldName; 347 348 emitJavaFieldDefaultInitialValue(out, fieldDeclaration); 349 } 350 351 void ArrayType::emitJavaFieldDefaultInitialValue( 352 Formatter &out, const std::string &declaredFieldName) const { 353 out << declaredFieldName 354 << " = new " 355 << getJavaType(true /* forInitializer */) 356 << ";\n"; 357 } 358 359 void ArrayType::emitJavaFieldReaderWriter( 360 Formatter &out, 361 size_t depth, 362 const std::string &parcelName, 363 const std::string &blobName, 364 const std::string &fieldName, 365 const std::string &offset, 366 bool isReader) const { 367 out << "{\n"; 368 out.indent(); 369 370 std::string offsetName = "_hidl_array_offset_" + std::to_string(depth); 371 out << "long " << offsetName << " = " << offset << ";\n"; 372 373 const bool isPrimitiveArray = mElementType->isScalar(); 374 375 /* If the element type corresponds to a Java primitive type we can optimize 376 the innermost loop by copying a linear range of memory instead of doing 377 a per-element copy. As a result the outer nested loop does not include 378 the final dimension. */ 379 const size_t loopDimensions = mSizes.size() - (isPrimitiveArray ? 1 : 0); 380 381 std::string indexString; 382 for (size_t dim = 0; dim < loopDimensions; ++dim) { 383 std::string iteratorName = 384 "_hidl_index_" + std::to_string(depth) + "_" + std::to_string(dim); 385 386 out << "for (int " 387 << iteratorName 388 << " = 0; " 389 << iteratorName 390 << " < " 391 << mSizes[dim]->javaValue() 392 << "; ++" 393 << iteratorName 394 << ") {\n"; 395 396 out.indent(); 397 398 indexString += "[" + iteratorName + "]"; 399 } 400 401 const bool isIndexed = (loopDimensions > 0); 402 const std::string fieldNameWithCast = isIndexed 403 ? "(" + getJavaTypeCast(fieldName) + ")" + indexString 404 : getJavaTypeCast(fieldName); 405 406 if (isReader && mElementType->isCompoundType()) { 407 mElementType->emitJavaFieldDefaultInitialValue(out, fieldNameWithCast); 408 } 409 410 if (!isPrimitiveArray) { 411 mElementType->emitJavaFieldReaderWriter( 412 out, 413 depth + 1, 414 parcelName, 415 blobName, 416 fieldNameWithCast, 417 offsetName, 418 isReader); 419 420 size_t elementAlign, elementSize; 421 mElementType->getAlignmentAndSize(&elementAlign, &elementSize); 422 423 out << offsetName << " += " << std::to_string(elementSize) << ";\n"; 424 } else { 425 if (isReader) { 426 out << blobName 427 << ".copyTo" 428 << mElementType->getJavaSuffix() 429 << "Array(" 430 << offsetName 431 << ", " 432 << fieldNameWithCast 433 << ", " 434 << mSizes.back()->javaValue() 435 << " /* size */);\n"; 436 } else { 437 std::string elemName = "_hidl_array_item_" + std::to_string(depth); 438 439 out << mElementType->getJavaType(false /* forInitializer */) 440 << "[] " 441 << elemName 442 << " = " 443 << fieldNameWithCast 444 << ";\n\n"; 445 446 out << "if (" 447 << elemName 448 << " == null || " 449 << elemName 450 << ".length != " 451 << mSizes.back()->javaValue() 452 << ") {\n"; 453 454 out.indent(); 455 456 out << "throw new IllegalArgumentException(" 457 << "\"Array element is not of the expected length\");\n"; 458 459 out.unindent(); 460 out << "}\n\n"; 461 462 out << blobName 463 << ".put" 464 << mElementType->getJavaSuffix() 465 << "Array(" 466 << offsetName 467 << ", " 468 << elemName 469 << ");\n"; 470 } 471 472 size_t elementAlign, elementSize; 473 mElementType->getAlignmentAndSize(&elementAlign, &elementSize); 474 475 out << offsetName 476 << " += " 477 << mSizes.back()->javaValue() 478 << " * " 479 << elementSize 480 << ";\n"; 481 } 482 483 for (size_t dim = 0; dim < loopDimensions; ++dim) { 484 out.unindent(); 485 out << "}\n"; 486 } 487 488 out.unindent(); 489 out << "}\n"; 490 } 491 492 void ArrayType::emitVtsTypeDeclarations(Formatter& out) const { 493 out << "type: " << getVtsType() << "\n"; 494 out << "vector_size: " << mSizes[0]->rawValue() << "\n"; 495 out << "vector_value: {\n"; 496 out.indent(); 497 // Simple array case. 498 if (mSizes.size() == 1) { 499 mElementType->emitVtsTypeDeclarations(out); 500 } else { // Multi-dimension array case. 501 for (size_t index = 1; index < mSizes.size(); index++) { 502 out << "type: " << getVtsType() << "\n"; 503 out << "vector_size: " << mSizes[index]->rawValue() << "\n"; 504 out << "vector_value: {\n"; 505 out.indent(); 506 if (index == mSizes.size() - 1) { 507 mElementType->emitVtsTypeDeclarations(out); 508 } 509 } 510 } 511 for (size_t index = 0; index < mSizes.size(); index++) { 512 out.unindent(); 513 out << "}\n"; 514 } 515 } 516 517 bool ArrayType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const { 518 if (!mElementType->isJavaCompatible(visited)) { 519 return false; 520 } 521 return Type::deepIsJavaCompatible(visited); 522 } 523 524 bool ArrayType::deepContainsPointer(std::unordered_set<const Type*>* visited) const { 525 if (mElementType->containsPointer(visited)) { 526 return true; 527 } 528 return Type::deepContainsPointer(visited); 529 } 530 531 void ArrayType::getAlignmentAndSize(size_t *align, size_t *size) const { 532 mElementType->getAlignmentAndSize(align, size); 533 534 for (auto sizeInDimension : mSizes) { 535 (*size) *= sizeInDimension->castSizeT(); 536 } 537 } 538 539 size_t ArrayType::dimension() const { 540 size_t numArrayElements = 1; 541 for (auto size : mSizes) { 542 numArrayElements *= size->castSizeT(); 543 } 544 return numArrayElements; 545 } 546 547 } // namespace android 548 549