Home | History | Annotate | Download | only in hidl
      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 "Type.h"
     18 
     19 #include "ConstantExpression.h"
     20 #include "NamedType.h"
     21 #include "ScalarType.h"
     22 #include "Scope.h"
     23 
     24 #include <android-base/logging.h>
     25 #include <hidl-util/Formatter.h>
     26 #include <algorithm>
     27 #include <iostream>
     28 
     29 namespace android {
     30 
     31 Type::Type(Scope* parent) : mParent(parent) {}
     32 
     33 Type::~Type() {}
     34 
     35 bool Type::isScope() const {
     36     return false;
     37 }
     38 
     39 bool Type::isInterface() const {
     40     return false;
     41 }
     42 
     43 bool Type::isScalar() const {
     44     return false;
     45 }
     46 
     47 bool Type::isString() const {
     48     return false;
     49 }
     50 
     51 bool Type::isEnum() const {
     52     return false;
     53 }
     54 
     55 bool Type::isBitField() const {
     56     return false;
     57 }
     58 
     59 bool Type::isHandle() const {
     60     return false;
     61 }
     62 
     63 bool Type::isTypeDef() const {
     64     return false;
     65 }
     66 
     67 bool Type::isBinder() const {
     68     return false;
     69 }
     70 
     71 bool Type::isNamedType() const {
     72     return false;
     73 }
     74 
     75 bool Type::isMemory() const {
     76     return false;
     77 }
     78 
     79 bool Type::isCompoundType() const {
     80     return false;
     81 }
     82 
     83 bool Type::isArray() const {
     84     return false;
     85 }
     86 
     87 bool Type::isVector() const {
     88     return false;
     89 }
     90 
     91 bool Type::isTemplatedType() const {
     92     return false;
     93 }
     94 
     95 bool Type::isPointer() const {
     96     return false;
     97 }
     98 
     99 Type* Type::resolve() {
    100     return const_cast<Type*>(static_cast<const Type*>(this)->resolve());
    101 }
    102 
    103 const Type* Type::resolve() const {
    104     return this;
    105 }
    106 
    107 std::vector<Type*> Type::getDefinedTypes() {
    108     const auto& constRet = static_cast<const Type*>(this)->getDefinedTypes();
    109     std::vector<Type*> ret(constRet.size());
    110     std::transform(constRet.begin(), constRet.end(), ret.begin(),
    111                    [](const auto* type) { return const_cast<Type*>(type); });
    112     return ret;
    113 }
    114 
    115 std::vector<const Type*> Type::getDefinedTypes() const {
    116     return {};
    117 }
    118 
    119 std::vector<Reference<Type>*> Type::getReferences() {
    120     const auto& constRet = static_cast<const Type*>(this)->getReferences();
    121     std::vector<Reference<Type>*> ret(constRet.size());
    122     std::transform(constRet.begin(), constRet.end(), ret.begin(),
    123                    [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
    124     return ret;
    125 }
    126 
    127 std::vector<const Reference<Type>*> Type::getReferences() const {
    128     return {};
    129 }
    130 
    131 std::vector<ConstantExpression*> Type::getConstantExpressions() {
    132     const auto& constRet = static_cast<const Type*>(this)->getConstantExpressions();
    133     std::vector<ConstantExpression*> ret(constRet.size());
    134     std::transform(constRet.begin(), constRet.end(), ret.begin(),
    135                    [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
    136     return ret;
    137 }
    138 
    139 std::vector<const ConstantExpression*> Type::getConstantExpressions() const {
    140     return {};
    141 }
    142 
    143 std::vector<Reference<Type>*> Type::getStrongReferences() {
    144     const auto& constRet = static_cast<const Type*>(this)->getStrongReferences();
    145     std::vector<Reference<Type>*> ret(constRet.size());
    146     std::transform(constRet.begin(), constRet.end(), ret.begin(),
    147                    [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
    148     return ret;
    149 }
    150 
    151 std::vector<const Reference<Type>*> Type::getStrongReferences() const {
    152     std::vector<const Reference<Type>*> ret;
    153     for (const auto* ref : getReferences()) {
    154         if (!ref->shallowGet()->isNeverStrongReference()) {
    155             ret.push_back(ref);
    156         }
    157     }
    158     return ret;
    159 }
    160 
    161 status_t Type::recursivePass(const std::function<status_t(Type*)>& func,
    162                              std::unordered_set<const Type*>* visited) {
    163     if (mIsPostParseCompleted) return OK;
    164 
    165     if (visited->find(this) != visited->end()) return OK;
    166     visited->insert(this);
    167 
    168     status_t err = func(this);
    169     if (err != OK) return err;
    170 
    171     for (auto* nextType : getDefinedTypes()) {
    172         err = nextType->recursivePass(func, visited);
    173         if (err != OK) return err;
    174     }
    175 
    176     for (auto* nextRef : getReferences()) {
    177         err = nextRef->shallowGet()->recursivePass(func, visited);
    178         if (err != OK) return err;
    179     }
    180 
    181     return OK;
    182 }
    183 
    184 status_t Type::recursivePass(const std::function<status_t(const Type*)>& func,
    185                              std::unordered_set<const Type*>* visited) const {
    186     if (mIsPostParseCompleted) return OK;
    187 
    188     if (visited->find(this) != visited->end()) return OK;
    189     visited->insert(this);
    190 
    191     status_t err = func(this);
    192     if (err != OK) return err;
    193 
    194     for (const auto* nextType : getDefinedTypes()) {
    195         err = nextType->recursivePass(func, visited);
    196         if (err != OK) return err;
    197     }
    198 
    199     for (const auto* nextRef : getReferences()) {
    200         err = nextRef->shallowGet()->recursivePass(func, visited);
    201         if (err != OK) return err;
    202     }
    203 
    204     return OK;
    205 }
    206 
    207 status_t Type::resolveInheritance() {
    208     return OK;
    209 }
    210 
    211 status_t Type::validate() const {
    212     return OK;
    213 }
    214 
    215 Type::CheckAcyclicStatus::CheckAcyclicStatus(status_t status, const Type* cycleEnd)
    216     : status(status), cycleEnd(cycleEnd) {
    217     CHECK(cycleEnd == nullptr || status != OK);
    218 }
    219 
    220 Type::CheckAcyclicStatus Type::topologicalOrder(
    221     std::unordered_map<const Type*, size_t>* reversedOrder,
    222     std::unordered_set<const Type*>* stack) const {
    223     if (stack->find(this) != stack->end()) {
    224         std::cerr << "ERROR: Cyclic declaration:\n";
    225         return CheckAcyclicStatus(UNKNOWN_ERROR, this);
    226     }
    227 
    228     if (reversedOrder->find(this) != reversedOrder->end()) return CheckAcyclicStatus(OK);
    229     stack->insert(this);
    230 
    231     for (const auto* nextType : getDefinedTypes()) {
    232         auto err = nextType->topologicalOrder(reversedOrder, stack);
    233 
    234         if (err.status != OK) {
    235             if (err.cycleEnd == nullptr) return err;
    236 
    237             std::cerr << "  '" << nextType->typeName() << "' in '" << typeName() << "'";
    238             if (nextType->isNamedType()) {
    239                 std::cerr << " at " << static_cast<const NamedType*>(nextType)->location();
    240             }
    241             std::cerr << "\n";
    242 
    243             if (err.cycleEnd == this) {
    244                 return CheckAcyclicStatus(err.status);
    245             }
    246             return err;
    247         }
    248     }
    249 
    250     for (const auto* nextRef : getStrongReferences()) {
    251         const auto* nextType = nextRef->shallowGet();
    252         auto err = nextType->topologicalOrder(reversedOrder, stack);
    253 
    254         if (err.status != OK) {
    255             if (err.cycleEnd == nullptr) return err;
    256 
    257             std::cerr << "  '" << nextType->typeName() << "' in '" << typeName() << "' at "
    258                       << nextRef->location() << "\n";
    259 
    260             if (err.cycleEnd == this) {
    261                 return CheckAcyclicStatus(err.status);
    262             }
    263             return err;
    264         }
    265     }
    266 
    267     CHECK(stack->find(this) != stack->end());
    268     stack->erase(this);
    269 
    270     CHECK(reversedOrder->find(this) == reversedOrder->end());
    271     // Do not call insert and size in one statement to not rely on
    272     // evaluation order.
    273     size_t index = reversedOrder->size();
    274     reversedOrder->insert({this, index});
    275 
    276     return CheckAcyclicStatus(OK);
    277 }
    278 
    279 status_t Type::checkForwardReferenceRestrictions(const Reference<Type>& ref) const {
    280     const Location& refLoc = ref.location();
    281     const Type* refType = ref.shallowGet();
    282 
    283     // Not NamedTypes are avaiable everywhere.
    284     // Only ArrayType and TemplatedType contain additional types in
    285     // their reference (which is actually a part of type definition),
    286     // so they are proceeded in this case.
    287     //
    288     // If we support named templated types one day, we will need to change
    289     // this logic.
    290     if (!refType->isNamedType()) {
    291         for (const Reference<Type>* innerRef : refType->getReferences()) {
    292             status_t err = checkForwardReferenceRestrictions(*innerRef);
    293             if (err != OK) return err;
    294         }
    295         return OK;
    296     }
    297 
    298     const Location& typeLoc = static_cast<const NamedType*>(refType)->location();
    299 
    300     // If referenced type is declared in another file or before reference,
    301     // there is no forward reference here.
    302     if (!Location::inSameFile(refLoc, typeLoc) ||
    303         (!Location::intersect(refLoc, typeLoc) && typeLoc < refLoc)) {
    304         return OK;
    305     }
    306 
    307     // Type must be declared somewhere in the current stack to make it
    308     // available for forward referencing.
    309     const Type* refTypeParent = refType->parent();
    310     for (const Type* ancestor = this; ancestor != nullptr; ancestor = ancestor->parent()) {
    311         if (ancestor == refTypeParent) return OK;
    312     }
    313 
    314     std::cerr << "ERROR: Forward reference of '" << refType->typeName() << "' at " << ref.location()
    315               << " is not supported.\n"
    316               << "C++ forward declaration doesn't support inner types.\n";
    317 
    318     return UNKNOWN_ERROR;
    319 }
    320 
    321 const ScalarType *Type::resolveToScalarType() const {
    322     return NULL;
    323 }
    324 
    325 bool Type::isValidEnumStorageType() const {
    326     const ScalarType *scalarType = resolveToScalarType();
    327 
    328     if (scalarType == NULL) {
    329         return false;
    330     }
    331 
    332     return scalarType->isValidEnumStorageType();
    333 }
    334 
    335 bool Type::isElidableType() const {
    336     return false;
    337 }
    338 
    339 bool Type::canCheckEquality() const {
    340     std::unordered_set<const Type*> visited;
    341     return canCheckEquality(&visited);
    342 }
    343 
    344 bool Type::canCheckEquality(std::unordered_set<const Type*>* visited) const {
    345     // See isJavaCompatible for similar structure.
    346     if (visited->find(this) != visited->end()) {
    347         return true;
    348     }
    349     visited->insert(this);
    350     return deepCanCheckEquality(visited);
    351 }
    352 
    353 bool Type::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
    354     return false;
    355 }
    356 
    357 void Type::setPostParseCompleted() {
    358     CHECK(!mIsPostParseCompleted);
    359     mIsPostParseCompleted = true;
    360 }
    361 
    362 Scope* Type::parent() {
    363     return mParent;
    364 }
    365 
    366 const Scope* Type::parent() const {
    367     return mParent;
    368 }
    369 
    370 std::string Type::getCppType(StorageMode, bool) const {
    371     CHECK(!"Should not be here");
    372     return std::string();
    373 }
    374 
    375 std::string Type::decorateCppName(
    376         const std::string &name, StorageMode mode, bool specifyNamespaces) const {
    377     return getCppType(mode, specifyNamespaces) + " " + name;
    378 }
    379 
    380 std::string Type::getJavaType(bool /* forInitializer */) const {
    381     CHECK(!"Should not be here");
    382     return std::string();
    383 }
    384 
    385 std::string Type::getJavaWrapperType() const {
    386     return getJavaType();
    387 }
    388 
    389 std::string Type::getJavaSuffix() const {
    390     CHECK(!"Should not be here");
    391     return std::string();
    392 }
    393 
    394 std::string Type::getVtsType() const {
    395     CHECK(!"Should not be here");
    396     return std::string();
    397 }
    398 
    399 std::string Type::getVtsValueName() const {
    400     CHECK(!"Should not be here");
    401     return std::string();
    402 }
    403 
    404 void Type::emitReaderWriter(
    405         Formatter &,
    406         const std::string &,
    407         const std::string &,
    408         bool,
    409         bool,
    410         ErrorMode) const {
    411     CHECK(!"Should not be here");
    412 }
    413 
    414 void Type::emitResolveReferences(
    415         Formatter &,
    416         const std::string &,
    417         bool,
    418         const std::string &,
    419         bool,
    420         bool,
    421         ErrorMode) const {
    422     CHECK(!"Should not be here");
    423 }
    424 
    425 void Type::emitResolveReferencesEmbedded(
    426         Formatter &,
    427         size_t,
    428         const std::string &,
    429         const std::string &,
    430         bool,
    431         const std::string &,
    432         bool,
    433         bool,
    434         ErrorMode,
    435         const std::string &,
    436         const std::string &) const {
    437     CHECK(!"Should not be here");
    438 }
    439 
    440 void Type::emitDump(
    441         Formatter &out,
    442         const std::string &streamName,
    443         const std::string &name) const {
    444     emitDumpWithMethod(out, streamName, "::android::hardware::toString", name);
    445 }
    446 
    447 void Type::emitDumpWithMethod(
    448         Formatter &out,
    449         const std::string &streamName,
    450         const std::string &methodName,
    451         const std::string &name) const {
    452     out << streamName
    453         << " += "
    454         << methodName
    455         << "("
    456         << name
    457         << ");\n";
    458 }
    459 
    460 void Type::emitJavaDump(
    461         Formatter &out,
    462         const std::string &streamName,
    463         const std::string &name) const {
    464     out << streamName << ".append(" << name << ");\n";
    465 }
    466 
    467 bool Type::useParentInEmitResolveReferencesEmbedded() const {
    468     return needsResolveReferences();
    469 }
    470 
    471 bool Type::useNameInEmitReaderWriterEmbedded(bool) const {
    472     return needsEmbeddedReadWrite();
    473 }
    474 
    475 void Type::emitReaderWriterEmbedded(
    476         Formatter &,
    477         size_t,
    478         const std::string &,
    479         const std::string &,
    480         bool,
    481         const std::string &,
    482         bool,
    483         bool,
    484         ErrorMode,
    485         const std::string &,
    486         const std::string &) const {
    487     CHECK(!"Should not be here");
    488 }
    489 
    490 void Type::emitJavaReaderWriter(
    491         Formatter &out,
    492         const std::string &parcelObj,
    493         const std::string &argName,
    494         bool isReader) const {
    495     emitJavaReaderWriterWithSuffix(
    496             out,
    497             parcelObj,
    498             argName,
    499             isReader,
    500             getJavaSuffix(),
    501             "" /* extra */);
    502 }
    503 
    504 void Type::emitJavaFieldInitializer(
    505         Formatter &out,
    506         const std::string &fieldName) const {
    507     out << getJavaType()
    508         << " "
    509         << fieldName
    510         << ";\n";
    511 }
    512 
    513 void Type::emitJavaFieldReaderWriter(
    514         Formatter &,
    515         size_t,
    516         const std::string &,
    517         const std::string &,
    518         const std::string &,
    519         const std::string &,
    520         bool) const {
    521     CHECK(!"Should not be here");
    522 }
    523 
    524 void Type::handleError(Formatter &out, ErrorMode mode) const {
    525     switch (mode) {
    526         case ErrorMode_Ignore:
    527         {
    528             out << "/* _hidl_err ignored! */\n\n";
    529             break;
    530         }
    531 
    532         case ErrorMode_Goto:
    533         {
    534             out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n";
    535             break;
    536         }
    537 
    538         case ErrorMode_Break:
    539         {
    540             out << "if (_hidl_err != ::android::OK) { break; }\n\n";
    541             break;
    542         }
    543 
    544         case ErrorMode_Return:
    545         {
    546             out << "if (_hidl_err != ::android::OK) { return _hidl_err; }\n\n";
    547             break;
    548         }
    549     }
    550 }
    551 
    552 void Type::emitReaderWriterEmbeddedForTypeName(
    553         Formatter &out,
    554         const std::string &name,
    555         bool nameIsPointer,
    556         const std::string &parcelObj,
    557         bool parcelObjIsPointer,
    558         bool isReader,
    559         ErrorMode mode,
    560         const std::string &parentName,
    561         const std::string &offsetText,
    562         const std::string &typeName,
    563         const std::string &childName,
    564         const std::string &funcNamespace) const {
    565 
    566         const std::string parcelObjDeref =
    567         parcelObjIsPointer ? ("*" + parcelObj) : parcelObj;
    568 
    569     const std::string parcelObjPointer =
    570         parcelObjIsPointer ? parcelObj : ("&" + parcelObj);
    571 
    572     const std::string nameDerefed = nameIsPointer ? ("*" + name) : name;
    573     const std::string namePointer = nameIsPointer ? name : ("&" + name);
    574 
    575     out << "_hidl_err = ";
    576 
    577     if (!funcNamespace.empty()) {
    578         out << funcNamespace << "::";
    579     }
    580 
    581     out << (isReader ? "readEmbeddedFromParcel(\n" : "writeEmbeddedToParcel(\n");
    582 
    583     out.indent();
    584     out.indent();
    585 
    586     if (isReader) {
    587         out << "const_cast<"
    588             << typeName
    589             << " &>("
    590             << nameDerefed
    591             << "),\n";
    592     } else {
    593         out << nameDerefed
    594             << ",\n";
    595     }
    596 
    597     out << (isReader ? parcelObjDeref : parcelObjPointer)
    598         << ",\n"
    599         << parentName
    600         << ",\n"
    601         << offsetText;
    602 
    603     if (!childName.empty()) {
    604         out << ", &"
    605             << childName;
    606     }
    607 
    608     out << ");\n\n";
    609 
    610     out.unindent();
    611     out.unindent();
    612 
    613     handleError(out, mode);
    614 }
    615 
    616 void Type::emitTypeDeclarations(Formatter&) const {}
    617 
    618 void Type::emitTypeForwardDeclaration(Formatter&) const {}
    619 
    620 void Type::emitGlobalTypeDeclarations(Formatter&) const {}
    621 
    622 void Type::emitPackageTypeDeclarations(Formatter&) const {}
    623 
    624 void Type::emitPackageHwDeclarations(Formatter&) const {}
    625 
    626 void Type::emitTypeDefinitions(Formatter&, const std::string&) const {}
    627 
    628 void Type::emitJavaTypeDeclarations(Formatter&, bool) const {}
    629 
    630 bool Type::needsEmbeddedReadWrite() const {
    631     return false;
    632 }
    633 
    634 bool Type::resultNeedsDeref() const {
    635     return false;
    636 }
    637 
    638 bool Type::needsResolveReferences() const {
    639     std::unordered_set<const Type*> visited;
    640     return needsResolveReferences(&visited);
    641 }
    642 
    643 bool Type::needsResolveReferences(std::unordered_set<const Type*>* visited) const {
    644     // See isJavaCompatible for similar structure.
    645     if (visited->find(this) != visited->end()) {
    646         return false;
    647     }
    648     visited->insert(this);
    649     return deepNeedsResolveReferences(visited);
    650 }
    651 
    652 bool Type::deepNeedsResolveReferences(std::unordered_set<const Type*>* /* visited */) const {
    653     return false;
    654 }
    655 
    656 std::string Type::getCppStackType(bool specifyNamespaces) const {
    657     return getCppType(StorageMode_Stack, specifyNamespaces);
    658 }
    659 
    660 std::string Type::getCppResultType(bool specifyNamespaces) const {
    661     return getCppType(StorageMode_Result, specifyNamespaces);
    662 }
    663 
    664 std::string Type::getCppArgumentType(bool specifyNamespaces) const {
    665     return getCppType(StorageMode_Argument, specifyNamespaces);
    666 }
    667 
    668 void Type::emitJavaReaderWriterWithSuffix(
    669         Formatter &out,
    670         const std::string &parcelObj,
    671         const std::string &argName,
    672         bool isReader,
    673         const std::string &suffix,
    674         const std::string &extra) const {
    675     out << parcelObj
    676         << "."
    677         << (isReader ? "read" : "write")
    678         << suffix
    679         << "(";
    680 
    681     if (isReader) {
    682         out << extra;
    683     } else {
    684         out << (extra.empty() ? "" : (extra + ", "));
    685         out << argName;
    686     }
    687 
    688     out << ");\n";
    689 }
    690 
    691 void Type::emitVtsTypeDeclarations(Formatter&) const {}
    692 
    693 void Type::emitVtsAttributeType(Formatter& out) const {
    694     emitVtsTypeDeclarations(out);
    695 }
    696 
    697 bool Type::isJavaCompatible() const {
    698     std::unordered_set<const Type*> visited;
    699     return isJavaCompatible(&visited);
    700 }
    701 
    702 bool Type::containsPointer() const {
    703     std::unordered_set<const Type*> visited;
    704     return containsPointer(&visited);
    705 }
    706 
    707 bool Type::isJavaCompatible(std::unordered_set<const Type*>* visited) const {
    708     // We need to find al least one path from requested vertex
    709     // to not java compatible.
    710     // That means that if we have already visited some vertex,
    711     // there is no need to determine whether it is java compatible
    712     // (and we can assume that it is java compatible),
    713     // as if not, the information about that would appear in the
    714     // requested vertex through another path.
    715     if (visited->find(this) != visited->end()) {
    716         return true;
    717     }
    718     visited->insert(this);
    719     return deepIsJavaCompatible(visited);
    720 }
    721 
    722 bool Type::containsPointer(std::unordered_set<const Type*>* visited) const {
    723     // See isJavaCompatible for similar structure.
    724     if (visited->find(this) != visited->end()) {
    725         return false;
    726     }
    727     visited->insert(this);
    728     return deepContainsPointer(visited);
    729 }
    730 
    731 bool Type::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
    732     return true;
    733 }
    734 
    735 bool Type::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
    736     return false;
    737 }
    738 
    739 void Type::getAlignmentAndSize(
    740         size_t * /* align */, size_t * /* size */) const {
    741     CHECK(!"Should not be here.");
    742 }
    743 
    744 void Type::appendToExportedTypesVector(
    745         std::vector<const Type *> * /* exportedTypes */) const {
    746 }
    747 
    748 void Type::emitExportedHeader(Formatter& /* out */, bool /* forJava */) const {}
    749 
    750 bool Type::isNeverStrongReference() const {
    751     return false;
    752 }
    753 
    754 ////////////////////////////////////////
    755 
    756 TemplatedType::TemplatedType(Scope* parent) : Type(parent) {}
    757 
    758 std::string TemplatedType::typeName() const {
    759     return templatedTypeName() + " of " + mElementType->typeName();
    760 }
    761 
    762 void TemplatedType::setElementType(const Reference<Type>& elementType) {
    763     // can only be set once.
    764     CHECK(mElementType.isEmptyReference());
    765     CHECK(!elementType.isEmptyReference());
    766 
    767     mElementType = elementType;
    768 }
    769 
    770 const Type* TemplatedType::getElementType() const {
    771     return mElementType.get();
    772 }
    773 
    774 bool TemplatedType::isTemplatedType() const {
    775     return true;
    776 }
    777 
    778 std::vector<const Reference<Type>*> TemplatedType::getReferences() const {
    779     return {&mElementType};
    780 }
    781 
    782 status_t TemplatedType::validate() const {
    783     if (!isCompatibleElementType(mElementType.get())) {
    784         std::cerr << "ERROR: " << typeName() /* contains element type */
    785                   << " is not supported at " << mElementType.location() << "\n";
    786         return UNKNOWN_ERROR;
    787     }
    788 
    789     return Type::validate();
    790 }
    791 
    792 void TemplatedType::emitVtsTypeDeclarations(Formatter& out) const {
    793     out << "type: " << getVtsType() << "\n";
    794     out << getVtsValueName() << ": {\n";
    795     out.indent();
    796     mElementType->emitVtsTypeDeclarations(out);
    797     out.unindent();
    798     out << "}\n";
    799 }
    800 
    801 void TemplatedType::emitVtsAttributeType(Formatter& out) const {
    802     out << "type: " << getVtsType() << "\n";
    803     out << getVtsValueName() << ": {\n";
    804     out.indent();
    805     mElementType->emitVtsAttributeType(out);
    806     out.unindent();
    807     out << "}\n";
    808 }
    809 
    810 }  // namespace android
    811 
    812