You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
536 lines
17 KiB
536 lines
17 KiB
/* |
|
* 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 "HandleType.h" |
|
#include "Interface.h" |
|
#include "Location.h" |
|
#include "FmqType.h" |
|
#include "Scope.h" |
|
#include "TypeDef.h" |
|
|
|
#include <android-base/logging.h> |
|
#include <hidl-util/FQName.h> |
|
#include <hidl-util/Formatter.h> |
|
#include <hidl-util/StringHelper.h> |
|
#include <stdlib.h> |
|
#include <algorithm> |
|
#include <iostream> |
|
|
|
namespace android { |
|
|
|
AST::AST(const Coordinator* coordinator, const std::string& path) |
|
: mCoordinator(coordinator), |
|
mPath(path), |
|
mRootScope("(root scope)", Location::startOf(path), nullptr /* parent */) {} |
|
|
|
Scope* AST::getRootScope() { |
|
return &mRootScope; |
|
} |
|
|
|
// used by the parser. |
|
void AST::addSyntaxError() { |
|
mSyntaxErrors++; |
|
} |
|
|
|
size_t AST::syntaxErrors() const { |
|
return mSyntaxErrors; |
|
} |
|
|
|
const std::string &AST::getFilename() const { |
|
return mPath; |
|
} |
|
|
|
bool AST::setPackage(const char *package) { |
|
mPackage.setTo(package); |
|
CHECK(mPackage.isValid()); |
|
|
|
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::containsInterfaces() const { |
|
return mRootScope.containsInterfaces(); |
|
} |
|
|
|
bool AST::addImport(const char *import) { |
|
FQName fqName(import); |
|
CHECK(fqName.isValid()); |
|
|
|
fqName.applyDefaults(mPackage.package(), mPackage.version()); |
|
|
|
// LOG(INFO) << "importing " << fqName.string(); |
|
|
|
if (fqName.name().empty()) { |
|
// import a package |
|
std::vector<FQName> 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; |
|
} |
|
// all previous single type imports are ignored. |
|
mImportedTypes.erase(ast); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
AST *importAST; |
|
|
|
// 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. |
|
importAST = mCoordinator->parse(interfaceName, &mImportedASTs, Coordinator::Enforce::NONE); |
|
|
|
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); |
|
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); |
|
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); |
|
return true; |
|
} |
|
|
|
// can't find an appropriate AST for fqName. |
|
return false; |
|
} |
|
|
|
void AST::addImportedAST(AST *ast) { |
|
mImportedASTs.insert(ast); |
|
} |
|
|
|
bool AST::addTypeDef(const char* localName, Type* type, const Location& location, |
|
std::string* errorMsg, Scope* scope) { |
|
// The reason we wrap the given type in a TypeDef is simply to suppress |
|
// emitting any type definitions later on, since this is just an alias |
|
// to a type defined elsewhere. |
|
return addScopedTypeInternal(new TypeDef(localName, location, scope, type), errorMsg, scope); |
|
} |
|
|
|
bool AST::addScopedType(NamedType* type, std::string* errorMsg, Scope* scope) { |
|
return addScopedTypeInternal(type, errorMsg, scope); |
|
} |
|
|
|
bool AST::addScopedTypeInternal(NamedType* type, std::string* errorMsg, Scope* scope) { |
|
bool success = scope->addType(type, errorMsg); |
|
if (!success) { |
|
return false; |
|
} |
|
|
|
std::vector<std::string> pathComponents{{type->localName()}}; |
|
for (; scope != &mRootScope; scope = scope->parent()) { |
|
pathComponents.push_back(scope->localName()); |
|
} |
|
|
|
std::reverse(pathComponents.begin(), pathComponents.end()); |
|
std::string path = StringHelper::JoinStrings(pathComponents, "."); |
|
|
|
FQName fqName(mPackage.package(), mPackage.version(), path); |
|
type->setFullName(fqName); |
|
|
|
mDefinedTypesByFullName[fqName] = type; |
|
|
|
return true; |
|
} |
|
|
|
EnumValue* AST::lookupEnumValue(const FQName& fqName, std::string* errorMsg, Scope* scope) { |
|
FQName enumTypeName = fqName.typeName(); |
|
std::string enumValueName = fqName.valueName(); |
|
|
|
CHECK(enumTypeName.isValid()); |
|
CHECK(!enumValueName.empty()); |
|
|
|
Type* type = lookupType(enumTypeName, scope); |
|
if(type == nullptr) { |
|
*errorMsg = "Cannot find type " + enumTypeName.string(); |
|
return nullptr; |
|
} |
|
if(!type->isEnum()) { |
|
*errorMsg = "Type " + enumTypeName.string() + " is not an enum type"; |
|
return nullptr; |
|
} |
|
|
|
EnumType *enumType = static_cast<EnumType *>(type); |
|
EnumValue *v = static_cast<EnumValue *>(enumType->lookupIdentifier(enumValueName)); |
|
if(v == nullptr) { |
|
*errorMsg = "Enum type " + enumTypeName.string() + " does not have " + enumValueName; |
|
return nullptr; |
|
} |
|
return v; |
|
} |
|
|
|
Type* AST::lookupType(const FQName& fqName, Scope* scope) { |
|
CHECK(fqName.isValid()); |
|
|
|
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; |
|
} |
|
} |
|
|
|
if (!fqName.isFullyQualified()) { |
|
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, 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) { |
|
// Resolve typeDefs to the target type. |
|
while (type->isTypeDef()) { |
|
type = static_cast<TypeDef *>(type)->referencedType(); |
|
} |
|
|
|
return type; |
|
} |
|
} |
|
|
|
return nullptr; |
|
} |
|
|
|
// Rule 1: auto-fill with current package |
|
status_t AST::lookupAutofilledType(const FQName &fqName, Type **returnedType) { |
|
CHECK(!fqName.isFullyQualified() && !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 = lookupType(autofilled, nullptr /* scope */); |
|
|
|
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<Type *> 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) { |
|
#if 0 |
|
LOG(INFO) << "found '" |
|
<< resolvedName.string() |
|
<< "' after looking for '" |
|
<< fqName.string() |
|
<< "'."; |
|
#endif |
|
|
|
// Resolve typeDefs to the target type. |
|
while (resolvedType->isTypeDef()) { |
|
resolvedType = |
|
static_cast<TypeDef *>(resolvedType)->referencedType(); |
|
} |
|
|
|
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. |
|
|
|
mImportedNames.insert( |
|
static_cast<Interface *>(resolvedType)->fqName()); |
|
} |
|
} |
|
|
|
return returnedType; |
|
} |
|
|
|
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; |
|
} |
|
|
|
void AST::getImportedPackages(std::set<FQName> *importSet) const { |
|
for (const auto &fqName : mImportedNames) { |
|
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<FQName> *importSet) const { |
|
getImportedPackages(importSet); |
|
std::set<FQName> 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<FQName> *allImportNames) const { |
|
for (const auto& name : mImportedNames) { |
|
allImportNames->insert(name); |
|
AST* ast = mCoordinator->parse(name, nullptr /* imported */, Coordinator::Enforce::NONE); |
|
ast->getAllImportedNames(allImportNames); |
|
} |
|
} |
|
|
|
bool AST::isJavaCompatible() const { |
|
if (!AST::isInterface()) { |
|
for (const auto* type : mRootScope.getSubTypes()) { |
|
if (!type->isJavaCompatible()) { |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
const Interface* iface = mRootScope.getInterface(); |
|
return iface->isJavaCompatible(); |
|
} |
|
|
|
void AST::appendToExportedTypesVector( |
|
std::vector<const Type *> *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"; |
|
} |
|
|
|
} // namespace android;
|
|
|