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