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
ArrayType(const Reference<Type> & elementType,ConstantExpression * size,Scope * parent)27 ArrayType::ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent)
28 : Type(parent), mElementType(elementType), mSizes{size} {
29 CHECK(!elementType.isEmptyReference());
30 }
31
appendDimension(ConstantExpression * size)32 void ArrayType::appendDimension(ConstantExpression *size) {
33 mSizes.push_back(size);
34 }
35
countDimensions() const36 size_t ArrayType::countDimensions() const {
37 return mSizes.size();
38 }
39
isArray() const40 bool ArrayType::isArray() const {
41 return true;
42 }
43
deepCanCheckEquality(std::unordered_set<const Type * > * visited) const44 bool ArrayType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
45 return mElementType->canCheckEquality(visited);
46 }
47
getElementType() const48 const Type* ArrayType::getElementType() const {
49 return mElementType.get();
50 }
51
typeName() const52 std::string ArrayType::typeName() const {
53 if (dimension() == 1) {
54 return "array of " + mElementType->typeName();
55 }
56
57 return std::to_string(dimension()) + "d array of " + mElementType->typeName();
58 }
59
getReferences() const60 std::vector<const Reference<Type>*> ArrayType::getReferences() const {
61 return {&mElementType};
62 }
63
getConstantExpressions() const64 std::vector<const ConstantExpression*> ArrayType::getConstantExpressions() const {
65 std::vector<const ConstantExpression*> ret;
66 ret.insert(ret.end(), mSizes.begin(), mSizes.end());
67 return ret;
68 }
69
resolveInheritance()70 status_t ArrayType::resolveInheritance() {
71 // Resolve for typedefs
72 while (mElementType->isArray()) {
73 ArrayType* innerArray = static_cast<ArrayType*>(mElementType.get());
74 mSizes.insert(mSizes.end(), innerArray->mSizes.begin(), innerArray->mSizes.end());
75 mElementType = innerArray->mElementType;
76 }
77 return Type::resolveInheritance();
78 }
79
validate() const80 status_t ArrayType::validate() const {
81 CHECK(!mElementType->isArray());
82
83 if (mElementType->isInterface()) {
84 std::cerr << "ERROR: Arrays of interface types are not supported"
85 << " at " << mElementType.location() << "\n";
86
87 return UNKNOWN_ERROR;
88 }
89 return Type::validate();
90 }
91
getCppType(StorageMode mode,bool specifyNamespaces) const92 std::string ArrayType::getCppType(StorageMode mode,
93 bool specifyNamespaces) const {
94 const std::string base = mElementType->getCppStackType(specifyNamespaces);
95
96 std::string space = specifyNamespaces ? "::android::hardware::" : "";
97 std::string arrayType = space + "hidl_array<" + base;
98
99 for (size_t i = 0; i < mSizes.size(); ++i) {
100 arrayType += ", " + mSizes[i]->cppValue();
101 }
102
103 arrayType += ">";
104
105 switch (mode) {
106 case StorageMode_Stack:
107 return arrayType;
108
109 case StorageMode_Argument:
110 return "const " + arrayType + "&";
111
112 case StorageMode_Result:
113 return "const " + arrayType + "*";
114 }
115
116 CHECK(!"Should not be here");
117 }
118
getInternalDataCppType() const119 std::string ArrayType::getInternalDataCppType() const {
120 std::string result = mElementType->getCppStackType();
121 for (size_t i = 0; i < mSizes.size(); ++i) {
122 result += "[";
123 result += mSizes[i]->cppValue();
124 result += "]";
125 }
126 return result;
127 }
128
getJavaType(bool forInitializer) const129 std::string ArrayType::getJavaType(bool forInitializer) const {
130 std::string base =
131 mElementType->getJavaType(forInitializer);
132
133 for (size_t i = 0; i < mSizes.size(); ++i) {
134 base += "[";
135
136 if (forInitializer) {
137 base += mSizes[i]->javaValue();
138 } else {
139 base += "/* " + mSizes[i]->expression() + " */";
140 }
141
142 base += "]";
143 }
144
145 return base;
146 }
147
getVtsType() const148 std::string ArrayType::getVtsType() const {
149 return "TYPE_ARRAY";
150 }
151
emitReaderWriter(Formatter & out,const std::string & name,const std::string & parcelObj,bool parcelObjIsPointer,bool isReader,ErrorMode mode) const152 void ArrayType::emitReaderWriter(
153 Formatter &out,
154 const std::string &name,
155 const std::string &parcelObj,
156 bool parcelObjIsPointer,
157 bool isReader,
158 ErrorMode mode) const {
159 std::string baseType = mElementType->getCppStackType();
160
161 const std::string parentName = "_hidl_" + name + "_parent";
162
163 out << "size_t " << parentName << ";\n\n";
164
165 const std::string parcelObjDeref =
166 parcelObj + (parcelObjIsPointer ? "->" : ".");
167
168 size_t numArrayElements = 1;
169 for (auto size : mSizes) {
170 numArrayElements *= size->castSizeT();
171 }
172 if (isReader) {
173 out << "_hidl_err = "
174 << parcelObjDeref
175 << "readBuffer("
176 << numArrayElements
177 << " * sizeof("
178 << baseType
179 << "), &"
180 << parentName
181 << ", "
182 << " reinterpret_cast<const void **>("
183 << "&" << name
184 << "));\n\n";
185
186 handleError(out, mode);
187 } else {
188
189 out << "_hidl_err = "
190 << parcelObjDeref
191 << "writeBuffer("
192 << name
193 << ".data(), "
194 << numArrayElements
195 << " * sizeof("
196 << baseType
197 << "), &"
198 << parentName
199 << ");\n";
200
201 handleError(out, mode);
202 }
203
204 emitReaderWriterEmbedded(
205 out,
206 0 /* depth */,
207 name,
208 name /* sanitizedName */,
209 isReader /* nameIsPointer */,
210 parcelObj,
211 parcelObjIsPointer,
212 isReader,
213 mode,
214 parentName,
215 "0 /* parentOffset */");
216 }
217
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) const218 void ArrayType::emitReaderWriterEmbedded(
219 Formatter &out,
220 size_t depth,
221 const std::string &name,
222 const std::string &sanitizedName,
223 bool nameIsPointer,
224 const std::string &parcelObj,
225 bool parcelObjIsPointer,
226 bool isReader,
227 ErrorMode mode,
228 const std::string &parentName,
229 const std::string &offsetText) const {
230 if (!mElementType->needsEmbeddedReadWrite()) {
231 return;
232 }
233
234 const std::string nameDeref = name + (nameIsPointer ? "->" : ".");
235
236 std::string baseType = mElementType->getCppStackType();
237
238 std::string iteratorName = "_hidl_index_" + std::to_string(depth);
239
240 out << "for (size_t "
241 << iteratorName
242 << " = 0; "
243 << iteratorName
244 << " < "
245 << dimension()
246 << "; ++"
247 << iteratorName
248 << ") {\n";
249
250 out.indent();
251
252 mElementType->emitReaderWriterEmbedded(
253 out,
254 depth + 1,
255 nameDeref + "data()[" + iteratorName + "]",
256 sanitizedName + "_indexed",
257 false /* nameIsPointer */,
258 parcelObj,
259 parcelObjIsPointer,
260 isReader,
261 mode,
262 parentName,
263 offsetText
264 + " + " + iteratorName + " * sizeof("
265 + baseType
266 + ")");
267
268 out.unindent();
269
270 out << "}\n\n";
271 }
272
emitResolveReferences(Formatter & out,const std::string & name,bool nameIsPointer,const std::string & parcelObj,bool parcelObjIsPointer,bool isReader,ErrorMode mode) const273 void ArrayType::emitResolveReferences(
274 Formatter &out,
275 const std::string &name,
276 bool nameIsPointer,
277 const std::string &parcelObj,
278 bool parcelObjIsPointer,
279 bool isReader,
280 ErrorMode mode) const {
281 emitResolveReferencesEmbedded(
282 out,
283 0 /* depth */,
284 name,
285 name /* sanitizedName */,
286 nameIsPointer,
287 parcelObj,
288 parcelObjIsPointer,
289 isReader,
290 mode,
291 "_hidl_" + name + "_parent",
292 "0 /* parentOffset */");
293 }
294
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) const295 void ArrayType::emitResolveReferencesEmbedded(
296 Formatter &out,
297 size_t depth,
298 const std::string &name,
299 const std::string &sanitizedName,
300 bool nameIsPointer,
301 const std::string &parcelObj,
302 bool parcelObjIsPointer,
303 bool isReader,
304 ErrorMode mode,
305 const std::string &parentName,
306 const std::string &offsetText) const {
307 CHECK(needsResolveReferences() && mElementType->needsResolveReferences());
308
309 const std::string nameDeref = name + (nameIsPointer ? "->" : ".");
310
311 std::string baseType = mElementType->getCppStackType();
312
313 std::string iteratorName = "_hidl_index_" + std::to_string(depth);
314
315 out << "for (size_t "
316 << iteratorName
317 << " = 0; "
318 << iteratorName
319 << " < "
320 << dimension()
321 << "; ++"
322 << iteratorName
323 << ") {\n";
324
325 out.indent();
326
327 mElementType->emitResolveReferencesEmbedded(
328 out,
329 depth + 1,
330 nameDeref + "data()[" + iteratorName + "]",
331 sanitizedName + "_indexed",
332 false /* nameIsPointer */,
333 parcelObj,
334 parcelObjIsPointer,
335 isReader,
336 mode,
337 parentName,
338 offsetText + " + " + iteratorName + " * sizeof("
339 + baseType
340 + ")");
341
342 out.unindent();
343
344 out << "}\n\n";
345 }
346
emitJavaDump(Formatter & out,const std::string & streamName,const std::string & name) const347 void ArrayType::emitJavaDump(
348 Formatter &out,
349 const std::string &streamName,
350 const std::string &name) const {
351 out << streamName << ".append(java.util.Arrays."
352 << (countDimensions() > 1 ? "deepToString" : "toString")
353 << "("
354 << name
355 << "));\n";
356 }
357
358
needsEmbeddedReadWrite() const359 bool ArrayType::needsEmbeddedReadWrite() const {
360 return mElementType->needsEmbeddedReadWrite();
361 }
362
deepNeedsResolveReferences(std::unordered_set<const Type * > * visited) const363 bool ArrayType::deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const {
364 if (mElementType->needsResolveReferences(visited)) {
365 return true;
366 }
367 return Type::deepNeedsResolveReferences(visited);
368 }
369
resultNeedsDeref() const370 bool ArrayType::resultNeedsDeref() const {
371 return true;
372 }
373
emitJavaReaderWriter(Formatter & out,const std::string & parcelObj,const std::string & argName,bool isReader) const374 void ArrayType::emitJavaReaderWriter(
375 Formatter &out,
376 const std::string &parcelObj,
377 const std::string &argName,
378 bool isReader) const {
379 size_t align, size;
380 getAlignmentAndSize(&align, &size);
381
382 if (isReader) {
383 out << "new "
384 << getJavaType(true /* forInitializer */)
385 << ";\n";
386 }
387
388 out << "{\n";
389 out.indent();
390
391 out << "android.os.HwBlob _hidl_blob = ";
392
393 if (isReader) {
394 out << parcelObj
395 << ".readBuffer("
396 << size
397 << " /* size */);\n";
398 } else {
399 out << "new android.os.HwBlob("
400 << size
401 << " /* size */);\n";
402 }
403
404 emitJavaFieldReaderWriter(
405 out,
406 0 /* depth */,
407 parcelObj,
408 "_hidl_blob",
409 argName,
410 "0 /* offset */",
411 isReader);
412
413 if (!isReader) {
414 out << parcelObj << ".writeBuffer(_hidl_blob);\n";
415 }
416
417 out.unindent();
418 out << "}\n";
419 }
420
emitJavaFieldInitializer(Formatter & out,const std::string & fieldName) const421 void ArrayType::emitJavaFieldInitializer(
422 Formatter &out, const std::string &fieldName) const {
423 const std::string typeName = getJavaType(false /* forInitializer */);
424 const std::string fieldDeclaration = typeName + " " + fieldName;
425
426 emitJavaFieldDefaultInitialValue(out, fieldDeclaration);
427 }
428
emitJavaFieldDefaultInitialValue(Formatter & out,const std::string & declaredFieldName) const429 void ArrayType::emitJavaFieldDefaultInitialValue(
430 Formatter &out, const std::string &declaredFieldName) const {
431 out << declaredFieldName
432 << " = new "
433 << getJavaType(true /* forInitializer */)
434 << ";\n";
435 }
436
emitJavaFieldReaderWriter(Formatter & out,size_t depth,const std::string & parcelName,const std::string & blobName,const std::string & fieldName,const std::string & offset,bool isReader) const437 void ArrayType::emitJavaFieldReaderWriter(
438 Formatter &out,
439 size_t depth,
440 const std::string &parcelName,
441 const std::string &blobName,
442 const std::string &fieldName,
443 const std::string &offset,
444 bool isReader) const {
445 out << "{\n";
446 out.indent();
447
448 std::string offsetName = "_hidl_array_offset_" + std::to_string(depth);
449 out << "long " << offsetName << " = " << offset << ";\n";
450
451 const bool isPrimitiveArray = mElementType->isScalar();
452
453 /* If the element type corresponds to a Java primitive type we can optimize
454 the innermost loop by copying a linear range of memory instead of doing
455 a per-element copy. As a result the outer nested loop does not include
456 the final dimension. */
457 const size_t loopDimensions = mSizes.size() - (isPrimitiveArray ? 1 : 0);
458
459 std::string indexString;
460 for (size_t dim = 0; dim < loopDimensions; ++dim) {
461 std::string iteratorName =
462 "_hidl_index_" + std::to_string(depth) + "_" + std::to_string(dim);
463
464 out << "for (int "
465 << iteratorName
466 << " = 0; "
467 << iteratorName
468 << " < "
469 << mSizes[dim]->javaValue()
470 << "; ++"
471 << iteratorName
472 << ") {\n";
473
474 out.indent();
475
476 indexString += "[" + iteratorName + "]";
477 }
478
479 const bool isIndexed = (loopDimensions > 0);
480 const std::string fieldNameWithCast = isIndexed
481 ? "(" + getJavaTypeCast(fieldName) + ")" + indexString
482 : getJavaTypeCast(fieldName);
483
484 if (isReader && mElementType->isCompoundType()) {
485 mElementType->emitJavaFieldDefaultInitialValue(out, fieldNameWithCast);
486 }
487
488 if (!isPrimitiveArray) {
489 mElementType->emitJavaFieldReaderWriter(
490 out,
491 depth + 1,
492 parcelName,
493 blobName,
494 fieldNameWithCast,
495 offsetName,
496 isReader);
497
498 size_t elementAlign, elementSize;
499 mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
500
501 out << offsetName << " += " << std::to_string(elementSize) << ";\n";
502 } else {
503 if (isReader) {
504 out << blobName
505 << ".copyTo"
506 << mElementType->getJavaSuffix()
507 << "Array("
508 << offsetName
509 << ", "
510 << fieldNameWithCast
511 << ", "
512 << mSizes.back()->javaValue()
513 << " /* size */);\n";
514 } else {
515 std::string elemName = "_hidl_array_item_" + std::to_string(depth);
516
517 out << mElementType->getJavaType(false /* forInitializer */)
518 << "[] "
519 << elemName
520 << " = "
521 << fieldNameWithCast
522 << ";\n\n";
523
524 out << "if ("
525 << elemName
526 << " == null || "
527 << elemName
528 << ".length != "
529 << mSizes.back()->javaValue()
530 << ") {\n";
531
532 out.indent();
533
534 out << "throw new IllegalArgumentException("
535 << "\"Array element is not of the expected length\");\n";
536
537 out.unindent();
538 out << "}\n\n";
539
540 out << blobName
541 << ".put"
542 << mElementType->getJavaSuffix()
543 << "Array("
544 << offsetName
545 << ", "
546 << elemName
547 << ");\n";
548 }
549
550 size_t elementAlign, elementSize;
551 mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
552
553 out << offsetName
554 << " += "
555 << mSizes.back()->javaValue()
556 << " * "
557 << elementSize
558 << ";\n";
559 }
560
561 for (size_t dim = 0; dim < loopDimensions; ++dim) {
562 out.unindent();
563 out << "}\n";
564 }
565
566 out.unindent();
567 out << "}\n";
568 }
569
emitVtsTypeDeclarations(Formatter & out) const570 void ArrayType::emitVtsTypeDeclarations(Formatter& out) const {
571 out << "type: " << getVtsType() << "\n";
572 out << "vector_size: " << mSizes[0]->rawValue() << "\n";
573 out << "vector_value: {\n";
574 out.indent();
575 // Simple array case.
576 if (mSizes.size() == 1) {
577 mElementType->emitVtsTypeDeclarations(out);
578 } else { // Multi-dimension array case.
579 for (size_t index = 1; index < mSizes.size(); index++) {
580 out << "type: " << getVtsType() << "\n";
581 out << "vector_size: " << mSizes[index]->rawValue() << "\n";
582 out << "vector_value: {\n";
583 out.indent();
584 if (index == mSizes.size() - 1) {
585 mElementType->emitVtsTypeDeclarations(out);
586 }
587 }
588 }
589 for (size_t index = 0; index < mSizes.size(); index++) {
590 out.unindent();
591 out << "}\n";
592 }
593 }
594
deepIsJavaCompatible(std::unordered_set<const Type * > * visited) const595 bool ArrayType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
596 if (!mElementType->isJavaCompatible(visited)) {
597 return false;
598 }
599 return Type::deepIsJavaCompatible(visited);
600 }
601
deepContainsPointer(std::unordered_set<const Type * > * visited) const602 bool ArrayType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
603 if (mElementType->containsPointer(visited)) {
604 return true;
605 }
606 return Type::deepContainsPointer(visited);
607 }
608
getAlignmentAndSize(size_t * align,size_t * size) const609 void ArrayType::getAlignmentAndSize(size_t *align, size_t *size) const {
610 mElementType->getAlignmentAndSize(align, size);
611
612 for (auto sizeInDimension : mSizes) {
613 (*size) *= sizeInDimension->castSizeT();
614 }
615 }
616
dimension() const617 size_t ArrayType::dimension() const {
618 size_t numArrayElements = 1;
619 for (auto size : mSizes) {
620 numArrayElements *= size->castSizeT();
621 }
622 return numArrayElements;
623 }
624
625 } // namespace android
626
627