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