536 lines
17 KiB
C++
536 lines
17 KiB
C++
/*
|
|
* 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;
|