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, elementType.localName()), mElementType(elementType) {
29     CHECK(!elementType.isEmptyReference());
30 
31     appendDimension(size);
32 }
33 
appendDimension(ConstantExpression * size)34 void ArrayType::appendDimension(ConstantExpression *size) {
35     mSizes.push_back(size);
36     mDefinedName = mDefinedName + "[" + size->expression() + "]";
37 }
38 
countDimensions() const39 size_t ArrayType::countDimensions() const {
40     return mSizes.size();
41 }
42 
isArray() const43 bool ArrayType::isArray() const {
44     return true;
45 }
46 
deepCanCheckEquality(std::unordered_set<const Type * > * visited) const47 bool ArrayType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
48     return mElementType->canCheckEquality(visited);
49 }
50 
getElementType() const51 const Type* ArrayType::getElementType() const {
52     return mElementType.get();
53 }
54 
typeName() const55 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 
getReferences() const63 std::vector<const Reference<Type>*> ArrayType::getReferences() const {
64     return {&mElementType};
65 }
66 
getConstantExpressions() const67 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 
resolveInheritance()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 
validate() const83 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 
getCppType(StorageMode mode,bool specifyNamespaces) const95 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 
getInternalDataCppType() const122 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 
getJavaType(bool forInitializer) const132 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 
getVtsType() const151 std::string ArrayType::getVtsType() const {
152     return "TYPE_ARRAY";
153 }
154 
emitReaderWriter(Formatter & out,const std::string & name,const std::string & parcelObj,bool parcelObjIsPointer,bool isReader,ErrorMode mode) const155 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 
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) const221 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 
emitJavaDump(Formatter & out,const std::string & streamName,const std::string & name) const276 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 
needsEmbeddedReadWrite() const288 bool ArrayType::needsEmbeddedReadWrite() const {
289     return mElementType->needsEmbeddedReadWrite();
290 }
291 
resultNeedsDeref() const292 bool ArrayType::resultNeedsDeref() const {
293     return true;
294 }
295 
emitJavaReaderWriter(Formatter & out,const std::string & parcelObj,const std::string & argName,bool isReader) const296 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 
emitJavaFieldInitializer(Formatter & out,const std::string & fieldName) const343 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 
emitJavaFieldDefaultInitialValue(Formatter & out,const std::string & declaredFieldName) const351 void ArrayType::emitJavaFieldDefaultInitialValue(
352         Formatter &out, const std::string &declaredFieldName) const {
353     out << declaredFieldName
354         << " = new "
355         << getJavaType(true /* forInitializer */)
356         << ";\n";
357 }
358 
emitJavaFieldReaderWriter(Formatter & out,size_t depth,const std::string & parcelName,const std::string & blobName,const std::string & fieldName,const std::string & offset,bool isReader) const359 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 
emitVtsTypeDeclarations(Formatter & out) const492 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 
deepIsJavaCompatible(std::unordered_set<const Type * > * visited) const517 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 
deepContainsPointer(std::unordered_set<const Type * > * visited) const524 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 
getAlignmentAndSize(size_t * align,size_t * size) const531 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 
dimension() const539 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