/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "Coordinator.h" #include "EnumType.h" #include "FmqType.h" #include "HandleType.h" #include "Interface.h" #include "Location.h" #include "Method.h" #include "Scope.h" #include "TypeDef.h" #include #include #include #include #include #include #include #include #include namespace android { AST::AST(const Coordinator* coordinator, const Hash* fileHash) : mCoordinator(coordinator), mFileHash(fileHash), mRootScope("(root scope)", FQName(), Location::startOf(coordinator->makeRelative(fileHash->getPath())), nullptr /* parent */) {} Scope* AST::getMutableRootScope() { return &mRootScope; } const Scope& AST::getRootScope() const { return mRootScope; } // used by the parser. void AST::addSyntaxError() { mSyntaxErrors++; } size_t AST::syntaxErrors() const { return mSyntaxErrors; } const std::string& AST::getFilename() const { return mFileHash->getPath(); } const Hash* AST::getFileHash() const { return mFileHash; } const Coordinator& AST::getCoordinator() const { return *mCoordinator; } bool AST::setPackage(const char *package) { if (!mPackage.setTo(package)) { return false; } if (mPackage.package().empty() || mPackage.version().empty() || !mPackage.name().empty()) { return false; } return true; } FQName AST::package() const { return mPackage; } bool AST::isInterface() const { return mRootScope.getInterface() != nullptr; } bool AST::definesInterfaces() const { return mRootScope.definesInterfaces(); } status_t AST::postParse() { status_t err; // lookupTypes is the first pass for references to be resolved. err = lookupTypes(); if (err != OK) return err; // Indicate that all types are now in "postParse" stage. err = setParseStage(Type::ParseStage::PARSE, Type::ParseStage::POST_PARSE); if (err != OK) return err; // validateDefinedTypesUniqueNames is the first call // after lookup, as other errors could appear because // user meant different type than we assumed. err = validateDefinedTypesUniqueNames(); if (err != OK) return err; // topologicalReorder is before resolveInheritance, as we // need to have no cycle while getting parent class. err = topologicalReorder(); if (err != OK) return err; err = resolveInheritance(); if (err != OK) return err; err = lookupConstantExpressions(); if (err != OK) return err; // checkAcyclicConstantExpressions is after resolveInheritance, // as resolveInheritance autofills enum values. err = checkAcyclicConstantExpressions(); if (err != OK) return err; err = validateConstantExpressions(); if (err != OK) return err; err = evaluateConstantExpressions(); if (err != OK) return err; err = validate(); if (err != OK) return err; err = checkForwardReferenceRestrictions(); if (err != OK) return err; err = gatherReferencedTypes(); if (err != OK) return err; // Make future packages not to call passes // for processed types and expressions constantExpressionRecursivePass( [](ConstantExpression* ce) { ce->setPostParseCompleted(); return OK; }, true /* processBeforeDependencies */); err = setParseStage(Type::ParseStage::POST_PARSE, Type::ParseStage::COMPLETED); if (err != OK) return err; return OK; } status_t AST::constantExpressionRecursivePass( const std::function& func, bool processBeforeDependencies) { std::unordered_set visitedTypes; std::unordered_set visitedCE; return mRootScope.recursivePass(Type::ParseStage::POST_PARSE, [&](Type* type) -> status_t { for (auto* ce : type->getConstantExpressions()) { status_t err = ce->recursivePass( func, &visitedCE, processBeforeDependencies); if (err != OK) return err; } return OK; }, &visitedTypes); } status_t AST::constantExpressionRecursivePass( const std::function& func, bool processBeforeDependencies) const { std::unordered_set visitedTypes; std::unordered_set visitedCE; return mRootScope.recursivePass(Type::ParseStage::POST_PARSE, [&](const Type* type) -> status_t { for (auto* ce : type->getConstantExpressions()) { status_t err = ce->recursivePass( func, &visitedCE, processBeforeDependencies); if (err != OK) return err; } return OK; }, &visitedTypes); } status_t AST::setParseStage(Type::ParseStage oldStage, Type::ParseStage newStage) { std::unordered_set visited; return mRootScope.recursivePass(oldStage, [oldStage, newStage](Type* type) { CHECK(type->getParseStage() == oldStage); type->setParseStage(newStage); return OK; }, &visited); } status_t AST::lookupTypes() { std::unordered_set visited; return mRootScope.recursivePass( Type::ParseStage::PARSE, [&](Type* type) -> status_t { Scope* scope = type->isScope() ? static_cast(type) : type->parent(); for (auto* nextRef : type->getReferences()) { if (nextRef->isResolved()) { continue; } Type* nextType = lookupType(nextRef->getLookupFqName(), scope); if (nextType == nullptr) { std::cerr << "ERROR: Failed to lookup type '" << nextRef->getLookupFqName().string() << "' at " << nextRef->location() << " (is it imported and spelled correctly?)\n"; return UNKNOWN_ERROR; } nextRef->set(nextType); } return OK; }, &visited); } status_t AST::gatherReferencedTypes() { std::unordered_set visited; return mRootScope.recursivePass( Type::ParseStage::POST_PARSE, [&](Type* type) -> status_t { for (auto* nextRef : type->getReferences()) { const Type *targetType = nextRef->get(); if (targetType->isNamedType()) { mReferencedTypeNames.insert( static_cast(targetType)->fqName()); } } return OK; }, &visited); } status_t AST::lookupConstantExpressions() { std::unordered_set visitedTypes; std::unordered_set visitedCE; return mRootScope.recursivePass( Type::ParseStage::POST_PARSE, [&](Type* type) -> status_t { Scope* scope = type->isScope() ? static_cast(type) : type->parent(); for (auto* ce : type->getConstantExpressions()) { status_t err = ce->recursivePass( [&](ConstantExpression* ce) { for (auto* nextRef : ce->getReferences()) { if (nextRef->isResolved()) continue; LocalIdentifier* iden = lookupLocalIdentifier(*nextRef, scope); if (iden == nullptr) return UNKNOWN_ERROR; nextRef->set(iden); } for (auto* nextRef : ce->getTypeReferences()) { if (nextRef->isResolved()) continue; Type* nextType = lookupType(nextRef->getLookupFqName(), scope); if (nextType == nullptr) { std::cerr << "ERROR: Failed to lookup type '" << nextRef->getLookupFqName().string() << "' at " << nextRef->location() << "\n"; return UNKNOWN_ERROR; } nextRef->set(nextType); } return OK; }, &visitedCE, true /* processBeforeDependencies */); if (err != OK) return err; } return OK; }, &visitedTypes); } status_t AST::validateDefinedTypesUniqueNames() const { std::unordered_set visited; return mRootScope.recursivePass( Type::ParseStage::POST_PARSE, [&](const Type* type) -> status_t { // We only want to validate type definition names in this place. if (type->isScope()) { return static_cast(type)->validateUniqueNames(); } return OK; }, &visited); } status_t AST::resolveInheritance() { std::unordered_set visited; return mRootScope.recursivePass(Type::ParseStage::POST_PARSE, &Type::resolveInheritance, &visited); } status_t AST::validateConstantExpressions() const { return constantExpressionRecursivePass( [](const ConstantExpression* ce) { return ce->validate(); }, true /* processBeforeDependencies */); } status_t AST::evaluateConstantExpressions() { return constantExpressionRecursivePass( [](ConstantExpression* ce) { ce->evaluate(); return OK; }, false /* processBeforeDependencies */); } status_t AST::validate() const { std::unordered_set visited; return mRootScope.recursivePass(Type::ParseStage::POST_PARSE, &Type::validate, &visited); } status_t AST::topologicalReorder() { std::unordered_map reversedOrder; std::unordered_set stack; status_t err = mRootScope.topologicalOrder(&reversedOrder, &stack).status; if (err != OK) return err; std::unordered_set visited; mRootScope.recursivePass(Type::ParseStage::POST_PARSE, [&](Type* type) { if (type->isScope()) { static_cast(type)->topologicalReorder(reversedOrder); } return OK; }, &visited); return OK; } status_t AST::checkAcyclicConstantExpressions() const { std::unordered_set visitedTypes; std::unordered_set visitedCE; std::unordered_set stack; return mRootScope.recursivePass(Type::ParseStage::POST_PARSE, [&](const Type* type) -> status_t { for (auto* ce : type->getConstantExpressions()) { status_t err = ce->checkAcyclic(&visitedCE, &stack).status; CHECK(err != OK || stack.empty()); if (err != OK) return err; } return OK; }, &visitedTypes); } status_t AST::checkForwardReferenceRestrictions() const { std::unordered_set visited; return mRootScope.recursivePass(Type::ParseStage::POST_PARSE, [](const Type* type) -> status_t { for (const Reference* ref : type->getReferences()) { status_t err = type->checkForwardReferenceRestrictions(*ref); if (err != OK) return err; } return OK; }, &visited); } bool AST::importFQName(const FQName& fqName) { if (fqName.name().empty()) { // import a package std::vector packageInterfaces; status_t err = mCoordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces); if (err != OK) { return false; } for (const auto& subFQName : packageInterfaces) { // Do not enforce restrictions on imports. AST* ast = mCoordinator->parse(subFQName, &mImportedASTs, Coordinator::Enforce::NONE); if (ast == nullptr) { return false; } addToImportedNamesGranular(subFQName); // all previous single type imports are ignored. mImportedTypes.erase(ast); } return true; } // cases like android.hardware.foo@1.0::IFoo.Internal // android.hardware.foo@1.0::Abc.Internal // assume it is an interface, and try to import it. const FQName interfaceName = fqName.getTopLevelType(); // Do not enforce restrictions on imports. AST* importAST; status_t err = mCoordinator->parseOptional(interfaceName, &importAST, &mImportedASTs, Coordinator::Enforce::NONE); if (err != OK) return false; // importAST nullptr == file doesn't exist if (importAST != nullptr) { // cases like android.hardware.foo@1.0::IFoo.Internal // and android.hardware.foo@1.0::IFoo if (fqName == interfaceName) { // import a single file. // all previous single type imports are ignored. // cases like android.hardware.foo@1.0::IFoo // and android.hardware.foo@1.0::types mImportedTypes.erase(importAST); addToImportedNamesGranular(fqName); return true; } // import a single type from this file // cases like android.hardware.foo@1.0::IFoo.Internal FQName matchingName; Type* match = importAST->findDefinedType(fqName, &matchingName); if (match == nullptr) { return false; } // will automatically create a set if it does not exist mImportedTypes[importAST].insert(match); addToImportedNamesGranular(fqName); return true; } // probably a type in types.hal, like android.hardware.foo@1.0::Abc.Internal FQName typesFQName = fqName.getTypesForPackage(); // Do not enforce restrictions on imports. importAST = mCoordinator->parse(typesFQName, &mImportedASTs, Coordinator::Enforce::NONE); if (importAST != nullptr) { // Attempt to find Abc.Internal in types. FQName matchingName; Type* match = importAST->findDefinedType(fqName, &matchingName); if (match == nullptr) { return false; } // will automatically create a set if not exist mImportedTypes[importAST].insert(match); addToImportedNamesGranular(fqName); return true; } // can't find an appropriate AST for fqName. return false; } bool AST::addImplicitImport(const FQName& fqName) { CHECK(fqName.isFullyQualified()); if (importFQName(fqName)) { mImplicitImports.push_back(fqName); return true; } return false; } bool AST::addImport(const char* import, const Location& location) { FQName fqName; if (!FQName::parse(import, &fqName)) { std::cerr << "ERROR: '" << import << "' is an invalid fully-qualified name." << std::endl; return false; } fqName.applyDefaults(mPackage.package(), mPackage.version()); if (importFQName(fqName)) { mImportStatements.push_back({fqName, location}); return true; } std::cerr << "while importing " << import << " at " << location << "." << std::endl; return false; } void AST::addImportedAST(AST *ast) { mImportedASTs.insert(ast); } FQName AST::makeFullName(const char* localName, Scope* scope) const { std::vector pathComponents{{localName}}; for (; scope != &mRootScope; scope = scope->parent()) { pathComponents.push_back(scope->definedName()); } std::reverse(pathComponents.begin(), pathComponents.end()); std::string path = StringHelper::JoinStrings(pathComponents, "."); return FQName(mPackage.package(), mPackage.version(), path); } void AST::addScopedType(NamedType* type, Scope* scope) { scope->addType(type); mDefinedTypesByFullName[type->fqName()] = type; } LocalIdentifier* AST::lookupLocalIdentifier(const Reference& ref, const Scope* scope) { const FQName& fqName = ref.getLookupFqName(); if (fqName.isIdentifier()) { LocalIdentifier* iden = scope->lookupIdentifier(fqName.name()); if (iden == nullptr) { std::cerr << "ERROR: identifier " << fqName.string() << " could not be found at " << ref.location() << "\n"; return nullptr; } return iden; } else { std::string errorMsg; EnumValue* enumValue = lookupEnumValue(fqName, &errorMsg, scope); if (enumValue == nullptr) { std::cerr << "ERROR: " << errorMsg << " at " << ref.location() << "\n"; return nullptr; } return enumValue; } } EnumValue* AST::lookupEnumValue(const FQName& fqName, std::string* errorMsg, const Scope* scope) { FQName enumTypeName = fqName.typeName(); std::string enumValueName = fqName.valueName(); CHECK(!enumValueName.empty()); Type* type = lookupType(enumTypeName, scope); if(type == nullptr) { *errorMsg = "Cannot find type " + enumTypeName.string(); return nullptr; } type = type->resolve(); if(!type->isEnum()) { *errorMsg = "Type " + enumTypeName.string() + " is not an enum type"; return nullptr; } EnumType *enumType = static_cast(type); EnumValue *v = static_cast(enumType->lookupIdentifier(enumValueName)); if(v == nullptr) { *errorMsg = "Enum type " + enumTypeName.string() + " does not have " + enumValueName; return nullptr; } mReferencedTypeNames.insert(enumType->fqName()); return v; } Type* AST::lookupType(const FQName& fqName, const Scope* scope) { if (fqName.name().empty()) { // Given a package and version??? return nullptr; } Type *returnedType = nullptr; if (fqName.package().empty() && fqName.version().empty()) { // resolve locally first if possible. returnedType = lookupTypeLocally(fqName, scope); if (returnedType != nullptr) { return returnedType; } } status_t status = lookupAutofilledType(fqName, &returnedType); if (status != OK) { return nullptr; } if (returnedType != nullptr) { return returnedType; } return lookupTypeFromImports(fqName); } // Rule 0: try resolve locally Type* AST::lookupTypeLocally(const FQName& fqName, const Scope* scope) { CHECK(fqName.package().empty() && fqName.version().empty() && !fqName.name().empty() && fqName.valueName().empty()); for (; scope != nullptr; scope = scope->parent()) { Type* type = scope->lookupType(fqName); if (type != nullptr) { return type; } } return nullptr; } // Rule 1: auto-fill with current package status_t AST::lookupAutofilledType(const FQName &fqName, Type **returnedType) { CHECK(!fqName.name().empty() && fqName.valueName().empty()); FQName autofilled = fqName; autofilled.applyDefaults(mPackage.package(), mPackage.version()); FQName matchingName; // Given this fully-qualified name, the type may be defined in this AST, or other files // in import. Type *local = findDefinedType(autofilled, &matchingName); CHECK(local == nullptr || autofilled == matchingName); Type* fromImport = lookupTypeFromImports(autofilled); if (local != nullptr && fromImport != nullptr && local != fromImport) { // Something bad happen; two types have the same FQName. std::cerr << "ERROR: Unable to resolve type name '" << fqName.string() << "' (i.e. '" << autofilled.string() << "'), multiple definitions found.\n"; return UNKNOWN_ERROR; } if (local != nullptr) { *returnedType = local; return OK; } // If fromImport is nullptr as well, return nullptr to fall through to next rule. *returnedType = fromImport; return OK; } // Rule 2: look at imports Type *AST::lookupTypeFromImports(const FQName &fqName) { Type *resolvedType = nullptr; Type *returnedType = nullptr; FQName resolvedName; for (const auto &importedAST : mImportedASTs) { if (mImportedTypes.find(importedAST) != mImportedTypes.end()) { // ignore single type imports continue; } FQName matchingName; Type *match = importedAST->findDefinedType(fqName, &matchingName); if (match != nullptr) { if (resolvedType != nullptr) { std::cerr << "ERROR: Unable to resolve type name '" << fqName.string() << "', multiple matches found:\n"; std::cerr << " " << resolvedName.string() << "\n"; std::cerr << " " << matchingName.string() << "\n"; return nullptr; } resolvedType = match; returnedType = resolvedType; resolvedName = matchingName; // Keep going even after finding a match. } } for (const auto &pair : mImportedTypes) { AST *importedAST = pair.first; std::set importedTypes = pair.second; FQName matchingName; Type *match = importedAST->findDefinedType(fqName, &matchingName); if (match != nullptr && importedTypes.find(match) != importedTypes.end()) { if (resolvedType != nullptr) { std::cerr << "ERROR: Unable to resolve type name '" << fqName.string() << "', multiple matches found:\n"; std::cerr << " " << resolvedName.string() << "\n"; std::cerr << " " << matchingName.string() << "\n"; return nullptr; } resolvedType = match; returnedType = resolvedType; resolvedName = matchingName; // Keep going even after finding a match. } } if (resolvedType) { returnedType = resolvedType; // If the resolved type is not an interface, we need to determine // whether it is defined in types.hal, or in some other interface. In // the latter case, we need to emit a dependency for the interface in // which the type is defined. // // Consider the following: // android.hardware.tests.foo@1.0::Record // android.hardware.tests.foo@1.0::IFoo.Folder // android.hardware.tests.foo@1.0::Folder // // If Record is an interface, then we keep track of it for the purpose // of emitting dependencies in the target language (for example #include // in C++). If Record is a UDT, then we assume it is defined in // types.hal in android.hardware.tests.foo@1.0. // // In the case of IFoo.Folder, the same applies. If IFoo is an // interface, we need to track this for the purpose of emitting // dependencies. If not, then it must have been defined in types.hal. // // In the case of just specifying Folder, the resolved type is // android.hardware.tests.foo@1.0::Folder, and the same logic as // above applies. if (!resolvedType->isInterface()) { FQName ifc = resolvedName.getTopLevelType(); for (const auto &importedAST : mImportedASTs) { FQName matchingName; Type *match = importedAST->findDefinedType(ifc, &matchingName); if (match != nullptr && match->isInterface()) { resolvedType = match; } } } if (!resolvedType->isInterface()) { // Non-interface types are declared in the associated types header. FQName typesName = resolvedName.getTypesForPackage(); mImportedNames.insert(typesName); } else { // Do _not_ use fqName, i.e. the name we used to look up the type, // but instead use the name of the interface we found. // This is necessary because if fqName pointed to a typedef which // in turn referenced the found interface we'd mistakenly use the // name of the typedef instead of the proper name of the interface. const FQName &typeName = static_cast(resolvedType)->fqName(); mImportedNames.insert(typeName); } } return returnedType; } void AST::addToImportedNamesGranular(const FQName &fqName) { if (fqName.package() == package().package() && fqName.version() == package().version()) { // Our own names are _defined_ here, not imported. return; } mImportedNamesGranular.insert(fqName); } Type *AST::findDefinedType(const FQName &fqName, FQName *matchingName) const { for (const auto &pair : mDefinedTypesByFullName) { const FQName &key = pair.first; Type* type = pair.second; if (key.endsWith(fqName)) { *matchingName = key; return type; } } return nullptr; } const std::vector& AST::getImportStatements() const { return mImportStatements; } void AST::getImportedPackages(std::set *importSet) const { for (const auto& fqName : mImportedNamesGranular) { FQName packageName = fqName.getPackageAndVersion(); if (packageName == mPackage) { // We only care about external imports, not our own package. continue; } importSet->insert(packageName); } } void AST::getImportedPackagesHierarchy(std::set *importSet) const { getImportedPackages(importSet); std::set newSet; for (const auto &ast : mImportedASTs) { if (importSet->find(ast->package()) != importSet->end()) { ast->getImportedPackagesHierarchy(&newSet); } } importSet->insert(newSet.begin(), newSet.end()); } void AST::getAllImportedNames(std::set *allImportNames) const { for (const auto& name : mImportedNames) { allImportNames->insert(name); AST* ast = mCoordinator->parse(name, nullptr /* imported */, Coordinator::Enforce::NONE); ast->getAllImportedNames(allImportNames); } } void AST::getAllImportedNamesGranular(std::set *allImportNames) const { for (const auto& fqName : mImportedNamesGranular) { if (fqName.name() == "types") { // A package will export everything _defined_ but will not // re-export anything it itself imported. AST* ast = mCoordinator->parse( fqName, nullptr /* imported */, Coordinator::Enforce::NONE); // imported names must have already been validated CHECK(ast != nullptr) << fqName.string(); ast->addDefinedTypes(allImportNames); } else { allImportNames->insert(fqName); } } } bool AST::isJavaCompatible() const { return mRootScope.isJavaCompatible(); } void AST::appendToExportedTypesVector( std::vector *exportedTypes) const { mRootScope.appendToExportedTypesVector(exportedTypes); } bool AST::isIBase() const { Interface* iface = mRootScope.getInterface(); return iface != nullptr && iface->isIBase(); } const Interface *AST::getInterface() const { return mRootScope.getInterface(); } std::string AST::getBaseName() const { const Interface* iface = mRootScope.getInterface(); return iface ? iface->getBaseName() : "types"; } void AST::addDefinedTypes(std::set *definedTypes) const { std::for_each( mDefinedTypesByFullName.begin(), mDefinedTypesByFullName.end(), [definedTypes](const auto &elem) { if (!elem.second->isTypeDef()) { definedTypes->insert(elem.first); } }); } void AST::addReferencedTypes(std::set *referencedTypes) const { std::for_each( mReferencedTypeNames.begin(), mReferencedTypeNames.end(), [referencedTypes](const auto &fqName) { referencedTypes->insert(fqName); }); } bool AST::addMethod(Method* method, Interface* iface) { if (iface->isIBase()) { if (!mAllReservedMethods.emplace(method->name(), method).second) { std::cerr << "ERROR: hidl-gen encountered duplicated reserved method " << method->name() << std::endl; return false; } // methods will be added to iface in addAllReservedMethodsToInterface return true; } iface->addUserDefinedMethod(method); return true; } bool AST::addAllReservedMethodsToInterface(Interface* iface) { std::map allReservedMethods(mAllReservedMethods); // Looking for the IBase AST which is imported for all interfaces that are not IBase for (const AST* importedAST : mImportedASTs) { allReservedMethods.insert(importedAST->mAllReservedMethods.begin(), importedAST->mAllReservedMethods.end()); } return iface->addAllReservedMethods(allReservedMethods); } void AST::setHeader(const DocComment* header) { mHeader = header; } const DocComment* AST::getHeader() const { return mHeader; } void AST::addUnhandledComment(const DocComment* docComment) { if (docComment != nullptr) mUnhandledComments.push_back(docComment); } const std::vector AST::getUnhandledComments() const { return mUnhandledComments; } } // namespace android;