[automerger skipped] Merge "Zero hidl-generated structs" into oc-dr1-dev am: c62f78a630 -s ours
am: 205dd6ed92 -s ours
am skip reason: change_id I8fb10532b6fdf9afa419f19fc634981e7cbd3c10 with SHA1 da1d449882 is in history
Change-Id: I7ad06c8e0212e5fbb5b56ea2c707552a1a7b4771
diff --git a/AST.cpp b/AST.cpp
index e33fbbf..cb260e2 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -18,10 +18,10 @@
#include "Coordinator.h"
#include "EnumType.h"
+#include "FmqType.h"
#include "HandleType.h"
#include "Interface.h"
#include "Location.h"
-#include "FmqType.h"
#include "Scope.h"
#include "TypeDef.h"
@@ -35,10 +35,11 @@
namespace android {
-AST::AST(const Coordinator* coordinator, const std::string& path)
+AST::AST(const Coordinator* coordinator, const Hash* fileHash)
: mCoordinator(coordinator),
- mPath(path),
- mRootScope("(root scope)", Location::startOf(path), nullptr /* parent */) {}
+ mFileHash(fileHash),
+ mRootScope("(root scope)", FQName(), Location::startOf(fileHash->getPath()),
+ nullptr /* parent */) {}
Scope* AST::getRootScope() {
return &mRootScope;
@@ -53,13 +54,17 @@
return mSyntaxErrors;
}
-const std::string &AST::getFilename() const {
- return mPath;
+const std::string& AST::getFilename() const {
+ return mFileHash->getPath();
+}
+const Hash* AST::getFileHash() const {
+ return mFileHash;
}
bool AST::setPackage(const char *package) {
- mPackage.setTo(package);
- CHECK(mPackage.isValid());
+ if (!mPackage.setTo(package)) {
+ return false;
+ }
if (mPackage.package().empty()
|| mPackage.version().empty()
@@ -82,16 +87,235 @@
return mRootScope.containsInterfaces();
}
+status_t AST::postParse() {
+ status_t err;
+
+ // lookupTypes is the first pass.
+ err = lookupTypes();
+ 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 = lookupLocalIdentifiers();
+ if (err != OK) return err;
+ // checkAcyclicConstantExpressions is after resolveInheritance,
+ // as resolveInheritance autofills enum values.
+ err = checkAcyclicConstantExpressions();
+ if (err != OK) return err;
+ err = evaluate();
+ 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 */);
+ std::unordered_set<const Type*> visited;
+ mRootScope.recursivePass(
+ [](Type* type) {
+ type->setPostParseCompleted();
+ return OK;
+ },
+ &visited);
+
+ return OK;
+}
+
+status_t AST::constantExpressionRecursivePass(
+ const std::function<status_t(ConstantExpression*)>& func, bool processBeforeDependencies) {
+ std::unordered_set<const Type*> visitedTypes;
+ std::unordered_set<const ConstantExpression*> visitedCE;
+ return mRootScope.recursivePass(
+ [&](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::lookupTypes() {
+ std::unordered_set<const Type*> visited;
+ return mRootScope.recursivePass(
+ [&](Type* type) -> status_t {
+ Scope* scope = type->isScope() ? static_cast<Scope*>(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() << "\n";
+ return UNKNOWN_ERROR;
+ }
+ nextRef->set(nextType);
+ }
+
+ return OK;
+ },
+ &visited);
+}
+
+status_t AST::gatherReferencedTypes() {
+ std::unordered_set<const Type*> visited;
+ return mRootScope.recursivePass(
+ [&](Type* type) -> status_t {
+ for (auto* nextRef : type->getReferences()) {
+ const Type *targetType = nextRef->get();
+ if (targetType->isNamedType()) {
+ mReferencedTypeNames.insert(
+ static_cast<const NamedType *>(targetType)->fqName());
+ }
+ }
+
+ return OK;
+ },
+ &visited);
+}
+
+status_t AST::lookupLocalIdentifiers() {
+ std::unordered_set<const Type*> visitedTypes;
+ std::unordered_set<const ConstantExpression*> visitedCE;
+
+ return mRootScope.recursivePass(
+ [&](Type* type) -> status_t {
+ Scope* scope = type->isScope() ? static_cast<Scope*>(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);
+ }
+ return OK;
+ },
+ &visitedCE, true /* processBeforeDependencies */);
+ if (err != OK) return err;
+ }
+
+ return OK;
+ },
+ &visitedTypes);
+}
+
+status_t AST::validateDefinedTypesUniqueNames() const {
+ std::unordered_set<const Type*> visited;
+ return mRootScope.recursivePass(
+ [&](const Type* type) -> status_t {
+ // We only want to validate type definition names in this place.
+ if (type->isScope()) {
+ return static_cast<const Scope*>(type)->validateUniqueNames();
+ }
+ return OK;
+ },
+ &visited);
+}
+
+status_t AST::resolveInheritance() {
+ std::unordered_set<const Type*> visited;
+ return mRootScope.recursivePass(&Type::resolveInheritance, &visited);
+}
+
+status_t AST::evaluate() {
+ return constantExpressionRecursivePass(
+ [](ConstantExpression* ce) {
+ ce->evaluate();
+ return OK;
+ },
+ false /* processBeforeDependencies */);
+}
+
+status_t AST::validate() const {
+ std::unordered_set<const Type*> visited;
+ return mRootScope.recursivePass(&Type::validate, &visited);
+}
+
+status_t AST::topologicalReorder() {
+ std::unordered_map<const Type*, size_t> reversedOrder;
+ std::unordered_set<const Type*> stack;
+ status_t err = mRootScope.topologicalOrder(&reversedOrder, &stack).status;
+ if (err != OK) return err;
+
+ std::unordered_set<const Type*> visited;
+ mRootScope.recursivePass(
+ [&](Type* type) {
+ if (type->isScope()) {
+ static_cast<Scope*>(type)->topologicalReorder(reversedOrder);
+ }
+ return OK;
+ },
+ &visited);
+ return OK;
+}
+
+status_t AST::checkAcyclicConstantExpressions() const {
+ std::unordered_set<const Type*> visitedTypes;
+ std::unordered_set<const ConstantExpression*> visitedCE;
+ std::unordered_set<const ConstantExpression*> stack;
+ return mRootScope.recursivePass(
+ [&](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<const Type*> visited;
+ return mRootScope.recursivePass(
+ [](const Type* type) -> status_t {
+ for (const Reference<Type>* ref : type->getReferences()) {
+ status_t err = type->checkForwardReferenceRestrictions(*ref);
+ if (err != OK) return err;
+ }
+ return OK;
+ },
+ &visited);
+}
+
bool AST::addImport(const char *import) {
- FQName fqName(import);
- CHECK(fqName.isValid());
+ 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());
- // LOG(INFO) << "importing " << fqName.string();
-
if (fqName.name().empty()) {
// import a package
+
std::vector<FQName> packageInterfaces;
status_t err =
@@ -103,6 +327,8 @@
}
for (const auto &subFQName : packageInterfaces) {
+ addToImportedNamesGranular(subFQName);
+
// Do not enforce restrictions on imports.
AST* ast = mCoordinator->parse(subFQName, &mImportedASTs, Coordinator::Enforce::NONE);
if (ast == nullptr) {
@@ -115,7 +341,7 @@
return true;
}
- AST *importAST;
+ addToImportedNamesGranular(fqName);
// cases like android.hardware.foo@1.0::IFoo.Internal
// android.hardware.foo@1.0::Abc.Internal
@@ -123,7 +349,11 @@
// 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);
+ 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
@@ -175,25 +405,8 @@
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()}};
+FQName AST::makeFullName(const char* localName, Scope* scope) const {
+ std::vector<std::string> pathComponents{{localName}};
for (; scope != &mRootScope; scope = scope->parent()) {
pathComponents.push_back(scope->localName());
}
@@ -201,12 +414,34 @@
std::reverse(pathComponents.begin(), pathComponents.end());
std::string path = StringHelper::JoinStrings(pathComponents, ".");
- FQName fqName(mPackage.package(), mPackage.version(), path);
- type->setFullName(fqName);
+ return FQName(mPackage.package(), mPackage.version(), path);
+}
- mDefinedTypesByFullName[fqName] = type;
+void AST::addScopedType(NamedType* type, Scope* scope) {
+ scope->addType(type);
+ mDefinedTypesByFullName[type->fqName()] = type;
+}
- return true;
+LocalIdentifier* AST::lookupLocalIdentifier(const Reference<LocalIdentifier>& ref, 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, Scope* scope) {
@@ -221,6 +456,7 @@
*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;
@@ -232,6 +468,9 @@
*errorMsg = "Enum type " + enumTypeName.string() + " does not have " + enumValueName;
return nullptr;
}
+
+ mReferencedTypeNames.insert(enumType->fqName());
+
return v;
}
@@ -253,14 +492,12 @@
}
}
- if (!fqName.isFullyQualified()) {
- status_t status = lookupAutofilledType(fqName, &returnedType);
- if (status != OK) {
- return nullptr;
- }
- if (returnedType != nullptr) {
- return returnedType;
- }
+ status_t status = lookupAutofilledType(fqName, &returnedType);
+ if (status != OK) {
+ return nullptr;
+ }
+ if (returnedType != nullptr) {
+ return returnedType;
}
return lookupTypeFromImports(fqName);
@@ -273,13 +510,7 @@
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;
}
}
@@ -289,7 +520,7 @@
// 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());
+ CHECK(!fqName.name().empty() && fqName.valueName().empty());
FQName autofilled = fqName;
autofilled.applyDefaults(mPackage.package(), mPackage.version());
@@ -298,7 +529,7 @@
// in import.
Type *local = findDefinedType(autofilled, &matchingName);
CHECK(local == nullptr || autofilled == matchingName);
- Type* fromImport = lookupType(autofilled, nullptr /* scope */);
+ Type* fromImport = lookupTypeFromImports(autofilled);
if (local != nullptr && fromImport != nullptr && local != fromImport) {
// Something bad happen; two types have the same FQName.
@@ -382,20 +613,6 @@
}
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
@@ -444,14 +661,26 @@
// 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());
+ const FQName &typeName =
+ static_cast<Interface *>(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;
@@ -467,7 +696,7 @@
}
void AST::getImportedPackages(std::set<FQName> *importSet) const {
- for (const auto &fqName : mImportedNames) {
+ for (const auto& fqName : mImportedNamesGranular) {
FQName packageName = fqName.getPackageAndVersion();
if (packageName == mPackage) {
@@ -481,6 +710,7 @@
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()) {
@@ -498,19 +728,23 @@
}
}
-bool AST::isJavaCompatible() const {
- if (!AST::isInterface()) {
- for (const auto* type : mRootScope.getSubTypes()) {
- if (!type->isJavaCompatible()) {
- return false;
- }
+void AST::getAllImportedNamesGranular(std::set<FQName> *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);
+
+ ast->addDefinedTypes(allImportNames);
+ } else {
+ allImportNames->insert(fqName);
}
-
- return true;
}
+}
- const Interface* iface = mRootScope.getInterface();
- return iface->isJavaCompatible();
+bool AST::isJavaCompatible() const {
+ return mRootScope.isJavaCompatible();
}
void AST::appendToExportedTypesVector(
@@ -533,4 +767,24 @@
return iface ? iface->getBaseName() : "types";
}
+void AST::addDefinedTypes(std::set<FQName> *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<FQName> *referencedTypes) const {
+ std::for_each(
+ mReferencedTypeNames.begin(),
+ mReferencedTypeNames.end(),
+ [referencedTypes](const auto &fqName) {
+ referencedTypes->insert(fqName);
+ });
+}
+
} // namespace android;
diff --git a/AST.h b/AST.h
index 6ef0ed3..4de98bd 100644
--- a/AST.h
+++ b/AST.h
@@ -19,7 +19,9 @@
#define AST_H_
#include <android-base/macros.h>
+#include <hidl-hash/Hash.h>
#include <hidl-util/FQName.h>
+#include <functional>
#include <map>
#include <set>
#include <string>
@@ -31,16 +33,19 @@
namespace android {
struct Coordinator;
+struct ConstantExpression;
+struct EnumValue;
struct Formatter;
struct Interface;
struct Location;
struct Method;
struct NamedType;
-struct TypedVar;
-struct EnumValue;
+template <class T>
+struct NamedReference;
+struct Type;
struct AST {
- AST(const Coordinator *coordinator, const std::string &path);
+ AST(const Coordinator* coordinator, const Hash* fileHash);
bool setPackage(const char *package);
bool addImport(const char *import);
@@ -50,14 +55,17 @@
bool isInterface() const;
bool containsInterfaces() const;
- // Returns true iff successful.
- bool addTypeDef(const char* localName, Type* type, const Location& location,
- std::string* errorMsg, Scope* scope);
+ // Adds package, version and scope stack to local name
+ FQName makeFullName(const char* localName, Scope* scope) const;
- // Returns true iff successful.
- bool addScopedType(NamedType* type, std::string* errorMsg, Scope* scope);
+ void addScopedType(NamedType* type, Scope* scope);
- const std::string &getFilename() const;
+ const std::string& getFilename() const;
+ const Hash* getFileHash() const;
+
+ // Look up local identifier.
+ // It could be plain identifier or enum value as described by lookupEnumValue.
+ LocalIdentifier* lookupLocalIdentifier(const Reference<LocalIdentifier>& ref, Scope* scope);
// Look up an enum value by "FQName:valueName".
EnumValue* lookupEnumValue(const FQName& fqName, std::string* errorMsg, Scope* scope);
@@ -69,20 +77,66 @@
void addImportedAST(AST *ast);
- status_t generateCpp(const std::string &outputPath) const;
- status_t generateCppHeaders(const std::string &outputPath) const;
- status_t generateCppSources(const std::string &outputPath) const;
- status_t generateCppImpl(const std::string &outputPath) const;
- status_t generateStubImplHeader(const std::string& outputPath) const;
- status_t generateStubImplSource(const std::string& outputPath) const;
+ // Calls all passes after parsing required before
+ // being ready to generate output.
+ status_t postParse();
- status_t generateJava(
- const std::string &outputPath,
- const std::string &limitToType) const;
+ // Recursive pass on constant expression tree
+ status_t constantExpressionRecursivePass(
+ const std::function<status_t(ConstantExpression*)>& func, bool processBeforeDependencies);
- status_t generateJavaTypes(
- const std::string &outputPath,
- const std::string &limitToType) const;
+ // Recursive tree pass that looks up all referenced types
+ status_t lookupTypes();
+
+ // Recursive tree pass that looks up all referenced local identifiers
+ status_t lookupLocalIdentifiers();
+
+ // Recursive tree pass that validates that all defined types
+ // have unique names in their scopes.
+ status_t validateDefinedTypesUniqueNames() const;
+
+ // Recursive tree pass that completes type declarations
+ // that depend on super types
+ status_t resolveInheritance();
+
+ // Recursive tree pass that evaluates constant expressions
+ status_t evaluate();
+
+ // Recursive tree pass that validates all type-related
+ // syntax restrictions
+ status_t validate() const;
+
+ // Recursive tree pass that ensures that type definitions and references
+ // are acyclic and reorderes type definitions in reversed topological order.
+ status_t topologicalReorder();
+
+ // Recursive tree pass that ensures that constant expressions
+ // are acyclic.
+ status_t checkAcyclicConstantExpressions() const;
+
+ // Recursive tree pass that checks C++ forward declaration restrictions.
+ status_t checkForwardReferenceRestrictions() const;
+
+ status_t gatherReferencedTypes();
+
+ void generateCppSource(Formatter& out) const;
+
+ void generateInterfaceHeader(Formatter& out) const;
+ void generateHwBinderHeader(Formatter& out) const;
+ void generateStubHeader(Formatter& out) const;
+ void generateProxyHeader(Formatter& out) const;
+ void generatePassthroughHeader(Formatter& out) const;
+
+ void generateCppImplHeader(Formatter& out) const;
+ void generateCppImplSource(Formatter& out) const;
+
+ void generateCppAdapterHeader(Formatter& out) const;
+ void generateCppAdapterSource(Formatter& out) const;
+
+ void generateJava(Formatter& out, const std::string& limitToType) const;
+ void generateJavaTypes(Formatter& out, const std::string& limitToType) const;
+
+ void generateVts(Formatter& out) const;
void getImportedPackages(std::set<FQName> *importSet) const;
@@ -90,19 +144,25 @@
// each AST in each package referenced in importSet.
void getImportedPackagesHierarchy(std::set<FQName> *importSet) const;
- status_t generateVts(const std::string &outputPath) const;
-
bool isJavaCompatible() const;
- // Return the set of FQNames for those interfaces and types that are
- // actually referenced in the AST, not merely imported.
- const std::set<FQName>& getImportedNames() const {
- return mImportedNames;
- }
-
- // Get transitive closure of imported interface/types.
+ // Warning: this only includes names explicitly referenced in code.
+ // It does not include all names which are imported.
+ //
+ // Currently, there is one valid usecase for this: importing exactly
+ // the names which need to be imported in generated code. If you import
+ // based on getAllImportedNamesGranular instead, you will import things
+ // that aren't actually used in the resultant code.
+ //
+ // Get transitive closure of imported interface/types. This will add
+ // everything exported by a package even if only a single type from
+ // that package was explicitly imported!
void getAllImportedNames(std::set<FQName> *allImportSet) const;
+ // Get imported types, this includes those explicitly imported as well
+ // as all types defined in imported packages.
+ void getAllImportedNamesGranular(std::set<FQName> *allImportSet) const;
+
void appendToExportedTypesVector(
std::vector<const Type *> *exportedTypes) const;
@@ -120,9 +180,17 @@
Scope* getRootScope();
+ static void generateCppPackageInclude(Formatter& out, const FQName& package,
+ const std::string& klass);
+
+ void addDefinedTypes(std::set<FQName> *definedTypes) const;
+ void addReferencedTypes(std::set<FQName> *referencedTypes) const;
+
+ void addToImportedNamesGranular(const FQName &fqName);
+
private:
- const Coordinator *mCoordinator;
- std::string mPath;
+ const Coordinator* mCoordinator;
+ const Hash* mFileHash;
RootScope mRootScope;
@@ -130,8 +198,16 @@
// A set of all external interfaces/types that are _actually_ referenced
// in this AST, this is a subset of those specified in import statements.
+ // Note that this set only resolves to the granularity of either an
+ // interface type or a whole package.
std::set<FQName> mImportedNames;
+ // This is the set of actually imported types.
+ std::set<FQName> mImportedNamesGranular;
+
+ // Warning: this only includes names explicitly referenced in code.
+ // It does not include all names which are imported.
+ //
// A set of all ASTs we explicitly or implicitly (types.hal) import.
std::set<AST *> mImportedASTs;
@@ -147,7 +223,7 @@
// used by the parser.
size_t mSyntaxErrors = 0;
- bool addScopedTypeInternal(NamedType* type, std::string* errorMsg, Scope* scope);
+ std::set<FQName> mReferencedTypeNames;
// Helper functions for lookupType.
Type* lookupTypeLocally(const FQName& fqName, Scope* scope);
@@ -164,65 +240,47 @@
void getPackageAndVersionComponents(
std::vector<std::string> *components, bool cpp_compatible) const;
- static void generateCppPackageInclude(
- Formatter &out,
- const FQName &package,
- const std::string &klass);
-
std::string makeHeaderGuard(const std::string &baseName,
bool indicateGenerated = true) const;
void enterLeaveNamespace(Formatter &out, bool enter) const;
static void generateCheckNonNull(Formatter &out, const std::string &nonNull);
- status_t generateInterfaceHeader(const std::string &outputPath) const;
- status_t generateHwBinderHeader(const std::string &outputPath) const;
- status_t generateStubHeader(const std::string &outputPath) const;
- status_t generateProxyHeader(const std::string &outputPath) const;
- status_t generatePassthroughHeader(const std::string &outputPath) const;
-
- status_t generateTypeSource(
- Formatter &out, const std::string &ifaceName) const;
+ void generateTypeSource(Formatter& out, const std::string& ifaceName) const;
// a method, and in which interface is it originally defined.
// be careful of the case where method.isHidlReserved(), where interface
// is effectively useless.
- using MethodGenerator = std::function<status_t(const Method *, const Interface *)>;
+ using MethodGenerator = std::function<void(const Method*, const Interface*)>;
void generateTemplatizationLink(Formatter& out) const;
+ void generateCppTag(Formatter& out, const std::string& tag) const;
- status_t generateMethods(Formatter &out, MethodGenerator gen, bool includeParents = true) const;
- status_t generateStubImplMethod(Formatter &out,
- const std::string &className,
- const Method *method) const;
- status_t generatePassthroughMethod(Formatter &out,
- const Method *method) const;
- status_t generateStaticProxyMethodSource(Formatter &out,
- const std::string &className,
- const Method *method) const;
- status_t generateProxyMethodSource(Formatter &out,
- const std::string &className,
- const Method *method,
- const Interface *superInterface) const;
+ void generateMethods(Formatter& out, const MethodGenerator& gen,
+ bool includeParents = true) const;
+ void generateStubImplMethod(Formatter& out, const std::string& className,
+ const Method* method) const;
+ void generatePassthroughMethod(Formatter& out, const Method* method) const;
+ void generateStaticProxyMethodSource(Formatter& out, const std::string& className,
+ const Method* method) const;
+ void generateProxyMethodSource(Formatter& out, const std::string& className,
+ const Method* method, const Interface* superInterface) const;
+ void generateAdapterMethod(Formatter& out, const Method* method) const;
void generateFetchSymbol(Formatter &out, const std::string &ifaceName) const;
- status_t generateProxySource(
- Formatter &out, const FQName &fqName) const;
+ void generateProxySource(Formatter& out, const FQName& fqName) const;
- status_t generateStubSource(
- Formatter &out, const Interface *iface) const;
+ void generateStubSource(Formatter& out, const Interface* iface) const;
- status_t generateStubSourceForMethod(Formatter &out,
- const Method *method,
- const Interface *superInterface) const;
- status_t generateStaticStubMethodSource(Formatter &out,
- const std::string &className,
- const Method *method) const;
+ void generateStubSourceForMethod(Formatter& out, const Method* method,
+ const Interface* superInterface) const;
+ void generateStaticStubMethodSource(Formatter& out, const FQName& fqName,
+ const Method* method) const;
- status_t generatePassthroughSource(Formatter &out) const;
+ void generatePassthroughSource(Formatter& out) const;
- status_t generateInterfaceSource(Formatter &out) const;
+ void generateInterfaceSource(Formatter& out) const;
enum InstrumentationEvent {
SERVER_API_ENTRY = 0,
@@ -247,39 +305,24 @@
InstrumentationEvent event,
const Method *method) const;
- void declareCppReaderLocals(
- Formatter &out,
- const std::vector<TypedVar *> &arg,
- bool forResults) const;
+ void declareCppReaderLocals(Formatter& out, const std::vector<NamedReference<Type>*>& arg,
+ bool forResults) const;
- void emitCppReaderWriter(
- Formatter &out,
- const std::string &parcelObj,
- bool parcelObjIsPointer,
- const TypedVar *arg,
- bool isReader,
- Type::ErrorMode mode,
- bool addPrefixToName) const;
+ void emitCppReaderWriter(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer,
+ const NamedReference<Type>* arg, bool isReader, Type::ErrorMode mode,
+ bool addPrefixToName) const;
- void emitCppResolveReferences(
- Formatter &out,
- const std::string &parcelObj,
- bool parcelObjIsPointer,
- const TypedVar *arg,
- bool isReader,
- Type::ErrorMode mode,
- bool addPrefixToName) const;
+ void emitCppResolveReferences(Formatter& out, const std::string& parcelObj,
+ bool parcelObjIsPointer, const NamedReference<Type>* arg,
+ bool isReader, Type::ErrorMode mode, bool addPrefixToName) const;
- void emitJavaReaderWriter(
- Formatter &out,
- const std::string &parcelObj,
- const TypedVar *arg,
- bool isReader,
- bool addPrefixToName) const;
+ void emitJavaReaderWriter(Formatter& out, const std::string& parcelObj,
+ const NamedReference<Type>* arg, bool isReader,
+ bool addPrefixToName) const;
- status_t emitTypeDeclarations(Formatter &out) const;
- status_t emitJavaTypeDeclarations(Formatter &out) const;
- status_t emitVtsTypeDeclarations(Formatter &out) const;
+ void emitTypeDeclarations(Formatter& out) const;
+ void emitJavaTypeDeclarations(Formatter& out) const;
+ void emitVtsTypeDeclarations(Formatter& out) const;
DISALLOW_COPY_AND_ASSIGN(AST);
};
diff --git a/Android.bp b/Android.bp
index ef388fc..dd51705 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,18 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-subdirs = [
- "c2hal",
- "test",
- "utils",
-]
-
-hidl_flags = [
- "-O0",
- "-g",
- "-Wall",
- "-Werror",
-]
+cc_defaults {
+ name: "hidl-gen-defaults",
+ cpp_std: "experimental",
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Werror",
+ ],
+}
// This configuration is inherited by all hidl-gen-generated modules.
cc_defaults {
@@ -31,13 +29,22 @@
cflags: [
"-Wall",
"-Werror",
+ "-Wextra-semi",
],
product_variables: {
debuggable: {
cflags: ["-D__ANDROID_DEBUGGABLE__"]
},
- treble: {
- cflags: ["-D__ANDROID_TREBLE__"]
+ },
+}
+
+// This configuration is inherited by all hidl-gen-java modules
+java_defaults {
+ name: "hidl-java-module-defaults",
+ // TODO(b/68433855): allow HIDL java to build in the PDK
+ product_variables: {
+ pdk: {
+ enabled: false,
},
},
}
@@ -48,7 +55,7 @@
cc_library {
name: "libhidl-gen-hash",
host_supported: true,
- cflags: hidl_flags,
+ defaults: ["hidl-gen-defaults"],
srcs: ["Hash.cpp"],
local_include_dirs: ["include_hash/hidl-hash"],
export_include_dirs: ["include_hash"],
@@ -64,17 +71,19 @@
//
cc_library_host_shared {
name: "libhidl-gen",
- cflags: hidl_flags,
+ defaults: ["hidl-gen-defaults"],
srcs: [
"Annotation.cpp",
"ArrayType.cpp",
"CompoundType.cpp",
"ConstantExpression.cpp",
"DeathRecipientType.cpp",
+ "DocComment.cpp",
"EnumType.cpp",
"HandleType.cpp",
"HidlTypeAssertion.cpp",
"Interface.cpp",
+ "Location.cpp",
"MemoryType.cpp",
"Method.cpp",
"NamedType.cpp",
@@ -94,6 +103,11 @@
"libhidl-gen-hash",
"libhidl-gen-utils",
],
+ export_shared_lib_headers: [
+ "libbase",
+ "libhidl-gen-utils",
+ ],
+ export_include_dirs: ["."], // for tests
}
//
@@ -102,10 +116,11 @@
cc_library_host_shared {
name: "libhidl-gen-ast",
- cflags: hidl_flags,
+ defaults: ["hidl-gen-defaults"],
srcs: [
"Coordinator.cpp",
"generateCpp.cpp",
+ "generateCppAdapter.cpp",
"generateCppImpl.cpp",
"generateJava.cpp",
"generateVts.cpp",
@@ -120,6 +135,11 @@
"libhidl-gen-hash",
"libhidl-gen-utils",
],
+ export_shared_lib_headers: [
+ "libbase",
+ "libhidl-gen-utils",
+ ],
+ export_include_dirs: ["."], // for tests
}
//
@@ -127,7 +147,7 @@
//
cc_binary_host {
name: "hidl-gen",
- cflags: hidl_flags,
+ defaults: ["hidl-gen-defaults"],
srcs: ["main.cpp"],
shared_libs: [
"libbase",
diff --git a/Annotation.cpp b/Annotation.cpp
index a3e49f8..e5a66b0 100644
--- a/Annotation.cpp
+++ b/Annotation.cpp
@@ -19,35 +19,27 @@
#include <android-base/logging.h>
#include <hidl-util/Formatter.h>
#include <hidl-util/StringHelper.h>
+#include <algorithm>
#include <vector>
namespace android {
+AnnotationParam::AnnotationParam(const std::string& name) : mName(name) {}
-AnnotationParam::AnnotationParam(const std::string &name,
- std::vector<std::string> *values)
-: mName(name), mValues(values) {}
-
-AnnotationParam::AnnotationParam(const std::string &name,
- std::vector<ConstantExpression *> *values)
- : mName(name) {
- mValues = new std::vector<std::string>();
- for(ConstantExpression *ce : *values) {
- mValues->push_back(ce->value() + " /* " + ce->description() + " */");
- }
-}
-
-const std::string &AnnotationParam::getName() const {
+const std::string& AnnotationParam::getName() const {
return mName;
}
-const std::vector<std::string> *AnnotationParam::getValues() const {
- return mValues;
+std::vector<ConstantExpression*> AnnotationParam::getConstantExpressions() {
+ const auto& constRet = static_cast<const AnnotationParam*>(this)->getConstantExpressions();
+ std::vector<ConstantExpression*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+ return ret;
}
-const std::string &AnnotationParam::getSingleValue() const {
- CHECK_EQ(mValues->size(), 1u) << mName << " requires one values but has multiple";
- return mValues->at(0);
+std::vector<const ConstantExpression*> AnnotationParam::getConstantExpressions() const {
+ return {};
}
std::string AnnotationParam::getSingleString() const {
@@ -75,11 +67,53 @@
return false;
}
-Annotation::Annotation(const char *name,AnnotationParamVector *params)
- : mName(name),
- mParams(params) {
+StringAnnotationParam::StringAnnotationParam(const std::string& name,
+ std::vector<std::string>* values)
+ : AnnotationParam(name), mValues(values) {}
+
+std::vector<std::string> StringAnnotationParam::getValues() const {
+ return *mValues;
}
+std::string StringAnnotationParam::getSingleValue() const {
+ CHECK_EQ(mValues->size(), 1u) << mName << " requires one value but has multiple";
+ return mValues->at(0);
+}
+
+ConstantExpressionAnnotationParam::ConstantExpressionAnnotationParam(
+ const std::string& name, std::vector<ConstantExpression*>* values)
+ : AnnotationParam(name), mValues(values) {}
+
+std::string convertToString(const ConstantExpression* value) {
+ if (value->descriptionIsTrivial()) {
+ return value->value();
+ }
+ return value->value() + " /* " + value->description() + " */";
+}
+
+std::vector<std::string> ConstantExpressionAnnotationParam::getValues() const {
+ std::vector<std::string> ret;
+ for (const auto* value : *mValues) {
+ ret.push_back(convertToString(value));
+ };
+ return ret;
+}
+
+std::string ConstantExpressionAnnotationParam::getSingleValue() const {
+ CHECK_EQ(mValues->size(), 1u) << mName << " requires one value but has multiple";
+ return convertToString(mValues->at(0));
+}
+
+std::vector<const ConstantExpression*> ConstantExpressionAnnotationParam::getConstantExpressions()
+ const {
+ std::vector<const ConstantExpression*> ret;
+ ret.insert(ret.end(), mValues->begin(), mValues->end());
+ return ret;
+}
+
+Annotation::Annotation(const char* name, AnnotationParamVector* params)
+ : mName(name), mParams(params) {}
+
std::string Annotation::name() const {
return mName;
}
@@ -89,7 +123,7 @@
}
const AnnotationParam *Annotation::getParam(const std::string &name) const {
- for (auto *i: *mParams) {
+ for (const auto* i : *mParams) {
if (i->getName() == name) {
return i;
}
@@ -98,6 +132,23 @@
return nullptr;
}
+std::vector<ConstantExpression*> Annotation::getConstantExpressions() {
+ const auto& constRet = static_cast<const Annotation*>(this)->getConstantExpressions();
+ std::vector<ConstantExpression*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+ return ret;
+}
+
+std::vector<const ConstantExpression*> Annotation::getConstantExpressions() const {
+ std::vector<const ConstantExpression*> ret;
+ for (const auto* param : *mParams) {
+ const auto& retParam = param->getConstantExpressions();
+ ret.insert(ret.end(), retParam.begin(), retParam.end());
+ }
+ return ret;
+}
+
void Annotation::dump(Formatter &out) const {
out << "@" << mName;
@@ -116,14 +167,14 @@
out << param->getName() << "=";
- const std::vector<std::string> *values = param->getValues();
- if (values->size() > 1) {
+ const std::vector<std::string>& values = param->getValues();
+ if (values.size() > 1) {
out << "{";
}
- out << StringHelper::JoinStrings(*values, ", ");
+ out << StringHelper::JoinStrings(values, ", ");
- if (values->size() > 1) {
+ if (values.size() > 1) {
out << "}";
}
}
diff --git a/Annotation.h b/Annotation.h
index 18d9ab0..dfeb59e 100644
--- a/Annotation.h
+++ b/Annotation.h
@@ -21,6 +21,7 @@
#include <android-base/macros.h>
#include <map>
#include <string>
+#include <vector>
#include "ConstantExpression.h"
@@ -29,15 +30,12 @@
struct Formatter;
struct AnnotationParam {
- AnnotationParam(const std::string &name,
- std::vector<std::string> *values);
- AnnotationParam(const std::string &name,
- std::vector<ConstantExpression *> *values);
+ virtual ~AnnotationParam() {}
- const std::string &getName() const;
- const std::vector<std::string> *getValues() const;
+ const std::string& getName() const;
- const std::string &getSingleValue() const;
+ virtual std::vector<std::string> getValues() const = 0;
+ virtual std::string getSingleValue() const = 0;
/* Returns unquoted version of getSingleValue */
std::string getSingleString() const;
@@ -45,12 +43,39 @@
/* Returns value interpretted as a boolean */
bool getSingleBool() const;
-private:
+ std::vector<ConstantExpression*> getConstantExpressions();
+ virtual std::vector<const ConstantExpression*> getConstantExpressions() const;
+
+ protected:
const std::string mName;
- std::vector<std::string> *mValues;
+
+ AnnotationParam(const std::string& name);
};
-using AnnotationParamVector = std::vector<const AnnotationParam*>;
+struct StringAnnotationParam : AnnotationParam {
+ StringAnnotationParam(const std::string& name, std::vector<std::string>* values);
+
+ std::vector<std::string> getValues() const override;
+ std::string getSingleValue() const override;
+
+ private:
+ std::vector<std::string>* const mValues;
+};
+
+struct ConstantExpressionAnnotationParam : AnnotationParam {
+ ConstantExpressionAnnotationParam(const std::string& name,
+ std::vector<ConstantExpression*>* values);
+
+ std::vector<std::string> getValues() const override;
+ std::string getSingleValue() const override;
+
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ private:
+ std::vector<ConstantExpression*>* const mValues;
+};
+
+using AnnotationParamVector = std::vector<AnnotationParam*>;
struct Annotation {
Annotation(const char *name, AnnotationParamVector *params);
@@ -59,9 +84,12 @@
const AnnotationParamVector ¶ms() const;
const AnnotationParam *getParam(const std::string &name) const;
+ std::vector<ConstantExpression*> getConstantExpressions();
+ std::vector<const ConstantExpression*> getConstantExpressions() const;
+
void dump(Formatter &out) const;
-private:
+ private:
std::string mName;
AnnotationParamVector *mParams;
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 382665b..5155a27 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -16,26 +16,17 @@
#include "ArrayType.h"
-#include <hidl-util/Formatter.h>
#include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <iostream>
#include "ConstantExpression.h"
namespace android {
-ArrayType::ArrayType(ArrayType *srcArray, ConstantExpression *size)
- : mElementType(srcArray->mElementType),
- mSizes(srcArray->mSizes) {
- prependDimension(size);
-}
-
-ArrayType::ArrayType(Type *elementType, ConstantExpression *size)
- : mElementType(elementType) {
- prependDimension(size);
-}
-
-void ArrayType::prependDimension(ConstantExpression *size) {
- mSizes.insert(mSizes.begin(), size);
+ArrayType::ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent)
+ : Type(parent), mElementType(elementType), mSizes{size} {
+ CHECK(!elementType.isEmptyReference());
}
void ArrayType::appendDimension(ConstantExpression *size) {
@@ -50,12 +41,12 @@
return true;
}
-bool ArrayType::canCheckEquality() const {
- return mElementType->canCheckEquality();
+bool ArrayType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
+ return mElementType->canCheckEquality(visited);
}
-Type *ArrayType::getElementType() const {
- return mElementType;
+const Type* ArrayType::getElementType() const {
+ return mElementType.get();
}
std::string ArrayType::typeName() const {
@@ -66,6 +57,38 @@
return std::to_string(dimension()) + "d array of " + mElementType->typeName();
}
+std::vector<const Reference<Type>*> ArrayType::getReferences() const {
+ return {&mElementType};
+}
+
+std::vector<const ConstantExpression*> ArrayType::getConstantExpressions() const {
+ std::vector<const ConstantExpression*> ret;
+ ret.insert(ret.end(), mSizes.begin(), mSizes.end());
+ return ret;
+}
+
+status_t ArrayType::resolveInheritance() {
+ // Resolve for typedefs
+ while (mElementType->isArray()) {
+ ArrayType* innerArray = static_cast<ArrayType*>(mElementType.get());
+ mSizes.insert(mSizes.end(), innerArray->mSizes.begin(), innerArray->mSizes.end());
+ mElementType = innerArray->mElementType;
+ }
+ return Type::resolveInheritance();
+}
+
+status_t ArrayType::validate() const {
+ CHECK(!mElementType->isArray());
+
+ if (mElementType->isBinder()) {
+ std::cerr << "ERROR: Arrays of interface types are not supported"
+ << " at " << mElementType.location() << "\n";
+
+ return UNKNOWN_ERROR;
+ }
+ return Type::validate();
+}
+
std::string ArrayType::getCppType(StorageMode mode,
bool specifyNamespaces) const {
const std::string base = mElementType->getCppStackType(specifyNamespaces);
@@ -351,8 +374,11 @@
return mElementType->needsEmbeddedReadWrite();
}
-bool ArrayType::needsResolveReferences() const {
- return mElementType->needsResolveReferences();
+bool ArrayType::deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const {
+ if (mElementType->needsResolveReferences(visited)) {
+ return true;
+ }
+ return Type::deepNeedsResolveReferences(visited);
}
bool ArrayType::resultNeedsDeref() const {
@@ -434,8 +460,16 @@
std::string offsetName = "_hidl_array_offset_" + std::to_string(depth);
out << "long " << offsetName << " = " << offset << ";\n";
+ const bool isPrimitiveArray = mElementType->isScalar();
+
+ /* If the element type corresponds to a Java primitive type we can optimize
+ the innermost loop by copying a linear range of memory instead of doing
+ a per-element copy. As a result the outer nested loop does not include
+ the final dimension. */
+ const size_t loopDimensions = mSizes.size() - (isPrimitiveArray ? 1 : 0);
+
std::string indexString;
- for (size_t dim = 0; dim < mSizes.size(); ++dim) {
+ for (size_t dim = 0; dim < loopDimensions; ++dim) {
std::string iteratorName =
"_hidl_index_" + std::to_string(depth) + "_" + std::to_string(dim);
@@ -465,21 +499,57 @@
<< "();\n";
}
- mElementType->emitJavaFieldReaderWriter(
- out,
- depth + 1,
- parcelName,
- blobName,
- fieldName + indexString,
- offsetName,
- isReader);
+ if (!isPrimitiveArray) {
+ mElementType->emitJavaFieldReaderWriter(
+ out,
+ depth + 1,
+ parcelName,
+ blobName,
+ fieldName + indexString,
+ offsetName,
+ isReader);
- size_t elementAlign, elementSize;
- mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
+ size_t elementAlign, elementSize;
+ mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
- out << offsetName << " += " << std::to_string(elementSize) << ";\n";
+ out << offsetName << " += " << std::to_string(elementSize) << ";\n";
+ } else {
+ if (isReader) {
+ out << blobName
+ << ".copyTo"
+ << mElementType->getJavaSuffix()
+ << "Array("
+ << offsetName
+ << ", "
+ << fieldName
+ << indexString
+ << ", "
+ << mSizes.back()->javaValue()
+ << " /* size */);\n";
+ } else {
+ out << blobName
+ << ".put"
+ << mElementType->getJavaSuffix()
+ << "Array("
+ << offsetName
+ << ", "
+ << fieldName
+ << indexString
+ << ");\n";
+ }
- for (size_t dim = 0; dim < mSizes.size(); ++dim) {
+ size_t elementAlign, elementSize;
+ mElementType->getAlignmentAndSize(&elementAlign, &elementSize);
+
+ out << offsetName
+ << " += "
+ << mSizes.back()->javaValue()
+ << " * "
+ << elementSize
+ << ";\n";
+ }
+
+ for (size_t dim = 0; dim < loopDimensions; ++dim) {
out.unindent();
out << "}\n";
}
@@ -488,17 +558,14 @@
out << "}\n";
}
-status_t ArrayType::emitVtsTypeDeclarations(Formatter &out) const {
+void ArrayType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
out << "vector_size: " << mSizes[0]->value() << "\n";
out << "vector_value: {\n";
out.indent();
// Simple array case.
if (mSizes.size() == 1) {
- status_t err = mElementType->emitVtsTypeDeclarations(out);
- if (err != OK) {
- return err;
- }
+ mElementType->emitVtsTypeDeclarations(out);
} else { // Multi-dimension array case.
for (size_t index = 1; index < mSizes.size(); index++) {
out << "type: " << getVtsType() << "\n";
@@ -506,10 +573,7 @@
out << "vector_value: {\n";
out.indent();
if (index == mSizes.size() - 1) {
- status_t err = mElementType->emitVtsTypeDeclarations(out);
- if (err != OK) {
- return err;
- }
+ mElementType->emitVtsTypeDeclarations(out);
}
}
}
@@ -517,15 +581,20 @@
out.unindent();
out << "}\n";
}
- return OK;
}
-bool ArrayType::isJavaCompatible() const {
- return mElementType->isJavaCompatible();
+bool ArrayType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+ if (!mElementType->isJavaCompatible(visited)) {
+ return false;
+ }
+ return Type::deepIsJavaCompatible(visited);
}
-bool ArrayType::containsPointer() const {
- return mElementType->containsPointer();
+bool ArrayType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
+ if (mElementType->containsPointer(visited)) {
+ return true;
+ }
+ return Type::deepContainsPointer(visited);
}
void ArrayType::getAlignmentAndSize(size_t *align, size_t *size) const {
diff --git a/ArrayType.h b/ArrayType.h
index 05ba783..2c13a33 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -18,6 +18,7 @@
#define ARRAY_TYPE_H_
+#include "Reference.h"
#include "Type.h"
#include <vector>
@@ -27,22 +28,27 @@
struct ConstantExpression;
struct ArrayType : public Type {
- // Extends existing array by adding another dimension.
- ArrayType(ArrayType *srcArray, ConstantExpression *size);
-
- ArrayType(Type *elementType, ConstantExpression *size);
+ ArrayType(const Reference<Type>& elementType, ConstantExpression* size, Scope* parent);
bool isArray() const override;
- bool canCheckEquality() const override;
+ bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
- Type *getElementType() const;
+ const Type* getElementType() const;
- void prependDimension(ConstantExpression *size);
void appendDimension(ConstantExpression *size);
size_t countDimensions() const;
std::string typeName() const override;
+ std::vector<const Reference<Type>*> getReferences() const override;
+
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ // Extends existing array by adding another dimension.
+ status_t resolveInheritance() override;
+
+ status_t validate() const override;
+
std::string getCppType(StorageMode mode,
bool specifyNamespaces) const override;
@@ -103,7 +109,7 @@
const std::string &name) const override;
bool needsEmbeddedReadWrite() const override;
- bool needsResolveReferences() const override;
+ bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
bool resultNeedsDeref() const override;
void emitJavaReaderWriter(
@@ -124,16 +130,16 @@
const std::string &offset,
bool isReader) const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
- bool isJavaCompatible() const override;
- bool containsPointer() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+ bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
-private:
- Type *mElementType;
- std::vector<ConstantExpression *> mSizes;
+ private:
+ Reference<Type> mElementType;
+ std::vector<ConstantExpression*> mSizes;
size_t dimension() const;
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..ebc0bad
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,47 @@
+# Copyright 2017 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/hardware/interfaces/tests/extension)
diff --git a/CompoundType.cpp b/CompoundType.cpp
index b138d7b..87f0f14 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -18,61 +18,82 @@
#include "ArrayType.h"
#include "VectorType.h"
-#include <hidl-util/Formatter.h>
+
#include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <iostream>
+#include <unordered_set>
namespace android {
-CompoundType::CompoundType(Style style, const char* localName, const Location& location,
- Scope* parent)
- : Scope(localName, location, parent), mStyle(style), mFields(NULL) {}
+CompoundType::CompoundType(Style style, const char* localName, const FQName& fullName,
+ const Location& location, Scope* parent)
+ : Scope(localName, fullName, location, parent), mStyle(style), mFields(NULL) {}
CompoundType::Style CompoundType::style() const {
return mStyle;
}
-bool CompoundType::setFields(
- std::vector<CompoundField *> *fields, std::string *errorMsg) {
+void CompoundType::setFields(std::vector<NamedReference<Type>*>* fields) {
mFields = fields;
+}
- for (const auto &field : *fields) {
- const Type &type = field->type();
+std::vector<const Reference<Type>*> CompoundType::getReferences() const {
+ std::vector<const Reference<Type>*> ret;
+ ret.insert(ret.begin(), mFields->begin(), mFields->end());
+ return ret;
+}
- if (type.isBinder()
- || (type.isVector()
- && static_cast<const VectorType *>(
- &type)->isVectorOfBinders())) {
- *errorMsg =
- "Structs/Unions must not contain references to interfaces.";
+status_t CompoundType::validate() const {
+ for (const auto* field : *mFields) {
+ const Type& type = field->type();
- return false;
+ if ((type.isVector() && static_cast<const VectorType*>(&type)->isVectorOfBinders())) {
+ std::cerr << "ERROR: Struct/Union must not contain references to interfaces at "
+ << field->location() << "\n";
+ return UNKNOWN_ERROR;
}
if (mStyle == STYLE_UNION) {
if (type.needsEmbeddedReadWrite()) {
- // Can't have those in a union.
-
- *errorMsg =
- "Unions must not contain any types that need fixup.";
-
- return false;
+ std::cerr << "ERROR: Union must not contain any types that need fixup at "
+ << field->location() << "\n";
+ return UNKNOWN_ERROR;
}
}
}
- return true;
+ status_t err = validateUniqueNames();
+ if (err != OK) return err;
+
+ return Scope::validate();
+}
+
+status_t CompoundType::validateUniqueNames() const {
+ std::unordered_set<std::string> names;
+
+ for (const auto* field : *mFields) {
+ if (names.find(field->name()) != names.end()) {
+ std::cerr << "ERROR: Redefinition of field '" << field->name() << "' at "
+ << field->location() << "\n";
+ return UNKNOWN_ERROR;
+ }
+ names.insert(field->name());
+ }
+
+ return OK;
}
bool CompoundType::isCompoundType() const {
return true;
}
-bool CompoundType::canCheckEquality() const {
+bool CompoundType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
if (mStyle == STYLE_UNION) {
return false;
}
- for (const auto &field : *mFields) {
- if (!field->type().canCheckEquality()) {
+ for (const auto* field : *mFields) {
+ if (!field->get()->canCheckEquality(visited)) {
return false;
}
}
@@ -104,7 +125,7 @@
return "const " + base + "&";
case StorageMode_Result:
- return "const " + base + "*";
+ return base + (containsInterface()?"":"*");
}
}
@@ -126,6 +147,22 @@
CHECK(!"Should not be here");
}
+bool CompoundType::containsInterface() const {
+ for (const auto& field : *mFields) {
+ if (field->type().isCompoundType()) {
+ const Type& t = field->type();
+ const CompoundType* ct = static_cast<const CompoundType*>(&t);
+ if (ct->containsInterface()) {
+ return true;
+ }
+ }
+ if (field->type().isInterface()) {
+ return true;
+ }
+ }
+ return false;
+}
+
void CompoundType::emitReaderWriter(
Formatter &out,
const std::string &name,
@@ -133,57 +170,47 @@
bool parcelObjIsPointer,
bool isReader,
ErrorMode mode) const {
- const std::string parentName = "_hidl_" + name + "_parent";
-
- out << "size_t " << parentName << ";\n\n";
const std::string parcelObjDeref =
parcelObj + (parcelObjIsPointer ? "->" : ".");
- if (isReader) {
- out << "_hidl_err = "
- << parcelObjDeref
- << "readBuffer("
- << "sizeof(*"
- << name
- << "), &"
- << parentName
- << ", "
- << " reinterpret_cast<const void **>("
- << "&" << name
- << "));\n";
-
- handleError(out, mode);
+ if(containsInterface()){
+ for (const auto& field : *mFields) {
+ field->type().emitReaderWriter(out, name + "." + field->name(),
+ parcelObj, parcelObjIsPointer, isReader, mode);
+ }
} else {
- out << "_hidl_err = "
- << parcelObjDeref
- << "writeBuffer(&"
- << name
- << ", sizeof("
- << name
- << "), &"
- << parentName
- << ");\n";
+ const std::string parentName = "_hidl_" + name + "_parent";
- handleError(out, mode);
+ out << "size_t " << parentName << ";\n\n";
+
+ if (isReader) {
+ out << "_hidl_err = " << parcelObjDeref << "readBuffer("
+ << "sizeof(*" << name << "), &" << parentName << ", "
+ << " const_cast<const void**>(reinterpret_cast<void **>("
+ << "&" << name << ")));\n";
+ handleError(out, mode);
+ } else {
+ out << "_hidl_err = "
+ << parcelObjDeref
+ << "writeBuffer(&"
+ << name
+ << ", sizeof("
+ << name
+ << "), &"
+ << parentName
+ << ");\n";
+ handleError(out, mode);
+ }
+ if (mStyle != STYLE_STRUCT) {
+ return;
+ }
+ if (needsEmbeddedReadWrite()) {
+ emitReaderWriterEmbedded(out, 0 /* depth */, name, name, /* sanitizedName */
+ isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer,
+ isReader, mode, parentName, "0 /* parentOffset */");
+ }
}
-
- if (mStyle != STYLE_STRUCT || !needsEmbeddedReadWrite()) {
- return;
- }
-
- emitReaderWriterEmbedded(
- out,
- 0 /* depth */,
- name,
- name, /* sanitizedName */
- isReader /* nameIsPointer */,
- parcelObj,
- parcelObjIsPointer,
- isReader,
- mode,
- parentName,
- "0 /* parentOffset */");
}
void CompoundType::emitReaderWriterEmbedded(
@@ -347,7 +374,7 @@
handleError(out, mode);
}
-status_t CompoundType::emitTypeDeclarations(Formatter &out) const {
+void CompoundType::emitTypeDeclarations(Formatter& out) const {
out << ((mStyle == STYLE_STRUCT) ? "struct" : "union")
<< " "
<< localName()
@@ -359,6 +386,7 @@
if (containsPointer()) {
for (const auto &field : *mFields) {
+ field->emitDocComment(out);
out << field->type().getCppStackType()
<< " "
<< field->name()
@@ -368,7 +396,7 @@
out.unindent();
out << "};\n\n";
- return OK;
+ return;
}
for (int pass = 0; pass < 2; ++pass) {
@@ -424,32 +452,62 @@
<< ") == "
<< structAlign
<< ", \"wrong alignment\");\n\n";
-
- return OK;
}
+void CompoundType::emitTypeForwardDeclaration(Formatter& out) const {
+ out << ((mStyle == STYLE_STRUCT) ? "struct" : "union") << " " << localName() << ";\n";
+}
-status_t CompoundType::emitGlobalTypeDeclarations(Formatter &out) const {
- Scope::emitGlobalTypeDeclarations(out);
+void CompoundType::emitPackageTypeDeclarations(Formatter& out) const {
+ Scope::emitPackageTypeDeclarations(out);
- out << "std::string toString("
+ out << "static inline std::string toString("
<< getCppArgumentType()
- << ");\n\n";
+ << (mFields->empty() ? "" : " o")
+ << ") ";
+
+ out.block([&] {
+ // include toString for scalar types
+ out << "using ::android::hardware::toString;\n"
+ << "std::string os;\n";
+ out << "os += \"{\";\n";
+
+ for (const NamedReference<Type>* field : *mFields) {
+ out << "os += \"";
+ if (field != *(mFields->begin())) {
+ out << ", ";
+ }
+ out << "." << field->name() << " = \";\n";
+ field->type().emitDump(out, "os", "o." + field->name());
+ }
+
+ out << "os += \"}\"; return os;\n";
+ }).endl().endl();
if (canCheckEquality()) {
- out << "bool operator==("
- << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n";
+ out << "static inline bool operator==("
+ << getCppArgumentType() << " " << (mFields->empty() ? "/* lhs */" : "lhs") << ", "
+ << getCppArgumentType() << " " << (mFields->empty() ? "/* rhs */" : "rhs") << ") ";
+ out.block([&] {
+ for (const auto &field : *mFields) {
+ out.sIf("lhs." + field->name() + " != rhs." + field->name(), [&] {
+ out << "return false;\n";
+ }).endl();
+ }
+ out << "return true;\n";
+ }).endl().endl();
- out << "bool operator!=("
- << getCppArgumentType() << ", " << getCppArgumentType() << ");\n\n";
+ out << "static inline bool operator!=("
+ << getCppArgumentType() << " lhs," << getCppArgumentType() << " rhs)";
+ out.block([&] {
+ out << "return !(lhs == rhs);\n";
+ }).endl().endl();
} else {
out << "// operator== and operator!= are not generated for " << localName() << "\n\n";
}
-
- return OK;
}
-status_t CompoundType::emitGlobalHwDeclarations(Formatter &out) const {
+void CompoundType::emitPackageHwDeclarations(Formatter& out) const {
if (needsEmbeddedReadWrite()) {
out << "::android::status_t readEmbeddedFromParcel(\n";
@@ -488,18 +546,11 @@
<< "size_t parentHandle, size_t parentOffset);\n\n";
out.unindent(2);
}
-
- return OK;
}
-status_t CompoundType::emitTypeDefinitions(
- Formatter &out, const std::string prefix) const {
+void CompoundType::emitTypeDefinitions(Formatter& out, const std::string& prefix) const {
std::string space = prefix.empty() ? "" : (prefix + "::");
- status_t err = Scope::emitTypeDefinitions(out, space + localName());
-
- if (err != OK) {
- return err;
- }
+ Scope::emitTypeDefinitions(out, space + localName());
if (needsEmbeddedReadWrite()) {
emitStructReaderWriter(out, prefix, true /* isReader */);
@@ -510,57 +561,9 @@
emitResolveReferenceDef(out, prefix, true /* isReader */);
emitResolveReferenceDef(out, prefix, false /* isReader */);
}
-
- out << "std::string toString("
- << getCppArgumentType()
- << (mFields->empty() ? "" : " o")
- << ") ";
-
- out.block([&] {
- // include toString for scalar types
- out << "using ::android::hardware::toString;\n"
- << "std::string os;\n";
- out << "os += \"{\";\n";
-
- for (const CompoundField *field : *mFields) {
- out << "os += \"";
- if (field != *(mFields->begin())) {
- out << ", ";
- }
- out << "." << field->name() << " = \";\n";
- field->type().emitDump(out, "os", "o." + field->name());
- }
-
- out << "os += \"}\"; return os;\n";
- }).endl().endl();
-
- if (canCheckEquality()) {
- out << "bool operator==("
- << getCppArgumentType() << " " << (mFields->empty() ? "/* lhs */" : "lhs") << ", "
- << getCppArgumentType() << " " << (mFields->empty() ? "/* rhs */" : "rhs") << ") ";
- out.block([&] {
- for (const auto &field : *mFields) {
- out.sIf("lhs." + field->name() + " != rhs." + field->name(), [&] {
- out << "return false;\n";
- }).endl();
- }
- out << "return true;\n";
- }).endl().endl();
-
- out << "bool operator!=("
- << getCppArgumentType() << " lhs," << getCppArgumentType() << " rhs)";
- out.block([&] {
- out << "return !(lhs == rhs);\n";
- }).endl().endl();
- } else {
- out << "// operator== and operator!= are not generated for " << localName() << "\n";
- }
-
- return OK;
}
-status_t CompoundType::emitJavaTypeDeclarations(
- Formatter &out, bool atTopLevel) const {
+void CompoundType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const {
out << "public final ";
if (!atTopLevel) {
@@ -575,7 +578,9 @@
Scope::emitJavaTypeDeclarations(out, false /* atTopLevel */);
- for (const auto &field : *mFields) {
+ for (const auto& field : *mFields) {
+ field->emitDocComment(out);
+
out << "public ";
field->type().emitJavaFieldInitializer(out, field->name());
@@ -651,9 +656,16 @@
out << "public final void readFromParcel(android.os.HwParcel parcel) {\n";
out.indent();
- out << "android.os.HwBlob blob = parcel.readBuffer(";
- out << structSize << "/* size */);\n";
- out << "readEmbeddedFromParcel(parcel, blob, 0 /* parentOffset */);\n";
+ if (containsInterface()) {
+ for (const auto& field : *mFields) {
+ out << field->name() << " = ";
+ field->type().emitJavaReaderWriter(out, "parcel", field->name(), true);
+ }
+ } else {
+ out << "android.os.HwBlob blob = parcel.readBuffer(";
+ out << structSize << "/* size */);\n";
+ out << "readEmbeddedFromParcel(parcel, blob, 0 /* parentOffset */);\n";
+ }
out.unindent();
out << "}\n\n";
@@ -662,77 +674,75 @@
size_t vecAlign, vecSize;
VectorType::getAlignmentAndSizeStatic(&vecAlign, &vecSize);
- out << "public static final java.util.ArrayList<"
- << localName()
+ out << "public static final java.util.ArrayList<" << localName()
<< "> readVectorFromParcel(android.os.HwParcel parcel) {\n";
out.indent();
- out << "java.util.ArrayList<"
- << localName()
- << "> _hidl_vec = new java.util.ArrayList();\n";
+ out << "java.util.ArrayList<" << localName() << "> _hidl_vec = new java.util.ArrayList();\n";
- out << "android.os.HwBlob _hidl_blob = parcel.readBuffer(";
- out << vecSize << " /* sizeof hidl_vec<T> */);\n\n";
+ if (containsInterface()) {
+ out << "int size = parcel.readInt32();\n";
+ out << "for(int i = 0 ; i < size; i ++) {\n";
+ out.indent();
+ out << fullJavaName() << " tmp = ";
+ emitJavaReaderWriter(out, "parcel", "tmp", true);
+ out << "_hidl_vec.add(tmp);\n";
+ out.unindent();
+ out << "}\n";
+ } else {
+ out << "android.os.HwBlob _hidl_blob = parcel.readBuffer(";
+ out << vecSize << " /* sizeof hidl_vec<T> */);\n\n";
- VectorType::EmitJavaFieldReaderWriterForElementType(
- out,
- 0 /* depth */,
- this,
- "parcel",
- "_hidl_blob",
- "_hidl_vec",
- "0",
- true /* isReader */);
-
- out << "\nreturn _hidl_vec;\n";
-
- out.unindent();
- out << "}\n\n";
-
- ////////////////////////////////////////////////////////////////////////////
-
- out << "public final void readEmbeddedFromParcel(\n";
- out.indent(2);
- out << "android.os.HwParcel parcel, android.os.HwBlob _hidl_blob, long _hidl_offset) {\n";
- out.unindent();
-
- size_t offset = 0;
- for (const auto &field : *mFields) {
- size_t fieldAlign, fieldSize;
- field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
-
- size_t pad = offset % fieldAlign;
- if (pad > 0) {
- offset += fieldAlign - pad;
- }
-
- field->type().emitJavaFieldReaderWriter(
- out,
- 0 /* depth */,
- "parcel",
- "_hidl_blob",
- field->name(),
- "_hidl_offset + " + std::to_string(offset),
- true /* isReader */);
-
- offset += fieldSize;
+ VectorType::EmitJavaFieldReaderWriterForElementType(out, 0 /* depth */, this, "parcel",
+ "_hidl_blob", "_hidl_vec", "0",
+ true /* isReader */);
}
-
+ out << "\nreturn _hidl_vec;\n";
out.unindent();
out << "}\n\n";
+ ////////////////////////////////////////////////////////////////////////////
+ if (containsInterface()) {
+ out << "// readEmbeddedFromParcel is not generated()\n";
+ } else {
+ out << "public final void readEmbeddedFromParcel(\n";
+ out.indent(2);
+ out << "android.os.HwParcel parcel, android.os.HwBlob _hidl_blob, long _hidl_offset) {\n";
+ out.unindent();
+ size_t offset = 0;
+ for (const auto& field : *mFields) {
+ size_t fieldAlign, fieldSize;
+ field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
+
+ size_t pad = offset % fieldAlign;
+ if (pad > 0) {
+ offset += fieldAlign - pad;
+ }
+
+ field->type().emitJavaFieldReaderWriter(
+ out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
+ "_hidl_offset + " + std::to_string(offset), true /* isReader */);
+ offset += fieldSize;
+ }
+ out.unindent();
+ out << "}\n\n";
+ }
////////////////////////////////////////////////////////////////////////////
out << "public final void writeToParcel(android.os.HwParcel parcel) {\n";
out.indent();
- out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob("
- << structSize
- << " /* size */);\n";
+ if (containsInterface()) {
+ for (const auto& field : *mFields) {
+ field->type().emitJavaReaderWriter(out, "parcel", field->name(), false);
+ }
+ } else {
+ out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << structSize
+ << " /* size */);\n";
- out << "writeEmbeddedToBlob(_hidl_blob, 0 /* parentOffset */);\n"
- << "parcel.writeBuffer(_hidl_blob);\n";
-
+ out << "writeEmbeddedToBlob(_hidl_blob, 0 /* parentOffset */);\n"
+ << "parcel.writeBuffer(_hidl_blob);\n";
+ }
out.unindent();
out << "}\n\n";
@@ -740,65 +750,56 @@
out << "public static final void writeVectorToParcel(\n";
out.indent(2);
- out << "android.os.HwParcel parcel, java.util.ArrayList<"
- << localName()
- << "> _hidl_vec) {\n";
+ out << "android.os.HwParcel parcel, java.util.ArrayList<" << localName() << "> _hidl_vec) {\n";
out.unindent();
- out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob("
- << vecSize << " /* sizeof(hidl_vec<T>) */);\n";
+ if (containsInterface()) {
+ out << "parcel.writeInt32(_hidl_vec.size());\n";
+ out << "for(" << fullJavaName() << " tmp: _hidl_vec)\n";
+ out.indent();
+ emitJavaReaderWriter(out, "parcel", "tmp", false);
+ out.unindent();
+ } else {
+ out << "android.os.HwBlob _hidl_blob = new android.os.HwBlob(" << vecSize
+ << " /* sizeof(hidl_vec<T>) */);\n";
- VectorType::EmitJavaFieldReaderWriterForElementType(
- out,
- 0 /* depth */,
- this,
- "parcel",
- "_hidl_blob",
- "_hidl_vec",
- "0",
- false /* isReader */);
+ VectorType::EmitJavaFieldReaderWriterForElementType(out, 0 /* depth */, this, "parcel",
+ "_hidl_blob", "_hidl_vec", "0",
+ false /* isReader */);
- out << "\nparcel.writeBuffer(_hidl_blob);\n";
-
+ out << "\nparcel.writeBuffer(_hidl_blob);\n";
+ }
out.unindent();
out << "}\n\n";
-
////////////////////////////////////////////////////////////////////////////
- out << "public final void writeEmbeddedToBlob(\n";
- out.indent(2);
- out << "android.os.HwBlob _hidl_blob, long _hidl_offset) {\n";
- out.unindent();
-
- offset = 0;
- for (const auto &field : *mFields) {
- size_t fieldAlign, fieldSize;
- field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
-
- size_t pad = offset % fieldAlign;
- if (pad > 0) {
- offset += fieldAlign - pad;
+ if (containsInterface()) {
+ out << "// writeEmbeddedFromParcel() is not generated\n";
+ } else {
+ out << "public final void writeEmbeddedToBlob(\n";
+ out.indent(2);
+ out << "android.os.HwBlob _hidl_blob, long _hidl_offset) {\n";
+ out.unindent();
+ size_t offset = 0;
+ for (const auto& field : *mFields) {
+ size_t fieldAlign, fieldSize;
+ field->type().getAlignmentAndSize(&fieldAlign, &fieldSize);
+ size_t pad = offset % fieldAlign;
+ if (pad > 0) {
+ offset += fieldAlign - pad;
+ }
+ field->type().emitJavaFieldReaderWriter(
+ out, 0 /* depth */, "parcel", "_hidl_blob", field->name(),
+ "_hidl_offset + " + std::to_string(offset), false /* isReader */);
+ offset += fieldSize;
}
- field->type().emitJavaFieldReaderWriter(
- out,
- 0 /* depth */,
- "parcel",
- "_hidl_blob",
- field->name(),
- "_hidl_offset + " + std::to_string(offset),
- false /* isReader */);
-
- offset += fieldSize;
+ out.unindent();
+ out << "}\n";
}
out.unindent();
- out << "}\n";
-
- out.unindent();
out << "};\n\n";
-
- return OK;
}
void CompoundType::emitStructReaderWriter(
@@ -872,8 +873,8 @@
out << "}\n\n";
}
-void CompoundType::emitResolveReferenceDef(
- Formatter &out, const std::string prefix, bool isReader) const {
+void CompoundType::emitResolveReferenceDef(Formatter& out, const std::string& prefix,
+ bool isReader) const {
out << "::android::status_t ";
const std::string space(prefix.empty() ? "" : (prefix + "::"));
@@ -962,25 +963,25 @@
return false;
}
-bool CompoundType::needsResolveReferences() const {
+bool CompoundType::deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const {
if (mStyle != STYLE_STRUCT) {
return false;
}
for (const auto &field : *mFields) {
- if (field->type().needsResolveReferences()) {
+ if (field->type().needsResolveReferences(visited)) {
return true;
}
}
- return false;
+ return Scope::deepNeedsResolveReferences(visited);
}
bool CompoundType::resultNeedsDeref() const {
- return true;
+ return !containsInterface() ;
}
-status_t CompoundType::emitVtsTypeDeclarations(Formatter &out) const {
+void CompoundType::emitVtsTypeDeclarations(Formatter& out) const {
out << "name: \"" << fullName() << "\"\n";
out << "type: " << getVtsType() << "\n";
@@ -999,10 +1000,7 @@
}
}
out.indent();
- status_t status(type->emitVtsTypeDeclarations(out));
- if (status != OK) {
- return status;
- }
+ type->emitVtsTypeDeclarations(out);
out.unindent();
out << "}\n";
}
@@ -1023,49 +1021,39 @@
}
out.indent();
out << "name: \"" << field->name() << "\"\n";
- status_t status = field->type().emitVtsAttributeType(out);
- if (status != OK) {
- return status;
- }
+ field->type().emitVtsAttributeType(out);
out.unindent();
out << "}\n";
}
-
- return OK;
}
-status_t CompoundType::emitVtsAttributeType(Formatter &out) const {
+void CompoundType::emitVtsAttributeType(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
out << "predefined_type: \"" << fullName() << "\"\n";
- return OK;
}
-bool CompoundType::isJavaCompatible() const {
- if (mStyle != STYLE_STRUCT || !Scope::isJavaCompatible()) {
+bool CompoundType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+ if (mStyle != STYLE_STRUCT) {
return false;
}
- for (const auto &field : *mFields) {
- if (!field->type().isJavaCompatible()) {
+ for (const auto* field : *mFields) {
+ if (!field->get()->isJavaCompatible(visited)) {
return false;
}
}
- return true;
+ return Scope::deepIsJavaCompatible(visited);
}
-bool CompoundType::containsPointer() const {
- if (Scope::containsPointer()) {
- return true;
- }
-
- for (const auto &field : *mFields) {
- if (field->type().containsPointer()) {
+bool CompoundType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
+ for (const auto* field : *mFields) {
+ if (field->get()->containsPointer(visited)) {
return true;
}
}
- return false;
+ return Scope::deepContainsPointer(visited);
}
void CompoundType::getAlignmentAndSize(size_t *align, size_t *size) const {
@@ -1113,20 +1101,5 @@
}
}
-////////////////////////////////////////////////////////////////////////////////
-
-CompoundField::CompoundField(const char *name, Type *type)
- : mName(name),
- mType(type) {
-}
-
-std::string CompoundField::name() const {
- return mName;
-}
-
-const Type &CompoundField::type() const {
- return *mType;
-}
-
} // namespace android
diff --git a/CompoundType.h b/CompoundType.h
index 4917dec..3ef5f02 100644
--- a/CompoundType.h
+++ b/CompoundType.h
@@ -18,32 +18,37 @@
#define COMPOUND_TYPE_H_
+#include "Reference.h"
#include "Scope.h"
#include <vector>
namespace android {
-struct CompoundField;
-
struct CompoundType : public Scope {
enum Style {
STYLE_STRUCT,
STYLE_UNION,
};
- CompoundType(Style style, const char* localName, const Location& location, Scope* parent);
+ CompoundType(Style style, const char* localName, const FQName& fullName,
+ const Location& location, Scope* parent);
Style style() const;
- bool setFields(std::vector<CompoundField *> *fields, std::string *errorMsg);
+ void setFields(std::vector<NamedReference<Type>*>* fields);
bool isCompoundType() const override;
- bool canCheckEquality() const override;
+ bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
std::string typeName() const override;
+ std::vector<const Reference<Type>*> getReferences() const override;
+
+ status_t validate() const override;
+ status_t validateUniqueNames() const;
+
std::string getCppType(StorageMode mode,
bool specifyNamespaces) const override;
@@ -112,53 +117,39 @@
const std::string &offset,
bool isReader) const override;
- status_t emitTypeDeclarations(Formatter &out) const override;
- status_t emitGlobalTypeDeclarations(Formatter &out) const override;
- status_t emitGlobalHwDeclarations(Formatter &out) const override;
+ void emitTypeDeclarations(Formatter& out) const override;
+ void emitTypeForwardDeclaration(Formatter& out) const override;
+ void emitPackageTypeDeclarations(Formatter& out) const override;
+ void emitPackageHwDeclarations(Formatter& out) const override;
- status_t emitTypeDefinitions(
- Formatter &out, const std::string prefix) const override;
+ void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
- status_t emitJavaTypeDeclarations(
- Formatter &out, bool atTopLevel) const override;
+ void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override;
bool needsEmbeddedReadWrite() const override;
- bool needsResolveReferences() const override;
+ bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
bool resultNeedsDeref() const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
- status_t emitVtsAttributeType(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
+ void emitVtsAttributeType(Formatter& out) const override;
- bool isJavaCompatible() const override;
- bool containsPointer() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+ bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const;
+ bool containsInterface() const;
private:
Style mStyle;
- std::vector<CompoundField *> *mFields;
+ std::vector<NamedReference<Type>*>* mFields;
void emitStructReaderWriter(
Formatter &out, const std::string &prefix, bool isReader) const;
- void emitResolveReferenceDef(
- Formatter &out, const std::string prefix, bool isReader) const;
+ void emitResolveReferenceDef(Formatter& out, const std::string& prefix, bool isReader) const;
DISALLOW_COPY_AND_ASSIGN(CompoundType);
};
-struct CompoundField {
- CompoundField(const char *name, Type *type);
-
- std::string name() const;
- const Type &type() const;
-
-private:
- std::string mName;
- Type *mType;
-
- DISALLOW_COPY_AND_ASSIGN(CompoundField);
-};
-
} // namespace android
#endif // COMPOUND_TYPE_H_
diff --git a/ConstantExpression.cpp b/ConstantExpression.cpp
index eba28e1..8a2b875 100644
--- a/ConstantExpression.cpp
+++ b/ConstantExpression.cpp
@@ -16,21 +16,23 @@
#include "ConstantExpression.h"
-#include <stdio.h>
-#include <string>
-#include <android-base/parseint.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <stdio.h>
+#include <algorithm>
+#include <iostream>
#include <sstream>
+#include <string>
#include "EnumType.h"
+#include "Scope.h" // LocalIdentifier
// The macros are really nasty here. Consider removing
// as many macros as possible.
-#define STREQ(__x__, __y__) (strcmp((__x__), (__y__)) == 0)
-#define OPEQ(__y__) STREQ(op, __y__)
-#define COMPUTE_UNARY(__op__) if(OPEQ(#__op__)) return __op__ val;
-#define COMPUTE_BINARY(__op__) if(OPEQ(#__op__)) return lval __op__ rval;
+#define OPEQ(__y__) (std::string(mOp) == std::string(__y__))
+#define COMPUTE_UNARY(__op__) if (op == std::string(#__op__)) return __op__ val;
+#define COMPUTE_BINARY(__op__) if (op == std::string(#__op__)) return lval __op__ rval;
#define OP_IS_BIN_ARITHMETIC (OPEQ("+") || OPEQ("-") || OPEQ("*") || OPEQ("/") || OPEQ("%"))
#define OP_IS_BIN_BITFLIP (OPEQ("|") || OPEQ("^") || OPEQ("&"))
#define OP_IS_BIN_COMP (OPEQ("<") || OPEQ(">") || OPEQ("<=") || OPEQ(">=") || OPEQ("==") || OPEQ("!="))
@@ -39,6 +41,7 @@
#define SK(__x__) ScalarType::Kind::KIND_##__x__
#define SHOULD_NOT_REACH() CHECK(false) << __LINE__ << ": should not reach here: "
+// NOLINT to suppress missing parentheses warnings about __def__.
#define SWITCH_KIND(__cond__, __action__, __def__) \
switch(__cond__) { \
case SK(BOOL): __action__(bool) \
@@ -50,13 +53,13 @@
case SK(INT32): __action__(int32_t) \
case SK(UINT64): __action__(uint64_t) \
case SK(INT64): __action__(int64_t) \
- default: __def__ \
- } \
+ default: __def__ /* NOLINT */ \
+ }
namespace android {
static inline bool isSupported(ScalarType::Kind kind) {
- return SK(BOOL) == kind || ScalarType(kind).isValidEnumStorageType();
+ return SK(BOOL) == kind || ScalarType(kind, nullptr /* parent */).isValidEnumStorageType();
}
/* See docs at the end for details on integral promotion. */
@@ -84,8 +87,7 @@
// Although there is such rule to return "the unsigned counterpart of
// the signed operand", it should not reach here in our HIDL grammar.
- LOG(FATAL) << "Could not do usual arithmetic conversion for type "
- << lft << "and" << rgt;
+ CHECK(false) << "Could not do usual arithmetic conversion for type " << lft << "and" << rgt;
switch(signedRank) {
case SK(INT8): return SK(UINT8);
case SK(INT16): return SK(UINT16);
@@ -96,7 +98,7 @@
}
template <class T>
-T handleUnary(const char *op, T val) {
+T handleUnary(const std::string& op, T val) {
COMPUTE_UNARY(+)
COMPUTE_UNARY(-)
COMPUTE_UNARY(!)
@@ -107,7 +109,7 @@
}
template <class T>
-T handleBinaryCommon(T lval, const char *op, T rval) {
+T handleBinaryCommon(T lval, const std::string& op, T rval) {
COMPUTE_BINARY(+)
COMPUTE_BINARY(-)
COMPUTE_BINARY(*)
@@ -130,173 +132,182 @@
}
template <class T>
-T handleShift(T lval, const char *op, int64_t rval) {
+T handleShift(T lval, const std::string& op, int64_t rval) {
// just cast rval to int64_t and it should fit.
COMPUTE_BINARY(>>)
COMPUTE_BINARY(<<)
// Should not reach here.
- SHOULD_NOT_REACH() << "Could not handleShift for"
+ SHOULD_NOT_REACH() << "Could not handleShift for "
<< lval << " " << op << " " << rval;
return static_cast<T>(0xdeadbeef);
}
-bool handleLogical(bool lval, const char *op, bool rval) {
+bool handleLogical(bool lval, const std::string& op, bool rval) {
COMPUTE_BINARY(||);
COMPUTE_BINARY(&&);
// Should not reach here.
- SHOULD_NOT_REACH() << "Could not handleLogical for"
+ SHOULD_NOT_REACH() << "Could not handleLogical for "
<< lval << " " << op << " " << rval;
return false;
}
-ConstantExpression::ConstantExpression() {
+std::unique_ptr<ConstantExpression> ConstantExpression::Zero(ScalarType::Kind kind) {
+ return ValueOf(kind, 0);
}
-ConstantExpression ConstantExpression::Zero(ScalarType::Kind kind) {
- ConstantExpression ce = ValueOf(kind, 0);
- ce.mExpr = "0";
- return ce;
+std::unique_ptr<ConstantExpression> ConstantExpression::One(ScalarType::Kind kind) {
+ return ValueOf(kind, 1);
}
-ConstantExpression ConstantExpression::One(ScalarType::Kind kind) {
- ConstantExpression ce = ValueOf(kind, 1);
- ce.mExpr = "1";
- return ce;
+std::unique_ptr<ConstantExpression> ConstantExpression::ValueOf(ScalarType::Kind kind,
+ uint64_t value) {
+ return std::make_unique<LiteralConstantExpression>(kind, value);
}
-ConstantExpression ConstantExpression::ValueOf(ScalarType::Kind kind, uint64_t value) {
- ConstantExpression ce;
+bool ConstantExpression::isEvaluated() const {
+ return mIsEvaluated;
+}
+
+LiteralConstantExpression::LiteralConstantExpression(
+ ScalarType::Kind kind, uint64_t value, const std::string& expr) {
+
+ CHECK(!expr.empty());
CHECK(isSupported(kind));
-
- ce.mExpr = "";
- ce.mType = kConstExprLiteral;
- ce.mValueKind = kind;
- ce.mValue = value;
- ce.mTrivialDescription = true;
- return ce;
-}
-ConstantExpression::ConstantExpression(const ConstantExpression& other) {
- *this = other;
-}
-
-/* Copy constructor, with the expr overriden and treated non-trivial */
-ConstantExpression::ConstantExpression(const ConstantExpression& other, std::string expr) {
- *this = other;
+ mTrivialDescription = true;
mExpr = expr;
- mTrivialDescription = false;
+ mValueKind = kind;
+ mValue = value;
+ mIsEvaluated = true;
}
-ConstantExpression& ConstantExpression::operator=(const ConstantExpression& other) {
- mType = other.mType;
- mValueKind = other.mValueKind;
- mValue = other.mValue;
- mExpr = other.mExpr;
- mTrivialDescription = other.mTrivialDescription;
- return *this;
-}
+LiteralConstantExpression::LiteralConstantExpression(ScalarType::Kind kind, uint64_t value)
+ : LiteralConstantExpression(kind, value, std::to_string(value)) {}
-/* Literals. */
-ConstantExpression::ConstantExpression(const char *value)
- : mExpr(value), mType(kConstExprLiteral), mTrivialDescription(true) {
- const char* head = value, *tail = head + strlen(value) - 1;
+LiteralConstantExpression* LiteralConstantExpression::tryParse(const std::string& value) {
+ CHECK(!value.empty());
+
bool isLong = false, isUnsigned = false;
- bool isHex = (value[0] == '0' && (value[1] == 'x' || value[1] == 'X'));
- while(tail >= head && (*tail == 'u' || *tail == 'U' || *tail == 'l' || *tail == 'L')) {
- isUnsigned |= (*tail == 'u' || *tail == 'U');
- isLong |= (*tail == 'l' || *tail == 'L');
- tail--;
+ bool isHex = (value[0] == '0' && value.length() > 1 && (value[1] == 'x' || value[1] == 'X'));
+
+ auto rbegin = value.rbegin();
+ auto rend = value.rend();
+ for (; rbegin != rend && (*rbegin == 'u' || *rbegin == 'U' || *rbegin == 'l' || *rbegin == 'L');
+ ++rbegin) {
+ isUnsigned |= (*rbegin == 'u' || *rbegin == 'U');
+ isLong |= (*rbegin == 'l' || *rbegin == 'L');
}
- char *newVal = strndup(value, tail - head + 1);
- bool parseOK = base::ParseUint(newVal, &mValue);
- free(newVal);
- CHECK(parseOK) << "Could not parse as integer: " << value;
+ std::string newVal(value.begin(), rbegin.base());
+ CHECK(!newVal.empty());
+
+ uint64_t rawValue = 0;
+
+ bool parseOK = base::ParseUint(newVal, &rawValue);
+ if (!parseOK) {
+ return nullptr;
+ }
+
+ ScalarType::Kind kind;
// guess literal type.
if(isLong) {
if(isUnsigned) // ul
- mValueKind = SK(UINT64);
+ kind = SK(UINT64);
else // l
- mValueKind = SK(INT64);
+ kind = SK(INT64);
} else { // no l suffix
if(isUnsigned) { // u
- if(mValue <= UINT32_MAX)
- mValueKind = SK(UINT32);
+ if(rawValue <= UINT32_MAX)
+ kind = SK(UINT32);
else
- mValueKind = SK(UINT64);
+ kind = SK(UINT64);
} else { // no suffix
if(isHex) {
- if(mValue <= INT32_MAX) // mValue always >= 0
- mValueKind = SK(INT32);
- else if(mValue <= UINT32_MAX)
- mValueKind = SK(UINT32);
- else if(mValue <= INT64_MAX) // mValue always >= 0
- mValueKind = SK(INT64);
- else if(mValue <= UINT64_MAX)
- mValueKind = SK(UINT64);
- } else {
- if(mValue <= INT32_MAX) // mValue always >= 0
- mValueKind = SK(INT32);
+ if(rawValue <= INT32_MAX) // rawValue always >= 0
+ kind = SK(INT32);
+ else if(rawValue <= UINT32_MAX)
+ kind = SK(UINT32);
+ else if(rawValue <= INT64_MAX) // rawValue always >= 0
+ kind = SK(INT64);
+ else if(rawValue <= UINT64_MAX)
+ kind = SK(UINT64);
else
- mValueKind = SK(INT64);
+ return nullptr;
+ } else {
+ if(rawValue <= INT32_MAX) // rawValue always >= 0
+ kind = SK(INT32);
+ else
+ kind = SK(INT64);
}
}
}
+
+ return new LiteralConstantExpression(kind, rawValue, value);
}
-/* Unary operations. */
-ConstantExpression::ConstantExpression(const char *op,
- const ConstantExpression *value)
- : mExpr(std::string("(") + op + value->mExpr + ")"),
- mType(kConstExprUnary),
- mValueKind(value->mValueKind) {
+void LiteralConstantExpression::evaluate() {
+ // Evaluated in constructor
+ CHECK(isEvaluated());
+}
-#define CASE_UNARY(__type__)\
- mValue = handleUnary(op, static_cast<__type__>(value->mValue)); return;
+void UnaryConstantExpression::evaluate() {
+ if (isEvaluated()) return;
+ CHECK(mUnary->isEvaluated());
+ mIsEvaluated = true;
+
+ mExpr = std::string("(") + mOp + mUnary->description() + ")";
+ mValueKind = mUnary->mValueKind;
+
+#define CASE_UNARY(__type__) \
+ mValue = handleUnary(mOp, static_cast<__type__>(mUnary->mValue)); \
+ return;
SWITCH_KIND(mValueKind, CASE_UNARY, SHOULD_NOT_REACH(); return;)
}
-/* Binary operations. */
-ConstantExpression::ConstantExpression(const ConstantExpression *lval,
- const char *op,
- const ConstantExpression* rval)
- : mExpr(std::string("(") + lval->mExpr + " " + op + " " + rval->mExpr + ")"),
- mType(kConstExprBinary)
-{
+void BinaryConstantExpression::evaluate() {
+ if (isEvaluated()) return;
+ CHECK(mLval->isEvaluated());
+ CHECK(mRval->isEvaluated());
+ mIsEvaluated = true;
+
+ mExpr = std::string("(") + mLval->description() + " " + mOp + " " + mRval->description() + ")";
bool isArithmeticOrBitflip = OP_IS_BIN_ARITHMETIC || OP_IS_BIN_BITFLIP;
// CASE 1: + - * / % | ^ & < > <= >= == !=
if(isArithmeticOrBitflip || OP_IS_BIN_COMP) {
// promoted kind for both operands.
- ScalarType::Kind promoted = usualArithmeticConversion(
- integralPromotion(lval->mValueKind),
- integralPromotion(rval->mValueKind));
+ ScalarType::Kind promoted = usualArithmeticConversion(integralPromotion(mLval->mValueKind),
+ integralPromotion(mRval->mValueKind));
// result kind.
mValueKind = isArithmeticOrBitflip
? promoted // arithmetic or bitflip operators generates promoted type
: SK(BOOL); // comparison operators generates bool
-#define CASE_BINARY_COMMON(__type__)\
- mValue = handleBinaryCommon(static_cast<__type__>(lval->mValue), op, static_cast<__type__>(rval->mValue)); return;
+#define CASE_BINARY_COMMON(__type__) \
+ mValue = handleBinaryCommon(static_cast<__type__>(mLval->mValue), mOp, \
+ static_cast<__type__>(mRval->mValue)); \
+ return;
SWITCH_KIND(promoted, CASE_BINARY_COMMON, SHOULD_NOT_REACH(); return;)
}
// CASE 2: << >>
+ std::string newOp = mOp;
if(OP_IS_BIN_SHIFT) {
- mValueKind = integralPromotion(lval->mValueKind);
+ mValueKind = integralPromotion(mLval->mValueKind);
// instead of promoting rval, simply casting it to int64 should also be good.
- int64_t numBits = rval->cast<int64_t>();
+ int64_t numBits = mRval->cast<int64_t>();
if(numBits < 0) {
// shifting with negative number of bits is undefined in C. In HIDL it
// is defined as shifting into the other direction.
- op = OPEQ("<<") ? ">>" : "<<";
+ newOp = OPEQ("<<") ? std::string(">>") : std::string("<<");
numBits = -numBits;
}
-#define CASE_SHIFT(__type__)\
- mValue = handleShift(static_cast<__type__>(lval->mValue), op, numBits); return;
+#define CASE_SHIFT(__type__) \
+ mValue = handleShift(static_cast<__type__>(mLval->mValue), newOp, numBits); \
+ return;
SWITCH_KIND(mValueKind, CASE_SHIFT, SHOULD_NOT_REACH(); return;)
}
@@ -305,63 +316,79 @@
if(OP_IS_BIN_LOGICAL) {
mValueKind = SK(BOOL);
// easy; everything is bool.
- mValue = handleLogical(lval->mValue, op, rval->mValue);
+ mValue = handleLogical(mLval->mValue, mOp, mRval->mValue);
return;
}
SHOULD_NOT_REACH();
}
-/* Ternary ?: operation. */
-ConstantExpression::ConstantExpression(const ConstantExpression *cond,
- const ConstantExpression *trueVal,
- const ConstantExpression *falseVal)
- : mExpr(std::string("(") + cond->mExpr + "?" + trueVal->mExpr
- + ":" + falseVal->mExpr + ")"),
- mType(kConstExprTernary) {
+void TernaryConstantExpression::evaluate() {
+ if (isEvaluated()) return;
+ CHECK(mCond->isEvaluated());
+ CHECK(mTrueVal->isEvaluated());
+ CHECK(mFalseVal->isEvaluated());
+ mIsEvaluated = true;
- // note: for ?:, unlike arithmetic ops, integral promotion is not necessary.
- mValueKind = usualArithmeticConversion(trueVal->mValueKind,
- falseVal->mValueKind);
+ mExpr = std::string("(") + mCond->description() + "?" + mTrueVal->description() + ":" +
+ mFalseVal->description() + ")";
-#define CASE_TERNARY(__type__)\
- mValue = cond->mValue ? (static_cast<__type__>(trueVal->mValue)) : (static_cast<__type__>(falseVal->mValue)); return;
+ // note: for ?:, unlike arithmetic ops, integral promotion is not processed.
+ mValueKind = usualArithmeticConversion(mTrueVal->mValueKind, mFalseVal->mValueKind);
+
+#define CASE_TERNARY(__type__) \
+ mValue = mCond->mValue ? (static_cast<__type__>(mTrueVal->mValue)) \
+ : (static_cast<__type__>(mFalseVal->mValue)); \
+ return;
SWITCH_KIND(mValueKind, CASE_TERNARY, SHOULD_NOT_REACH(); return;)
}
-ConstantExpression ConstantExpression::addOne() const {
- ConstantExpression myOne = ConstantExpression::One(mValueKind);
- return ConstantExpression(this, "+", &myOne).toLiteral();
+void ReferenceConstantExpression::evaluate() {
+ if (isEvaluated()) return;
+ CHECK(mReference->constExpr() != nullptr);
+
+ ConstantExpression* expr = mReference->constExpr();
+ CHECK(expr->isEvaluated());
+
+ mValueKind = expr->mValueKind;
+ mValue = expr->mValue;
+ mIsEvaluated = true;
}
-ConstantExpression &ConstantExpression::toLiteral() {
- mExpr = value();
- mType = kConstExprLiteral;
- return *this;
+std::unique_ptr<ConstantExpression> ConstantExpression::addOne(ScalarType::Kind baseKind) {
+ auto ret = std::make_unique<BinaryConstantExpression>(
+ this, "+", ConstantExpression::One(baseKind).release());
+ return ret;
}
-const std::string &ConstantExpression::description() const {
+const std::string& ConstantExpression::description() const {
+ CHECK(isEvaluated());
return mExpr;
}
bool ConstantExpression::descriptionIsTrivial() const {
+ CHECK(isEvaluated());
return mTrivialDescription;
}
std::string ConstantExpression::value() const {
+ CHECK(isEvaluated());
return rawValue(mValueKind);
}
std::string ConstantExpression::value(ScalarType::Kind castKind) const {
+ CHECK(isEvaluated());
return rawValue(castKind);
}
std::string ConstantExpression::cppValue() const {
+ CHECK(isEvaluated());
return cppValue(mValueKind);
}
std::string ConstantExpression::cppValue(ScalarType::Kind castKind) const {
+ CHECK(isEvaluated());
std::string literal(rawValue(castKind));
// this is a hack to translate
// enum x : int64_t { y = 1l << 63 };
@@ -372,9 +399,9 @@
// -(uint64_t)9223372036854775808 == 9223372036854775808 could not
// be narrowed to int64_t.
if(castKind == SK(INT64) && (int64_t)mValue == INT64_MIN) {
- return strdup(("static_cast<"
- + ScalarType(SK(INT64)).getCppStackType() // "int64_t"
- + ">(" + literal + "ull)").c_str());
+ return "static_cast<" +
+ ScalarType(SK(INT64), nullptr /* parent */).getCppStackType() // "int64_t"
+ + ">(" + literal + "ull)";
}
// add suffix if necessary.
@@ -384,10 +411,12 @@
}
std::string ConstantExpression::javaValue() const {
+ CHECK(isEvaluated());
return javaValue(mValueKind);
}
std::string ConstantExpression::javaValue(ScalarType::Kind castKind) const {
+ CHECK(isEvaluated());
switch(castKind) {
case SK(UINT64): return rawValue(SK(INT64)) + "L";
case SK(INT64): return rawValue(SK(INT64)) + "L";
@@ -395,13 +424,14 @@
case SK(UINT16): return rawValue(SK(INT16));
case SK(UINT8) : return rawValue(SK(INT8));
case SK(BOOL) :
- return this->cast<bool>() ? strdup("true") : strdup("false");
+ return this->cast<bool>() ? "true" : "false";
default: break;
}
return rawValue(castKind);
}
std::string ConstantExpression::rawValue(ScalarType::Kind castKind) const {
+ CHECK(isEvaluated());
#define CASE_STR(__type__) return std::to_string(this->cast<__type__>());
@@ -410,6 +440,7 @@
template<typename T>
T ConstantExpression::cast() const {
+ CHECK(isEvaluated());
#define CASE_CAST_T(__type__) return static_cast<T>(static_cast<__type__>(mValue));
@@ -417,9 +448,213 @@
}
size_t ConstantExpression::castSizeT() const {
+ CHECK(isEvaluated());
return this->cast<size_t>();
}
+bool ConstantExpression::isReferenceConstantExpression() const {
+ return false;
+}
+
+std::vector<ConstantExpression*> ConstantExpression::getConstantExpressions() {
+ const auto& constRet = static_cast<const ConstantExpression*>(this)->getConstantExpressions();
+ std::vector<ConstantExpression*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+ return ret;
+}
+
+std::vector<Reference<LocalIdentifier>*> ConstantExpression::getReferences() {
+ const auto& constRet = static_cast<const ConstantExpression*>(this)->getReferences();
+ std::vector<Reference<LocalIdentifier>*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ce) { return const_cast<Reference<LocalIdentifier>*>(ce); });
+ return ret;
+}
+
+std::vector<const Reference<LocalIdentifier>*> ConstantExpression::getReferences() const {
+ return {};
+}
+
+status_t ConstantExpression::recursivePass(const std::function<status_t(ConstantExpression*)>& func,
+ std::unordered_set<const ConstantExpression*>* visited,
+ bool processBeforeDependencies) {
+ if (mIsPostParseCompleted) return OK;
+
+ if (visited->find(this) != visited->end()) return OK;
+ visited->insert(this);
+
+ if (processBeforeDependencies) {
+ status_t err = func(this);
+ if (err != OK) return err;
+ }
+
+ for (auto* nextCE : getConstantExpressions()) {
+ status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+ if (err != OK) return err;
+ }
+
+ for (auto* nextRef : getReferences()) {
+ auto* nextCE = nextRef->shallowGet()->constExpr();
+ CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+ status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+ if (err != OK) return err;
+ }
+
+ if (!processBeforeDependencies) {
+ status_t err = func(this);
+ if (err != OK) return err;
+ }
+
+ return OK;
+}
+
+status_t ConstantExpression::recursivePass(
+ const std::function<status_t(const ConstantExpression*)>& func,
+ std::unordered_set<const ConstantExpression*>* visited, bool processBeforeDependencies) const {
+ if (mIsPostParseCompleted) return OK;
+
+ if (visited->find(this) != visited->end()) return OK;
+ visited->insert(this);
+
+ if (processBeforeDependencies) {
+ status_t err = func(this);
+ if (err != OK) return err;
+ }
+
+ for (const auto* nextCE : getConstantExpressions()) {
+ status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+ if (err != OK) return err;
+ }
+
+ for (const auto* nextRef : getReferences()) {
+ const auto* nextCE = nextRef->shallowGet()->constExpr();
+ CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+ status_t err = nextCE->recursivePass(func, visited, processBeforeDependencies);
+ if (err != OK) return err;
+ }
+
+ if (!processBeforeDependencies) {
+ status_t err = func(this);
+ if (err != OK) return err;
+ }
+
+ return OK;
+}
+
+ConstantExpression::CheckAcyclicStatus::CheckAcyclicStatus(
+ status_t status, const ConstantExpression* cycleEnd,
+ const ReferenceConstantExpression* lastReference)
+ : status(status), cycleEnd(cycleEnd), lastReference(lastReference) {
+ CHECK(cycleEnd == nullptr || status != OK);
+ CHECK((cycleEnd == nullptr) == (lastReference == nullptr));
+}
+
+ConstantExpression::CheckAcyclicStatus ConstantExpression::checkAcyclic(
+ std::unordered_set<const ConstantExpression*>* visited,
+ std::unordered_set<const ConstantExpression*>* stack) const {
+ if (stack->find(this) != stack->end()) {
+ CHECK(isReferenceConstantExpression())
+ << "Only reference constant expression could be the cycle end";
+
+ std::cerr << "ERROR: Cyclic declaration:\n";
+ return CheckAcyclicStatus(UNKNOWN_ERROR, this,
+ static_cast<const ReferenceConstantExpression*>(this));
+ }
+
+ if (visited->find(this) != visited->end()) return CheckAcyclicStatus(OK);
+ visited->insert(this);
+ stack->insert(this);
+
+ for (const auto* nextCE : getConstantExpressions()) {
+ auto err = nextCE->checkAcyclic(visited, stack);
+ if (err.status != OK) {
+ return err;
+ }
+ }
+
+ for (const auto* nextRef : getReferences()) {
+ const auto* nextCE = nextRef->shallowGet()->constExpr();
+ CHECK(nextCE != nullptr) << "Local identifier is not a constant expression";
+ auto err = nextCE->checkAcyclic(visited, stack);
+
+ if (err.status != OK) {
+ if (err.cycleEnd == nullptr) return err;
+
+ // Only ReferenceConstantExpression has references,
+ CHECK(isReferenceConstantExpression())
+ << "Only reference constant expression could have refereneces";
+
+ // mExpr is defined explicitly before evaluation
+ std::cerr << " '" << err.lastReference->mExpr << "' in '" << mExpr << "' at "
+ << nextRef->location() << "\n";
+
+ if (err.cycleEnd == this) {
+ return CheckAcyclicStatus(err.status);
+ }
+ return CheckAcyclicStatus(err.status, err.cycleEnd,
+ static_cast<const ReferenceConstantExpression*>(this));
+ }
+ }
+
+ CHECK(stack->find(this) != stack->end());
+ stack->erase(this);
+ return CheckAcyclicStatus(OK);
+}
+
+void ConstantExpression::setPostParseCompleted() {
+ CHECK(!mIsPostParseCompleted);
+ mIsPostParseCompleted = true;
+}
+
+std::vector<const ConstantExpression*> LiteralConstantExpression::getConstantExpressions() const {
+ return {};
+}
+
+UnaryConstantExpression::UnaryConstantExpression(const std::string& op, ConstantExpression* value)
+ : mUnary(value), mOp(op) {}
+
+std::vector<const ConstantExpression*> UnaryConstantExpression::getConstantExpressions() const {
+ return {mUnary};
+}
+
+BinaryConstantExpression::BinaryConstantExpression(ConstantExpression* lval, const std::string& op,
+ ConstantExpression* rval)
+ : mLval(lval), mRval(rval), mOp(op) {}
+
+std::vector<const ConstantExpression*> BinaryConstantExpression::getConstantExpressions() const {
+ return {mLval, mRval};
+}
+
+TernaryConstantExpression::TernaryConstantExpression(ConstantExpression* cond,
+ ConstantExpression* trueVal,
+ ConstantExpression* falseVal)
+ : mCond(cond), mTrueVal(trueVal), mFalseVal(falseVal) {}
+
+std::vector<const ConstantExpression*> TernaryConstantExpression::getConstantExpressions() const {
+ return {mCond, mTrueVal, mFalseVal};
+}
+
+ReferenceConstantExpression::ReferenceConstantExpression(const Reference<LocalIdentifier>& value,
+ const std::string& expr)
+ : mReference(value) {
+ mExpr = expr;
+ mTrivialDescription = mExpr.empty();
+}
+
+bool ReferenceConstantExpression::isReferenceConstantExpression() const {
+ return true;
+}
+
+std::vector<const ConstantExpression*> ReferenceConstantExpression::getConstantExpressions() const {
+ // Returns reference instead
+ return {};
+}
+
+std::vector<const Reference<LocalIdentifier>*> ReferenceConstantExpression::getReferences() const {
+ return {&mReference};
+}
+
/*
Evaluating expressions in HIDL language
diff --git a/ConstantExpression.h b/ConstantExpression.h
index 2c955d4..9784885 100644
--- a/ConstantExpression.h
+++ b/ConstantExpression.h
@@ -19,45 +19,80 @@
#define CONSTANT_EXPRESSION_H_
#include <android-base/macros.h>
+#include <functional>
+#include <memory>
#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "Reference.h"
#include "ScalarType.h"
namespace android {
+struct LocalIdentifier;
+
+struct LiteralConstantExpression;
+struct UnaryConstantExpression;
+struct BinaryConstantExpression;
+struct TernaryConstantExpression;
+struct ReferenceConstantExpression;
+
/**
* A constant expression is represented by a tree.
*/
struct ConstantExpression {
+ static std::unique_ptr<ConstantExpression> Zero(ScalarType::Kind kind);
+ static std::unique_ptr<ConstantExpression> One(ScalarType::Kind kind);
+ static std::unique_ptr<ConstantExpression> ValueOf(ScalarType::Kind kind, uint64_t value);
- enum ConstExprType {
- kConstExprLiteral,
- kConstExprUnary,
- kConstExprBinary,
- kConstExprTernary
+ virtual ~ConstantExpression() {}
+
+ virtual bool isReferenceConstantExpression() const;
+
+ // Proceeds recursive pass
+ // Makes sure to visit each node only once
+ // Used to provide lookup and lazy evaluation
+ status_t recursivePass(const std::function<status_t(ConstantExpression*)>& func,
+ std::unordered_set<const ConstantExpression*>* visited,
+ bool processBeforeDependencies);
+ status_t recursivePass(const std::function<status_t(const ConstantExpression*)>& func,
+ std::unordered_set<const ConstantExpression*>* visited,
+ bool processBeforeDependencies) const;
+
+ // Evaluates current constant expression
+ // Doesn't call recursive evaluation, so must be called after dependencies
+ virtual void evaluate() = 0;
+
+ std::vector<ConstantExpression*> getConstantExpressions();
+ virtual std::vector<const ConstantExpression*> getConstantExpressions() const = 0;
+
+ std::vector<Reference<LocalIdentifier>*> getReferences();
+ virtual std::vector<const Reference<LocalIdentifier>*> getReferences() const;
+
+ // Recursive tree pass checkAcyclic return type.
+ // Stores cycle end for nice error messages.
+ struct CheckAcyclicStatus {
+ CheckAcyclicStatus(status_t status, const ConstantExpression* cycleEnd = nullptr,
+ const ReferenceConstantExpression* lastReferenceExpression = nullptr);
+
+ status_t status;
+
+ // If a cycle is found, stores the end of cycle.
+ // While going back in recursion, this is used to stop printing the cycle.
+ const ConstantExpression* cycleEnd;
+
+ // The last ReferenceConstantExpression visited on the cycle.
+ const ReferenceConstantExpression* lastReference;
};
- /* Default constructor. */
- ConstantExpression();
- /* Copy constructor. */
- ConstantExpression(const ConstantExpression& other);
- /* Copy constructor, with the expr overriden. */
- ConstantExpression(const ConstantExpression& other, std::string expr);
- /* Literals */
- ConstantExpression(const char *value);
- /* binary operations */
- ConstantExpression(const ConstantExpression *value1,
- const char *op, const ConstantExpression* value2);
- /* unary operations */
- ConstantExpression(const char *op, const ConstantExpression *value);
- /* ternary ?: */
- ConstantExpression(const ConstantExpression *cond,
- const ConstantExpression *trueVal,
- const ConstantExpression *falseVal);
+ // Recursive tree pass that ensures that constant expressions definitions
+ // are acyclic.
+ CheckAcyclicStatus checkAcyclic(std::unordered_set<const ConstantExpression*>* visited,
+ std::unordered_set<const ConstantExpression*>* stack) const;
- static ConstantExpression Zero(ScalarType::Kind kind);
- static ConstantExpression One(ScalarType::Kind kind);
- static ConstantExpression ValueOf(ScalarType::Kind kind, uint64_t value);
-
+ /* Returns true iff the value has already been evaluated. */
+ bool isEvaluated() const;
/* Evaluated result in a string form. */
std::string value() const;
/* Evaluated result in a string form. */
@@ -70,22 +105,24 @@
std::string cppValue(ScalarType::Kind castKind) const;
/* Evaluated result in a string form, with given contextual kind. */
std::string javaValue(ScalarType::Kind castKind) const;
- /* Original expression with type. */
- const std::string &description() const;
+ /* Formatted expression with type. */
+ const std::string& description() const;
/* See mTrivialDescription */
bool descriptionIsTrivial() const;
/* Return a ConstantExpression that is 1 plus the original. */
- ConstantExpression addOne() const;
- /* Assignment operator. */
- ConstantExpression& operator=(const ConstantExpression& other);
+ std::unique_ptr<ConstantExpression> addOne(ScalarType::Kind baseKind);
size_t castSizeT() const;
-private:
+ // Marks that package proceeding is completed
+ // Post parse passes must be proceeded during owner package parsin
+ void setPostParseCompleted();
+
+ private:
+ /* If the result value has been evaluated. */
+ bool mIsEvaluated = false;
/* The formatted expression. */
std::string mExpr;
- /* The type of the expression. Hints on its original form. */
- ConstExprType mType;
/* The kind of the result value. */
ScalarType::Kind mValueKind;
/* The stored result value. */
@@ -93,21 +130,85 @@
/* true if description() does not offer more information than value(). */
bool mTrivialDescription = false;
+ bool mIsPostParseCompleted = false;
+
/*
* Helper function for all cpp/javaValue methods.
* Returns a plain string (without any prefixes or suffixes, just the
* digits) converted from mValue.
*/
std::string rawValue(ScalarType::Kind castKind) const;
- /* Trim unnecessary information. Only mValue and mValueKind is kept. */
- ConstantExpression &toLiteral();
/*
* Return the value casted to the given type.
* First cast it according to mValueKind, then cast it to T.
* Assumes !containsIdentifiers()
*/
- template <typename T> T cast() const;
+ template <typename T>
+ T cast() const;
+
+ friend struct LiteralConstantExpression;
+ friend struct UnaryConstantExpression;
+ friend struct BinaryConstantExpression;
+ friend struct TernaryConstantExpression;
+ friend struct ReferenceConstantExpression;
+};
+
+struct LiteralConstantExpression : public ConstantExpression {
+ LiteralConstantExpression(ScalarType::Kind kind, uint64_t value);
+ void evaluate() override;
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ static LiteralConstantExpression* tryParse(const std::string& value);
+
+private:
+ LiteralConstantExpression(ScalarType::Kind kind, uint64_t value, const std::string& expr);
+};
+
+struct UnaryConstantExpression : public ConstantExpression {
+ UnaryConstantExpression(const std::string& mOp, ConstantExpression* value);
+ void evaluate() override;
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ private:
+ ConstantExpression* const mUnary;
+ std::string mOp;
+};
+
+struct BinaryConstantExpression : public ConstantExpression {
+ BinaryConstantExpression(ConstantExpression* lval, const std::string& op,
+ ConstantExpression* rval);
+ void evaluate() override;
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ private:
+ ConstantExpression* const mLval;
+ ConstantExpression* const mRval;
+ const std::string mOp;
+};
+
+struct TernaryConstantExpression : public ConstantExpression {
+ TernaryConstantExpression(ConstantExpression* cond, ConstantExpression* trueVal,
+ ConstantExpression* falseVal);
+ void evaluate() override;
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ private:
+ ConstantExpression* const mCond;
+ ConstantExpression* const mTrueVal;
+ ConstantExpression* const mFalseVal;
+};
+
+struct ReferenceConstantExpression : public ConstantExpression {
+ ReferenceConstantExpression(const Reference<LocalIdentifier>& value, const std::string& expr);
+
+ bool isReferenceConstantExpression() const override;
+ void evaluate() override;
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+ std::vector<const Reference<LocalIdentifier>*> getReferences() const override;
+
+ private:
+ Reference<LocalIdentifier> mReference;
};
} // namespace android
diff --git a/Coordinator.cpp b/Coordinator.cpp
index 8c89d5e..96c9658 100644
--- a/Coordinator.cpp
+++ b/Coordinator.cpp
@@ -25,11 +25,11 @@
#include <android-base/logging.h>
#include <hidl-hash/Hash.h>
#include <hidl-util/StringHelper.h>
+#include <iostream>
#include "AST.h"
#include "Interface.h"
-
-extern android::status_t parseFile(android::AST *ast);
+#include "hidl-gen_l.h"
static bool existdir(const char *name) {
DIR *dir = opendir(name);
@@ -42,40 +42,200 @@
namespace android {
-Coordinator::Coordinator(
- const std::vector<std::string> &packageRootPaths,
- const std::vector<std::string> &packageRoots,
- const std::string &rootPath)
- : mPackageRootPaths(packageRootPaths),
- mPackageRoots(packageRoots),
- mRootPath(rootPath) {
- // empty
+const std::string &Coordinator::getRootPath() const {
+ return mRootPath;
}
-Coordinator::~Coordinator() {
- // empty
-}
+void Coordinator::setRootPath(const std::string &rootPath) {
+ mRootPath = rootPath;
-void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
- if (std::find(mPackageRoots.begin(), mPackageRoots.end(), root) == mPackageRoots.end()) {
- mPackageRoots.push_back(root);
- mPackageRootPaths.push_back(path);
+ if (!mRootPath.empty() && !StringHelper::EndsWith(mRootPath, "/")) {
+ mRootPath += "/";
}
}
+void Coordinator::setOutputPath(const std::string& outputPath) {
+ mOutputPath = outputPath;
+}
+
+void Coordinator::setVerbose(bool verbose) {
+ mVerbose = verbose;
+}
+
+bool Coordinator::isVerbose() const {
+ return mVerbose;
+}
+
+void Coordinator::setDepFile(const std::string& depFile) {
+ mDepFile = depFile;
+}
+
+const std::string& Coordinator::getOwner() const {
+ return mOwner;
+}
+void Coordinator::setOwner(const std::string& owner) {
+ mOwner = owner;
+}
+
+status_t Coordinator::addPackagePath(const std::string& root, const std::string& path, std::string* error) {
+ FQName package = FQName(root, "0.0", "");
+ for (const PackageRoot &packageRoot : mPackageRoots) {
+ if (packageRoot.root.inPackage(root) || package.inPackage(packageRoot.root.package())) {
+ if (error != nullptr) {
+ *error = "ERROR: conflicting package roots " +
+ packageRoot.root.package() +
+ " and " +
+ root;
+ }
+
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ mPackageRoots.push_back({path, package});
+ return OK;
+}
+void Coordinator::addDefaultPackagePath(const std::string& root, const std::string& path) {
+ addPackagePath(root, path, nullptr /* error */);
+}
+
+Formatter Coordinator::getFormatter(const FQName& fqName, Location location,
+ const std::string& fileName) const {
+ if (location == Location::STANDARD_OUT) {
+ return Formatter(stdout);
+ }
+
+ std::string filepath;
+ status_t err = getFilepath(fqName, location, fileName, &filepath);
+ if (err != OK) {
+ return Formatter::invalid();
+ }
+
+ onFileAccess(filepath, "w");
+
+ if (!Coordinator::MakeParentHierarchy(filepath)) {
+ fprintf(stderr, "ERROR: could not make directories for %s.\n", filepath.c_str());
+ return Formatter::invalid();
+ }
+
+ FILE* file = fopen(filepath.c_str(), "w");
+
+ if (file == nullptr) {
+ fprintf(stderr, "ERROR: could not open file %s: %d\n", filepath.c_str(), errno);
+ return Formatter::invalid();
+ }
+
+ return Formatter(file);
+}
+
+status_t Coordinator::getFilepath(const FQName& fqName, Location location,
+ const std::string& fileName, std::string* path) const {
+ status_t err;
+ std::string packagePath;
+ std::string packageRootPath;
+
+ switch (location) {
+ case Location::DIRECT: { /* nothing */
+ *path = mOutputPath + fileName;
+ } break;
+ case Location::PACKAGE_ROOT: {
+ err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
+ if (err != OK) return err;
+
+ *path = mOutputPath + packagePath + fileName;
+ } break;
+ case Location::GEN_OUTPUT: {
+ err = convertPackageRootToPath(fqName, &packageRootPath);
+ if (err != OK) return err;
+ err = getPackagePath(fqName, true /* relative */, false /* sanitized */, &packagePath);
+ if (err != OK) return err;
+
+ *path = mOutputPath + packageRootPath + packagePath + fileName;
+ } break;
+ case Location::GEN_SANITIZED: {
+ err = convertPackageRootToPath(fqName, &packageRootPath);
+ if (err != OK) return err;
+ err = getPackagePath(fqName, true /* relative */, true /* sanitized */, &packagePath);
+ if (err != OK) return err;
+
+ *path = mOutputPath + packageRootPath + packagePath + fileName;
+ } break;
+ default: { CHECK(false) << "Invalid location: " << static_cast<size_t>(location); }
+ }
+
+ return OK;
+}
+
+void Coordinator::onFileAccess(const std::string& path, const std::string& mode) const {
+ if (mode == "r") {
+ // This is a global list. It's not cleared when a second fqname is processed for
+ // two reasons:
+ // 1). If there is a bug in hidl-gen, the dependencies on the first project from
+ // the second would be required to recover correctly when the bug is fixed.
+ // 2). This option is never used in Android builds.
+ mReadFiles.insert(StringHelper::LTrim(path, mRootPath));
+ }
+
+ if (!mVerbose) {
+ return;
+ }
+
+ fprintf(stderr,
+ "VERBOSE: file access %s %s\n", path.c_str(), mode.c_str());
+}
+
+status_t Coordinator::writeDepFile(const std::string& forFile) const {
+ // No dep file requested
+ if (mDepFile.empty()) return OK;
+
+ onFileAccess(mDepFile, "w");
+
+ FILE* file = fopen(mDepFile.c_str(), "w");
+ if (file == nullptr) {
+ fprintf(stderr, "ERROR: could not open dep file at %s.\n", mDepFile.c_str());
+ return UNKNOWN_ERROR;
+ }
+
+ Formatter out(file, 2 /* spacesPerIndent */);
+ out << StringHelper::LTrim(forFile, mOutputPath) << ": \\\n";
+ out.indent([&] {
+ for (const std::string& file : mReadFiles) {
+ out << StringHelper::LTrim(file, mRootPath) << " \\\n";
+ }
+ });
+ return OK;
+}
+
AST* Coordinator::parse(const FQName& fqName, std::set<AST*>* parsedASTs,
Enforce enforcement) const {
+ AST* ret;
+ status_t err = parseOptional(fqName, &ret, parsedASTs, enforcement);
+ if (err != OK) CHECK(ret == nullptr); // internal consistency
+
+ // only in a handful of places do we want to distinguish between
+ // a missing file and a bad AST. Everywhere else, we just want to
+ // throw an error if we expect an AST to be present but it is not.
+ return ret;
+}
+
+status_t Coordinator::parseOptional(const FQName& fqName, AST** ast, std::set<AST*>* parsedASTs,
+ Enforce enforcement) const {
CHECK(fqName.isFullyQualified());
auto it = mCache.find(fqName);
if (it != mCache.end()) {
- AST *ast = (*it).second;
+ *ast = (*it).second;
- if (ast != nullptr && parsedASTs != nullptr) {
- parsedASTs->insert(ast);
+ if (*ast != nullptr && parsedASTs != nullptr) {
+ parsedASTs->insert(*ast);
}
- return ast;
+ if (*ast == nullptr) {
+ // circular import OR that AST has errors in it
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
}
// Add this to the cache immediately, so we can discover circular imports.
@@ -87,35 +247,47 @@
// Any interface file implicitly imports its package's types.hal.
FQName typesName = fqName.getTypesForPackage();
// Do not enforce on imports. Do not add imports' imports to this AST.
- typesAST = parse(typesName, nullptr, Enforce::NONE);
+ status_t err = parseOptional(typesName, &typesAST, nullptr, Enforce::NONE);
+ if (err != OK) return err;
// fall through.
}
- std::string path = getAbsolutePackagePath(fqName);
+ std::string packagePath;
+ status_t err =
+ getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath);
+ if (err != OK) return err;
- path.append(fqName.name());
- path.append(".hal");
+ const std::string path = makeAbsolute(packagePath + fqName.name() + ".hal");
- AST *ast = new AST(this, path);
+ *ast = new AST(this, &Hash::getHash(path));
if (typesAST != NULL) {
// If types.hal for this AST's package existed, make it's defined
// types available to the (about to be parsed) AST right away.
- ast->addImportedAST(typesAST);
+ (*ast)->addImportedAST(typesAST);
}
- status_t err = parseFile(ast);
+ std::unique_ptr<FILE, std::function<void(FILE*)>> file(fopen(path.c_str(), "rb"), fclose);
- if (err != OK) {
- delete ast;
- ast = nullptr;
-
- return nullptr;
+ if (file == nullptr) {
+ mCache.erase(fqName); // nullptr in cache is used to find circular imports
+ delete *ast;
+ *ast = nullptr;
+ return OK; // File does not exist, nullptr AST* == file doesn't exist.
}
- if (ast->package().package() != fqName.package()
- || ast->package().version() != fqName.version()) {
+ onFileAccess(path, "r");
+
+ // parse file takes ownership of file
+ if (parseFile(*ast, std::move(file)) != OK || (*ast)->postParse() != OK) {
+ delete *ast;
+ *ast = nullptr;
+ return UNKNOWN_ERROR;
+ }
+
+ if ((*ast)->package().package() != fqName.package() ||
+ (*ast)->package().version() != fqName.version()) {
fprintf(stderr,
"ERROR: File at '%s' does not match expected package and/or "
"version.\n",
@@ -123,16 +295,15 @@
err = UNKNOWN_ERROR;
} else {
- if (ast->isInterface()) {
+ if ((*ast)->isInterface()) {
if (fqName.name() == "types") {
fprintf(stderr,
"ERROR: File at '%s' declares an interface '%s' "
"instead of the expected types common to the package.\n",
- path.c_str(),
- ast->getInterface()->localName().c_str());
+ path.c_str(), (*ast)->getInterface()->localName().c_str());
err = UNKNOWN_ERROR;
- } else if (ast->getInterface()->localName() != fqName.name()) {
+ } else if ((*ast)->getInterface()->localName() != fqName.name()) {
fprintf(stderr,
"ERROR: File at '%s' does not declare interface type "
"'%s'.\n",
@@ -149,7 +320,7 @@
fqName.name().c_str());
err = UNKNOWN_ERROR;
- } else if (ast->containsInterfaces()) {
+ } else if ((*ast)->containsInterfaces()) {
fprintf(stderr,
"ERROR: types.hal file at '%s' declares at least one "
"interface type.\n",
@@ -160,32 +331,32 @@
}
if (err != OK) {
- delete ast;
- ast = nullptr;
-
- return nullptr;
+ delete *ast;
+ *ast = nullptr;
+ return err;
}
- if (parsedASTs != nullptr) { parsedASTs->insert(ast); }
+ if (parsedASTs != nullptr) {
+ parsedASTs->insert(*ast);
+ }
// put it into the cache now, so that enforceRestrictionsOnPackage can
// parse fqName.
- mCache[fqName] = ast;
+ mCache[fqName] = *ast;
// For each .hal file that hidl-gen parses, the whole package will be checked.
err = enforceRestrictionsOnPackage(fqName, enforcement);
if (err != OK) {
mCache[fqName] = nullptr;
- delete ast;
- ast = nullptr;
- return nullptr;
+ delete *ast;
+ *ast = nullptr;
+ return err;
}
- return ast;
+ return OK;
}
-std::vector<std::string>::const_iterator
-Coordinator::findPackageRoot(const FQName &fqName) const {
+const Coordinator::PackageRoot* Coordinator::findPackageRoot(const FQName& fqName) const {
CHECK(!fqName.package().empty());
// Find the right package prefix and path for this FQName. For
@@ -195,92 +366,77 @@
// prefix "android.hardware" and the package root
// "hardware/interfaces".
- auto it = mPackageRoots.begin();
auto ret = mPackageRoots.end();
- for (; it != mPackageRoots.end(); it++) {
- if (!fqName.inPackage(*it)) {
+ for (auto it = mPackageRoots.begin(); it != mPackageRoots.end(); it++) {
+ if (!fqName.inPackage(it->root.package())) {
continue;
}
- CHECK(ret == mPackageRoots.end())
- << "Multiple package roots found for " << fqName.string()
- << " (" << *it << " and " << *ret << ")";
+ if (ret != mPackageRoots.end()) {
+ std::cerr << "ERROR: Multiple package roots found for " << fqName.string() << " ("
+ << it->root.package() << " and " << ret->root.package() << ")\n";
+ return nullptr;
+ }
ret = it;
}
- CHECK(ret != mPackageRoots.end())
- << "Unable to find package root for " << fqName.string();
- return ret;
-}
-
-std::string Coordinator::getAbsolutePackagePath(const FQName& fqName) const {
- const std::string packagePath = getPackagePath(fqName);
-
- if (StringHelper::StartsWith(packagePath, "/") || mRootPath.empty()) {
- return packagePath;
+ if (ret == mPackageRoots.end()) {
+ std::cerr << "ERROR: Package root not specified for " << fqName.string() << "\n";
+ return nullptr;
}
- return StringHelper::RTrim(mRootPath, "/") + "/" + packagePath;
+ return &(*ret);
}
-std::string Coordinator::getPackageRoot(const FQName &fqName) const {
- auto it = findPackageRoot(fqName);
- auto prefix = *it;
- return prefix;
-}
-
-std::string Coordinator::getPackageRootPath(const FQName &fqName) const {
- auto it = findPackageRoot(fqName);
- auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
- return root;
-}
-
-std::string Coordinator::getPackageRootOption(const FQName &fqName) const {
- return getPackageRoot(fqName) + ":" + getPackageRootPath(fqName);
-}
-
-std::string Coordinator::getPackagePath(
- const FQName &fqName, bool relative, bool sanitized) const {
-
- auto it = findPackageRoot(fqName);
- auto prefix = *it;
- auto root = mPackageRootPaths[std::distance(mPackageRoots.begin(), it)];
-
- // Make sure the prefix ends on a '.' and the root path on a '/'
- if ((*--prefix.end()) != '.') {
- prefix += '.';
+std::string Coordinator::makeAbsolute(const std::string& path) const {
+ if (StringHelper::StartsWith(path, "/") || mRootPath.empty()) {
+ return path;
}
- if ((*--root.end()) != '/') {
- root += '/';
+ return mRootPath + path;
+}
+
+status_t Coordinator::getPackageRoot(const FQName& fqName, std::string* root) const {
+ const PackageRoot* packageRoot = findPackageRoot(fqName);
+ if (root == nullptr) {
+ return UNKNOWN_ERROR;
}
+ *root = packageRoot->root.package();
+ return OK;
+}
- // Given FQName of "android.hardware.nfc@1.0::IFoo" and a prefix
- // "android.hardware.", the suffix is "nfc@1.0::IFoo".
- const std::string packageSuffix = fqName.package().substr(prefix.length());
+status_t Coordinator::getPackageRootPath(const FQName& fqName, std::string* path) const {
+ const PackageRoot* packageRoot = findPackageRoot(fqName);
+ if (packageRoot == nullptr) {
+ return UNKNOWN_ERROR;
+ }
+ *path = packageRoot->path;
+ return OK;
+}
- std::string packagePath;
+status_t Coordinator::getPackagePath(const FQName& fqName, bool relative, bool sanitized,
+ std::string* path) const {
+ const PackageRoot* packageRoot = findPackageRoot(fqName);
+ if (packageRoot == nullptr) return UNKNOWN_ERROR;
+
+ // Given FQName of "android.hardware.nfc.test@1.0::IFoo" and a prefix
+ // "android.hardware", the suffix is "nfc.test".
+ std::string suffix = StringHelper::LTrim(fqName.package(), packageRoot->root.package());
+ suffix = StringHelper::LTrim(suffix, ".");
+
+ std::vector<std::string> suffixComponents;
+ StringHelper::SplitString(suffix, '.', &suffixComponents);
+
+ std::vector<std::string> components;
if (!relative) {
- packagePath = root;
+ components.push_back(StringHelper::RTrimAll(packageRoot->path, "/"));
}
+ components.insert(components.end(), suffixComponents.begin(), suffixComponents.end());
+ components.push_back(sanitized ? fqName.sanitizedVersion() : fqName.version());
- size_t startPos = 0;
- size_t dotPos;
- while ((dotPos = packageSuffix.find('.', startPos)) != std::string::npos) {
- packagePath.append(packageSuffix.substr(startPos, dotPos - startPos));
- packagePath.append("/");
-
- startPos = dotPos + 1;
- }
- CHECK_LT(startPos + 1, packageSuffix.length());
- packagePath.append(packageSuffix.substr(startPos));
- packagePath.append("/");
-
- packagePath.append(sanitized ? fqName.sanitizedVersion() : fqName.version());
- packagePath.append("/");
-
- return packagePath;
+ *path = StringHelper::JoinStrings(components, "/") + "/";
+ return OK;
}
status_t Coordinator::getPackageInterfaceFiles(
@@ -288,16 +444,17 @@
std::vector<std::string> *fileNames) const {
fileNames->clear();
- const std::string packagePath = getAbsolutePackagePath(package);
+ std::string packagePath;
+ status_t err =
+ getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath);
+ if (err != OK) return err;
- DIR *dir = opendir(packagePath.c_str());
+ const std::string path = makeAbsolute(packagePath);
+ DIR* dir = opendir(path.c_str());
if (dir == NULL) {
- fprintf(stderr,
- "ERROR: Could not open package path %s for package %s:\n%s\n",
- getPackagePath(package).c_str(),
- package.string().c_str(),
- packagePath.c_str());
+ fprintf(stderr, "ERROR: Could not open package path %s for package %s:\n%s\n",
+ packagePath.c_str(), package.string().c_str(), path.c_str());
return -errno;
}
@@ -349,28 +506,17 @@
}
for (const auto &fileName : fileNames) {
- FQName subFQName(
- package.package() + package.atVersion() + "::" + fileName);
-
- if (!subFQName.isValid()) {
- LOG(WARNING)
- << "Whole-package import encountered invalid filename '"
- << fileName
- << "' in package "
- << package.package()
- << package.atVersion();
-
- continue;
- }
-
+ FQName subFQName(package.package(), package.version(), fileName);
packageInterfaces->push_back(subFQName);
}
return OK;
}
-std::string Coordinator::convertPackageRootToPath(const FQName &fqName) const {
- std::string packageRoot = getPackageRoot(fqName);
+status_t Coordinator::convertPackageRootToPath(const FQName& fqName, std::string* path) const {
+ std::string packageRoot;
+ status_t err = getPackageRoot(fqName, &packageRoot);
+ if (err != OK) return err;
if (*(packageRoot.end()--) != '.') {
packageRoot += '.';
@@ -378,7 +524,85 @@
std::replace(packageRoot.begin(), packageRoot.end(), '.', '/');
- return packageRoot; // now converted to a path
+ *path = packageRoot; // now converted to a path
+ return OK;
+}
+
+status_t Coordinator::isTypesOnlyPackage(const FQName& package, bool* result) const {
+ std::vector<FQName> packageInterfaces;
+
+ status_t err = appendPackageInterfacesToVector(package, &packageInterfaces);
+
+ if (err != OK) {
+ *result = false;
+ return err;
+ }
+
+ *result = packageInterfaces.size() == 1 && packageInterfaces[0].name() == "types";
+ return OK;
+}
+
+status_t Coordinator::addUnreferencedTypes(const std::vector<FQName>& packageInterfaces,
+ std::set<FQName>* unreferencedDefinitions,
+ std::set<FQName>* unreferencedImports) const {
+ CHECK(unreferencedDefinitions != nullptr);
+ CHECK(unreferencedImports != nullptr);
+
+ std::set<FQName> packageDefinedTypes;
+ std::set<FQName> packageReferencedTypes;
+ std::set<FQName> packageImportedTypes;
+ std::set<FQName> typesDefinedTypes; // only types.hal types
+
+ for (const auto& fqName : packageInterfaces) {
+ AST* ast = parse(fqName);
+ if (!ast) {
+ std::cerr << "ERROR: Could not parse " << fqName.string() << ". Aborting." << std::endl;
+
+ return UNKNOWN_ERROR;
+ }
+
+ ast->addDefinedTypes(&packageDefinedTypes);
+ ast->addReferencedTypes(&packageReferencedTypes);
+ ast->getAllImportedNamesGranular(&packageImportedTypes);
+
+ if (fqName.name() == "types") {
+ ast->addDefinedTypes(&typesDefinedTypes);
+ }
+ }
+
+#if 0
+ for (const auto &fqName : packageDefinedTypes) {
+ std::cout << "VERBOSE: DEFINED " << fqName.string() << std::endl;
+ }
+
+ for (const auto &fqName : packageImportedTypes) {
+ std::cout << "VERBOSE: IMPORTED " << fqName.string() << std::endl;
+ }
+
+ for (const auto &fqName : packageReferencedTypes) {
+ std::cout << "VERBOSE: REFERENCED " << fqName.string() << std::endl;
+ }
+
+ for (const auto &fqName : typesDefinedTypes) {
+ std::cout << "VERBOSE: DEFINED in types.hal " << fqName.string() << std::endl;
+ }
+#endif
+
+ for (const auto& fqName : packageReferencedTypes) {
+ packageDefinedTypes.erase(fqName);
+ packageImportedTypes.erase(fqName);
+ }
+
+ // A package implicitly imports its own types.hal, only track them in one set.
+ for (const auto& fqName : typesDefinedTypes) {
+ packageImportedTypes.erase(fqName);
+ }
+
+ // defined but not referenced
+ unreferencedDefinitions->insert(packageDefinedTypes.begin(), packageDefinedTypes.end());
+ // imported but not referenced
+ unreferencedImports->insert(packageImportedTypes.begin(), packageImportedTypes.end());
+ return OK;
}
status_t Coordinator::enforceRestrictionsOnPackage(const FQName& fqName,
@@ -389,8 +613,8 @@
// need fqName to be something like android.hardware.foo@1.0.
// name and valueName is ignored.
if (fqName.package().empty() || fqName.version().empty()) {
- LOG(ERROR) << "Cannot enforce restrictions on package " << fqName.string()
- << ": package or version is missing.";
+ std::cerr << "ERROR: Cannot enforce restrictions on package " << fqName.string()
+ << ": package or version is missing." << std::endl;
return BAD_VALUE;
}
@@ -407,7 +631,7 @@
// enforce all rules.
status_t err;
- err = enforceMinorVersionUprevs(package);
+ err = enforceMinorVersionUprevs(package, enforcement);
if (err != OK) {
return err;
}
@@ -424,10 +648,11 @@
return OK;
}
-status_t Coordinator::enforceMinorVersionUprevs(const FQName ¤tPackage) const {
+status_t Coordinator::enforceMinorVersionUprevs(const FQName& currentPackage,
+ Enforce enforcement) const {
if(!currentPackage.hasVersion()) {
- LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
- << ": missing version.";
+ std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
+ << ": missing version." << std::endl;
return UNKNOWN_ERROR;
}
@@ -439,7 +664,13 @@
FQName prevPackage = currentPackage;
while (prevPackage.getPackageMinorVersion() > 0) {
prevPackage = prevPackage.downRev();
- if (existdir(getAbsolutePackagePath(prevPackage).c_str())) {
+
+ std::string prevPackagePath;
+ status_t err = getPackagePath(prevPackage, false /* relative */, false /* sanitized */,
+ &prevPackagePath);
+ if (err != OK) return err;
+
+ if (existdir(makeAbsolute(prevPackagePath).c_str())) {
hasPrevPackage = true;
break;
}
@@ -450,13 +681,22 @@
}
if (prevPackage != currentPackage.downRev()) {
- LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
- << ": Found package " << prevPackage.string() << " but missing "
- << currentPackage.downRev().string() << "; you cannot skip a minor version.";
+ std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string()
+ << ": Found package " << prevPackage.string() << " but missing "
+ << currentPackage.downRev().string() << "; you cannot skip a minor version."
+ << std::endl;
return UNKNOWN_ERROR;
}
- status_t err;
+ bool prevIsTypesOnly;
+ status_t err = isTypesOnlyPackage(prevPackage, &prevIsTypesOnly);
+ if (err != OK) return err;
+
+ if (prevIsTypesOnly) {
+ // A types only package can be extended in any way.
+ return OK;
+ }
+
std::vector<FQName> packageInterfaces;
err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
if (err != OK) {
@@ -470,19 +710,20 @@
}
const Interface *iface = nullptr;
- AST *currentAST = parse(currentFQName);
+ AST* currentAST = parse(currentFQName, nullptr /* parsedASTs */, enforcement);
if (currentAST != nullptr) {
iface = currentAST->getInterface();
}
if (iface == nullptr) {
if (currentAST == nullptr) {
- LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
- << " because it could not be found or parsed"
- << " or " << currentPackage.string()
- << " doesn't pass all requirements.";
+ std::cerr << "WARNING: Skipping " << currentFQName.string()
+ << " because it could not be found or parsed"
+ << " or " << currentPackage.string() << " doesn't pass all requirements."
+ << std::endl;
} else {
- LOG(WARNING) << "Warning: Skipping " << currentFQName.string()
- << " because the file might contain more than one interface.";
+ std::cerr << "WARNING: Skipping " << currentFQName.string()
+ << " because the file might contain more than one interface."
+ << std::endl;
}
continue;
}
@@ -508,73 +749,144 @@
bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr;
- if (iface->superType()->fqName() != lastFQName && lastFQNameExists) {
- LOG(ERROR) << "Cannot enforce minor version uprevs for " << currentPackage.string()
- << ": " << iface->fqName().string() << " extends "
- << iface->superType()->fqName().string()
- << ", which is not allowed. It must extend " << lastFQName.string();
+ if (!lastFQNameExists) {
+ continue;
+ }
+
+ if (iface->superType()->fqName() != lastFQName) {
+ std::cerr << "ERROR: Cannot enforce minor version uprevs for "
+ << currentPackage.string() << ": " << iface->fqName().string() << " extends "
+ << iface->superType()->fqName().string()
+ << ", which is not allowed. It must extend " << lastFQName.string()
+ << std::endl;
return UNKNOWN_ERROR;
}
// at least one interface must extend the previous version
+ // @2.0::IFoo does not work. It must be @2.1::IFoo for at least one interface.
if (lastFQName.getPackageAndVersion() == prevPackage.getPackageAndVersion()) {
extendedInterface = true;
}
- LOG(VERBOSE) << "enforceMinorVersionUprevs: " << currentFQName.string() << " passes.";
+ if (mVerbose) {
+ std::cout << "VERBOSE: EnforceMinorVersionUprevs: " << currentFQName.string()
+ << " passes." << std::endl;
+ }
}
if (!extendedInterface) {
// No interface extends the interface with the same name in @x.(y-1).
- LOG(ERROR) << currentPackage.string() << " doesn't pass minor version uprev requirement. "
- << "Requires at least one interface to extend an interface with the same name "
- << "from " << prevPackage.string() << ".";
+ std::cerr << "ERROR: " << currentPackage.string()
+ << " doesn't pass minor version uprev requirement. "
+ << "Requires at least one interface to extend an interface with the same name "
+ << "from " << prevPackage.string() << "." << std::endl;
return UNKNOWN_ERROR;
}
return OK;
}
-status_t Coordinator::enforceHashes(const FQName ¤tPackage) const {
- status_t err = OK;
+Coordinator::HashStatus Coordinator::checkHash(const FQName& fqName) const {
+ AST* ast = parse(fqName);
+ if (ast == nullptr) return HashStatus::ERROR;
+
+ std::string rootPath;
+ status_t err = getPackageRootPath(fqName, &rootPath);
+ if (err != OK) return HashStatus::ERROR;
+
+ std::string hashPath = makeAbsolute(rootPath) + "/current.txt";
+ std::string error;
+ bool fileExists;
+ std::vector<std::string> frozen =
+ Hash::lookupHash(hashPath, fqName.string(), &error, &fileExists);
+ if (fileExists) onFileAccess(hashPath, "r");
+
+ if (error.size() > 0) {
+ std::cerr << "ERROR: " << error << std::endl;
+ return HashStatus::ERROR;
+ }
+
+ // hash not defined, interface not frozen
+ if (frozen.size() == 0) {
+ // This ensures that it can be detected.
+ Hash::clearHash(ast->getFilename());
+
+ return HashStatus::UNFROZEN;
+ }
+
+ std::string currentHash = ast->getFileHash()->hexString();
+
+ if (std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
+ std::cerr << "ERROR: " << fqName.string() << " has hash " << currentHash
+ << " which does not match hash on record. This interface has "
+ << "been frozen. Do not change it!" << std::endl;
+ return HashStatus::CHANGED;
+ }
+
+ return HashStatus::FROZEN;
+}
+
+status_t Coordinator::getUnfrozenDependencies(const FQName& fqName,
+ std::set<FQName>* result) const {
+ CHECK(result != nullptr);
+
+ AST* ast = parse(fqName);
+ if (ast == nullptr) return UNKNOWN_ERROR;
+
+ std::set<FQName> imported;
+ ast->getImportedPackages(&imported);
+
+ // no circular dependency is already guaranteed by parsing
+ // indirect dependencies will be checked when the imported interface frozen checks are done
+ for (const FQName& importedPackage : imported) {
+ std::vector<FQName> packageInterfaces;
+ status_t err = appendPackageInterfacesToVector(importedPackage, &packageInterfaces);
+ if (err != OK) {
+ return err;
+ }
+
+ for (const FQName& importedName : packageInterfaces) {
+ HashStatus status = checkHash(importedName);
+ if (status == HashStatus::ERROR) return UNKNOWN_ERROR;
+ if (status == HashStatus::UNFROZEN) {
+ result->insert(importedName);
+ }
+ }
+ }
+
+ return OK;
+}
+
+status_t Coordinator::enforceHashes(const FQName& currentPackage) const {
std::vector<FQName> packageInterfaces;
- err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
+ status_t err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces);
if (err != OK) {
return err;
}
- for (const FQName ¤tFQName : packageInterfaces) {
- AST *ast = parse(currentFQName);
+ for (const FQName& currentFQName : packageInterfaces) {
+ HashStatus status = checkHash(currentFQName);
- if (ast == nullptr) {
- err = UNKNOWN_ERROR;
- continue;
+ if (status == HashStatus::ERROR) return UNKNOWN_ERROR;
+ if (status == HashStatus::CHANGED) return UNKNOWN_ERROR;
+
+ // frozen interface can only depend on a frozen interface
+ if (status == HashStatus::FROZEN) {
+ std::set<FQName> unfrozenDependencies;
+ err = getUnfrozenDependencies(currentFQName, &unfrozenDependencies);
+ if (err != OK) return err;
+
+ if (!unfrozenDependencies.empty()) {
+ std::cerr << "ERROR: Frozen interface " << currentFQName.string()
+ << " cannot depend on unfrozen thing(s):" << std::endl;
+ for (const FQName& name : unfrozenDependencies) {
+ std::cerr << " (unfrozen) " << name.string() << std::endl;
+ }
+ return UNKNOWN_ERROR;
+ }
}
- std::string hashPath = getPackageRootPath(currentFQName) + "/current.txt";
- std::string error;
- std::vector<std::string> frozen = Hash::lookupHash(hashPath, currentFQName.string(), &error);
-
- if (error.size() > 0) {
- LOG(ERROR) << error;
- err = UNKNOWN_ERROR;
- continue;
- }
-
- // hash not define, interface not frozen
- if (frozen.size() == 0) {
- continue;
- }
-
- std::string currentHash = Hash::getHash(ast->getFilename()).hexString();
-
- if(std::find(frozen.begin(), frozen.end(), currentHash) == frozen.end()) {
- LOG(ERROR) << currentFQName.string() << " has hash " << currentHash
- << " which does not match hash on record. This interface has "
- << "been frozen. Do not change it!";
- err = UNKNOWN_ERROR;
- continue;
- }
+ // UNFROZEN, ignore
}
return err;
@@ -585,7 +897,7 @@
size_t start = 1; // Ignore leading '/'
size_t slashPos;
- while ((slashPos = path.find("/", start)) != std::string::npos) {
+ while ((slashPos = path.find('/', start)) != std::string::npos) {
std::string partial = path.substr(0, slashPos);
struct stat st;
diff --git a/Coordinator.h b/Coordinator.h
index 2e761df..2dd1ca3 100644
--- a/Coordinator.h
+++ b/Coordinator.h
@@ -19,30 +19,58 @@
#define COORDINATOR_H_
#include <android-base/macros.h>
-#include <functional>
+#include <hidl-util/FQName.h>
+#include <hidl-util/Formatter.h>
+#include <utils/Errors.h>
#include <map>
#include <set>
#include <string>
-#include <utils/Errors.h>
#include <vector>
namespace android {
struct AST;
-struct FQName;
struct Type;
struct Coordinator {
- Coordinator(
- const std::vector<std::string> &packageRootPaths,
- const std::vector<std::string> &packageRoots,
- const std::string &rootPath);
+ Coordinator() {};
- ~Coordinator();
+ const std::string& getRootPath() const;
+ void setRootPath(const std::string &rootPath);
+ void setOutputPath(const std::string& outputPath);
+
+ void setVerbose(bool value);
+ bool isVerbose() const;
+
+ void setDepFile(const std::string& depFile);
+
+ const std::string& getOwner() const;
+ void setOwner(const std::string& owner);
// adds path only if it doesn't exist
+ status_t addPackagePath(const std::string& root, const std::string& path, std::string* error);
+ // adds path if it hasn't already been added
void addDefaultPackagePath(const std::string& root, const std::string& path);
+ enum class Location {
+ STANDARD_OUT,
+ DIRECT, // mOutputPath + file name
+ PACKAGE_ROOT, // e.x. mRootPath + /nfc/1.0/Android.bp
+ GEN_OUTPUT, // e.x. mOutputPath + /android/hardware/foo/1.0/*.cpp
+ GEN_SANITIZED, // e.x. mOutputPath + /android/hardware/foo/V1_0/*.cpp
+ };
+
+ status_t getFilepath(const FQName& fqName, Location location, const std::string& fileName,
+ std::string* path) const;
+
+ Formatter getFormatter(const FQName& fqName, Location location,
+ const std::string& fileName) const;
+
+ // must be called before file access
+ void onFileAccess(const std::string& path, const std::string& mode) const;
+
+ status_t writeDepFile(const std::string& forFile) const;
+
enum class Enforce {
FULL, // default
NO_HASH, // only for use with -Lhash
@@ -58,35 +86,29 @@
AST* parse(const FQName& fqName, std::set<AST*>* parsedASTs = nullptr,
Enforce enforcement = Enforce::FULL) const;
+ // Same as parse, but it distinguishes between "missing file" and "could not parse AST"
+ // return OK, out *ast:
+ // 0xdeadbeef -> successfully parsed
+ // nullptr -> file not present
+ // return !OK
+ // could not parse AST and file exists
+ status_t parseOptional(const FQName& fqName, AST** ast, std::set<AST*>* parsedASTs = nullptr,
+ Enforce enforcement = Enforce::FULL) const;
+
// Given package-root paths of ["hardware/interfaces",
// "vendor/<something>/interfaces"], package roots of
// ["android.hardware", "vendor.<something>.hardware"], and a
// FQName of "android.hardware.nfc@1.0::INfc, then getPackagePath()
// will return "hardware/interfaces/nfc/1.0" (if sanitized = false)
// or "hardware/interfaces/nfc/V1_0" (if sanitized = true).
- std::string getPackagePath(
- const FQName &fqName, bool relative = false,
- bool sanitized = false) const;
+ status_t getPackagePath(const FQName& fqName, bool relative, bool sanitized,
+ std::string* path) const;
// Given package roots of ["android.hardware",
// "vendor.<something>.hardware"] and a FQName of
// "android.hardware.nfc@1.0::INfc, then getPackageRoot() will
// return "android.hardware".
- std::string getPackageRoot(const FQName &fqName) const;
-
- // Given package-root paths of ["hardware/interfaces",
- // "vendor/<something>/interfaces"], package roots of
- // ["android.hardware", "vendor.<something>.hardware"], and a
- // FQName of "android.hardware.nfc@1.0::INfc, then getPackageRootPath()
- // will return "hardware/interfaces".
- std::string getPackageRootPath(const FQName &fqName) const;
-
- // return getPackageRoot + ":" + getPackageRootPath
- std::string getPackageRootOption(const FQName &fqName) const;
-
- // Given an FQName of "android.hardware.nfc@1.0::INfc", return
- // "android/hardware/".
- std::string convertPackageRootToPath(const FQName &fqName) const;
+ status_t getPackageRoot(const FQName& fqName, std::string* root) const;
status_t getPackageInterfaceFiles(
const FQName &package,
@@ -96,6 +118,13 @@
const FQName &package,
std::vector<FQName> *packageInterfaces) const;
+ status_t isTypesOnlyPackage(const FQName& package, bool* result) const;
+
+ // Returns types which are imported/defined but not referenced in code
+ status_t addUnreferencedTypes(const std::vector<FQName>& packageInterfaces,
+ std::set<FQName>* unreferencedDefinitions,
+ std::set<FQName>* unreferencedImports) const;
+
// Enforce a set of restrictions on a set of packages. These include:
// - minor version upgrades
// "packages" contains names like "android.hardware.nfc@1.1".
@@ -103,19 +132,46 @@
status_t enforceRestrictionsOnPackage(const FQName& fqName,
Enforce enforcement = Enforce::FULL) const;
+private:
static bool MakeParentHierarchy(const std::string &path);
-private:
- // A list of top-level directories (mPackageRootPaths)
- // corresponding to a list of package roots (mPackageRoots). For
- // example, if mPackageRootPaths[0] == "hardware/interfaces" and
- // mPackageRoots[0] == "android.hardware" this means that all
- // packages starting with "android.hardware" will be looked up in
- // "hardware/interfaces".
- std::vector<std::string> mPackageRootPaths;
- std::vector<std::string> mPackageRoots;
+ enum class HashStatus {
+ ERROR,
+ UNFROZEN,
+ FROZEN,
+ CHANGED, // frozen but changed
+ };
+ HashStatus checkHash(const FQName& fqName) const;
+ status_t getUnfrozenDependencies(const FQName& fqName, std::set<FQName>* result) const;
- std::string mRootPath;
+ // indicates that packages in "android.hardware" will be looked up in hardware/interfaces
+ struct PackageRoot {
+ std::string path; // e.x. hardware/interfaces
+ FQName root; // e.x. android.hardware@0.0
+ };
+
+ // nullptr if it doesn't exist
+ const PackageRoot* findPackageRoot(const FQName& fqName) const;
+
+ // Given package-root paths of ["hardware/interfaces",
+ // "vendor/<something>/interfaces"], package roots of
+ // ["android.hardware", "vendor.<something>.hardware"], and a
+ // FQName of "android.hardware.nfc@1.0::INfc, then getPackageRootPath()
+ // will return "hardware/interfaces".
+ status_t getPackageRootPath(const FQName& fqName, std::string* path) const;
+
+ // Given an FQName of "android.hardware.nfc@1.0::INfc", return
+ // "android/hardware/".
+ status_t convertPackageRootToPath(const FQName& fqName, std::string* path) const;
+
+ std::vector<PackageRoot> mPackageRoots;
+ std::string mRootPath; // root of android source tree (to locate package roots)
+ std::string mOutputPath; // root of output directory
+ std::string mDepFile; // location to write depfile
+
+ // hidl-gen options
+ bool mVerbose = false;
+ std::string mOwner;
// cache to parse().
mutable std::map<FQName, AST *> mCache;
@@ -123,19 +179,14 @@
// cache to enforceRestrictionsOnPackage().
mutable std::set<FQName> mPackagesEnforced;
- std::vector<std::string>::const_iterator findPackageRoot(
- const FQName &fqName) const;
+ mutable std::set<std::string> mReadFiles;
- // Returns abs package path by prepending the root path if a package
- // path is non-absolute.
- // If root is '/android/master' and getPackagePath returns 'h/i/nfc/V1_0'
- // this will return '/android/master/h/i/nfc/V1_0'.
- // If root is '/android/master' and getPackagePath returns '/abs/path/to/nfc/V1_0'
- // this will return '/abs/path/to/nfc/V1_0'
- std::string getAbsolutePackagePath(const FQName& fqName) const;
+ // Returns the given path if it is absolute, otherwise it returns
+ // the path relative to mRootPath
+ std::string makeAbsolute(const std::string& string) const;
// Rules of enforceRestrictionsOnPackage are listed below.
- status_t enforceMinorVersionUprevs(const FQName &fqName) const;
+ status_t enforceMinorVersionUprevs(const FQName& fqName, Enforce enforcement) const;
status_t enforceHashes(const FQName &fqName) const;
DISALLOW_COPY_AND_ASSIGN(Coordinator);
diff --git a/DeathRecipientType.cpp b/DeathRecipientType.cpp
index 828f217..abc69fc 100644
--- a/DeathRecipientType.cpp
+++ b/DeathRecipientType.cpp
@@ -21,7 +21,7 @@
namespace android {
-DeathRecipientType::DeathRecipientType() {}
+DeathRecipientType::DeathRecipientType(Scope* parent) : Type(parent) {}
std::string DeathRecipientType::typeName() const {
return "death recipient";
@@ -74,17 +74,12 @@
return true;
}
-bool DeathRecipientType::isJavaCompatible() const {
- return true;
-}
-
void DeathRecipientType::getAlignmentAndSize(size_t *align, size_t *size) const {
*align = *size = 0; // this object should only be used in passthrough mode
}
-status_t DeathRecipientType::emitVtsTypeDeclarations(Formatter &out) const {
+void DeathRecipientType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
- return OK;
}
} // namespace android
diff --git a/DeathRecipientType.h b/DeathRecipientType.h
index 386cec5..a7213cd 100644
--- a/DeathRecipientType.h
+++ b/DeathRecipientType.h
@@ -23,7 +23,7 @@
namespace android {
struct DeathRecipientType : public Type {
- DeathRecipientType();
+ DeathRecipientType(Scope* parent);
std::string getCppType(
StorageMode mode,
@@ -45,11 +45,9 @@
bool needsEmbeddedReadWrite() const override;
bool resultNeedsDeref() const override;
- bool isJavaCompatible() const override;
-
void getAlignmentAndSize(size_t *align, size_t *size) const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
};
} // namespace android
diff --git a/DocComment.cpp b/DocComment.cpp
new file mode 100644
index 0000000..91bc676
--- /dev/null
+++ b/DocComment.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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 "DocComment.h"
+
+#include <hidl-util/StringHelper.h>
+
+#include <cctype>
+#include <sstream>
+
+namespace android {
+
+DocComment::DocComment(const std::string& comment) {
+ std::vector<std::string> lines;
+ StringHelper::SplitString(comment, '\n', &lines);
+
+ bool foundFirstLine = false;
+
+ std::ostringstream is;
+ for (size_t l = 0; l < lines.size(); l++) {
+ const std::string& line = lines[l];
+
+ // Delete prefixes like " * ", " *", or " ".
+ size_t idx = 0;
+ for (; idx < line.size() && isspace(line[idx]); idx++)
+ ;
+ if (idx < line.size() && line[idx] == '*') idx++;
+ if (idx < line.size() && line[idx] == ' ') idx++;
+
+ if (idx < line.size()) {
+ foundFirstLine = true;
+ }
+
+ if (!foundFirstLine) continue;
+
+ is << line.substr(idx);
+
+ if (l + 1 < lines.size()) {
+ is << "\n";
+ }
+ }
+
+ mComment = is.str();
+}
+
+void DocComment::merge(const DocComment* comment) {
+ mComment = mComment + "\n\n" + comment->mComment;
+}
+
+void DocComment::emit(Formatter& out) const {
+ out << "/**\n";
+ out.setLinePrefix(" * ");
+ out << mComment;
+ out.unsetLinePrefix();
+ out << " */\n";
+}
+
+} // namespace android
diff --git a/DocComment.h b/DocComment.h
new file mode 100644
index 0000000..e0677eb
--- /dev/null
+++ b/DocComment.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef DOC_COMMENT_H_
+
+#define DOC_COMMENT_H_
+
+#include <hidl-util/Formatter.h>
+
+#include <string>
+
+namespace android {
+
+struct DocComment {
+ DocComment(const std::string& comment);
+
+ void merge(const DocComment* comment);
+
+ void emit(Formatter& out) const;
+
+ private:
+ std::string mComment;
+};
+
+struct DocCommentable {
+ void setDocComment(const DocComment* docComment) { mDocComment = docComment; }
+ void emitDocComment(Formatter& out) const {
+ if (mDocComment != nullptr) {
+ mDocComment->emit(out);
+ }
+ }
+
+ private:
+ const DocComment* mDocComment = nullptr;
+};
+
+} // namespace android
+
+#endif // DOC_COMMENT_H_
diff --git a/EnumType.cpp b/EnumType.cpp
index fb8545f..ead64ba 100644
--- a/EnumType.cpp
+++ b/EnumType.cpp
@@ -16,46 +16,123 @@
#include "EnumType.h"
-#include "Annotation.h"
-#include "ScalarType.h"
-
-#include <inttypes.h>
#include <hidl-util/Formatter.h>
-#include <android-base/logging.h>
+#include <inttypes.h>
+#include <iostream>
+#include <unordered_map>
+
+#include "Annotation.h"
+#include "Location.h"
+#include "ScalarType.h"
namespace android {
-EnumType::EnumType(const char* localName, const Location& location, Type* storageType,
- Scope* parent)
- : Scope(localName, location, parent), mValues(), mStorageType(storageType) {
- mBitfieldType = new BitFieldType();
- mBitfieldType->setElementType(this);
-}
+EnumType::EnumType(const char* localName, const FQName& fullName, const Location& location,
+ const Reference<Type>& storageType, Scope* parent)
+ : Scope(localName, fullName, location, parent), mValues(), mStorageType(storageType) {}
const Type *EnumType::storageType() const {
- return mStorageType;
+ return mStorageType.get();
}
const std::vector<EnumValue *> &EnumType::values() const {
return mValues;
}
-void EnumType::addValue(EnumValue *value) {
- CHECK(value != nullptr);
+void EnumType::forEachValueFromRoot(const std::function<void(EnumValue*)> f) const {
+ std::vector<const EnumType*> chain = typeChain();
+ for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
+ const auto& type = *it;
+ for (EnumValue* v : type->values()) {
+ f(v);
+ }
+ }
+}
- EnumValue *prev = nullptr;
- std::vector<const EnumType *> chain;
- getTypeChain(&chain);
- for (auto it = chain.begin(); it != chain.end(); ++it) {
- const auto &type = *it;
- if(!type->values().empty()) {
- prev = type->values().back();
+void EnumType::addValue(EnumValue* value) {
+ CHECK(value != nullptr);
+ mValues.push_back(value);
+}
+
+status_t EnumType::resolveInheritance() {
+ const EnumType* prevType = nullptr;
+ EnumValue* prevValue = nullptr;
+
+ for (const auto* type : superTypeChain()) {
+ if (!type->values().empty()) {
+ prevType = type;
+ prevValue = type->values().back();
break;
}
}
- value->autofill(prev, resolveToScalarType());
- mValues.push_back(value);
+ for (auto* value : mValues) {
+ value->autofill(prevType, prevValue, mStorageType->resolveToScalarType());
+ prevType = this;
+ prevValue = value;
+ }
+
+ return Scope::resolveInheritance();
+}
+
+std::vector<const Reference<Type>*> EnumType::getReferences() const {
+ return {&mStorageType};
+}
+
+std::vector<const ConstantExpression*> EnumType::getConstantExpressions() const {
+ std::vector<const ConstantExpression*> ret;
+ for (const auto* value : mValues) {
+ ret.push_back(value->constExpr());
+ }
+ return ret;
+}
+
+status_t EnumType::validate() const {
+ CHECK(getSubTypes().empty());
+
+ if (!isElidableType() || !mStorageType->isValidEnumStorageType()) {
+ std::cerr << "ERROR: Invalid enum storage type (" << (mStorageType)->typeName()
+ << ") specified at " << mStorageType.location() << "\n";
+ return UNKNOWN_ERROR;
+ }
+
+ status_t err = validateUniqueNames();
+ if (err != OK) return err;
+
+ return Scope::validate();
+}
+
+status_t EnumType::validateUniqueNames() const {
+ std::unordered_map<std::string, const EnumType*> registeredValueNames;
+ for (const auto* type : superTypeChain()) {
+ for (const auto* enumValue : type->mValues) {
+ // No need to check super value uniqueness
+ registeredValueNames[enumValue->name()] = type;
+ }
+ }
+
+ for (const auto* value : mValues) {
+ auto registered = registeredValueNames.find(value->name());
+
+ if (registered != registeredValueNames.end()) {
+ const EnumType* definedInType = registered->second;
+
+ if (definedInType == this) {
+ // Defined in this enum
+ std::cerr << "ERROR: Redefinition of value '" << value->name() << "'";
+ } else {
+ // Defined in super enum
+ std::cerr << "ERROR: Redefinition of value '" << value->name()
+ << "' defined in enum '" << definedInType->fullName() << "'";
+ }
+ std::cerr << " at " << value->location() << "\n";
+ return UNKNOWN_ERROR;
+ }
+
+ registeredValueNames[value->name()] = this;
+ }
+
+ return OK;
}
bool EnumType::isElidableType() const {
@@ -74,7 +151,7 @@
return true;
}
-bool EnumType::canCheckEquality() const {
+bool EnumType::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
@@ -99,13 +176,21 @@
return "TYPE_ENUM";
}
-BitFieldType *EnumType::getBitfieldType() const {
- return mBitfieldType;
+std::string EnumType::getBitfieldCppType(StorageMode /* mode */, bool specifyNamespaces) const {
+ const std::string space = specifyNamespaces ? "::android::hardware::" : "";
+ return space + "hidl_bitfield<" + (specifyNamespaces ? fullName() : localName()) + ">";
+}
+
+std::string EnumType::getBitfieldJavaType(bool forInitializer) const {
+ return resolveToScalarType()->getJavaType(forInitializer);
+}
+
+std::string EnumType::getBitfieldJavaWrapperType() const {
+ return resolveToScalarType()->getJavaWrapperType();
}
LocalIdentifier *EnumType::lookupIdentifier(const std::string &name) const {
- std::vector<const EnumType *> chain;
- getTypeChain(&chain);
+ std::vector<const EnumType*> chain = typeChain();
for (auto it = chain.begin(); it != chain.end(); ++it) {
const auto &type = *it;
for(EnumValue *v : type->values()) {
@@ -149,7 +234,7 @@
out, depth, parcelName, blobName, fieldName, offset, isReader);
}
-status_t EnumType::emitTypeDeclarations(Formatter &out) const {
+void EnumType::emitTypeDeclarations(Formatter& out) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != nullptr);
@@ -163,13 +248,14 @@
out.indent();
- std::vector<const EnumType *> chain;
- getTypeChain(&chain);
+ std::vector<const EnumType*> chain = typeChain();
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
for (const auto &entry : type->values()) {
+ entry->emitDocComment(out);
+
out << entry->name();
std::string value = entry->cppValue(scalarType->getKind());
@@ -179,7 +265,7 @@
out << ",";
std::string comment = entry->comment();
- if (!comment.empty() && comment != value) {
+ if (!comment.empty()) {
out << " // " << comment;
}
@@ -189,8 +275,42 @@
out.unindent();
out << "};\n\n";
+}
- return OK;
+void EnumType::emitTypeForwardDeclaration(Formatter& out) const {
+ const ScalarType* scalarType = mStorageType->resolveToScalarType();
+ const std::string storageType = scalarType->getCppStackType();
+
+ out << "enum class " << localName() << " : " << storageType << ";\n";
+}
+
+void EnumType::emitIteratorDeclaration(Formatter& out) const {
+ size_t elementCount = 0;
+ for (const auto* type : typeChain()) {
+ elementCount += type->mValues.size();
+ }
+
+ out << "template<> struct hidl_enum_iterator<" << getCppStackType() << ">\n";
+ out.block([&] {
+ out << "const " << getCppStackType() << "* begin() { return static_begin(); }\n";
+ out << "const " << getCppStackType() << "* end() { return begin() + " << elementCount
+ << "; }\n";
+ out << "private:\n";
+ out << "static const " << getCppStackType() << "* static_begin() ";
+ out.block([&] {
+ out << "static const " << getCppStackType() << " kVals[" << elementCount << "] ";
+ out.block([&] {
+ auto enumerators = typeChain();
+ std::reverse(enumerators.begin(), enumerators.end());
+ for (const auto* type : enumerators) {
+ for (const auto* enumValue : type->mValues) {
+ out << fullName() << "::" << enumValue->name() << ",\n";
+ }
+ }
+ }) << ";\n";
+ out << "return &kVals[0];\n";
+ });
+ }) << ";\n\n";
}
void EnumType::emitEnumBitwiseOperator(
@@ -258,7 +378,17 @@
out << "}\n\n";
}
-status_t EnumType::emitGlobalTypeDeclarations(Formatter &out) const {
+void EnumType::emitGlobalTypeDeclarations(Formatter& out) const {
+ out << "namespace android {\n";
+ out << "namespace hardware {\n";
+
+ emitIteratorDeclaration(out);
+
+ out << "} // namespace hardware\n";
+ out << "} // namespace android\n";
+}
+
+void EnumType::emitPackageTypeDeclarations(Formatter& out) const {
emitEnumBitwiseOperator(out, true /* lhsIsEnum */, true /* rhsIsEnum */, "|");
emitEnumBitwiseOperator(out, false /* lhsIsEnum */, true /* rhsIsEnum */, "|");
emitEnumBitwiseOperator(out, true /* lhsIsEnum */, false /* rhsIsEnum */, "|");
@@ -269,40 +399,22 @@
emitBitFieldBitwiseAssignmentOperator(out, "|");
emitBitFieldBitwiseAssignmentOperator(out, "&");
- // toString for bitfields, equivalent to dumpBitfield in Java
- out << "template<typename>\n"
- << "std::string toString("
- << resolveToScalarType()->getCppArgumentType()
- << " o);\n";
- out << "template<>\n"
- << "std::string toString<" << getCppStackType() << ">("
- << resolveToScalarType()->getCppArgumentType()
- << " o);\n\n";
-
- // toString for enum itself
- out << "std::string toString("
- << getCppArgumentType()
- << " o);\n\n";
-
- return OK;
-}
-
-status_t EnumType::emitTypeDefinitions(Formatter &out, const std::string /* prefix */) const {
-
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != NULL);
+ out << "template<typename>\n"
+ << "static inline std::string toString(" << resolveToScalarType()->getCppArgumentType()
+ << " o);\n";
out << "template<>\n"
- << "std::string toString<" << getCppStackType() << ">("
- << scalarType->getCppArgumentType()
- << " o) ";
+ << "inline std::string toString<" << getCppStackType() << ">("
+ << scalarType->getCppArgumentType() << " o) ";
out.block([&] {
// include toHexString for scalar types
out << "using ::android::hardware::details::toHexString;\n"
<< "std::string os;\n"
- << getBitfieldType()->getCppStackType() << " flipped = 0;\n"
+ << getBitfieldCppType(StorageMode_Stack) << " flipped = 0;\n"
<< "bool first = true;\n";
- for (EnumValue *value : values()) {
+ forEachValueFromRoot([&](EnumValue* value) {
std::string valueName = fullName() + "::" + value->name();
out.sIf("(o & " + valueName + ")" +
" == static_cast<" + scalarType->getCppStackType() +
@@ -312,7 +424,7 @@
<< "first = false;\n"
<< "flipped |= " << valueName << ";\n";
}).endl();
- }
+ });
// put remaining bits
out.sIf("o != flipped", [&] {
out << "os += (first ? \"\" : \" | \");\n";
@@ -325,27 +437,23 @@
out << "return os;\n";
}).endl().endl();
- out << "std::string toString("
- << getCppArgumentType()
- << " o) ";
+ out << "static inline std::string toString(" << getCppArgumentType() << " o) ";
out.block([&] {
out << "using ::android::hardware::details::toHexString;\n";
- for (EnumValue *value : values()) {
+ forEachValueFromRoot([&](EnumValue* value) {
out.sIf("o == " + fullName() + "::" + value->name(), [&] {
out << "return \"" << value->name() << "\";\n";
}).endl();
- }
+ });
out << "std::string os;\n";
scalarType->emitHexDump(out, "os",
"static_cast<" + scalarType->getCppStackType() + ">(o)");
out << "return os;\n";
}).endl().endl();
-
- return OK;
}
-status_t EnumType::emitJavaTypeDeclarations(Formatter &out, bool atTopLevel) const {
+void EnumType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
CHECK(scalarType != NULL);
@@ -360,13 +468,14 @@
const std::string typeName =
scalarType->getJavaType(false /* forInitializer */);
- std::vector<const EnumType *> chain;
- getTypeChain(&chain);
+ std::vector<const EnumType*> chain = typeChain();
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
for (const auto &entry : type->values()) {
+ entry->emitDocComment(out);
+
out << "public static final "
<< typeName
<< " "
@@ -381,7 +490,7 @@
out << ";";
std::string comment = entry->comment();
- if (!comment.empty() && comment != value) {
+ if (!comment.empty()) {
out << " // " << comment;
}
@@ -392,34 +501,34 @@
out << "public static final String toString("
<< typeName << " o) ";
out.block([&] {
- for (EnumValue *value : values()) {
+ forEachValueFromRoot([&](EnumValue* value) {
out.sIf("o == " + value->name(), [&] {
out << "return \"" << value->name() << "\";\n";
}).endl();
- }
+ });
out << "return \"0x\" + ";
scalarType->emitConvertToJavaHexString(out, "o");
out << ";\n";
}).endl();
- auto bitfieldType = getBitfieldType()->getJavaType(false /* forInitializer */);
- auto bitfieldWrapperType = getBitfieldType()->getJavaWrapperType();
+ auto bitfieldType = getBitfieldJavaType(false /* forInitializer */);
+ auto bitfieldWrapperType = getBitfieldJavaWrapperType();
out << "\n"
<< "public static final String dumpBitfield("
<< bitfieldType << " o) ";
out.block([&] {
out << "java.util.ArrayList<String> list = new java.util.ArrayList<>();\n";
out << bitfieldType << " flipped = 0;\n";
- for (EnumValue *value : values()) {
+ forEachValueFromRoot([&](EnumValue* value) {
if (value->constExpr()->castSizeT() == 0) {
out << "list.add(\"" << value->name() << "\"); // " << value->name() << " == 0\n";
- continue;
+ return; // continue to next value
}
out.sIf("(o & " + value->name() + ") == " + value->name(), [&] {
out << "list.add(\"" << value->name() << "\");\n";
out << "flipped |= " << value->name() << ";\n";
}).endl();
- }
+ });
// put remaining bits
out.sIf("o != flipped", [&] {
out << "list.add(\"0x\" + ";
@@ -431,11 +540,9 @@
out.unindent();
out << "};\n\n";
-
- return OK;
}
-status_t EnumType::emitVtsTypeDeclarations(Formatter &out) const {
+void EnumType::emitVtsTypeDeclarations(Formatter& out) const {
const ScalarType *scalarType = mStorageType->resolveToScalarType();
out << "name: \"" << fullName() << "\"\n";
@@ -446,8 +553,7 @@
out << "scalar_type: \""
<< scalarType->getVtsScalarType()
<< "\"\n\n";
- std::vector<const EnumType *> chain;
- getTypeChain(&chain);
+ std::vector<const EnumType*> chain = typeChain();
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const auto &type = *it;
@@ -470,13 +576,11 @@
out.unindent();
out << "}\n";
- return OK;
}
-status_t EnumType::emitVtsAttributeType(Formatter &out) const {
+void EnumType::emitVtsAttributeType(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
out << "predefined_type: \"" << fullName() << "\"\n";
- return OK;
}
void EnumType::emitJavaDump(
@@ -487,19 +591,28 @@
<< name << "));\n";
}
-void EnumType::getTypeChain(std::vector<const EnumType *> *out) const {
- out->clear();
- const EnumType *type = this;
- for (;;) {
- out->push_back(type);
+std::vector<const EnumType*> EnumType::typeChain() const {
+ std::vector<const EnumType*> types;
+ for (const EnumType* type = this; type != nullptr;) {
+ types.push_back(type);
- const Type *superType = type->storageType();
- if (superType == NULL || !superType->isEnum()) {
- break;
+ const Type* superType = type->storageType();
+ if (superType != nullptr && superType->isEnum()) {
+ type = static_cast<const EnumType*>(superType);
+ } else {
+ type = nullptr;
}
-
- type = static_cast<const EnumType *>(superType);
}
+
+ return types;
+}
+
+std::vector<const EnumType*> EnumType::superTypeChain() const {
+ const Type* superType = storageType();
+ if (superType == nullptr || !superType->isEnum()) {
+ return {};
+ }
+ return static_cast<const EnumType*>(superType)->typeChain();
}
void EnumType::getAlignmentAndSize(size_t *align, size_t *size) const {
@@ -523,7 +636,7 @@
}
}
-status_t EnumType::emitExportedHeader(Formatter &out, bool forJava) const {
+void EnumType::emitExportedHeader(Formatter& out, bool forJava) const {
const Annotation *annotation = findExportAnnotation();
CHECK(annotation != nullptr);
@@ -557,7 +670,7 @@
std::vector<const EnumType *> chain;
if (exportParent) {
- getTypeChain(&chain);
+ chain = typeChain();
} else {
chain = { this };
}
@@ -596,7 +709,7 @@
out << ";";
std::string comment = entry->comment();
- if (!comment.empty() && comment != value) {
+ if (!comment.empty()) {
out << " // " << comment;
}
@@ -610,7 +723,7 @@
}
out << "\n";
- return OK;
+ return;
}
if (!name.empty()) {
@@ -634,7 +747,7 @@
out << ",";
std::string comment = entry->comment();
- if (!comment.empty() && comment != value) {
+ if (!comment.empty()) {
out << " // " << comment;
}
@@ -650,17 +763,12 @@
}
out << ";\n\n";
-
- return OK;
}
////////////////////////////////////////////////////////////////////////////////
-EnumValue::EnumValue(const char *name, ConstantExpression *value)
- : mName(name),
- mValue(value),
- mIsAutoFill(false) {
-}
+EnumValue::EnumValue(const char* name, ConstantExpression* value, const Location& location)
+ : mName(name), mValue(value), mLocation(location), mIsAutoFill(false) {}
std::string EnumValue::name() const {
return mName;
@@ -682,6 +790,7 @@
std::string EnumValue::comment() const {
CHECK(mValue != nullptr);
+ if (mValue->descriptionIsTrivial()) return "";
return mValue->description();
}
@@ -690,18 +799,21 @@
return mValue;
}
-void EnumValue::autofill(const EnumValue *prev, const ScalarType *type) {
- if(mValue != nullptr)
- return;
+void EnumValue::autofill(const EnumType* prevType, EnumValue* prevValue, const ScalarType* type) {
+ // Value is defined explicitly
+ if (mValue != nullptr) return;
+
+ CHECK((prevType == nullptr) == (prevValue == nullptr));
+
mIsAutoFill = true;
- ConstantExpression *value = new ConstantExpression();
- if(prev == nullptr) {
- *value = ConstantExpression::Zero(type->getKind());
+ if (prevValue == nullptr) {
+ mValue = ConstantExpression::Zero(type->getKind()).release();
} else {
- CHECK(prev->mValue != nullptr);
- *value = prev->mValue->addOne();
+ std::string description = prevType->fullName() + "." + prevValue->name() + " implicitly";
+ auto* prevReference = new ReferenceConstantExpression(
+ Reference<LocalIdentifier>(prevValue, mLocation), description);
+ mValue = prevReference->addOne(type->getKind()).release();
}
- mValue = value;
}
bool EnumValue::isAutoFill() const {
@@ -712,17 +824,28 @@
return true;
}
+const Location& EnumValue::location() const {
+ return mLocation;
+}
+
////////////////////////////////////////////////////////////////////////////////
+BitFieldType::BitFieldType(Scope* parent) : TemplatedType(parent) {}
+
bool BitFieldType::isBitField() const {
return true;
}
-std::string BitFieldType::typeName() const {
- return "mask" + (mElementType == nullptr ? "" : (" of " + mElementType->typeName()));
+const EnumType* BitFieldType::getElementEnumType() const {
+ CHECK(mElementType.get() != nullptr && mElementType->isEnum());
+ return static_cast<const EnumType*>(mElementType.get());
}
-bool BitFieldType::isCompatibleElementType(Type *elementType) const {
+std::string BitFieldType::templatedTypeName() const {
+ return "mask";
+}
+
+bool BitFieldType::isCompatibleElementType(const Type* elementType) const {
return elementType->isEnum();
}
@@ -732,11 +855,11 @@
std::string BitFieldType::getCppType(StorageMode mode,
bool specifyNamespaces) const {
- return resolveToScalarType()->getCppType(mode, specifyNamespaces);
+ return getElementEnumType()->getBitfieldCppType(mode, specifyNamespaces);
}
std::string BitFieldType::getJavaType(bool forInitializer) const {
- return resolveToScalarType()->getJavaType(forInitializer);
+ return getElementEnumType()->getBitfieldJavaType(forInitializer);
}
std::string BitFieldType::getJavaSuffix() const {
@@ -744,7 +867,7 @@
}
std::string BitFieldType::getJavaWrapperType() const {
- return resolveToScalarType()->getJavaWrapperType();
+ return getElementEnumType()->getBitfieldJavaWrapperType();
}
std::string BitFieldType::getVtsType() const {
@@ -755,18 +878,17 @@
return resolveToScalarType()->isElidableType();
}
-bool BitFieldType::canCheckEquality() const {
- return resolveToScalarType()->canCheckEquality();
+bool BitFieldType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
+ return resolveToScalarType()->canCheckEquality(visited);
}
-status_t BitFieldType::emitVtsAttributeType(Formatter &out) const {
+void BitFieldType::emitVtsAttributeType(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
out << "scalar_type: \""
<< mElementType->resolveToScalarType()->getVtsScalarType()
<< "\"\n";
- out << "predefined_type: \""
- << static_cast<NamedType *>(mElementType)->fullName() << "\"\n";
- return OK;
+ out << "predefined_type: \"" << static_cast<const NamedType*>(mElementType.get())->fullName()
+ << "\"\n";
}
void BitFieldType::getAlignmentAndSize(size_t *align, size_t *size) const {
@@ -790,9 +912,9 @@
true /* needsCast */);
}
-EnumType *BitFieldType::getEnumType() const {
+const EnumType* BitFieldType::getEnumType() const {
CHECK(mElementType->isEnum());
- return static_cast<EnumType *>(mElementType);
+ return static_cast<const EnumType*>(mElementType.get());
}
// a bitfield maps to the underlying scalar type in C++, so operator<< is
diff --git a/EnumType.h b/EnumType.h
index d829292..eef4eb2 100644
--- a/EnumType.h
+++ b/EnumType.h
@@ -19,6 +19,7 @@
#define ENUM_TYPE_H_
#include "ConstantExpression.h"
+#include "Reference.h"
#include "Scope.h"
#include <vector>
@@ -29,12 +30,15 @@
struct BitFieldType;
struct EnumType : public Scope {
- EnumType(const char* localName, const Location& location, Type* storageType, Scope* parent);
+ EnumType(const char* localName, const FQName& fullName, const Location& location,
+ const Reference<Type>& storageType, Scope* parent);
const Type *storageType() const;
const std::vector<EnumValue *> &values() const;
void addValue(EnumValue *value);
+ void forEachValueFromRoot(const std::function<void(EnumValue*)> f) const;
+
LocalIdentifier *lookupIdentifier(const std::string &name) const override;
bool isElidableType() const override;
@@ -42,7 +46,7 @@
std::string typeName() const override;
bool isEnum() const override;
- bool canCheckEquality() const override;
+ bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
std::string getCppType(StorageMode mode,
bool specifyNamespaces) const override;
@@ -55,8 +59,19 @@
std::string getVtsType() const override;
+ std::string getBitfieldCppType(StorageMode mode, bool specifyNamespaces = true) const;
+ std::string getBitfieldJavaType(bool forInitializer = false) const;
+ std::string getBitfieldJavaWrapperType() const;
+
// Return the type that corresponds to bitfield<T>.
- BitFieldType *getBitfieldType() const;
+ const BitFieldType* getBitfieldType() const;
+
+ std::vector<const Reference<Type>*> getReferences() const override;
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ status_t resolveInheritance() override;
+ status_t validate() const override;
+ status_t validateUniqueNames() const;
void emitReaderWriter(
Formatter &out,
@@ -75,15 +90,15 @@
const std::string &offset,
bool isReader) const override;
- status_t emitTypeDeclarations(Formatter &out) const override;
- status_t emitGlobalTypeDeclarations(Formatter &out) const override;
- status_t emitTypeDefinitions(Formatter &out, const std::string prefix) const override;
+ void emitTypeDeclarations(Formatter& out) const override;
+ void emitTypeForwardDeclaration(Formatter& out) const override;
+ void emitGlobalTypeDeclarations(Formatter& out) const override;
+ void emitPackageTypeDeclarations(Formatter& out) const override;
- status_t emitJavaTypeDeclarations(
- Formatter &out, bool atTopLevel) const override;
+ void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
- status_t emitVtsAttributeType(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
+ void emitVtsAttributeType(Formatter& out) const override;
void emitJavaDump(
Formatter &out,
@@ -95,12 +110,17 @@
void appendToExportedTypesVector(
std::vector<const Type *> *exportedTypes) const override;
- status_t emitExportedHeader(Formatter &out, bool forJava) const override;
+ void emitExportedHeader(Formatter& out, bool forJava) const override;
-private:
- void getTypeChain(std::vector<const EnumType *> *out) const;
+ private:
+ std::vector<const EnumType*> typeChain() const;
+ std::vector<const EnumType*> superTypeChain() const;
+
const Annotation *findExportAnnotation() const;
+ void emitIteratorDeclaration(Formatter& out) const;
+ void emitIteratorDefinitions(Formatter& out) const;
+
void emitEnumBitwiseOperator(
Formatter &out,
bool lhsIsEnum,
@@ -112,45 +132,50 @@
const std::string &op) const;
std::vector<EnumValue *> mValues;
- Type *mStorageType;
- BitFieldType *mBitfieldType;
+ Reference<Type> mStorageType;
DISALLOW_COPY_AND_ASSIGN(EnumType);
};
-struct EnumValue : public LocalIdentifier {
- EnumValue(const char *name, ConstantExpression *value = nullptr);
+struct EnumValue : public LocalIdentifier, DocCommentable {
+ EnumValue(const char* name, ConstantExpression* value, const Location& location);
std::string name() const;
std::string value(ScalarType::Kind castKind) const;
std::string cppValue(ScalarType::Kind castKind) const;
std::string javaValue(ScalarType::Kind castKind) const;
std::string comment() const;
- void autofill(const EnumValue *prev, const ScalarType *type);
- ConstantExpression *constExpr() const;
+ void autofill(const EnumType* prevType, EnumValue* prevValue, const ScalarType* type);
+ ConstantExpression* constExpr() const override;
bool isAutoFill() const;
bool isEnumValue() const override;
+ const Location& location() const;
+ private:
std::string mName;
- ConstantExpression *mValue;
+ ConstantExpression* mValue;
+ const Location mLocation;
bool mIsAutoFill;
DISALLOW_COPY_AND_ASSIGN(EnumValue);
};
struct BitFieldType : public TemplatedType {
+ BitFieldType(Scope* parent);
- std::string typeName() const override;
+ std::string templatedTypeName() const override;
+
+ const EnumType* getElementEnumType() const;
bool isBitField() const override;
- bool isCompatibleElementType(Type *elementType) const override;
+ bool isCompatibleElementType(const Type* elementType) const override;
bool isElidableType() const override;
- bool canCheckEquality() const override;
+ bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
const ScalarType *resolveToScalarType() const override;
@@ -165,9 +190,9 @@
std::string getVtsType() const override;
- EnumType *getEnumType() const;
+ const EnumType* getEnumType() const;
- status_t emitVtsAttributeType(Formatter &out) const override;
+ void emitVtsAttributeType(Formatter& out) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
diff --git a/FmqType.cpp b/FmqType.cpp
index ec32bd0..f5eaaa6 100644
--- a/FmqType.cpp
+++ b/FmqType.cpp
@@ -23,12 +23,11 @@
namespace android {
-FmqType::FmqType(const char *nsp, const char *name)
- : mNamespace(nsp), mName(name) {
-}
+FmqType::FmqType(const char* nsp, const char* name, Scope* parent)
+ : TemplatedType(parent), mNamespace(nsp), mName(name) {}
-std::string FmqType::typeName() const {
- return mName + " of " + mElementType->typeName();
+std::string FmqType::templatedTypeName() const {
+ return mName;
}
std::string FmqType::fullName() const {
@@ -138,7 +137,7 @@
mNamespace);
}
-bool FmqType::isJavaCompatible() const {
+bool FmqType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
@@ -160,7 +159,7 @@
return true;
}
-bool FmqType::isCompatibleElementType(Type *elementType) const {
+bool FmqType::isCompatibleElementType(const Type* elementType) const {
return (!elementType->isInterface() && !elementType->needsEmbeddedReadWrite());
}
@@ -169,9 +168,9 @@
return "TYPE_FMQ_SYNC";
} else if (mName == "MQDescriptorUnsync") {
return "TYPE_FMQ_UNSYNC";
- } else {
- LOG(ERROR) << "Invalid fmq type name.\n";
}
+
+ CHECK(false) << "Invalid FmqType.";
return "";
}
diff --git a/FmqType.h b/FmqType.h
index ef2b216..77ca150 100644
--- a/FmqType.h
+++ b/FmqType.h
@@ -23,11 +23,11 @@
namespace android {
struct FmqType : public TemplatedType {
- FmqType(const char *nsp, const char *name);
+ FmqType(const char* nsp, const char* name, Scope* parent);
std::string fullName() const;
- std::string typeName() const;
+ std::string templatedTypeName() const;
std::string getCppType(
StorageMode mode,
@@ -54,13 +54,13 @@
const std::string &parentName,
const std::string &offsetText) const override;
- bool isJavaCompatible() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
bool needsEmbeddedReadWrite() const override;
bool resultNeedsDeref() const override;
- bool isCompatibleElementType(Type *elementType) const override;
+ bool isCompatibleElementType(const Type* elementType) const override;
std::string getVtsType() const override;
std::string getVtsValueName() const override;
diff --git a/HandleType.cpp b/HandleType.cpp
index 0e198f6..7b6545d 100644
--- a/HandleType.cpp
+++ b/HandleType.cpp
@@ -23,7 +23,7 @@
namespace android {
-HandleType::HandleType() {}
+HandleType::HandleType(Scope* parent) : Type(parent) {}
bool HandleType::isHandle() const {
return true;
@@ -157,7 +157,7 @@
return true;
}
-bool HandleType::isJavaCompatible() const {
+bool HandleType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
@@ -167,9 +167,8 @@
*size = assertion.size();
}
-status_t HandleType::emitVtsTypeDeclarations(Formatter &out) const {
+void HandleType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
- return OK;
}
} // namespace android
diff --git a/HandleType.h b/HandleType.h
index ad1f681..feaddb0 100644
--- a/HandleType.h
+++ b/HandleType.h
@@ -23,7 +23,7 @@
namespace android {
struct HandleType : public Type {
- HandleType();
+ HandleType(Scope* parent);
bool isHandle() const override;
@@ -58,13 +58,13 @@
bool needsEmbeddedReadWrite() const override;
- bool isJavaCompatible() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
bool useNameInEmitReaderWriterEmbedded(bool isReader) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
};
} // namespace android
diff --git a/Hash.cpp b/Hash.cpp
index 032227f..7eb315b 100644
--- a/Hash.cpp
+++ b/Hash.cpp
@@ -16,6 +16,7 @@
#include "Hash.h"
+#include <algorithm>
#include <fstream>
#include <iomanip>
#include <map>
@@ -27,7 +28,9 @@
namespace android {
-const Hash &Hash::getHash(const std::string &path) {
+const std::vector<uint8_t> Hash::kEmptyHash = std::vector<uint8_t>(SHA256_DIGEST_LENGTH, 0);
+
+Hash& Hash::getMutableHash(const std::string& path) {
static std::map<std::string, Hash> hashes;
auto it = hashes.find(path);
@@ -39,6 +42,14 @@
return it->second;
}
+const Hash& Hash::getHash(const std::string& path) {
+ return getMutableHash(path);
+}
+
+void Hash::clearHash(const std::string& path) {
+ getMutableHash(path).mHash = kEmptyHash;
+}
+
static std::vector<uint8_t> sha256File(const std::string &path) {
std::ifstream stream(path);
std::stringstream fileStream;
@@ -156,16 +167,18 @@
std::map<std::string,std::vector<std::string>> hashes;
};
-std::vector<std::string> Hash::lookupHash(const std::string &path,
- const std::string &interfaceName,
- std::string *err) {
+std::vector<std::string> Hash::lookupHash(const std::string& path, const std::string& interfaceName,
+ std::string* err, bool* fileExists) {
*err = "";
const HashFile *file = HashFile::parse(path, err);
if (file == nullptr || err->size() > 0) {
+ if (fileExists != nullptr) *fileExists = false;
return {};
}
+ if (fileExists != nullptr) *fileExists = true;
+
return file->lookup(interfaceName);
}
diff --git a/Interface.cpp b/Interface.cpp
index 243d103..1c66b31 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -28,10 +28,11 @@
#include <unistd.h>
#include <iostream>
+#include <memory>
#include <sstream>
+#include <unordered_map>
#include <android-base/logging.h>
-#include <hidl-hash/Hash.h>
#include <hidl-util/Formatter.h>
#include <hidl-util/StringHelper.h>
@@ -67,14 +68,18 @@
LAST_HIDL_TRANSACTION = 0x0fffffff,
};
-Interface::Interface(const char* localName, const Location& location, Scope* parent,
- Interface* super)
- : Scope(localName, location, parent), mSuperType(super), mIsJavaCompatibleInProgress(false) {}
+Interface::Interface(const char* localName, const FQName& fullName, const Location& location,
+ Scope* parent, const Reference<Type>& superType, const Hash* fileHash)
+ : Scope(localName, fullName, location, parent), mSuperType(superType), mFileHash(fileHash) {}
std::string Interface::typeName() const {
return "interface " + localName();
}
+const Hash* Interface::getFileHash() const {
+ return mFileHash;
+}
+
bool Interface::fillPingMethod(Method *method) const {
if (method->name() != "ping") {
return false;
@@ -100,7 +105,6 @@
out << "return;\n";
}
},
- {IMPL_STUB, nullptr /* don't generate code */}
} /*javaImpl*/
);
@@ -211,7 +215,7 @@
out << "return ::android::hardware::Void();";
} } }, /*cppImpl */
{ { IMPL_INTERFACE, [](auto &out) { /* javaImpl */
- out << "android.os.SystemProperties.reportSyspropChanged();";
+ out << "android.os.HwBinder.enableInstrumentation();";
} } } /*javaImpl */
);
return true;
@@ -284,22 +288,21 @@
return true;
}
-static void emitDigestChain(
- Formatter &out,
- const std::string &prefix,
- const std::vector<const Interface *> &chain,
- std::function<std::string(const ConstantExpression &)> byteToString) {
- out.join(chain.begin(), chain.end(), ",\n", [&] (const auto &iface) {
- const Hash &hash = Hash::getHash(iface->location().begin().filename());
+void Interface::emitDigestChain(
+ Formatter& out, const std::string& prefix, const std::vector<const Interface*>& chain,
+ std::function<std::string(std::unique_ptr<ConstantExpression>)> byteToString) const {
+ out.join(chain.begin(), chain.end(), ",\n", [&](const auto& iface) {
out << prefix;
out << "{";
- out.join(hash.raw().begin(), hash.raw().end(), ",", [&](const auto &e) {
- // Use ConstantExpression::cppValue / javaValue
- // because Java used signed byte for uint8_t.
- out << byteToString(ConstantExpression::ValueOf(ScalarType::Kind::KIND_UINT8, e));
- });
+ out.join(
+ iface->getFileHash()->raw().begin(), iface->getFileHash()->raw().end(), ",",
+ [&](const auto& e) {
+ // Use ConstantExpression::cppValue / javaValue
+ // because Java used signed byte for uint8_t.
+ out << byteToString(ConstantExpression::ValueOf(ScalarType::Kind::KIND_UINT8, e));
+ });
out << "} /* ";
- out << hash.hexString();
+ out << iface->getFileHash()->hexString();
out << " */";
});
}
@@ -317,8 +320,8 @@
std::vector<const Interface *> chain = typeChain();
out << "_hidl_cb(";
out.block([&] {
- emitDigestChain(out, "(" + digestType->getInternalDataCppType() + ")",
- chain, [](const auto &e){return e.cppValue();});
+ emitDigestChain(out, "(" + digestType->getInternalDataCppType() + ")", chain,
+ [](const auto& e) { return e->cppValue(); });
});
out << ");\n";
out << "return ::android::hardware::Void();\n";
@@ -331,7 +334,7 @@
out.indent(2, [&] {
// No need for dimensions when elements are explicitly provided.
emitDigestChain(out, "new " + digestType->getJavaType(false /* forInitializer */),
- chain, [](const auto &e){return e.javaValue();});
+ chain, [](const auto& e) { return e->javaValue(); });
});
out << "));\n";
} } } /* javaImpl */
@@ -390,8 +393,7 @@
{IMPL_STUB_IMPL,
[](auto &out) {
out << "::android::hidl::base::V1_0::DebugInfo info = {};\n";
- out << "info.pid = ::android::hardware::details::debuggable()"
- << "? getpid() : -1 /* pid */,\n";
+ out << "info.pid = ::android::hardware::details::getPidIfSharable();\n";
out << "info.ptr = ::android::hardware::details::debuggable()"
<< "? reinterpret_cast<uint64_t>(this) : 0;\n";
out << "info.arch = \n" << sArch << ";\n";
@@ -404,10 +406,9 @@
const Type &refInfo = method->results().front()->type();
out << refInfo.getJavaType(false /* forInitializer */) << " info = new "
<< refInfo.getJavaType(true /* forInitializer */) << "();\n"
- // TODO(b/34777099): PID for java.
- << "info.pid = -1;\n"
+ << "info.pid = android.os.HidlSupport.getPidIfSharable();\n"
<< "info.ptr = 0;\n"
- << "info.arch = android.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;"
+ << "info.arch = android.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;\n"
<< "return info;";
} } } /* javaImpl */
);
@@ -444,8 +445,8 @@
bool Interface::addMethod(Method *method) {
if (isIBase()) {
if (!gAllReservedMethods.emplace(method->name(), method).second) {
- LOG(ERROR) << "ERROR: hidl-gen encountered duplicated reserved method "
- << method->name();
+ std::cerr << "ERROR: hidl-gen encountered duplicated reserved method " << method->name()
+ << std::endl;
return false;
}
// will add it in addAllReservedMethods
@@ -453,28 +454,151 @@
}
CHECK(!method->isHidlReserved());
- if (lookupMethod(method->name()) != nullptr) {
- LOG(ERROR) << "Redefinition of method " << method->name();
- return false;
- }
- size_t serial = FIRST_CALL_TRANSACTION;
-
- serial += userDefinedMethods().size();
-
- const Interface *ancestor = mSuperType;
- while (ancestor != nullptr) {
- serial += ancestor->userDefinedMethods().size();
- ancestor = ancestor->superType();
- }
-
- CHECK(serial <= LAST_CALL_TRANSACTION) << "More than "
- << LAST_CALL_TRANSACTION << " methods are not allowed.";
- method->setSerialId(serial);
mUserMethods.push_back(method);
return true;
}
+std::vector<const Reference<Type>*> Interface::getReferences() const {
+ std::vector<const Reference<Type>*> ret;
+
+ if (!isIBase()) {
+ ret.push_back(&mSuperType);
+ }
+
+ for (const auto* method : methods()) {
+ const auto& references = method->getReferences();
+ ret.insert(ret.end(), references.begin(), references.end());
+ }
+
+ return ret;
+}
+
+std::vector<const ConstantExpression*> Interface::getConstantExpressions() const {
+ std::vector<const ConstantExpression*> ret;
+ for (const auto* method : methods()) {
+ const auto& retMethod = method->getConstantExpressions();
+ ret.insert(ret.end(), retMethod.begin(), retMethod.end());
+ }
+ return ret;
+}
+
+std::vector<const Reference<Type>*> Interface::getStrongReferences() const {
+ // Interface is a special case as a reference:
+ // its definiton must be completed for extension but
+ // not necessary for other references.
+
+ std::vector<const Reference<Type>*> ret;
+ if (!isIBase()) {
+ ret.push_back(&mSuperType);
+ }
+
+ for (const auto* method : methods()) {
+ const auto& references = method->getStrongReferences();
+ ret.insert(ret.end(), references.begin(), references.end());
+ }
+
+ return ret;
+}
+
+status_t Interface::resolveInheritance() {
+ size_t serial = FIRST_CALL_TRANSACTION;
+ for (const auto* ancestor : superTypeChain()) {
+ serial += ancestor->mUserMethods.size();
+ }
+
+ for (Method* method : mUserMethods) {
+ if (serial > LAST_CALL_TRANSACTION) {
+ std::cerr << "ERROR: More than " << LAST_CALL_TRANSACTION
+ << " methods (including super and reserved) are not allowed at " << location()
+ << std::endl;
+ return UNKNOWN_ERROR;
+ }
+
+ method->setSerialId(serial);
+ serial++;
+ }
+
+ return Scope::resolveInheritance();
+}
+
+status_t Interface::validate() const {
+ CHECK(isIBase() == mSuperType.isEmptyReference());
+
+ if (!isIBase() && !mSuperType->isInterface()) {
+ std::cerr << "ERROR: You can only extend interfaces at " << mSuperType.location()
+ << std::endl;
+ return UNKNOWN_ERROR;
+ }
+
+ status_t err;
+
+ err = validateUniqueNames();
+ if (err != OK) return err;
+
+ err = validateAnnotations();
+ if (err != OK) return err;
+
+ return Scope::validate();
+}
+
+void Interface::getAlignmentAndSize(size_t* align, size_t* size) const {
+ *align = 8;
+ *size = 8;
+}
+
+status_t Interface::validateUniqueNames() const {
+ std::unordered_map<std::string, const Interface*> registeredMethodNames;
+ for (auto const& tuple : allSuperMethodsFromRoot()) {
+ // No need to check super method uniqueness
+ registeredMethodNames[tuple.method()->name()] = tuple.interface();
+ }
+
+ for (const Method* method : mUserMethods) {
+ auto registered = registeredMethodNames.find(method->name());
+
+ if (registered != registeredMethodNames.end()) {
+ const Interface* definedInType = registered->second;
+
+ if (definedInType == this) {
+ // Defined in this interface
+ std::cerr << "ERROR: Redefinition of method '" << method->name() << "'";
+ } else if (definedInType->isIBase()) {
+ // Defined in IBase
+ std::cerr << "ERROR: Redefinition of reserved method '" << method->name() << "'";
+ } else {
+ // Defined in super not IBase
+ std::cerr << "ERROR: Redefinition of method '" << method->name()
+ << "' defined in interface '" << definedInType->fullName() << "'";
+ }
+ std::cerr << " at " << method->location() << std::endl;
+ return UNKNOWN_ERROR;
+ }
+
+ registeredMethodNames[method->name()] = this;
+ }
+
+ return OK;
+}
+
+status_t Interface::validateAnnotations() const {
+ for (const Method* method : methods()) {
+ for (const Annotation* annotation : method->annotations()) {
+ const std::string name = annotation->name();
+
+ if (name == "entry" || name == "exit" || name == "callflow") {
+ continue;
+ }
+
+ std::cerr << "ERROR: Unrecognized annotation '" << name
+ << "' for method: " << method->name() << ". An annotation should be one of: "
+ << "entry, exit, callflow." << std::endl;
+ return UNKNOWN_ERROR;
+ }
+ }
+ return OK;
+}
+
bool Interface::addAllReservedMethods() {
// use a sorted map to insert them in serial ID order.
std::map<int32_t, Method *> reservedMethodsById;
@@ -492,15 +616,14 @@
|| fillDebugMethod(method);
if (!fillSuccess) {
- LOG(ERROR) << "ERROR: hidl-gen does not recognize a reserved method "
- << method->name();
+ std::cerr << "ERROR: hidl-gen does not recognize a reserved method " << method->name()
+ << std::endl;
return false;
}
if (!reservedMethodsById.emplace(method->getSerialId(), method).second) {
- LOG(ERROR) << "ERROR: hidl-gen uses duplicated serial id for "
- << method->name() << " and "
- << reservedMethodsById[method->getSerialId()]->name()
- << ", serialId = " << method->getSerialId();
+ std::cerr << "ERROR: hidl-gen uses duplicated serial id for " << method->name()
+ << " and " << reservedMethodsById[method->getSerialId()]->name()
+ << ", serialId = " << method->getSerialId() << std::endl;
return false;
}
}
@@ -510,8 +633,14 @@
return true;
}
-const Interface *Interface::superType() const {
- return mSuperType;
+const Interface* Interface::superType() const {
+ if (isIBase()) return nullptr;
+ if (!mSuperType->isInterface()) {
+ // This is actually an error
+ // that would be caught in validate
+ return nullptr;
+ }
+ return static_cast<const Interface*>(mSuperType.get());
}
std::vector<const Interface *> Interface::typeChain() const {
@@ -519,13 +648,13 @@
const Interface *iface = this;
while (iface != nullptr) {
v.push_back(iface);
- iface = iface->mSuperType;
+ iface = iface->superType();
}
return v;
}
std::vector<const Interface *> Interface::superTypeChain() const {
- return superType()->typeChain(); // should work even if superType is nullptr
+ return isIBase() ? std::vector<const Interface*>() : superType()->typeChain();
}
bool Interface::isElidableType() const {
@@ -571,21 +700,18 @@
return v;
}
-Method *Interface::lookupMethod(std::string name) const {
- for (const auto &tuple : allMethodsFromRoot()) {
- Method *method = tuple.method();
- if (method->name() == name) {
- return method;
- }
- }
-
- return nullptr;
+std::vector<InterfaceAndMethod> Interface::allSuperMethodsFromRoot() const {
+ return isIBase() ? std::vector<InterfaceAndMethod>() : superType()->allMethodsFromRoot();
}
std::string Interface::getBaseName() const {
return fqName().getInterfaceBaseName();
}
+std::string Interface::getAdapterName() const {
+ return fqName().getInterfaceAdapterName();
+}
+
std::string Interface::getProxyName() const {
return fqName().getInterfaceProxyName();
}
@@ -658,8 +784,7 @@
out << "{\n";
out.indent();
- const std::string binderName = "_hidl_" + name + "_binder";
-
+ const std::string binderName = "_hidl_binder";
out << "::android::sp<::android::hardware::IBinder> "
<< binderName << ";\n";
@@ -720,29 +845,10 @@
}
}
-status_t Interface::emitGlobalTypeDeclarations(Formatter &out) const {
- status_t status = Scope::emitGlobalTypeDeclarations(out);
- if (status != OK) {
- return status;
- }
- out << "std::string toString("
- << getCppArgumentType()
- << ");\n";
- return OK;
-}
+void Interface::emitPackageTypeDeclarations(Formatter& out) const {
+ Scope::emitPackageTypeDeclarations(out);
-
-status_t Interface::emitTypeDefinitions(
- Formatter &out, const std::string prefix) const {
- std::string space = prefix.empty() ? "" : (prefix + "::");
- status_t err = Scope::emitTypeDefinitions(out, space + localName());
- if (err != OK) {
- return err;
- }
-
- out << "std::string toString("
- << getCppArgumentType()
- << " o) ";
+ out << "static inline std::string toString(" << getCppArgumentType() << " o) ";
out.block([&] {
out << "std::string os = \"[class or subclass of \";\n"
@@ -751,8 +857,12 @@
<< "os += o->isRemote() ? \"@remote\" : \"@local\";\n"
<< "return os;\n";
}).endl().endl();
+}
- return OK;
+void Interface::emitTypeDefinitions(Formatter& out, const std::string& prefix) const {
+ std::string space = prefix.empty() ? "" : (prefix + "::");
+
+ Scope::emitTypeDefinitions(out, space + localName());
}
void Interface::emitJavaReaderWriter(
@@ -775,7 +885,7 @@
}
}
-status_t Interface::emitVtsAttributeDeclaration(Formatter &out) const {
+void Interface::emitVtsAttributeDeclaration(Formatter& out) const {
for (const auto &type : getSubTypes()) {
// Skip for TypeDef as it is just an alias of a defined type.
if (type->isTypeDef()) {
@@ -783,17 +893,13 @@
}
out << "attribute: {\n";
out.indent();
- status_t status = type->emitVtsTypeDeclarations(out);
- if (status != OK) {
- return status;
- }
+ type->emitVtsTypeDeclarations(out);
out.unindent();
out << "}\n\n";
}
- return OK;
}
-status_t Interface::emitVtsMethodDeclaration(Formatter &out) const {
+void Interface::emitVtsMethodDeclaration(Formatter& out) const {
for (const auto &method : methods()) {
if (method->isHidlReserved()) {
continue;
@@ -806,10 +912,7 @@
for (const auto &result : method->results()) {
out << "return_type_hidl: {\n";
out.indent();
- status_t status = result->type().emitVtsAttributeType(out);
- if (status != OK) {
- return status;
- }
+ result->type().emitVtsAttributeType(out);
out.unindent();
out << "}\n";
}
@@ -817,10 +920,7 @@
for (const auto &arg : method->args()) {
out << "arg: {\n";
out.indent();
- status_t status = arg->type().emitVtsAttributeType(out);
- if (status != OK) {
- return status;
- }
+ arg->type().emitVtsAttributeType(out);
out.unindent();
out << "}\n";
}
@@ -828,7 +928,7 @@
for (const auto &annotation : method->annotations()) {
out << "callflow: {\n";
out.indent();
- std::string name = annotation->name();
+ const std::string name = annotation->name();
if (name == "entry") {
out << "entry: true\n";
} else if (name == "exit") {
@@ -837,15 +937,12 @@
const AnnotationParam *param =
annotation->getParam("next");
if (param != nullptr) {
- for (auto value : *param->getValues()) {
+ for (const auto& value : param->getValues()) {
out << "next: " << value << "\n";
}
}
} else {
- std::cerr << "Unrecognized annotation '"
- << name << "' for method: " << method->name()
- << ". A VTS annotation should be one of: "
- << "entry, exit, callflow. \n";
+ CHECK(false);
}
out.unindent();
out << "}\n";
@@ -853,15 +950,13 @@
out.unindent();
out << "}\n\n";
}
- return OK;
}
-status_t Interface::emitVtsAttributeType(Formatter &out) const {
+void Interface::emitVtsAttributeType(Formatter& out) const {
out << "type: " << getVtsType() << "\n"
<< "predefined_type: \""
<< fullName()
<< "\"\n";
- return OK;
}
bool Interface::hasOnewayMethods() const {
@@ -880,37 +975,21 @@
return false;
}
-bool Interface::isJavaCompatible() const {
- if (mIsJavaCompatibleInProgress) {
- // We're currently trying to determine if this Interface is
- // java-compatible and something is referencing this interface through
- // one of its methods. Assume we'll ultimately succeed, if we were wrong
- // the original invocation of Interface::isJavaCompatible() will then
- // return the correct "false" result.
- return true;
- }
-
- if (mSuperType != nullptr && !mSuperType->isJavaCompatible()) {
- mIsJavaCompatibleInProgress = false;
+bool Interface::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+ if (superType() != nullptr && !superType()->isJavaCompatible(visited)) {
return false;
}
- mIsJavaCompatibleInProgress = true;
-
- if (!Scope::isJavaCompatible()) {
- mIsJavaCompatibleInProgress = false;
- return false;
- }
-
- for (const auto &method : methods()) {
- if (!method->isJavaCompatible()) {
- mIsJavaCompatibleInProgress = false;
+ for (const auto* method : methods()) {
+ if (!method->deepIsJavaCompatible(visited)) {
return false;
}
}
- mIsJavaCompatibleInProgress = false;
+ return Scope::isJavaCompatible(visited);
+}
+bool Interface::isNeverStrongReference() const {
return true;
}
diff --git a/Interface.h b/Interface.h
index b37dc60..0c94c0c 100644
--- a/Interface.h
+++ b/Interface.h
@@ -18,16 +18,28 @@
#define INTERFACE_H_
-#include "Scope.h"
#include <vector>
+#include <hidl-hash/Hash.h>
+
+#include "Reference.h"
+#include "Scope.h"
+
namespace android {
struct Method;
struct InterfaceAndMethod;
struct Interface : public Scope {
- Interface(const char* localName, const Location& location, Scope* parent, Interface* super);
+ enum {
+ /////////////////// Flag(s) - DO NOT CHANGE
+ FLAG_ONEWAY = 0x00000001,
+ };
+
+ Interface(const char* localName, const FQName& fullName, const Location& location,
+ Scope* parent, const Reference<Type>& superType, const Hash* fileHash);
+
+ const Hash* getFileHash() const;
bool addMethod(Method *method);
bool addAllReservedMethods();
@@ -35,13 +47,11 @@
bool isElidableType() const override;
bool isInterface() const override;
bool isBinder() const override;
- bool isRootType() const { return mSuperType == nullptr; }
bool isIBase() const { return fqName() == gIBaseFqName; }
std::string typeName() const override;
- const Interface *superType() const;
+ const Interface* superType() const;
- Method *lookupMethod(std::string name) const;
// Super type chain to root type.
// First element is superType().
std::vector<const Interface *> superTypeChain() const;
@@ -65,8 +75,12 @@
// this->hidlReservedMethods()
std::vector<InterfaceAndMethod> allMethodsFromRoot() const;
+ // allMethodsFromRoot for parent
+ std::vector<InterfaceAndMethod> allSuperMethodsFromRoot() const;
+
// aliases for corresponding methods in this->fqName()
std::string getBaseName() const;
+ std::string getAdapterName() const;
std::string getProxyName() const;
std::string getStubName() const;
std::string getPassthroughName() const;
@@ -82,6 +96,16 @@
std::string getJavaType(bool forInitializer) const override;
std::string getVtsType() const override;
+ std::vector<const Reference<Type>*> getReferences() const override;
+ std::vector<const Reference<Type>*> getStrongReferences() const override;
+
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ status_t resolveInheritance() override;
+ status_t validate() const override;
+ status_t validateUniqueNames() const;
+ status_t validateAnnotations() const;
+
void emitReaderWriter(
Formatter &out,
const std::string &name,
@@ -90,40 +114,49 @@
bool isReader,
ErrorMode mode) const override;
- status_t emitGlobalTypeDeclarations(Formatter &out) const override;
- status_t emitTypeDefinitions(
- Formatter &out, const std::string prefix) const override;
+ void emitPackageTypeDeclarations(Formatter& out) const override;
+ void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
+ void getAlignmentAndSize(size_t* align, size_t* size) const override;
void emitJavaReaderWriter(
Formatter &out,
const std::string &parcelObj,
const std::string &argName,
bool isReader) const override;
- status_t emitVtsAttributeType(Formatter &out) const override;
+ void emitVtsAttributeType(Formatter& out) const override;
- status_t emitVtsAttributeDeclaration(Formatter &out) const;
- status_t emitVtsMethodDeclaration(Formatter &out) const;
+ void emitVtsAttributeDeclaration(Formatter& out) const;
+ void emitVtsMethodDeclaration(Formatter& out) const;
bool hasOnewayMethods() const;
- bool isJavaCompatible() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
-private:
- Interface *mSuperType;
- std::vector<Method *> mUserMethods;
- std::vector<Method *> mReservedMethods;
- mutable bool mIsJavaCompatibleInProgress;
- bool fillPingMethod(Method *method) const;
- bool fillDescriptorChainMethod(Method *method) const;
- bool fillGetDescriptorMethod(Method *method) const;
- bool fillHashChainMethod(Method *method) const;
- bool fillSyspropsChangedMethod(Method *method) const;
- bool fillLinkToDeathMethod(Method *method) const;
- bool fillUnlinkToDeathMethod(Method *method) const;
- bool fillSetHALInstrumentationMethod(Method *method) const;
- bool fillGetDebugInfoMethod(Method *method) const;
- bool fillDebugMethod(Method *method) const;
+ bool isNeverStrongReference() const override;
+
+ private:
+ Reference<Type> mSuperType;
+
+ std::vector<Method*> mUserMethods;
+ std::vector<Method*> mReservedMethods;
+
+ const Hash* mFileHash;
+
+ bool fillPingMethod(Method* method) const;
+ bool fillDescriptorChainMethod(Method* method) const;
+ bool fillGetDescriptorMethod(Method* method) const;
+ bool fillHashChainMethod(Method* method) const;
+ bool fillSyspropsChangedMethod(Method* method) const;
+ bool fillLinkToDeathMethod(Method* method) const;
+ bool fillUnlinkToDeathMethod(Method* method) const;
+ bool fillSetHALInstrumentationMethod(Method* method) const;
+ bool fillGetDebugInfoMethod(Method* method) const;
+ bool fillDebugMethod(Method* method) const;
+
+ void emitDigestChain(
+ Formatter& out, const std::string& prefix, const std::vector<const Interface*>& chain,
+ std::function<std::string(std::unique_ptr<ConstantExpression>)> byteToString) const;
DISALLOW_COPY_AND_ASSIGN(Interface);
};
@@ -135,7 +168,8 @@
mMethod(method) {}
Method *method() const { return mMethod; }
const Interface *interface() const { return mInterface; }
-private:
+
+ private:
// do not own these objects.
const Interface *mInterface;
Method *mMethod;
diff --git a/Location.cpp b/Location.cpp
new file mode 100644
index 0000000..2d834fb
--- /dev/null
+++ b/Location.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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 "Location.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+
+Position::Position(std::string filename, size_t line, size_t column)
+ : mFilename(filename), mLine(line), mColumn(column) {}
+
+const std::string& Position::filename() const {
+ return mFilename;
+}
+
+size_t Position::line() const {
+ return mLine;
+}
+
+size_t Position::column() const {
+ return mColumn;
+}
+
+bool Position::inSameFile(const Position& lhs, const Position& rhs) {
+ return lhs.mFilename == rhs.mFilename;
+}
+
+bool Position::operator<(const Position& pos) const {
+ CHECK(inSameFile(*this, pos)) << "Cannot compare positions in different files";
+ if (mLine == pos.mLine) {
+ return mColumn < pos.mColumn;
+ }
+ return mLine < pos.mLine;
+}
+
+std::ostream& operator<<(std::ostream& ostr, const Position& pos) {
+ if (!pos.filename().empty()) {
+ ostr << pos.filename() << ":";
+ }
+ return ostr << pos.line() << "." << pos.column();
+}
+
+////////////////////////////////////////
+
+Location::Location(const Position& begin, const Position& end)
+ : mIsValid(true), mBegin(begin), mEnd(end) {}
+
+void Location::setLocation(const Position& begin, const Position& end) {
+ mIsValid = true;
+ mBegin = begin;
+ mEnd = end;
+}
+
+bool Location::isValid() const {
+ return mIsValid;
+}
+
+const Position& Location::begin() const {
+ return mBegin;
+}
+
+const Position& Location::end() const {
+ return mEnd;
+}
+
+Location Location::startOf(const std::string& path) {
+ return Location(Position(path, 1, 1), Position(path, 1, 1));
+}
+
+bool Location::inSameFile(const Location& lhs, const Location& rhs) {
+ return Position::inSameFile(lhs.mBegin, rhs.mBegin);
+}
+
+bool Location::intersect(const Location& lhs, const Location& rhs) {
+ if (!inSameFile(lhs, rhs)) return false;
+ return !(lhs.mEnd < rhs.mBegin || rhs.mEnd < lhs.mBegin);
+}
+
+bool Location::operator<(const Location& loc) const {
+ CHECK(inSameFile(*this, loc)) << "Cannot compare locations in different files";
+ CHECK(!intersect(*this, loc));
+ return mEnd < loc.mBegin;
+}
+
+std::ostream& operator<<(std::ostream& ostr, const Location& loc) {
+ Position last = Position(loc.end().filename(), loc.end().line(),
+ std::max<size_t>(1u, loc.end().column() - 1));
+ ostr << loc.begin();
+ if (loc.begin().filename() != last.filename()) {
+ ostr << "-" << last;
+ } else if (loc.begin().line() != last.line()) {
+ ostr << "-" << last.line() << "." << last.column();
+ } else if (loc.begin().column() != last.column()) {
+ ostr << "-" << last.column();
+ }
+ return ostr;
+}
+
+} // namespace android
diff --git a/Location.h b/Location.h
index 84dfc0e..2e99ab8 100644
--- a/Location.h
+++ b/Location.h
@@ -17,68 +17,67 @@
#ifndef LOCATION_H_
#define LOCATION_H_
-#include <iostream>
#include <stdint.h>
+#include <ostream>
#include <string>
// Mimics for yy::location and yy::position
namespace android {
struct Position {
- Position(std::string f, size_t l, size_t c)
- : mFilename(f), mLine(l), mColumn(c) {}
- inline const std::string &filename() const { return mFilename; }
- inline size_t line() const { return mLine; }
- inline size_t column() const { return mColumn; }
+ Position() = default;
+ Position(std::string filename, size_t line, size_t column);
-private:
+ const std::string& filename() const;
+
+ size_t line() const;
+ size_t column() const;
+
+ static bool inSameFile(const Position& lhs, const Position& rhs);
+
+ // Precondition: inSameFile()
+ bool operator<(const Position& pos) const;
+
+ private:
// File name to which this position refers.
- const std::string mFilename;
+ std::string mFilename;
// Current line number.
- const size_t mLine;
+ size_t mLine;
// Current column number.
- const size_t mColumn;
+ size_t mColumn;
};
-inline std::ostream& operator<< (std::ostream& ostr, const Position& pos) {
- if (!pos.filename().empty()) {
- ostr << pos.filename() << ":";
- }
- return ostr << pos.line() << "." << pos.column();
-}
+std::ostream& operator<<(std::ostream& ostr, const Position& pos);
struct Location {
- Location (Position begin, Position end)
- : mBegin(begin), mEnd(end) {}
- inline const Position &begin() const { return mBegin; }
- inline const Position &end() const { return mEnd; }
+ Location() = default;
+ Location(const Position& begin, const Position& end);
- inline static Location startOf(const std::string &path) {
- return Location(Position(path, 1, 1), Position(path, 1, 1));
- }
+ void setLocation(const Position& begin, const Position& end);
-private:
+ bool isValid() const;
+ const Position& begin() const;
+ const Position& end() const;
+
+ static Location startOf(const std::string& path);
+
+ static bool inSameFile(const Location& lhs, const Location& rhs);
+ static bool intersect(const Location& lhs, const Location& rhs);
+
+ // Precondition: inSameFile() && !intersect()
+ bool operator<(const Location& loc) const;
+
+ private:
+ bool mIsValid = false;
+
// Beginning of the located region.
- const Position mBegin;
+ Position mBegin;
// End of the located region.
- const Position mEnd;
+ Position mEnd;
};
-inline std::ostream& operator<< (std::ostream& ostr, const Location& loc) {
- Position last = Position(loc.end().filename(),
- loc.end().line(),
- std::max<size_t>(1u, loc.end().column() - 1));
- ostr << loc.begin();
- if (loc.begin().filename() != last.filename()) {
- ostr << "-" << last;
- } else if (loc.begin().line() != last.line()) {
- ostr << "-" << last.line() << "." << last.column();
- } else if (loc.begin().column() != last.column()) {
- ostr << "-" << last.column();
- }
- return ostr;
-}
+std::ostream& operator<<(std::ostream& ostr, const Location& loc);
} // namespace android
-#endif
+#endif // LOCATION_H_
diff --git a/MemoryType.cpp b/MemoryType.cpp
index 251691e..444ba92 100644
--- a/MemoryType.cpp
+++ b/MemoryType.cpp
@@ -23,7 +23,7 @@
namespace android {
-MemoryType::MemoryType() {}
+MemoryType::MemoryType(Scope* parent) : Type(parent) {}
std::string MemoryType::getCppType(StorageMode mode,
bool specifyNamespaces) const {
@@ -145,7 +145,7 @@
return true;
}
-bool MemoryType::isJavaCompatible() const {
+bool MemoryType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
@@ -155,9 +155,8 @@
*size = assertion.size();
}
-status_t MemoryType::emitVtsTypeDeclarations(Formatter &out) const {
+void MemoryType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
- return OK;
}
} // namespace android
diff --git a/MemoryType.h b/MemoryType.h
index cd7329a..7bce55f 100644
--- a/MemoryType.h
+++ b/MemoryType.h
@@ -23,7 +23,7 @@
namespace android {
struct MemoryType : public Type {
- MemoryType();
+ MemoryType(Scope* parent);
std::string typeName() const override;
@@ -58,11 +58,12 @@
bool resultNeedsDeref() const override;
bool isMemory() const override;
- bool isJavaCompatible() const override;
+
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
};
} // namespace android
diff --git a/Method.cpp b/Method.cpp
index 6c0ebf5..64a9a65 100644
--- a/Method.cpp
+++ b/Method.cpp
@@ -17,25 +17,25 @@
#include "Method.h"
#include "Annotation.h"
+#include "ConstantExpression.h"
#include "ScalarType.h"
#include "Type.h"
#include <android-base/logging.h>
#include <hidl-util/Formatter.h>
+#include <algorithm>
namespace android {
-Method::Method(const char *name,
- std::vector<TypedVar *> *args,
- std::vector<TypedVar *> *results,
- bool oneway,
- std::vector<Annotation *> *annotations)
+Method::Method(const char* name, std::vector<NamedReference<Type>*>* args,
+ std::vector<NamedReference<Type>*>* results, bool oneway,
+ std::vector<Annotation*>* annotations, const Location& location)
: mName(name),
mArgs(args),
mResults(results),
mOneway(oneway),
- mAnnotations(annotations) {
-}
+ mAnnotations(annotations),
+ mLocation(location) {}
void Method::fillImplementation(
size_t serial,
@@ -57,11 +57,11 @@
return mName;
}
-const std::vector<TypedVar *> &Method::args() const {
+const std::vector<NamedReference<Type>*>& Method::args() const {
return *mArgs;
}
-const std::vector<TypedVar *> &Method::results() const {
+const std::vector<NamedReference<Type>*>& Method::results() const {
return *mResults;
}
@@ -69,6 +69,56 @@
return *mAnnotations;
}
+std::vector<Reference<Type>*> Method::getReferences() {
+ const auto& constRet = static_cast<const Method*>(this)->getReferences();
+ std::vector<Reference<Type>*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+ return ret;
+}
+
+std::vector<const Reference<Type>*> Method::getReferences() const {
+ std::vector<const Reference<Type>*> ret;
+ ret.insert(ret.end(), mArgs->begin(), mArgs->end());
+ ret.insert(ret.end(), mResults->begin(), mResults->end());
+ return ret;
+}
+
+std::vector<Reference<Type>*> Method::getStrongReferences() {
+ const auto& constRet = static_cast<const Method*>(this)->getStrongReferences();
+ std::vector<Reference<Type>*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+ return ret;
+}
+
+std::vector<const Reference<Type>*> Method::getStrongReferences() const {
+ std::vector<const Reference<Type>*> ret;
+ for (const auto* ref : getReferences()) {
+ if (!ref->shallowGet()->isNeverStrongReference()) {
+ ret.push_back(ref);
+ }
+ }
+ return ret;
+}
+
+std::vector<ConstantExpression*> Method::getConstantExpressions() {
+ const auto& constRet = static_cast<const Method*>(this)->getConstantExpressions();
+ std::vector<ConstantExpression*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+ return ret;
+}
+
+std::vector<const ConstantExpression*> Method::getConstantExpressions() const {
+ std::vector<const ConstantExpression*> ret;
+ for (const auto* annotation : *mAnnotations) {
+ const auto& retAnnotation = annotation->getConstantExpressions();
+ ret.insert(ret.end(), retAnnotation.begin(), retAnnotation.end());
+ }
+ return ret;
+}
+
void Method::cppImpl(MethodImplType type, Formatter &out) const {
CHECK(mIsHidlReserved);
auto it = mCppImpl.find(type);
@@ -104,7 +154,7 @@
}
Method *Method::copySignature() const {
- return new Method(mName.c_str(), mArgs, mResults, mOneway, mAnnotations);
+ return new Method(mName.c_str(), mArgs, mResults, mOneway, mAnnotations, Location());
}
void Method::setSerialId(size_t serial) {
@@ -121,7 +171,7 @@
}
void Method::generateCppReturnType(Formatter &out, bool specifyNamespaces) const {
- const TypedVar *elidedReturn = canElideCallback();
+ const NamedReference<Type>* elidedReturn = canElideCallback();
const std::string space = (specifyNamespaces ? "::android::hardware::" : "");
if (elidedReturn == nullptr) {
@@ -149,9 +199,9 @@
out << ")";
}
-static void emitCppArgResultSignature(Formatter &out,
- const std::vector<TypedVar *> &args,
- bool specifyNamespaces) {
+static void emitCppArgResultSignature(Formatter& out,
+ const std::vector<NamedReference<Type>*>& args,
+ bool specifyNamespaces) {
out.join(args.begin(), args.end(), ", ", [&](auto arg) {
out << arg->type().getCppArgumentType(specifyNamespaces);
out << " ";
@@ -159,7 +209,8 @@
});
}
-static void emitJavaArgResultSignature(Formatter &out, const std::vector<TypedVar *> &args) {
+static void emitJavaArgResultSignature(Formatter& out,
+ const std::vector<NamedReference<Type>*>& args) {
out.join(args.begin(), args.end(), ", ", [&](auto arg) {
out << arg->type().getJavaType();
out << " ";
@@ -171,7 +222,7 @@
emitCppArgResultSignature(out, args(), specifyNamespaces);
const bool returnsValue = !results().empty();
- const TypedVar *elidedReturn = canElideCallback();
+ const NamedReference<Type>* elidedReturn = canElideCallback();
if (returnsValue && elidedReturn == nullptr) {
if (!args().empty()) {
out << ", ";
@@ -205,33 +256,31 @@
out << "\n";
}
-bool Method::isJavaCompatible() const {
+bool Method::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
if (isHiddenFromJava()) {
return true;
}
- for (const auto &arg : *mArgs) {
- if (!arg->isJavaCompatible()) {
- return false;
- }
+ if (!std::all_of(mArgs->begin(), mArgs->end(),
+ [&](const auto* arg) { return (*arg)->isJavaCompatible(visited); })) {
+ return false;
}
- for (const auto &result : *mResults) {
- if (!result->isJavaCompatible()) {
- return false;
- }
+ if (!std::all_of(mResults->begin(), mResults->end(),
+ [&](const auto* arg) { return (*arg)->isJavaCompatible(visited); })) {
+ return false;
}
return true;
}
-const TypedVar* Method::canElideCallback() const {
+const NamedReference<Type>* Method::canElideCallback() const {
// Can't elide callback for void or tuple-returning methods
if (mResults->size() != 1) {
return nullptr;
}
- const TypedVar *typedVar = mResults->at(0);
+ const NamedReference<Type>* typedVar = mResults->at(0);
if (typedVar->type().isElidableType()) {
return typedVar;
@@ -240,27 +289,13 @@
return nullptr;
}
-////////////////////////////////////////////////////////////////////////////////
-
-TypedVar::TypedVar(const char *name, Type *type)
- : mName(name),
- mType(type) {
-}
-
-std::string TypedVar::name() const {
- return mName;
-}
-
-const Type &TypedVar::type() const {
- return *mType;
-}
-
-bool TypedVar::isJavaCompatible() const {
- return mType->isJavaCompatible();
+const Location& Method::location() const {
+ return mLocation;
}
////////////////////////////////////////////////////////////////////////////////
-bool TypedVarVector::add(TypedVar *v) {
+
+bool TypedVarVector::add(NamedReference<Type>* v) {
if (mNames.emplace(v->name()).second) {
push_back(v);
return true;
diff --git a/Method.h b/Method.h
index 6d7664b..711f087 100644
--- a/Method.h
+++ b/Method.h
@@ -19,20 +19,26 @@
#define METHOD_H_
#include <android-base/macros.h>
-#include <functional>
#include <hidl-util/Formatter.h>
+#include <utils/Errors.h>
+#include <functional>
#include <map>
#include <set>
#include <string>
+#include <unordered_set>
#include <vector>
+#include "DocComment.h"
+#include "Location.h"
+#include "Reference.h"
+
namespace android {
struct Annotation;
+struct ConstantExpression;
struct Formatter;
struct ScalarType;
struct Type;
-struct TypedVar;
struct TypedVarVector;
enum MethodImplType {
@@ -45,16 +51,14 @@
using MethodImpl = std::map<MethodImplType, std::function<void(Formatter &)>>;
-struct Method {
- Method(const char *name,
- std::vector<TypedVar *> *args,
- std::vector<TypedVar *> *results,
- bool oneway,
- std::vector<Annotation *> *annotations);
+struct Method : DocCommentable {
+ Method(const char* name, std::vector<NamedReference<Type>*>* args,
+ std::vector<NamedReference<Type>*>* results, bool oneway,
+ std::vector<Annotation*>* annotations, const Location& location);
std::string name() const;
- const std::vector<TypedVar *> &args() const;
- const std::vector<TypedVar *> &results() const;
+ const std::vector<NamedReference<Type>*>& args() const;
+ const std::vector<NamedReference<Type>*>& results() const;
bool isOneway() const { return mOneway; }
bool overridesCppImpl(MethodImplType type) const;
bool overridesJavaImpl(MethodImplType type) const;
@@ -64,6 +68,15 @@
bool isHiddenFromJava() const;
const std::vector<Annotation *> &annotations() const;
+ std::vector<Reference<Type>*> getReferences();
+ std::vector<const Reference<Type>*> getReferences() const;
+
+ std::vector<Reference<Type>*> getStrongReferences();
+ std::vector<const Reference<Type>*> getStrongReferences() const;
+
+ std::vector<ConstantExpression*> getConstantExpressions();
+ std::vector<const ConstantExpression*> getConstantExpressions() const;
+
// Make a copy with the same name, args, results, oneway, annotations.
// Implementations, serial are not copied.
Method *copySignature() const;
@@ -90,17 +103,19 @@
void emitJavaArgSignature(Formatter &out) const;
void emitJavaResultSignature(Formatter &out) const;
- const TypedVar* canElideCallback() const;
+ const NamedReference<Type>* canElideCallback() const;
void dumpAnnotations(Formatter &out) const;
- bool isJavaCompatible() const;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const;
-private:
+ const Location& location() const;
+
+ private:
std::string mName;
size_t mSerial = 0;
- std::vector<TypedVar *> *mArgs;
- std::vector<TypedVar *> *mResults;
+ std::vector<NamedReference<Type>*>* mArgs;
+ std::vector<NamedReference<Type>*>* mResults;
bool mOneway;
std::vector<Annotation *> *mAnnotations;
@@ -110,29 +125,17 @@
MethodImpl mCppImpl;
MethodImpl mJavaImpl;
+ const Location mLocation;
+
DISALLOW_COPY_AND_ASSIGN(Method);
};
-struct TypedVar {
- TypedVar(const char *name, Type *type);
-
- std::string name() const;
- const Type &type() const;
-
- bool isJavaCompatible() const;
-
-private:
- std::string mName;
- Type *mType;
-
- DISALLOW_COPY_AND_ASSIGN(TypedVar);
-};
-
-struct TypedVarVector : public std::vector<TypedVar *> {
+struct TypedVarVector : public std::vector<NamedReference<Type>*> {
TypedVarVector() = default;
- bool add(TypedVar *v);
-private:
+ bool add(NamedReference<Type>* v);
+
+ private:
std::set<std::string> mNames;
};
diff --git a/NamedType.cpp b/NamedType.cpp
index f6d876f..645c63d 100644
--- a/NamedType.cpp
+++ b/NamedType.cpp
@@ -18,17 +18,14 @@
namespace android {
-NamedType::NamedType(const char* localName, const Location& loc, Scope* parent)
- : mLocalName(localName), mLocation(loc), mParent(parent) {}
+NamedType::NamedType(const char* localName, const FQName& fullName, const Location& loc,
+ Scope* parent)
+ : Type(parent), mLocalName(localName), mFullName(fullName), mLocation(loc) {}
bool NamedType::isNamedType() const {
return true;
}
-void NamedType::setFullName(const FQName &fullName) {
- mFullName = fullName;
-}
-
const FQName &NamedType::fqName() const {
return mFullName;
}
@@ -41,10 +38,6 @@
return mFullName.cppName();
}
-std::string NamedType::partialCppName() const {
- return mFullName.cppLocalName();
-}
-
std::string NamedType::fullJavaName() const {
return mFullName.javaName();
}
@@ -53,10 +46,6 @@
return mLocation;
}
-Scope* NamedType::parent() const {
- return mParent;
-}
-
void NamedType::emitDump(
Formatter &out,
const std::string &streamName,
diff --git a/NamedType.h b/NamedType.h
index cfc3df8..857f3a3 100644
--- a/NamedType.h
+++ b/NamedType.h
@@ -27,41 +27,31 @@
namespace android {
-struct Scope;
-
struct NamedType : public Type {
- NamedType(const char* localName, const Location& loc, Scope* parent);
+ NamedType(const char* localName, const FQName& fullName, const Location& loc, Scope* parent);
bool isNamedType() const override;
- void setFullName(const FQName &fullName);
-
const FQName &fqName() const;
std::string localName() const;
/* short for fqName().cppName() */
std::string fullName() const;
- /* short for fqName().cppLocalName() */
- std::string partialCppName() const;
/* short for fqName().fullJavaName() */
std::string fullJavaName() const;
- // returns null if no location is set for this type.
- const Location &location() const;
+ const Location& location() const;
void emitDump(
Formatter &out,
const std::string &streamName,
const std::string &name) const override;
- Scope* parent() const;
-
private:
- std::string mLocalName;
- FQName mFullName;
- Location mLocation;
- Scope* const mParent;
+ const std::string mLocalName;
+ const FQName mFullName;
+ const Location mLocation;
DISALLOW_COPY_AND_ASSIGN(NamedType);
};
diff --git a/PointerType.cpp b/PointerType.cpp
index 8684e4e..cdf4a99 100644
--- a/PointerType.cpp
+++ b/PointerType.cpp
@@ -21,7 +21,7 @@
namespace android {
-PointerType::PointerType() {}
+PointerType::PointerType(Scope* parent) : Type(parent) {}
bool PointerType::isPointer() const {
return true;
@@ -63,17 +63,16 @@
return false;
}
-bool PointerType::isJavaCompatible() const {
+bool PointerType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
-bool PointerType::containsPointer() const {
+bool PointerType::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
-status_t PointerType::emitVtsTypeDeclarations(Formatter &out) const {
+void PointerType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
- return OK;
}
} // namespace android
diff --git a/PointerType.h b/PointerType.h
index 4fb6a2f..30c4a32 100644
--- a/PointerType.h
+++ b/PointerType.h
@@ -23,7 +23,7 @@
namespace android {
struct PointerType : public Type {
- PointerType();
+ PointerType(Scope* parent);
bool isPointer() const override;
@@ -48,10 +48,10 @@
bool needsEmbeddedReadWrite() const override;
bool resultNeedsDeref() const override;
- bool isJavaCompatible() const override;
- bool containsPointer() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+ bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
};
} // namespace android
diff --git a/RefType.cpp b/RefType.cpp
index b43c832..9947399 100644
--- a/RefType.cpp
+++ b/RefType.cpp
@@ -24,11 +24,14 @@
namespace android {
-RefType::RefType() {
+RefType::RefType(Scope* parent) : TemplatedType(parent) {}
+
+std::string RefType::templatedTypeName() const {
+ return "ref";
}
-std::string RefType::typeName() const {
- return "ref" + (mElementType == nullptr ? "" : (" of " + mElementType->typeName()));
+std::vector<const Reference<Type>*> RefType::getStrongReferences() const {
+ return {};
}
std::string RefType::getVtsType() const {
@@ -39,7 +42,7 @@
return "ref_value";
}
-bool RefType::isCompatibleElementType(Type *elementType) const {
+bool RefType::isCompatibleElementType(const Type* elementType) const {
if (elementType->isScalar()) {
return true;
}
@@ -52,15 +55,17 @@
if (elementType->isBitField()) {
return true;
}
- if (elementType->isCompoundType()
- && static_cast<CompoundType *>(elementType)->style() == CompoundType::STYLE_STRUCT) {
+ if (elementType->isCompoundType() &&
+ static_cast<const CompoundType*>(elementType)->style() == CompoundType::STYLE_STRUCT) {
return true;
}
if (elementType->isTemplatedType()) {
- return this->isCompatibleElementType(static_cast<TemplatedType *>(elementType)->getElementType());
+ return this->isCompatibleElementType(
+ static_cast<const TemplatedType*>(elementType)->getElementType());
}
if (elementType->isArray()) {
- return this->isCompatibleElementType(static_cast<ArrayType *>(elementType)->getElementType());
+ return this->isCompatibleElementType(
+ static_cast<const ArrayType*>(elementType)->getElementType());
}
return false;
}
@@ -225,7 +230,7 @@
out << "}\n\n";
}
-bool RefType::needsResolveReferences() const {
+bool RefType::deepNeedsResolveReferences(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
@@ -237,11 +242,11 @@
return false;
}
-bool RefType::isJavaCompatible() const {
+bool RefType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
-bool RefType::containsPointer() const {
+bool RefType::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
diff --git a/RefType.h b/RefType.h
index c44b8ad..d7b428c 100644
--- a/RefType.h
+++ b/RefType.h
@@ -18,15 +18,21 @@
#define REF_TYPE_H_
+#include <vector>
+
+#include "Reference.h"
#include "Type.h"
namespace android {
struct RefType : public TemplatedType {
- RefType();
+ RefType(Scope* parent);
- std::string typeName() const override;
- bool isCompatibleElementType(Type *elementType) const override;
+ std::string templatedTypeName() const override;
+
+ bool isCompatibleElementType(const Type* elementType) const override;
+
+ std::vector<const Reference<Type>*> getStrongReferences() const override;
std::string getCppType(StorageMode mode,
bool specifyNamespaces) const override;
@@ -65,13 +71,13 @@
const std::string &offsetText) const override;
bool needsEmbeddedReadWrite() const override;
- bool needsResolveReferences() const override;
+ bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
bool resultNeedsDeref() const override;
- bool isJavaCompatible() const override;
- bool containsPointer() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+ bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
- private:
+ private:
DISALLOW_COPY_AND_ASSIGN(RefType);
};
diff --git a/Reference.h b/Reference.h
new file mode 100644
index 0000000..7ddc889
--- /dev/null
+++ b/Reference.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef REFERENCE_H_
+
+#define REFERENCE_H_
+
+#include <android-base/logging.h>
+#include <hidl-util/FQName.h>
+
+#include "DocComment.h"
+#include "Location.h"
+
+namespace android {
+
+/**
+ * Reference placeholder
+ */
+template <class T>
+struct Reference {
+ Reference() = default;
+ virtual ~Reference() {}
+
+ Reference(const FQName& fqName, const Location& location)
+ : mResolved(nullptr), mFqName(fqName), mLocation(location) {}
+
+ Reference(T* type, const Location& location) : mResolved(type), mLocation(location) {
+ CHECK(type != nullptr);
+ }
+
+ template <class OtherT>
+ Reference(const Reference<OtherT>& ref)
+ : mResolved(ref.mResolved), mFqName(ref.mFqName), mLocation(ref.mLocation) {}
+
+ template <class OtherT>
+ Reference(const Reference<OtherT>& ref, const Location& location)
+ : mResolved(ref.mResolved), mFqName(ref.mFqName), mLocation(location) {}
+
+ /* Returns true iff referred type is resolved
+ Referred type's field might be not resolved */
+ bool isResolved() const { return mResolved != nullptr; }
+
+ T* operator->() { return get(); }
+ const T* operator->() const { return get(); }
+
+ /* Returns referenced object.
+ If a type is referenced, all typedefs are unwrapped. */
+ T* get() {
+ CHECK(mResolved != nullptr);
+ return mResolved->resolve();
+ }
+ const T* get() const {
+ CHECK(mResolved != nullptr);
+ return mResolved->resolve();
+ }
+
+ /* Returns exact referenced object.
+ If a type is referenced, typedefs are not unwraped. */
+ T* shallowGet() {
+ CHECK(mResolved != nullptr);
+ return mResolved;
+ }
+ const T* shallowGet() const {
+ CHECK(mResolved != nullptr);
+ return mResolved;
+ }
+
+ void set(T* resolved) {
+ CHECK(!isResolved());
+ CHECK(resolved != nullptr);
+ mResolved = resolved;
+ }
+
+ /* Returns true iff this is reference to null:
+ not resolved and has not name for lookup */
+ bool isEmptyReference() const { return !isResolved() && !hasLookupFqName(); }
+
+ const FQName& getLookupFqName() const {
+ CHECK(hasLookupFqName());
+ return mFqName;
+ }
+
+ bool hasLocation() const { return mLocation.isValid(); }
+
+ const Location& location() const {
+ CHECK(hasLocation());
+ return mLocation;
+ }
+
+ private:
+ /* Referred type */
+ T* mResolved = nullptr;
+ /* Reference name for lookup */
+ FQName mFqName;
+ /* Reference location is mainly used for printing errors
+ and handling forward reference restrictions */
+ Location mLocation;
+
+ bool hasLookupFqName() const {
+ // Valid only while not resolved to prevent confusion when
+ // ref.hasLookupFqName() is false while ref,get()->fqName is valid.
+ CHECK(!isResolved());
+ return mFqName.isValid();
+ }
+
+ template <class OtherT>
+ friend struct Reference;
+};
+
+template <class T>
+struct NamedReference : public Reference<T>, DocCommentable {
+ NamedReference(const std::string& name, const Reference<T>& reference, const Location& location)
+ : Reference<T>(reference, location), mName(name) {}
+
+ const std::string& name() const { return mName; }
+
+ // TODO(b/64715470) Legacy
+ const T& type() const { return *Reference<T>::get(); }
+
+ private:
+ const std::string mName;
+};
+
+} // namespace android
+
+#endif // REFERENCE_H_
diff --git a/ScalarType.cpp b/ScalarType.cpp
index fefd418..2e2e7b2 100644
--- a/ScalarType.cpp
+++ b/ScalarType.cpp
@@ -20,9 +20,7 @@
namespace android {
-ScalarType::ScalarType(Kind kind)
- : mKind(kind) {
-}
+ScalarType::ScalarType(Kind kind, Scope* parent) : Type(parent), mKind(kind) {}
const ScalarType *ScalarType::resolveToScalarType() const {
return this;
@@ -41,7 +39,7 @@
return true;
}
-bool ScalarType::canCheckEquality() const {
+bool ScalarType::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
@@ -279,10 +277,9 @@
<< ");\n";
}
-status_t ScalarType::emitVtsTypeDeclarations(Formatter &out) const {
+void ScalarType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
out << "scalar_type: \"" << getVtsScalarType() << "\"\n";
- return OK;
}
void ScalarType::getAlignmentAndSize(size_t *align, size_t *size) const {
diff --git a/ScalarType.h b/ScalarType.h
index 737da9a..86305b8 100644
--- a/ScalarType.h
+++ b/ScalarType.h
@@ -37,14 +37,14 @@
KIND_DOUBLE,
};
- ScalarType(Kind kind);
+ ScalarType(Kind kind, Scope* parent);
bool isScalar() const override;
bool isElidableType() const override;
const ScalarType *resolveToScalarType() const override;
- bool canCheckEquality() const override;
+ bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
std::string typeName() const override;
bool isValidEnumStorageType() const;
@@ -96,7 +96,7 @@
const std::string &offset,
bool isReader) const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
diff --git a/Scope.cpp b/Scope.cpp
index 13a6f28..bff097f 100644
--- a/Scope.cpp
+++ b/Scope.cpp
@@ -16,42 +16,44 @@
#include "Scope.h"
+#include "Annotation.h"
+#include "ConstantExpression.h"
#include "Interface.h"
#include <android-base/logging.h>
#include <hidl-util/Formatter.h>
+#include <hidl-util/StringHelper.h>
+#include <algorithm>
+#include <iostream>
#include <vector>
namespace android {
-Scope::Scope(const char* localName, const Location& location, Scope* parent)
- : NamedType(localName, location, parent) {}
+Scope::Scope(const char* localName, const FQName& fullName, const Location& location, Scope* parent)
+ : NamedType(localName, fullName, location, parent) {}
Scope::~Scope(){}
-bool Scope::addType(NamedType *type, std::string *errorMsg) {
- const std::string &localName = type->localName();
-
- auto it = mTypeIndexByName.find(localName);
-
- if (it != mTypeIndexByName.end()) {
- *errorMsg = "A type named '";
- (*errorMsg) += localName;
- (*errorMsg) += "' is already declared in the current scope.";
-
- return false;
- }
-
+void Scope::addType(NamedType* type) {
size_t index = mTypes.size();
mTypes.push_back(type);
- mTypeIndexByName[localName] = index;
+ mTypeIndexByName[type->localName()] = index;
+}
- return true;
+status_t Scope::validateUniqueNames() const {
+ for (const auto* type : mTypes) {
+ if (mTypes[mTypeIndexByName.at(type->localName())] != type) {
+ std::cerr << "ERROR: A type named '" << type->localName()
+ << "' is already declared in the scope at " << type->location() << std::endl;
+ return UNKNOWN_ERROR;
+ }
+ }
+ return OK;
}
NamedType *Scope::lookupType(const FQName &fqName) const {
CHECK(fqName.package().empty() && fqName.version().empty());
if (!fqName.valueName().empty()) {
- LOG(WARNING) << fqName.string() << " does not refer to a type.";
+ std::cerr << "ERROR: " << fqName.string() << " does not refer to a type." << std::endl;
return nullptr;
}
std::vector<std::string> names = fqName.names();
@@ -73,7 +75,8 @@
Scope *outerScope = static_cast<Scope *>(outerType);
// *slowly* pop first element
names.erase(names.begin());
- FQName innerName(names);
+ FQName innerName;
+ CHECK(FQName::parse(StringHelper::JoinStrings(names, "."), &innerName));
return outerScope->lookupType(innerName);
}
@@ -103,96 +106,144 @@
return false;
}
-status_t Scope::forEachType(std::function<status_t(Type *)> func) const {
- for (size_t i = 0; i < mTypes.size(); ++i) {
- status_t err = func(mTypes[i]);
+const std::vector<Annotation*>& Scope::annotations() const {
+ return mAnnotations;
+}
- if (err != OK) {
- return err;
- }
+void Scope::setAnnotations(std::vector<Annotation*>* annotations) {
+ CHECK(mAnnotations.empty());
+ CHECK(annotations != nullptr);
+ mAnnotations = *annotations;
+}
+
+std::vector<const Type*> Scope::getDefinedTypes() const {
+ std::vector<const Type*> ret;
+ ret.insert(ret.end(), mTypes.begin(), mTypes.end());
+ return ret;
+}
+
+std::vector<const ConstantExpression*> Scope::getConstantExpressions() const {
+ std::vector<const ConstantExpression*> ret;
+ for (const auto* annotation : mAnnotations) {
+ const auto& retAnnotation = annotation->getConstantExpressions();
+ ret.insert(ret.end(), retAnnotation.begin(), retAnnotation.end());
+ }
+ return ret;
+}
+
+void Scope::topologicalReorder(const std::unordered_map<const Type*, size_t>& reversedOrder) {
+ auto less = [&](const Type* lhs, const Type* rhs) {
+ return reversedOrder.at(lhs) < reversedOrder.at(rhs);
+ };
+
+ if (std::is_sorted(mTypes.begin(), mTypes.end(), less)) return;
+
+ mTypeOrderChanged = true;
+ std::sort(mTypes.begin(), mTypes.end(), less);
+
+ for (size_t i = 0; i != mTypes.size(); ++i) {
+ mTypeIndexByName.at(mTypes[i]->localName()) = i;
+ }
+}
+
+void Scope::emitTypeDeclarations(Formatter& out) const {
+ if (mTypes.empty()) return;
+
+ out << "// Forward declaration for forward reference support:\n";
+ for (const Type* type : mTypes) {
+ type->emitTypeForwardDeclaration(out);
+ }
+ out << "\n";
+
+ if (mTypeOrderChanged) {
+ out << "// Order of inner types was changed for forward reference support.\n\n";
}
- return OK;
+ for (const Type* type : mTypes) {
+ type->emitDocComment(out);
+ type->emitTypeDeclarations(out);
+ }
}
-status_t Scope::emitTypeDeclarations(Formatter &out) const {
- return forEachType([&](Type *type) {
- return type->emitTypeDeclarations(out);
- });
+void Scope::emitGlobalTypeDeclarations(Formatter& out) const {
+ for (const Type* type : mTypes) {
+ type->emitGlobalTypeDeclarations(out);
+ }
}
-status_t Scope::emitGlobalTypeDeclarations(Formatter &out) const {
- return forEachType([&](Type *type) {
- return type->emitGlobalTypeDeclarations(out);
- });
+void Scope::emitPackageTypeDeclarations(Formatter& out) const {
+ for (const Type* type : mTypes) {
+ type->emitPackageTypeDeclarations(out);
+ }
}
-status_t Scope::emitGlobalHwDeclarations(Formatter &out) const {
- return forEachType([&](Type *type) {
- return type->emitGlobalHwDeclarations(out);
- });
+void Scope::emitPackageHwDeclarations(Formatter& out) const {
+ for (const Type* type : mTypes) {
+ type->emitPackageHwDeclarations(out);
+ }
}
-status_t Scope::emitJavaTypeDeclarations(
- Formatter &out, bool atTopLevel) const {
- return forEachType([&](Type *type) {
- return type->emitJavaTypeDeclarations(out, atTopLevel);
- });
+void Scope::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const {
+ if (mTypeOrderChanged) {
+ out << "// Order of inner types was changed for forward reference support.\n\n";
+ }
+
+ for (const Type* type : mTypes) {
+ type->emitDocComment(out);
+ type->emitJavaTypeDeclarations(out, atTopLevel);
+ }
}
-status_t Scope::emitTypeDefinitions(
- Formatter &out, const std::string prefix) const {
- return forEachType([&](Type *type) {
- return type->emitTypeDefinitions(out, prefix);
- });
+void Scope::emitTypeDefinitions(Formatter& out, const std::string& prefix) const {
+ for (const Type* type : mTypes) {
+ type->emitTypeDefinitions(out, prefix);
+ }
}
const std::vector<NamedType *> &Scope::getSubTypes() const {
return mTypes;
}
-status_t Scope::emitVtsTypeDeclarations(Formatter &out) const {
- return forEachType([&](Type *type) {
- return type->emitVtsTypeDeclarations(out);
- });
+void Scope::emitVtsTypeDeclarations(Formatter& out) const {
+ for (const Type* type : mTypes) {
+ type->emitVtsTypeDeclarations(out);
+ }
}
-bool Scope::isJavaCompatible() const {
- for (const auto &type : mTypes) {
- if (!type->isJavaCompatible()) {
+bool Scope::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+ for (const Type* type : mTypes) {
+ if (!type->isJavaCompatible(visited)) {
return false;
}
}
-
- return true;
-}
-
-bool Scope::containsPointer() const {
- for (const auto &type : mTypes) {
- if (type->containsPointer()) {
- return true;
- }
- }
-
- return false;
+ return Type::deepIsJavaCompatible(visited);
}
void Scope::appendToExportedTypesVector(
std::vector<const Type *> *exportedTypes) const {
- forEachType([&](Type *type) {
+ for (const Type* type : mTypes) {
type->appendToExportedTypesVector(exportedTypes);
- return OK;
- });
+ }
}
-RootScope::RootScope(const char* localName, const Location& location, Scope* parent)
- : Scope(localName, location, parent) {}
+////////////////////////////////////////
+
+RootScope::RootScope(const char* localName, const FQName& fullName, const Location& location,
+ Scope* parent)
+ : Scope(localName, fullName, location, parent) {}
RootScope::~RootScope() {}
std::string RootScope::typeName() const {
return "(root scope)";
}
+status_t RootScope::validate() const {
+ CHECK(annotations().empty());
+ return Scope::validate();
+}
+
+////////////////////////////////////////
+
LocalIdentifier::LocalIdentifier(){}
LocalIdentifier::~LocalIdentifier(){}
@@ -200,5 +251,17 @@
return false;
}
+const LocalIdentifier* LocalIdentifier::resolve() const {
+ return this;
+}
+
+LocalIdentifier* LocalIdentifier::resolve() {
+ return this;
+}
+
+ConstantExpression* LocalIdentifier::constExpr() const {
+ return nullptr;
+}
+
} // namespace android
diff --git a/Scope.h b/Scope.h
index 599182f..e8c9431 100644
--- a/Scope.h
+++ b/Scope.h
@@ -21,19 +21,24 @@
#include "NamedType.h"
#include <map>
+#include <unordered_map>
#include <vector>
namespace android {
+struct Annotation;
+struct ConstantExpression;
struct Formatter;
struct Interface;
struct LocalIdentifier;
struct Scope : public NamedType {
- Scope(const char* localName, const Location& location, Scope* parent);
+ Scope(const char* localName, const FQName& fullName, const Location& location, Scope* parent);
virtual ~Scope();
- bool addType(NamedType *type, std::string *errorMsg);
+ void addType(NamedType* type);
+
+ status_t validateUniqueNames() const;
// lookup a type given an FQName.
// Assume fqName.package(), fqName.version(), fqName.valueName() is empty.
@@ -48,39 +53,51 @@
bool containsInterfaces() const;
- status_t emitTypeDeclarations(Formatter &out) const override;
- status_t emitGlobalTypeDeclarations(Formatter &out) const override;
- status_t emitGlobalHwDeclarations(Formatter &out) const override;
+ const std::vector<Annotation*>& annotations() const;
- status_t emitJavaTypeDeclarations(
- Formatter &out, bool atTopLevel) const override;
+ void setAnnotations(std::vector<Annotation*>* annotations);
- status_t emitTypeDefinitions(
- Formatter &out, const std::string prefix) const override;
+ std::vector<const Type*> getDefinedTypes() const override;
+
+ std::vector<const ConstantExpression*> getConstantExpressions() const override;
+
+ void topologicalReorder(const std::unordered_map<const Type*, size_t>& reversedOrder);
+
+ void emitTypeDeclarations(Formatter& out) const override;
+ void emitGlobalTypeDeclarations(Formatter& out) const override;
+ void emitPackageTypeDeclarations(Formatter& out) const override;
+ void emitPackageHwDeclarations(Formatter& out) const override;
+
+ void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override;
+
+ void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override;
const std::vector<NamedType *> &getSubTypes() const;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
- bool isJavaCompatible() const override;
- bool containsPointer() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
void appendToExportedTypesVector(
std::vector<const Type *> *exportedTypes) const override;
-private:
+ private:
std::vector<NamedType *> mTypes;
std::map<std::string, size_t> mTypeIndexByName;
+ std::vector<Annotation*> mAnnotations;
- status_t forEachType(std::function<status_t(Type *)> func) const;
+ bool mTypeOrderChanged = false;
DISALLOW_COPY_AND_ASSIGN(Scope);
};
struct RootScope : public Scope {
- RootScope(const char* localName, const Location& location, Scope* parent);
+ RootScope(const char* localName, const FQName& fullName, const Location& location,
+ Scope* parent);
virtual ~RootScope();
+ virtual status_t validate() const override;
+
std::string typeName() const override;
};
@@ -88,6 +105,11 @@
LocalIdentifier();
virtual ~LocalIdentifier();
virtual bool isEnumValue() const;
+
+ const LocalIdentifier* resolve() const;
+ LocalIdentifier* resolve();
+
+ virtual ConstantExpression* constExpr() const;
};
} // namespace android
diff --git a/StringType.cpp b/StringType.cpp
index 7a60277..a07086f 100644
--- a/StringType.cpp
+++ b/StringType.cpp
@@ -22,13 +22,13 @@
namespace android {
-StringType::StringType() {}
+StringType::StringType(Scope* parent) : Type(parent) {}
bool StringType::isString() const {
return true;
}
-bool StringType::canCheckEquality() const {
+bool StringType::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
@@ -209,9 +209,8 @@
return true;
}
-status_t StringType::emitVtsTypeDeclarations(Formatter &out) const {
+void StringType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
- return OK;
}
static HidlTypeAssertion assertion("hidl_string", 16 /* size */);
diff --git a/StringType.h b/StringType.h
index 36299f7..a4e3691 100644
--- a/StringType.h
+++ b/StringType.h
@@ -23,11 +23,11 @@
namespace android {
struct StringType : public Type {
- StringType();
+ StringType(Scope* parent);
bool isString() const override;
- bool canCheckEquality() const override;
+ bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
std::string typeName() const override;
@@ -77,7 +77,7 @@
bool needsEmbeddedReadWrite() const override;
bool resultNeedsDeref() const override;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
+ void emitVtsTypeDeclarations(Formatter& out) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
};
diff --git a/Type.cpp b/Type.cpp
index ea8df98..84be36f 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -16,28 +16,22 @@
#include "Type.h"
-#include "Annotation.h"
+#include "ConstantExpression.h"
+#include "NamedType.h"
#include "ScalarType.h"
+#include "Scope.h"
-#include <hidl-util/Formatter.h>
#include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <algorithm>
+#include <iostream>
namespace android {
-Type::Type()
- : mAnnotations(nullptr) {
-}
+Type::Type(Scope* parent) : mParent(parent) {}
Type::~Type() {}
-void Type::setAnnotations(std::vector<Annotation *> *annotations) {
- mAnnotations = annotations;
-}
-
-const std::vector<Annotation *> &Type::annotations() const {
- return *mAnnotations;
-}
-
bool Type::isScope() const {
return false;
}
@@ -102,6 +96,228 @@
return false;
}
+Type* Type::resolve() {
+ return const_cast<Type*>(static_cast<const Type*>(this)->resolve());
+}
+
+const Type* Type::resolve() const {
+ return this;
+}
+
+std::vector<Type*> Type::getDefinedTypes() {
+ const auto& constRet = static_cast<const Type*>(this)->getDefinedTypes();
+ std::vector<Type*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* type) { return const_cast<Type*>(type); });
+ return ret;
+}
+
+std::vector<const Type*> Type::getDefinedTypes() const {
+ return {};
+}
+
+std::vector<Reference<Type>*> Type::getReferences() {
+ const auto& constRet = static_cast<const Type*>(this)->getReferences();
+ std::vector<Reference<Type>*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+ return ret;
+}
+
+std::vector<const Reference<Type>*> Type::getReferences() const {
+ return {};
+}
+
+std::vector<ConstantExpression*> Type::getConstantExpressions() {
+ const auto& constRet = static_cast<const Type*>(this)->getConstantExpressions();
+ std::vector<ConstantExpression*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ce) { return const_cast<ConstantExpression*>(ce); });
+ return ret;
+}
+
+std::vector<const ConstantExpression*> Type::getConstantExpressions() const {
+ return {};
+}
+
+std::vector<Reference<Type>*> Type::getStrongReferences() {
+ const auto& constRet = static_cast<const Type*>(this)->getStrongReferences();
+ std::vector<Reference<Type>*> ret(constRet.size());
+ std::transform(constRet.begin(), constRet.end(), ret.begin(),
+ [](const auto* ref) { return const_cast<Reference<Type>*>(ref); });
+ return ret;
+}
+
+std::vector<const Reference<Type>*> Type::getStrongReferences() const {
+ std::vector<const Reference<Type>*> ret;
+ for (const auto* ref : getReferences()) {
+ if (!ref->shallowGet()->isNeverStrongReference()) {
+ ret.push_back(ref);
+ }
+ }
+ return ret;
+}
+
+status_t Type::recursivePass(const std::function<status_t(Type*)>& func,
+ std::unordered_set<const Type*>* visited) {
+ if (mIsPostParseCompleted) return OK;
+
+ if (visited->find(this) != visited->end()) return OK;
+ visited->insert(this);
+
+ status_t err = func(this);
+ if (err != OK) return err;
+
+ for (auto* nextType : getDefinedTypes()) {
+ err = nextType->recursivePass(func, visited);
+ if (err != OK) return err;
+ }
+
+ for (auto* nextRef : getReferences()) {
+ err = nextRef->shallowGet()->recursivePass(func, visited);
+ if (err != OK) return err;
+ }
+
+ return OK;
+}
+
+status_t Type::recursivePass(const std::function<status_t(const Type*)>& func,
+ std::unordered_set<const Type*>* visited) const {
+ if (mIsPostParseCompleted) return OK;
+
+ if (visited->find(this) != visited->end()) return OK;
+ visited->insert(this);
+
+ status_t err = func(this);
+ if (err != OK) return err;
+
+ for (const auto* nextType : getDefinedTypes()) {
+ err = nextType->recursivePass(func, visited);
+ if (err != OK) return err;
+ }
+
+ for (const auto* nextRef : getReferences()) {
+ err = nextRef->shallowGet()->recursivePass(func, visited);
+ if (err != OK) return err;
+ }
+
+ return OK;
+}
+
+status_t Type::resolveInheritance() {
+ return OK;
+}
+
+status_t Type::validate() const {
+ return OK;
+}
+
+Type::CheckAcyclicStatus::CheckAcyclicStatus(status_t status, const Type* cycleEnd)
+ : status(status), cycleEnd(cycleEnd) {
+ CHECK(cycleEnd == nullptr || status != OK);
+}
+
+Type::CheckAcyclicStatus Type::topologicalOrder(
+ std::unordered_map<const Type*, size_t>* reversedOrder,
+ std::unordered_set<const Type*>* stack) const {
+ if (stack->find(this) != stack->end()) {
+ std::cerr << "ERROR: Cyclic declaration:\n";
+ return CheckAcyclicStatus(UNKNOWN_ERROR, this);
+ }
+
+ if (reversedOrder->find(this) != reversedOrder->end()) return CheckAcyclicStatus(OK);
+ stack->insert(this);
+
+ for (const auto* nextType : getDefinedTypes()) {
+ auto err = nextType->topologicalOrder(reversedOrder, stack);
+
+ if (err.status != OK) {
+ if (err.cycleEnd == nullptr) return err;
+
+ std::cerr << " '" << nextType->typeName() << "' in '" << typeName() << "'";
+ if (nextType->isNamedType()) {
+ std::cerr << " at " << static_cast<const NamedType*>(nextType)->location();
+ }
+ std::cerr << "\n";
+
+ if (err.cycleEnd == this) {
+ return CheckAcyclicStatus(err.status);
+ }
+ return err;
+ }
+ }
+
+ for (const auto* nextRef : getStrongReferences()) {
+ const auto* nextType = nextRef->shallowGet();
+ auto err = nextType->topologicalOrder(reversedOrder, stack);
+
+ if (err.status != OK) {
+ if (err.cycleEnd == nullptr) return err;
+
+ std::cerr << " '" << nextType->typeName() << "' in '" << typeName() << "' at "
+ << nextRef->location() << "\n";
+
+ if (err.cycleEnd == this) {
+ return CheckAcyclicStatus(err.status);
+ }
+ return err;
+ }
+ }
+
+ CHECK(stack->find(this) != stack->end());
+ stack->erase(this);
+
+ CHECK(reversedOrder->find(this) == reversedOrder->end());
+ // Do not call insert and size in one statement to not rely on
+ // evaluation order.
+ size_t index = reversedOrder->size();
+ reversedOrder->insert({this, index});
+
+ return CheckAcyclicStatus(OK);
+}
+
+status_t Type::checkForwardReferenceRestrictions(const Reference<Type>& ref) const {
+ const Location& refLoc = ref.location();
+ const Type* refType = ref.shallowGet();
+
+ // Not NamedTypes are avaiable everywhere.
+ // Only ArrayType and TemplatedType contain additional types in
+ // their reference (which is actually a part of type definition),
+ // so they are proceeded in this case.
+ //
+ // If we support named templated types one day, we will need to change
+ // this logic.
+ if (!refType->isNamedType()) {
+ for (const Reference<Type>* innerRef : refType->getReferences()) {
+ status_t err = checkForwardReferenceRestrictions(*innerRef);
+ if (err != OK) return err;
+ }
+ return OK;
+ }
+
+ const Location& typeLoc = static_cast<const NamedType*>(refType)->location();
+
+ // If referenced type is declared in another file or before reference,
+ // there is no forward reference here.
+ if (!Location::inSameFile(refLoc, typeLoc) ||
+ (!Location::intersect(refLoc, typeLoc) && typeLoc < refLoc)) {
+ return OK;
+ }
+
+ // Type must be declared somewhere in the current stack to make it
+ // available for forward referencing.
+ const Type* refTypeParent = refType->parent();
+ for (const Type* ancestor = this; ancestor != nullptr; ancestor = ancestor->parent()) {
+ if (ancestor == refTypeParent) return OK;
+ }
+
+ std::cerr << "ERROR: Forward reference of '" << refType->typeName() << "' at " << ref.location()
+ << " is not supported.\n"
+ << "C++ forward declaration doesn't support inner types.\n";
+
+ return UNKNOWN_ERROR;
+}
+
const ScalarType *Type::resolveToScalarType() const {
return NULL;
}
@@ -121,9 +337,36 @@
}
bool Type::canCheckEquality() const {
+ std::unordered_set<const Type*> visited;
+ return canCheckEquality(&visited);
+}
+
+bool Type::canCheckEquality(std::unordered_set<const Type*>* visited) const {
+ // See isJavaCompatible for similar structure.
+ if (visited->find(this) != visited->end()) {
+ return true;
+ }
+ visited->insert(this);
+ return deepCanCheckEquality(visited);
+}
+
+bool Type::deepCanCheckEquality(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
+void Type::setPostParseCompleted() {
+ CHECK(!mIsPostParseCompleted);
+ mIsPostParseCompleted = true;
+}
+
+Scope* Type::parent() {
+ return mParent;
+}
+
+const Scope* Type::parent() const {
+ return mParent;
+}
+
std::string Type::getCppType(StorageMode, bool) const {
CHECK(!"Should not be here");
return std::string();
@@ -370,36 +613,43 @@
handleError(out, mode);
}
-status_t Type::emitTypeDeclarations(Formatter &) const {
- return OK;
-}
+void Type::emitTypeDeclarations(Formatter&) const {}
-status_t Type::emitGlobalTypeDeclarations(Formatter &) const {
- return OK;
-}
+void Type::emitTypeForwardDeclaration(Formatter&) const {}
-status_t Type::emitGlobalHwDeclarations(Formatter &) const {
- return OK;
-}
+void Type::emitGlobalTypeDeclarations(Formatter&) const {}
-status_t Type::emitTypeDefinitions(
- Formatter &, const std::string) const {
- return OK;
-}
+void Type::emitPackageTypeDeclarations(Formatter&) const {}
-status_t Type::emitJavaTypeDeclarations(Formatter &, bool) const {
- return OK;
-}
+void Type::emitPackageHwDeclarations(Formatter&) const {}
+
+void Type::emitTypeDefinitions(Formatter&, const std::string&) const {}
+
+void Type::emitJavaTypeDeclarations(Formatter&, bool) const {}
bool Type::needsEmbeddedReadWrite() const {
return false;
}
-bool Type::needsResolveReferences() const {
+bool Type::resultNeedsDeref() const {
return false;
}
-bool Type::resultNeedsDeref() const {
+bool Type::needsResolveReferences() const {
+ std::unordered_set<const Type*> visited;
+ return needsResolveReferences(&visited);
+}
+
+bool Type::needsResolveReferences(std::unordered_set<const Type*>* visited) const {
+ // See isJavaCompatible for similar structure.
+ if (visited->find(this) != visited->end()) {
+ return false;
+ }
+ visited->insert(this);
+ return deepNeedsResolveReferences(visited);
+}
+
+bool Type::deepNeedsResolveReferences(std::unordered_set<const Type*>* /* visited */) const {
return false;
}
@@ -438,79 +688,124 @@
out << ");\n";
}
-status_t Type::emitVtsTypeDeclarations(Formatter &) const {
- return OK;
-}
+void Type::emitVtsTypeDeclarations(Formatter&) const {}
-status_t Type::emitVtsAttributeType(Formatter &out) const {
- return emitVtsTypeDeclarations(out);
+void Type::emitVtsAttributeType(Formatter& out) const {
+ emitVtsTypeDeclarations(out);
}
bool Type::isJavaCompatible() const {
+ std::unordered_set<const Type*> visited;
+ return isJavaCompatible(&visited);
+}
+
+bool Type::containsPointer() const {
+ std::unordered_set<const Type*> visited;
+ return containsPointer(&visited);
+}
+
+bool Type::isJavaCompatible(std::unordered_set<const Type*>* visited) const {
+ // We need to find al least one path from requested vertex
+ // to not java compatible.
+ // That means that if we have already visited some vertex,
+ // there is no need to determine whether it is java compatible
+ // (and we can assume that it is java compatible),
+ // as if not, the information about that would appear in the
+ // requested vertex through another path.
+ if (visited->find(this) != visited->end()) {
+ return true;
+ }
+ visited->insert(this);
+ return deepIsJavaCompatible(visited);
+}
+
+bool Type::containsPointer(std::unordered_set<const Type*>* visited) const {
+ // See isJavaCompatible for similar structure.
+ if (visited->find(this) != visited->end()) {
+ return false;
+ }
+ visited->insert(this);
+ return deepContainsPointer(visited);
+}
+
+bool Type::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
return true;
}
+bool Type::deepContainsPointer(std::unordered_set<const Type*>* /* visited */) const {
+ return false;
+}
+
void Type::getAlignmentAndSize(
size_t * /* align */, size_t * /* size */) const {
CHECK(!"Should not be here.");
}
-bool Type::containsPointer() const {
- return false;
-}
-
void Type::appendToExportedTypesVector(
std::vector<const Type *> * /* exportedTypes */) const {
}
-status_t Type::emitExportedHeader(
- Formatter & /* out */, bool /* forJava */) const {
- return OK;
+void Type::emitExportedHeader(Formatter& /* out */, bool /* forJava */) const {}
+
+bool Type::isNeverStrongReference() const {
+ return false;
}
////////////////////////////////////////
-TemplatedType::TemplatedType() : mElementType(nullptr) {
+TemplatedType::TemplatedType(Scope* parent) : Type(parent) {}
+
+std::string TemplatedType::typeName() const {
+ return templatedTypeName() + " of " + mElementType->typeName();
}
-void TemplatedType::setElementType(Type *elementType) {
- CHECK(mElementType == nullptr); // can only be set once.
- CHECK(isCompatibleElementType(elementType));
+void TemplatedType::setElementType(const Reference<Type>& elementType) {
+ // can only be set once.
+ CHECK(mElementType.isEmptyReference());
+ CHECK(!elementType.isEmptyReference());
+
mElementType = elementType;
}
-Type *TemplatedType::getElementType() const {
- return mElementType;
+const Type* TemplatedType::getElementType() const {
+ return mElementType.get();
}
bool TemplatedType::isTemplatedType() const {
return true;
}
-status_t TemplatedType::emitVtsTypeDeclarations(Formatter &out) const {
- out << "type: " << getVtsType() << "\n";
- out << getVtsValueName() << ": {\n";
- out.indent();
- status_t err = mElementType->emitVtsTypeDeclarations(out);
- if (err != OK) {
- return err;
- }
- out.unindent();
- out << "}\n";
- return OK;
+std::vector<const Reference<Type>*> TemplatedType::getReferences() const {
+ return {&mElementType};
}
-status_t TemplatedType::emitVtsAttributeType(Formatter &out) const {
+status_t TemplatedType::validate() const {
+ if (!isCompatibleElementType(mElementType.get())) {
+ std::cerr << "ERROR: " << typeName() /* contains element type */
+ << " is not supported at " << mElementType.location() << "\n";
+ return UNKNOWN_ERROR;
+ }
+
+ return Type::validate();
+}
+
+void TemplatedType::emitVtsTypeDeclarations(Formatter& out) const {
out << "type: " << getVtsType() << "\n";
out << getVtsValueName() << ": {\n";
out.indent();
- status_t status = mElementType->emitVtsAttributeType(out);
- if (status != OK) {
- return status;
- }
+ mElementType->emitVtsTypeDeclarations(out);
out.unindent();
out << "}\n";
- return OK;
}
+
+void TemplatedType::emitVtsAttributeType(Formatter& out) const {
+ out << "type: " << getVtsType() << "\n";
+ out << getVtsValueName() << ": {\n";
+ out.indent();
+ mElementType->emitVtsAttributeType(out);
+ out.unindent();
+ out << "}\n";
+}
+
} // namespace android
diff --git a/Type.h b/Type.h
index 8e139d4..a0ed5bc 100644
--- a/Type.h
+++ b/Type.h
@@ -19,20 +19,26 @@
#define TYPE_H_
#include <android-base/macros.h>
-#include <string>
#include <utils/Errors.h>
-#include <vector>
#include <set>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "DocComment.h"
+#include "Reference.h"
namespace android {
-struct Annotation;
+struct ConstantExpression;
struct Formatter;
-struct ScalarType;
struct FQName;
+struct ScalarType;
+struct Scope;
-struct Type {
- Type();
+struct Type : DocCommentable {
+ Type(Scope* parent);
virtual ~Type();
virtual bool isArray() const;
@@ -52,13 +58,82 @@
virtual bool isTypeDef() const;
virtual bool isVector() const;
+ // Resolves the type by unwrapping typedefs
+ Type* resolve();
+ virtual const Type* resolve() const;
+
+ // All types defined in this type.
+ std::vector<Type*> getDefinedTypes();
+ virtual std::vector<const Type*> getDefinedTypes() const;
+
+ // All types referenced in this type.
+ std::vector<Reference<Type>*> getReferences();
+ virtual std::vector<const Reference<Type>*> getReferences() const;
+
+ // All constant expressions referenced in this type.
+ std::vector<ConstantExpression*> getConstantExpressions();
+ virtual std::vector<const ConstantExpression*> getConstantExpressions() const;
+
+ // All types referenced in this type that must have completed
+ // definiton before being referenced.
+ std::vector<Reference<Type>*> getStrongReferences();
+ virtual std::vector<const Reference<Type>*> getStrongReferences() const;
+
+ // Proceeds recursive pass
+ // Makes sure to visit each node only once.
+ status_t recursivePass(const std::function<status_t(Type*)>& func,
+ std::unordered_set<const Type*>* visited);
+ status_t recursivePass(const std::function<status_t(const Type*)>& func,
+ std::unordered_set<const Type*>* visited) const;
+
+ // Recursive tree pass that completes type declarations
+ // that depend on super types
+ virtual status_t resolveInheritance();
+
+ // Recursive tree pass that validates all type-related
+ // syntax restrictions
+ virtual status_t validate() const;
+
+ // Recursive tree pass checkAcyclic return type.
+ // Stores cycle end for nice error messages.
+ struct CheckAcyclicStatus {
+ CheckAcyclicStatus(status_t status, const Type* cycleEnd = nullptr);
+
+ status_t status;
+
+ // If a cycle is found, stores the end of cycle.
+ // While going back in recursion, this is used to stop printing the cycle.
+ const Type* cycleEnd;
+ };
+
+ // Recursive tree pass that ensures that type definitions and references
+ // are acyclic and builds reversed topological order of the types.
+ // If some cases allow using of incomplete types, these cases are to be
+ // declared in Type::getStrongReferences.
+ CheckAcyclicStatus topologicalOrder(std::unordered_map<const Type*, size_t>* reversedOrder,
+ std::unordered_set<const Type*>* stack) const;
+
+ // Checks following C++ restriction on forward declaration:
+ // inner struct could be forward declared only inside its parent.
+ status_t checkForwardReferenceRestrictions(const Reference<Type>& ref) const;
+
virtual const ScalarType *resolveToScalarType() const;
virtual std::string typeName() const = 0;
bool isValidEnumStorageType() const;
virtual bool isElidableType() const;
+
virtual bool canCheckEquality() const;
+ bool canCheckEquality(std::unordered_set<const Type*>* visited) const;
+ virtual bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const;
+
+ // Marks that package proceeding is completed
+ // Post parse passes must be proceeded during owner package parsing
+ void setPostParseCompleted();
+
+ Scope* parent();
+ const Scope* parent() const;
enum StorageMode {
StorageMode_Stack,
@@ -176,47 +251,65 @@
const std::string &offset,
bool isReader) const;
- virtual status_t emitTypeDeclarations(Formatter &out) const;
+ virtual void emitTypeDeclarations(Formatter& out) const;
+
+ virtual void emitGlobalTypeDeclarations(Formatter& out) const;
+
+ // Emit scope C++ forward declaration.
+ // There is no need to forward declare interfaces, as
+ // they are always declared in global scope in dedicated file.
+ virtual void emitTypeForwardDeclaration(Formatter& out) const;
// Emit any declarations pertaining to this type that have to be
// at global scope, i.e. enum class operators.
- virtual status_t emitGlobalTypeDeclarations(Formatter &out) const;
+ // For android.hardware.foo@1.0::*, this will be in namespace
+ // android::hardware::foo::V1_0
+ virtual void emitPackageTypeDeclarations(Formatter& out) const;
// Emit any declarations pertaining to this type that have to be
// at global scope for transport, e.g. read/writeEmbeddedTo/FromParcel
- virtual status_t emitGlobalHwDeclarations(Formatter &out) const;
+ // For android.hardware.foo@1.0::*, this will be in namespace
+ // android::hardware::foo::V1_0
+ virtual void emitPackageHwDeclarations(Formatter& out) const;
- virtual status_t emitTypeDefinitions(
- Formatter &out, const std::string prefix) const;
+ virtual void emitTypeDefinitions(Formatter& out, const std::string& prefix) const;
- virtual status_t emitJavaTypeDeclarations(
- Formatter &out, bool atTopLevel) const;
+ virtual void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const;
virtual bool needsEmbeddedReadWrite() const;
- virtual bool needsResolveReferences() const;
virtual bool resultNeedsDeref() const;
+ bool needsResolveReferences() const;
+ bool needsResolveReferences(std::unordered_set<const Type*>* visited) const;
+ virtual bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const;
+
// Generates type declaration for vts proto file.
// TODO (b/30844146): make it a pure virtual method.
- virtual status_t emitVtsTypeDeclarations(Formatter &out) const;
+ virtual void emitVtsTypeDeclarations(Formatter& out) const;
// Generates type declaration as attribute of method (return value or method
// argument) or attribute of compound type for vts proto file.
- virtual status_t emitVtsAttributeType(Formatter &out) const;
+ virtual void emitVtsAttributeType(Formatter& out) const;
// Returns true iff this type is supported through the Java backend.
- virtual bool isJavaCompatible() const;
- virtual bool containsPointer() const;
- virtual void getAlignmentAndSize(size_t *align, size_t *size) const;
+ bool isJavaCompatible() const;
+ bool isJavaCompatible(std::unordered_set<const Type*>* visited) const;
+ virtual bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const;
+ // Returns true iff type contains pointer
+ // (excluding methods and inner types).
+ bool containsPointer() const;
+ bool containsPointer(std::unordered_set<const Type*>* visited) const;
+ virtual bool deepContainsPointer(std::unordered_set<const Type*>* visited) const;
- void setAnnotations(std::vector<Annotation *> *annotations);
- const std::vector<Annotation *> &annotations() const;
+ virtual void getAlignmentAndSize(size_t *align, size_t *size) const;
virtual void appendToExportedTypesVector(
std::vector<const Type *> *exportedTypes) const;
- virtual status_t emitExportedHeader(Formatter &out, bool forJava) const;
+ virtual void emitExportedHeader(Formatter& out, bool forJava) const;
-protected:
+ virtual bool isNeverStrongReference() const;
+
+ protected:
void handleError(Formatter &out, ErrorMode mode) const;
void emitReaderWriterEmbeddedForTypeName(
@@ -247,24 +340,37 @@
const std::string &methodName,
const std::string &name) const;
-private:
- std::vector<Annotation *> *mAnnotations;
+ private:
+ bool mIsPostParseCompleted = false;
+ Scope* const mParent;
DISALLOW_COPY_AND_ASSIGN(Type);
};
/* Base type for VectorType and RefType. */
struct TemplatedType : public Type {
- void setElementType(Type *elementType);
- Type *getElementType() const;
+ void setElementType(const Reference<Type>& elementType);
+ const Type* getElementType() const;
+
+ virtual std::string templatedTypeName() const = 0;
+ std::string typeName() const override;
+
bool isTemplatedType() const override;
- virtual bool isCompatibleElementType(Type *elementType) const = 0;
- status_t emitVtsTypeDeclarations(Formatter &out) const override;
- status_t emitVtsAttributeType(Formatter &out) const override;
-protected:
- TemplatedType();
- Type *mElementType;
-private:
+
+ virtual bool isCompatibleElementType(const Type* elementType) const = 0;
+
+ std::vector<const Reference<Type>*> getReferences() const override;
+
+ virtual status_t validate() const override;
+
+ void emitVtsTypeDeclarations(Formatter& out) const override;
+ void emitVtsAttributeType(Formatter& out) const override;
+
+ protected:
+ TemplatedType(Scope* parent);
+ Reference<Type> mElementType;
+
+ private:
DISALLOW_COPY_AND_ASSIGN(TemplatedType);
};
diff --git a/TypeDef.cpp b/TypeDef.cpp
index f419efe..dcefc57 100644
--- a/TypeDef.cpp
+++ b/TypeDef.cpp
@@ -21,16 +21,21 @@
namespace android {
-TypeDef::TypeDef(const char* localName, const Location& location, Scope* parent, Type* type)
- : NamedType(localName, location, parent), mReferencedType(type) {}
+TypeDef::TypeDef(const char* localName, const FQName& fullName, const Location& location,
+ Scope* parent, const Reference<Type>& type)
+ : NamedType(localName, fullName, location, parent), mReferencedType(type) {}
const ScalarType *TypeDef::resolveToScalarType() const {
CHECK(!"Should not be here");
return NULL;
}
-Type *TypeDef::referencedType() const {
- return mReferencedType;
+Type* TypeDef::referencedType() {
+ return mReferencedType.get();
+}
+
+const Type* TypeDef::referencedType() const {
+ return mReferencedType.get();
}
bool TypeDef::isInterface() const {
@@ -50,6 +55,14 @@
return true;
}
+const Type* TypeDef::resolve() const {
+ return mReferencedType.get();
+}
+
+std::vector<const Reference<Type>*> TypeDef::getReferences() const {
+ return {&mReferencedType};
+}
+
bool TypeDef::needsEmbeddedReadWrite() const {
CHECK(!"Should not be here");
return false;
@@ -60,14 +73,12 @@
return false;
}
-status_t TypeDef::emitTypeDeclarations(Formatter &out) const {
+void TypeDef::emitTypeDeclarations(Formatter& out) const {
out << "typedef "
<< mReferencedType->getCppStackType()
<< " "
<< localName()
<< ";\n\n";
-
- return OK;
}
} // namespace android
diff --git a/TypeDef.h b/TypeDef.h
index 7914363..5ebba3e 100644
--- a/TypeDef.h
+++ b/TypeDef.h
@@ -23,13 +23,15 @@
namespace android {
struct TypeDef : public NamedType {
- TypeDef(const char* localName, const Location& location, Scope* parent, Type* type);
+ TypeDef(const char* localName, const FQName& fullName, const Location& location, Scope* parent,
+ const Reference<Type>& type);
const ScalarType *resolveToScalarType() const override;
std::string typeName() const override;
- Type *referencedType() const;
+ Type* referencedType();
+ const Type* referencedType() const;
bool isInterface() const override;
bool isEnum() const override;
@@ -37,10 +39,14 @@
bool needsEmbeddedReadWrite() const override;
bool resultNeedsDeref() const override;
- status_t emitTypeDeclarations(Formatter &out) const override;
+ const Type* resolve() const override;
-private:
- Type *mReferencedType;
+ std::vector<const Reference<Type>*> getReferences() const override;
+
+ void emitTypeDeclarations(Formatter& out) const override;
+
+ private:
+ Reference<Type> mReferencedType;
DISALLOW_COPY_AND_ASSIGN(TypeDef);
};
diff --git a/VectorType.cpp b/VectorType.cpp
index 4d1dae8..03604dc 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -25,14 +25,13 @@
namespace android {
-VectorType::VectorType() {
+VectorType::VectorType(Scope* parent) : TemplatedType(parent) {}
+
+std::string VectorType::templatedTypeName() const {
+ return "vector";
}
-std::string VectorType::typeName() const {
- return "vector" + (mElementType == nullptr ? "" : (" of " + mElementType->typeName()));
-}
-
-bool VectorType::isCompatibleElementType(Type *elementType) const {
+bool VectorType::isCompatibleElementType(const Type* elementType) const {
if (elementType->isScalar()) {
return true;
}
@@ -45,8 +44,7 @@
if (elementType->isBitField()) {
return true;
}
- if (elementType->isCompoundType()
- && static_cast<CompoundType *>(elementType)->style() == CompoundType::STYLE_STRUCT) {
+ if (elementType->isCompoundType()) {
return true;
}
if (elementType->isInterface()) {
@@ -59,11 +57,11 @@
return true;
}
if (elementType->isTemplatedType()) {
- Type *inner = static_cast<TemplatedType *>(elementType)->getElementType();
+ const Type* inner = static_cast<const TemplatedType*>(elementType)->getElementType();
return this->isCompatibleElementType(inner) && !inner->isInterface();
}
if (elementType->isArray()) {
- Type *inner = static_cast<ArrayType *>(elementType)->getElementType();
+ const Type* inner = static_cast<const ArrayType*>(elementType)->getElementType();
return this->isCompatibleElementType(inner) && !inner->isInterface();
}
return false;
@@ -77,8 +75,12 @@
return mElementType->isBinder();
}
-bool VectorType::canCheckEquality() const {
- return mElementType->canCheckEquality();
+bool VectorType::deepCanCheckEquality(std::unordered_set<const Type*>* visited) const {
+ return mElementType->canCheckEquality(visited);
+}
+
+std::vector<const Reference<Type>*> VectorType::getStrongReferences() const {
+ return {};
}
std::string VectorType::getCppType(StorageMode mode,
@@ -564,7 +566,7 @@
VectorType::EmitJavaFieldReaderWriterForElementType(
out,
depth,
- mElementType,
+ mElementType.get(),
parcelName,
blobName,
fieldName,
@@ -711,21 +713,24 @@
return true;
}
-bool VectorType::needsResolveReferences() const {
- return mElementType->needsResolveReferences();
+bool VectorType::deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const {
+ if (mElementType->needsResolveReferences(visited)) {
+ return true;
+ }
+ return TemplatedType::deepNeedsResolveReferences(visited);
}
bool VectorType::resultNeedsDeref() const {
return !isVectorOfBinders();
}
-bool VectorType::isJavaCompatible() const {
- if (!mElementType->isJavaCompatible()) {
+bool VectorType::deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const {
+ if (!mElementType->isJavaCompatible(visited)) {
return false;
}
if (mElementType->isArray()) {
- return static_cast<ArrayType *>(mElementType)->countDimensions() == 1;
+ return static_cast<const ArrayType*>(mElementType.get())->countDimensions() == 1;
}
if (mElementType->isVector()) {
@@ -736,11 +741,14 @@
return false;
}
- return true;
+ return TemplatedType::deepIsJavaCompatible(visited);
}
-bool VectorType::containsPointer() const {
- return mElementType->containsPointer();
+bool VectorType::deepContainsPointer(std::unordered_set<const Type*>* visited) const {
+ if (mElementType->containsPointer(visited)) {
+ return true;
+ }
+ return TemplatedType::deepContainsPointer(visited);
}
// All hidl_vec<T> have the same size.
diff --git a/VectorType.h b/VectorType.h
index e868355..6f6ede7 100644
--- a/VectorType.h
+++ b/VectorType.h
@@ -18,19 +18,25 @@
#define VECTOR_TYPE_H_
+#include <vector>
+
+#include "Reference.h"
#include "Type.h"
namespace android {
struct VectorType : public TemplatedType {
- VectorType();
+ VectorType(Scope* parent);
bool isVector() const override;
bool isVectorOfBinders() const;
- std::string typeName() const override;
- bool isCompatibleElementType(Type *elementType) const override;
- bool canCheckEquality() const override;
+ std::string templatedTypeName() const override;
+ bool isCompatibleElementType(const Type* elementType) const override;
+
+ std::vector<const Reference<Type>*> getStrongReferences() const override;
+
+ bool deepCanCheckEquality(std::unordered_set<const Type*>* visited) const override;
std::string getCppType(
StorageMode mode,
@@ -115,11 +121,11 @@
bool isReader);
bool needsEmbeddedReadWrite() const override;
- bool needsResolveReferences() const override;
+ bool deepNeedsResolveReferences(std::unordered_set<const Type*>* visited) const override;
bool resultNeedsDeref() const override;
- bool isJavaCompatible() const override;
- bool containsPointer() const override;
+ bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
+ bool deepContainsPointer(std::unordered_set<const Type*>* visited) const override;
void getAlignmentAndSize(size_t *align, size_t *size) const override;
static void getAlignmentAndSizeStatic(size_t *align, size_t *size);
diff --git a/build/Android.bp b/build/Android.bp
new file mode 100644
index 0000000..9b290a3
--- /dev/null
+++ b/build/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2017 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.
+
+bootstrap_go_package {
+ name: "hidl-soong-rules",
+ pkgPath: "android/soong/hidl",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ "soong-cc",
+ "soong-genrule",
+ "soong-java",
+ ],
+ srcs: [
+ "fqName.go",
+ "hidl_interface.go",
+ "hidl_package_root.go",
+ "properties.go",
+ "utils.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/build/fqName.go b/build/fqName.go
new file mode 100644
index 0000000..88f7dc0
--- /dev/null
+++ b/build/fqName.go
@@ -0,0 +1,126 @@
+// Copyright (C) 2017 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.
+
+package hidl
+
+import (
+ "errors"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+type fqName struct {
+ packageComponents []string
+ minor string
+ major string
+}
+
+// See system/tools/hidl/FQName.h
+var component = "[a-zA-Z_][a-zA-Z_0-9]*"
+var pkg = "(" + component + "(?:[.]" + component + ")*)"
+var digits = "([0-9]+)"
+var re_package = regexp.MustCompile("^" + pkg + "@" + digits + "." + digits + "$")
+
+func parseFqName(f string) (*fqName, error) {
+ matches := re_package.FindAllStringSubmatch(f, 3)
+
+ if matches == nil {
+ return nil, errors.New("Poorly formed hal interface name: '" + f + "' must match '" + re_package.String() + "'")
+ }
+
+ ret := fqName{
+ packageComponents: strings.Split(matches[0][1], "."),
+ major: matches[0][2],
+ minor: matches[0][3],
+ }
+
+ return &ret, nil
+}
+
+func (f *fqName) inPackage(pkg string) bool {
+ components := strings.Split(pkg, ".")
+
+ if len(components) > len(f.packageComponents) {
+ return false
+ }
+
+ for i, v := range components {
+ if f.packageComponents[i] != v {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (f *fqName) pkg() string {
+ return strings.Join(f.packageComponents, ".")
+}
+func (f *fqName) version() string {
+ return f.major + "." + f.minor
+}
+func (f *fqName) sanitizedVersion() string {
+ return "V" + f.major + "_" + f.minor
+}
+func (f *fqName) string() string {
+ return f.pkg() + "@" + f.version()
+}
+func (f *fqName) sanitizedString() string {
+ return f.pkg() + "-V" + f.version() // NOT sanitizedVersion
+}
+
+func (f *fqName) dir() string {
+ return filepath.Join(filepath.Join(f.packageComponents...), f.version()) + "/"
+}
+func (f *fqName) sanitizedDir() string {
+ return filepath.Join(filepath.Join(f.packageComponents...), f.sanitizedVersion()) + "/"
+}
+
+func (f *fqName) fileGroupName() string {
+ return f.string() + "_hal"
+}
+func (f *fqName) sourcesName() string {
+ return f.string() + "_genc++"
+}
+func (f *fqName) headersName() string {
+ return f.string() + "_genc++_headers"
+}
+func (f *fqName) javaName() string {
+ return f.sanitizedString() + "-java"
+}
+func (f *fqName) javaSourcesName() string {
+ return f.sanitizedString() + "-java_gen_java"
+}
+func (f *fqName) javaConstantsName() string {
+ return f.sanitizedString() + "-java-constants"
+}
+func (f *fqName) javaConstantsSourcesName() string {
+ return f.sanitizedString() + "-java-constants_gen_java"
+}
+func (f *fqName) adapterName() string {
+ return f.string() + "-adapter"
+}
+func (f *fqName) adapterSourcesName() string {
+ return f.string() + "-adapter_genc++"
+}
+func (f *fqName) adapterHelperName() string {
+ return f.string() + "-adapter-helper"
+}
+func (f *fqName) adapterHelperSourcesName() string {
+ return f.string() + "-adapter-helper_genc++"
+}
+func (f *fqName) adapterHelperHeadersName() string {
+ return f.string() + "-adapter-helper_genc++_headers"
+}
diff --git a/build/hidl_interface.go b/build/hidl_interface.go
new file mode 100644
index 0000000..55cba94
--- /dev/null
+++ b/build/hidl_interface.go
@@ -0,0 +1,428 @@
+// Copyright (C) 2017 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.
+
+package hidl
+
+import (
+ "strings"
+ "sync"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/genrule"
+ "android/soong/java"
+)
+
+var (
+ hidlInterfaceSuffix = "_interface"
+)
+
+func init() {
+ android.RegisterModuleType("hidl_interface", hidlInterfaceFactory)
+}
+
+type hidlInterfaceProperties struct {
+ // Vndk properties for interface library only.
+ cc.VndkProperties
+
+ // The owner of the module
+ Owner *string
+
+ // List of .hal files which compose this interface.
+ Srcs []string
+
+ // List of hal interface packages that this library depends on.
+ Interfaces []string
+
+ // Package root for this package, must be a prefix of name
+ Root string
+
+ // List of non-TypeDef types declared in types.hal.
+ Types []string
+
+ // Whether to generate the Java library stubs.
+ // Default: true
+ Gen_java *bool
+
+ // Whether to generate a Java library containing constants
+ // expressed by @export annotations in the hal files.
+ Gen_java_constants bool
+
+ // Don't generate "android.hidl.foo@1.0" C library. Instead
+ // only generate the genrules so that this package can be
+ // included in libhidltransport.
+ Core_interface bool
+}
+
+type hidlInterface struct {
+ android.ModuleBase
+
+ properties hidlInterfaceProperties
+}
+
+func processSources(mctx android.LoadHookContext, srcs []string) ([]string, []string, bool) {
+ var interfaces []string
+ var types []string // hidl-gen only supports types.hal, but don't assume that here
+
+ hasError := false
+
+ for _, v := range srcs {
+ if !strings.HasSuffix(v, ".hal") {
+ mctx.PropertyErrorf("srcs", "Source must be a .hal file: "+v)
+ hasError = true
+ continue
+ }
+
+ name := strings.TrimSuffix(v, ".hal")
+
+ if strings.HasPrefix(name, "I") {
+ baseName := strings.TrimPrefix(name, "I")
+ interfaces = append(interfaces, baseName)
+ } else {
+ types = append(types, name)
+ }
+ }
+
+ return interfaces, types, !hasError
+}
+
+func processDependencies(mctx android.LoadHookContext, interfaces []string) ([]string, []string, bool) {
+ var dependencies []string
+ var javaDependencies []string
+
+ hasError := false
+
+ for _, v := range interfaces {
+ name, err := parseFqName(v)
+ if err != nil {
+ mctx.PropertyErrorf("interfaces", err.Error())
+ hasError = true
+ continue
+ }
+ dependencies = append(dependencies, name.string())
+ javaDependencies = append(javaDependencies, name.javaName())
+ }
+
+ return dependencies, javaDependencies, !hasError
+}
+
+func getRootList(mctx android.LoadHookContext, interfaces []string) ([]string, bool) {
+ var roots []string
+ hasError := false
+
+ for _, i := range interfaces {
+ interfaceObject := lookupInterface(i)
+ if interfaceObject == nil {
+ mctx.PropertyErrorf("interfaces", "Cannot find interface "+i)
+ hasError = true
+ continue
+ }
+ root := interfaceObject.properties.Root
+ rootObject := lookupPackageRoot(root)
+ if rootObject == nil {
+ mctx.PropertyErrorf("interfaces", `Cannot find package root specification for package `+
+ `root '%s' needed for module '%s'. Either this is a mispelling of the package `+
+ `root, or a new hidl_package_root module needs to be added. For example, you can `+
+ `fix this error by adding the following to <some path>/Android.bp:
+
+hidl_package_root {
+ name: "%s",
+ path: "<some path>",
+}
+
+This corresponds to the "-r%s:<some path>" option that would be passed into hidl-gen.`, root, i, root, root)
+ hasError = true
+ continue
+ }
+
+ roots = append(roots, root+":"+rootObject.properties.Path)
+ }
+
+ return android.FirstUniqueStrings(roots), !hasError
+}
+
+func removeCoreDependencies(mctx android.LoadHookContext, dependencies []string) ([]string, bool) {
+ var ret []string
+ hasError := false
+
+ for _, i := range dependencies {
+ interfaceObject := lookupInterface(i)
+ if interfaceObject == nil {
+ mctx.PropertyErrorf("interfaces", "Cannot find interface "+i)
+ hasError = true
+ continue
+ }
+
+ if !interfaceObject.properties.Core_interface {
+ ret = append(ret, i)
+ }
+ }
+
+ return ret, !hasError
+}
+
+func hidlGenCommand(lang string, roots []string, name *fqName) *string {
+ cmd := "$(location hidl-gen) -d $(depfile) -o $(genDir)"
+ cmd += " -L" + lang
+ cmd += " " + strings.Join(wrap("-r", roots, ""), " ")
+ cmd += " " + name.string()
+ return &cmd
+}
+
+func hidlInterfaceMutator(mctx android.LoadHookContext, i *hidlInterface) {
+ name, err := parseFqName(i.ModuleBase.Name())
+ if err != nil {
+ mctx.PropertyErrorf("name", err.Error())
+ }
+
+ if !name.inPackage(i.properties.Root) {
+ mctx.PropertyErrorf("root", "Root, "+i.properties.Root+", for "+name.string()+" must be a prefix.")
+ }
+
+ interfaces, types, _ := processSources(mctx, i.properties.Srcs)
+
+ if len(interfaces) == 0 && len(types) == 0 {
+ mctx.PropertyErrorf("srcs", "No sources provided.")
+ }
+
+ dependencies, javaDependencies, _ := processDependencies(mctx, i.properties.Interfaces)
+ roots, _ := getRootList(mctx, append(dependencies, name.string()))
+ cppDependencies, _ := removeCoreDependencies(mctx, dependencies)
+
+ if mctx.Failed() {
+ return
+ }
+
+ shouldGenerateLibrary := !i.properties.Core_interface
+ // explicitly true if not specified to give early warning to devs
+ shouldGenerateJava := i.properties.Gen_java == nil || *i.properties.Gen_java
+ shouldGenerateJavaConstants := i.properties.Gen_java_constants
+
+ var libraryIfExists []string
+ if shouldGenerateLibrary {
+ libraryIfExists = []string{name.string()}
+ }
+
+ // TODO(b/69002743): remove filegroups
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.FileGroupFactory), &fileGroupProperties{
+ Name: proptools.StringPtr(name.fileGroupName()),
+ Owner: i.properties.Owner,
+ Srcs: i.properties.Srcs,
+ })
+
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+ Name: proptools.StringPtr(name.sourcesName()),
+ Depfile: proptools.BoolPtr(true),
+ Owner: i.properties.Owner,
+ Tools: []string{"hidl-gen"},
+ Cmd: hidlGenCommand("c++-sources", roots, name),
+ Srcs: i.properties.Srcs,
+ Out: concat(wrap(name.dir(), interfaces, "All.cpp"),
+ wrap(name.dir(), types, ".cpp")),
+ })
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+ Name: proptools.StringPtr(name.headersName()),
+ Depfile: proptools.BoolPtr(true),
+ Owner: i.properties.Owner,
+ Tools: []string{"hidl-gen"},
+ Cmd: hidlGenCommand("c++-headers", roots, name),
+ Srcs: i.properties.Srcs,
+ Out: concat(wrap(name.dir()+"I", interfaces, ".h"),
+ wrap(name.dir()+"Bs", interfaces, ".h"),
+ wrap(name.dir()+"BnHw", interfaces, ".h"),
+ wrap(name.dir()+"BpHw", interfaces, ".h"),
+ wrap(name.dir()+"IHw", interfaces, ".h"),
+ wrap(name.dir(), types, ".h"),
+ wrap(name.dir()+"hw", types, ".h")),
+ })
+
+ if shouldGenerateLibrary {
+ mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
+ Name: proptools.StringPtr(name.string()),
+ Owner: i.properties.Owner,
+ Vendor_available: proptools.BoolPtr(true),
+ Defaults: []string{"hidl-module-defaults"},
+ Generated_sources: []string{name.sourcesName()},
+ Generated_headers: []string{name.headersName()},
+ Shared_libs: concat(cppDependencies, []string{
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "libcutils",
+ }),
+ Export_shared_lib_headers: concat(cppDependencies, []string{
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "libutils",
+ }),
+ Export_generated_headers: []string{name.headersName()},
+ }, &i.properties.VndkProperties)
+ }
+
+ if shouldGenerateJava {
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+ Name: proptools.StringPtr(name.javaSourcesName()),
+ Depfile: proptools.BoolPtr(true),
+ Owner: i.properties.Owner,
+ Tools: []string{"hidl-gen"},
+ Cmd: hidlGenCommand("java", roots, name),
+ Srcs: i.properties.Srcs,
+ Out: concat(wrap(name.sanitizedDir()+"I", interfaces, ".java"),
+ wrap(name.sanitizedDir(), i.properties.Types, ".java")),
+ })
+ mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+ Name: proptools.StringPtr(name.javaName()),
+ Owner: i.properties.Owner,
+ Sdk_version: proptools.StringPtr("system_current"),
+ Defaults: []string{"hidl-java-module-defaults"},
+ No_framework_libs: proptools.BoolPtr(true),
+ Srcs: []string{":" + name.javaSourcesName()},
+ Static_libs: append(javaDependencies, "hwbinder"),
+ })
+ }
+
+ if shouldGenerateJavaConstants {
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+ Name: proptools.StringPtr(name.javaConstantsSourcesName()),
+ Depfile: proptools.BoolPtr(true),
+ Owner: i.properties.Owner,
+ Tools: []string{"hidl-gen"},
+ Cmd: hidlGenCommand("java-constants", roots, name),
+ Srcs: i.properties.Srcs,
+ Out: []string{name.sanitizedDir() + "Constants.java"},
+ })
+ mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+ Name: proptools.StringPtr(name.javaConstantsName()),
+ Owner: i.properties.Owner,
+ Defaults: []string{"hidl-java-module-defaults"},
+ No_framework_libs: proptools.BoolPtr(true),
+ Srcs: []string{":" + name.javaConstantsSourcesName()},
+ })
+ }
+
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+ Name: proptools.StringPtr(name.adapterHelperSourcesName()),
+ Depfile: proptools.BoolPtr(true),
+ Owner: i.properties.Owner,
+ Tools: []string{"hidl-gen"},
+ Cmd: hidlGenCommand("c++-adapter-sources", roots, name),
+ Srcs: i.properties.Srcs,
+ Out: wrap(name.dir()+"A", concat(interfaces, types), ".cpp"),
+ })
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+ Name: proptools.StringPtr(name.adapterHelperHeadersName()),
+ Depfile: proptools.BoolPtr(true),
+ Owner: i.properties.Owner,
+ Tools: []string{"hidl-gen"},
+ Cmd: hidlGenCommand("c++-adapter-headers", roots, name),
+ Srcs: i.properties.Srcs,
+ Out: wrap(name.dir()+"A", concat(interfaces, types), ".h"),
+ })
+
+ mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
+ Name: proptools.StringPtr(name.adapterHelperName()),
+ Owner: i.properties.Owner,
+ Vendor_available: proptools.BoolPtr(true),
+ Defaults: []string{"hidl-module-defaults"},
+ Generated_sources: []string{name.adapterHelperSourcesName()},
+ Generated_headers: []string{name.adapterHelperHeadersName()},
+ Shared_libs: []string{
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ },
+ Static_libs: concat([]string{
+ "libhidladapter",
+ }, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+ Export_shared_lib_headers: []string{
+ "libhidlbase",
+ "libhidltransport",
+ },
+ Export_static_lib_headers: concat([]string{
+ "libhidladapter",
+ }, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+ Export_generated_headers: []string{name.adapterHelperHeadersName()},
+ Group_static_libs: proptools.BoolPtr(true),
+ })
+ mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+ Name: proptools.StringPtr(name.adapterSourcesName()),
+ Depfile: proptools.BoolPtr(true),
+ Owner: i.properties.Owner,
+ Tools: []string{"hidl-gen"},
+ Cmd: hidlGenCommand("c++-adapter-main", roots, name),
+ Srcs: i.properties.Srcs,
+ Out: []string{"main.cpp"},
+ })
+ mctx.CreateModule(android.ModuleFactoryAdaptor(cc.TestFactory), &ccProperties{
+ Name: proptools.StringPtr(name.adapterName()),
+ Owner: i.properties.Owner,
+ Generated_sources: []string{name.adapterSourcesName()},
+ Shared_libs: []string{
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ },
+ Static_libs: concat([]string{
+ "libhidladapter",
+ name.adapterHelperName(),
+ }, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+ Group_static_libs: proptools.BoolPtr(true),
+ })
+}
+
+func (h *hidlInterface) Name() string {
+ return h.ModuleBase.Name() + hidlInterfaceSuffix
+}
+func (h *hidlInterface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+func (h *hidlInterface) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+var hidlInterfaceMutex sync.Mutex
+var hidlInterfaces []*hidlInterface
+
+func hidlInterfaceFactory() android.Module {
+ i := &hidlInterface{}
+ i.AddProperties(&i.properties)
+ android.InitAndroidModule(i)
+ android.AddLoadHook(i, func(ctx android.LoadHookContext) { hidlInterfaceMutator(ctx, i) })
+
+ hidlInterfaceMutex.Lock()
+ hidlInterfaces = append(hidlInterfaces, i)
+ hidlInterfaceMutex.Unlock()
+
+ return i
+}
+
+func lookupInterface(name string) *hidlInterface {
+ for _, i := range hidlInterfaces {
+ if i.ModuleBase.Name() == name {
+ return i
+ }
+ }
+ return nil
+}
diff --git a/build/hidl_package_root.go b/build/hidl_package_root.go
new file mode 100644
index 0000000..1f68af4
--- /dev/null
+++ b/build/hidl_package_root.go
@@ -0,0 +1,63 @@
+// Copyright (C) 2017 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.
+
+package hidl
+
+import (
+ "sync"
+
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("hidl_package_root", hidlPackageRootFactory)
+}
+
+type hidlPackageRoot struct {
+ android.ModuleBase
+
+ properties struct {
+ // path to this module from root
+ Path string
+ }
+}
+
+func (r *hidlPackageRoot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+func (r *hidlPackageRoot) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+var packageRootsMutex sync.Mutex
+var packageRoots []*hidlPackageRoot
+
+func hidlPackageRootFactory() android.Module {
+ r := &hidlPackageRoot{}
+ r.AddProperties(&r.properties)
+ android.InitAndroidModule(r)
+
+ packageRootsMutex.Lock()
+ packageRoots = append(packageRoots, r)
+ packageRootsMutex.Unlock()
+
+ return r
+}
+
+func lookupPackageRoot(name string) *hidlPackageRoot {
+ for _, i := range packageRoots {
+ if i.ModuleBase.Name() == name {
+ return i
+ }
+ }
+ return nil
+}
diff --git a/build/properties.go b/build/properties.go
new file mode 100644
index 0000000..d2f8a35
--- /dev/null
+++ b/build/properties.go
@@ -0,0 +1,61 @@
+// Copyright (C) 2017 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.
+
+package hidl
+
+type nameProperties struct {
+ Name *string
+}
+
+type fileGroupProperties struct {
+ Name *string
+ Owner *string
+ Srcs []string
+}
+
+type genruleProperties struct {
+ Name *string
+ Owner *string
+ Tools []string
+ Cmd *string
+ Srcs []string
+ Out []string
+ Depfile *bool
+}
+
+type ccProperties struct {
+ Name *string
+ Owner *string
+ Defaults []string
+ Vendor_available *bool
+ Generated_sources []string
+ Generated_headers []string
+ Group_static_libs *bool
+ Shared_libs []string
+ Static_libs []string
+ Export_shared_lib_headers []string
+ Export_static_lib_headers []string
+ Export_generated_headers []string
+}
+
+type javaProperties struct {
+ Name *string
+ Owner *string
+ Defaults []string
+ No_framework_libs *bool
+ Sdk_version *string
+ Srcs []string
+ Libs []string
+ Static_libs []string
+}
diff --git a/build/utils.go b/build/utils.go
new file mode 100644
index 0000000..ba3ac8e
--- /dev/null
+++ b/build/utils.go
@@ -0,0 +1,43 @@
+// Copyright (C) 2017 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.
+
+package hidl
+
+// wrap(p, a, s) = [p + v + s for v in a]
+func wrap(prefix string, strs []string, suffix string) []string {
+ ret := make([]string, len(strs))
+ for i, v := range strs {
+ ret[i] = prefix + v + suffix
+ }
+ return ret
+}
+
+// concat(a...) = sum((i for i in a), [])
+func concat(sstrs ...[]string) []string {
+ var ret []string
+ for _, v := range sstrs {
+ ret = append(ret, v...)
+ }
+ return ret
+}
+
+func remove(str string, strs []string) []string {
+ var ret []string
+ for _, v := range strs {
+ if v != str {
+ ret = append(ret, v)
+ }
+ }
+ return ret
+}
diff --git a/c2hal/AST.cpp b/c2hal/AST.cpp
index e2ae13c..783a04e 100644
--- a/c2hal/AST.cpp
+++ b/c2hal/AST.cpp
@@ -29,7 +29,6 @@
#include <string>
#include <algorithm>
#include <stdlib.h>
-#include <sys/dir.h>
#include <sys/stat.h>
namespace android {
@@ -317,7 +316,7 @@
size_t start = 1; // Ignore leading '/'
size_t slashPos;
- while ((slashPos = path.find("/", start)) != std::string::npos) {
+ while ((slashPos = path.find('/', start)) != std::string::npos) {
std::string partial = path.substr(0, slashPos);
struct stat st;
diff --git a/c2hal/Android.bp b/c2hal/Android.bp
index 03987f5..3dd9c6a 100644
--- a/c2hal/Android.bp
+++ b/c2hal/Android.bp
@@ -12,13 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-subdirs = [
- "test",
-]
-
cc_binary_host {
name: "c2hal",
- cflags: hidl_flags,
+ defaults: ["hidl-gen-defaults"],
srcs: [
"AST.cpp",
"c2hal_l.ll",
diff --git a/c2hal/Expression.cpp b/c2hal/Expression.cpp
index 7863b15..0ab864f 100644
--- a/c2hal/Expression.cpp
+++ b/c2hal/Expression.cpp
@@ -29,7 +29,7 @@
static const std::regex RE_S64("[^ul](l|ll)$");
static const std::regex RE_U64("[^ul](ul|ull)$");
-Expression::Type Expression::integralType(std::string integer) {
+Expression::Type Expression::integralType(const std::string& integer) {
if (std::regex_search(integer, RE_S32)) {
return Type::S32;
}
@@ -55,8 +55,8 @@
// because we are reducing everything to two ranks, we can heavily simplify
// conversion rules
-#define SIGNED(i) (i & 2) // i & 0b10
-#define MAX_RANK(i) (i | 1) // i | 0b01
+#define SIGNED(i) ((i) & 2) // i & 0b10
+#define MAX_RANK(i) ((i) | 1) // i | 0b01
if (lhs == rhs) {
return lhs;
diff --git a/c2hal/Expression.h b/c2hal/Expression.h
index 9f04837..5449e55 100644
--- a/c2hal/Expression.h
+++ b/c2hal/Expression.h
@@ -63,7 +63,7 @@
}
}
- static Type integralType(std::string integer);
+ static Type integralType(const std::string& integer);
static Type coalesceTypes(Type lhs, Type rhs);
static Expression *parenthesize(Expression *inner);
diff --git a/c2hal/c2hal_l.ll b/c2hal/c2hal_l.ll
index d5e4511..f613adc 100644
--- a/c2hal/c2hal_l.ll
+++ b/c2hal/c2hal_l.ll
@@ -50,8 +50,6 @@
int check_type(yyscan_t yyscanner, struct yyguts_t *yyg);
-// TODO convert to parse params/lex params
-
extern int start_token;
extern std::string last_comment;
@@ -80,6 +78,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wdeprecated-register"
+#pragma clang diagnostic ignored "-Wregister"
%}
diff --git a/c2hal/test/simple.h b/c2hal/test/simple.h
index e3d7f3e..701ebe4 100644
--- a/c2hal/test/simple.h
+++ b/c2hal/test/simple.h
@@ -32,7 +32,7 @@
/* Simple example */
typedef struct simple_t {
- /**
+ /*
* Common methods of the simple device.
*/
struct hw_device_t common;
@@ -67,7 +67,7 @@
} simple_location_t;
-/** convenience API for coloring */
+/* convenience API for coloring */
static inline int showColor(const struct hw_module_t* module,
struct simple_t** device) {
diff --git a/c2hal/test/test.h b/c2hal/test/test.h
index b1a0058..3a83b0c 100644
--- a/c2hal/test/test.h
+++ b/c2hal/test/test.h
@@ -34,7 +34,6 @@
get rid of magic numbers */
/* test */
-/** test */
/* test **/
/* test / ** ** / test */
/* test //// ***** test /****/
@@ -91,7 +90,6 @@
#undef ONE
-/*****************************************************************************/
typedef void (*no_arg_fun)(void);
typedef int (*other_fun)(int j);
@@ -110,7 +108,7 @@
int (*global_fun_2)(struct framebuffer_device_t* dev, int enable);
typedef struct framebuffer_device_t {
- /**
+ /*
* Common methods of the framebuffer device.
*/
struct hw_device_t common;
@@ -324,7 +322,7 @@
float c;
} that_t;
-/**
+/*
* return the frame size (number of bytes per sample) of an output stream.
*/
static inline size_t audio_stream_out_frame_size(const struct audio_stream_out *s)
diff --git a/docs/Android.bp b/docs/Android.bp
new file mode 100644
index 0000000..2d7d78b
--- /dev/null
+++ b/docs/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C) 2007 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.
+
+//
+// hidl-doc
+//
+java_binary_host {
+ name: "hidl-doc",
+ manifest: "etc/manifest.txt",
+ srcs: [
+ "src/main.kt",
+ "src/writer/formatutils.kt",
+ "src/writer/elements/TypedefElement.kt",
+ "src/writer/elements/EnumElement.kt",
+ "src/writer/elements/AbstractElement.kt",
+ "src/writer/elements/CompoundElement.kt",
+ "src/writer/elements/MethodElement.kt",
+ "src/writer/files/AbstractFileWriter.kt",
+ "src/writer/files/InterfaceFileWriter.kt",
+ "src/writer/files/IndexFileWriter.kt",
+ "src/writer/files/TypesFileWriter.kt",
+ "src/writer/files/AbstractParserFileWriter.kt",
+ "src/writer/files/resources.kt",
+ "src/lexer/ILexer.kt",
+ "src/lexer/Token.kt",
+ "src/lexer/DocLexer.kt",
+ "src/lexer/HidlLexer.kt",
+ "src/parser/elements/EntryParser.kt",
+ "src/parser/elements/DocParser.kt",
+ "src/parser/elements/EntryCollectionParser.kt",
+ "src/parser/elements/AbstractParser.kt",
+ "src/parser/elements/DocAnnotationParser.kt",
+ "src/parser/elements/declarations/EnumDeclarationParser.kt",
+ "src/parser/elements/declarations/InterfaceDeclarationParser.kt",
+ "src/parser/elements/declarations/TypedefDeclarationParser.kt",
+ "src/parser/elements/declarations/MethodDeclarationParser.kt",
+ "src/parser/elements/declarations/AbstractDeclarationParser.kt",
+ "src/parser/elements/declarations/CompoundDeclarationParser.kt",
+ "src/parser/elements/AnnotationParser.kt",
+ "src/parser/utils.kt",
+ "src/parser/config.kt",
+ "src/parser/files/InterfaceFileParser.kt",
+ "src/parser/files/TypesFileParser.kt",
+ "src/parser/files/AbstractFileParser.kt",
+ "src/parser/files/package.kt",
+ ],
+ java_resources: [
+ "resources/template/index.html",
+ "resources/template/types.html",
+ "resources/template/interface.html",
+ "resources/assets/style.css",
+ ],
+}
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..9624451
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,35 @@
+# Install
+
+~~~
+$ m -j hidl-doc
+~~~
+
+# Usage
+
+View usage info:
+
+~~~
+$ ./bin/hidl-doc -h
+~~~
+
+Parse the audio `types.hal` file in the Android repo and output
+generated HTML reference to the reference directory. Enable verbose mode:
+
+~~~
+$ ./bin/hidl-doc -v -i /path/to/android/hardware/interfaces/audio/2.0/types.hal \
+ -o /path/to/output/en/reference/hidl/
+~~~
+
+Parse all HAL files in the Android `/hardware/interfaces/` directory
+and output generated HTML reference docs to reference directory. Skip files
+that encounter doc parse errors:
+
+~~~
+$ ./bin/hidl-doc -v -s -i /path/to/android/hardware/interfaces/ \
+ -o /path/to/output/en/reference/hidl/
+~~~
+# Templates
+
+HTML templates are used to generate the output docs and are in the
+`resources/template/` directory. Since these files are bundled up in the fat jar
+file, if you make any changes to the templates, `hidl-doc.jar` must be rebuilt.
diff --git a/docs/etc/manifest.txt b/docs/etc/manifest.txt
new file mode 100644
index 0000000..82888cf
--- /dev/null
+++ b/docs/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: MainKt
\ No newline at end of file
diff --git a/docs/resources/assets/style.css b/docs/resources/assets/style.css
new file mode 100644
index 0000000..a9b8b16
--- /dev/null
+++ b/docs/resources/assets/style.css
@@ -0,0 +1,15 @@
+header #api-info-block {
+ float: right;
+ font-size: 0.9em;
+ color: #999;
+}
+
+header #api-info-block code {
+ font-size: 0.8em;
+ color: #37474f;
+}
+
+header code {
+ padding: 0;
+ background-color: transparent;
+}
\ No newline at end of file
diff --git a/docs/resources/template/index.html b/docs/resources/template/index.html
new file mode 100644
index 0000000..d4faee5
--- /dev/null
+++ b/docs/resources/template/index.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html devsite>
+<!--################ THIS FILE IS GENERATED ################-->
+<!--################ DO NOT EDIT ################-->
+<!--################ CHANGE THE HAL DOC COMMENTS ################-->
+<head>
+ <title>$title | HIDL reference</title>
+ <meta name="project_path" value="/_project.yaml">
+ <meta name="book_path" value="/reference/hidl/_book.yaml">
+ <meta name="hide_page_heading" value="true">
+ <link href="/reference/hidl/assets/style.css" rel="stylesheet">
+</head>
+<body>
+ <header>
+ <h1 class="hide-from-toc">HIDL Reference</h1>
+ <p>
+ The HAL Interface Description Language (HIDL) specifies the interface
+ between a HAL and its users. It defines types and method calls, collected
+ into interfaces and packages. HIDL is a system for communicating between
+ codebases that may be compiled independently and is intended for
+ inter-process communication. See the
+ <a href="/devices/architecture/hidl">HIDL guides</a>.
+ </p>
+ </header>
+ <section>
+ <h2 class="hide-from-toc">Interfaces and types</h2>
+$entries
+ </section>
+</body>
+</html>
diff --git a/docs/resources/template/interface.html b/docs/resources/template/interface.html
new file mode 100644
index 0000000..feed37b
--- /dev/null
+++ b/docs/resources/template/interface.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html devsite>
+<!--################ THIS FILE IS GENERATED ################-->
+<!--################ DO NOT EDIT ################-->
+<!--################ CHANGE THE HAL DOC COMMENTS ################-->
+<head>
+ <title>$name | HIDL reference</title>
+ <meta name="project_path" value="/_project.yaml">
+ <meta name="book_path" value="/reference/hidl/_book.yaml">
+ <meta name="hide_page_heading" value="true">
+ <link href="/reference/hidl/assets/style.css" rel="stylesheet">
+</head>
+<body>
+$header
+$propertyDefs
+$methodDefs
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/resources/template/types.html b/docs/resources/template/types.html
new file mode 100644
index 0000000..d7d25a2
--- /dev/null
+++ b/docs/resources/template/types.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html devsite>
+<!--################ THIS FILE IS GENERATED ################-->
+<!--################ DO NOT EDIT ################-->
+<!--################ CHANGE THE HAL DOC COMMENTS ################-->
+<head>
+ <title>$name | HIDL reference</title>
+ <meta name="project_path" value="/_project.yaml">
+ <meta name="book_path" value="/reference/hidl/_book.yaml">
+ <meta name="hide_page_heading" value="true">
+ <link href="/reference/hidl/assets/style.css" rel="stylesheet">
+</head>
+<body>
+$header
+$propertyDefs
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/src/lexer/DocLexer.kt b/docs/src/lexer/DocLexer.kt
new file mode 100644
index 0000000..ec1a745
--- /dev/null
+++ b/docs/src/lexer/DocLexer.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package lexer
+
+import java.util.*
+
+object DocLexer : ILexer {
+
+ /**
+ * @param str The string should already be padded from the file lexer.
+ */
+ override fun tokenize(str: String): List<Token> {
+ val tokens = mutableListOf<Token>()
+ var token: String
+
+ //remove docblock comment indention - must go before others
+ val formattedStr = str.lines()
+ //remove docblock comment indent prefix (must go before others) ...
+ .map { it.replace(Regex("^\\s*\\*[^/]"), "") } //indented prefix except doc_end
+ .map { it.replace(Regex("^\\s*\\*$"), "") } //prefix empty lines remain
+ //replace empty lines with something the scanner can pick out ...
+ .map { it.replace(Regex("$\\s*^"), TokenGrammar.EMPTY_LINE.value) }
+ .joinToString("\n")
+
+ Scanner(formattedStr).use { scanner ->
+ while (scanner.hasNext()) {
+ token = scanner.next()
+
+ when (token) {
+ TokenGrammar.EMPTY_LINE.value -> tokens.add(TokenGrammar.newToken("", TokenGrammar.EMPTY_LINE))
+
+ //if part of annotation, add following tag as well
+ TokenGrammar.AT.value -> {
+ tokens.add(TokenGrammar.newToken(token)) //'@'
+ //determine if part of annotation
+ for (annotation in TokenGrammar.docAnnotations()) {
+ if (scanner.hasNext(annotation.value)) {
+ tokens.add(TokenGrammar.newToken(scanner.next()))
+ }
+ }
+ }
+
+ //default to DocWord
+ else -> {
+ val id = TokenGrammar.getFromValueOrDefault(token)
+ val category = if (id == TokenGrammar.WORD) TokenCategory.DocWord else id.category
+ tokens.add(TokenGrammar.newToken(token, category))
+ //TODO: Perhaps make all docblock words a DocWord
+ //tokens.add(TokenGrammar.newToken(token, TokenCategory.DocWord))
+ }
+ }
+ }
+ }
+ return tokens.toList()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/lexer/HidlLexer.kt b/docs/src/lexer/HidlLexer.kt
new file mode 100644
index 0000000..1bc04fa
--- /dev/null
+++ b/docs/src/lexer/HidlLexer.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package lexer
+
+import java.text.ParseException
+import java.util.*
+
+object HidlLexer : ILexer {
+
+ /**
+ * Given the text of a HIDL file, return a list ot tokens.
+ * Scanner moves forward only, but can check queued tokens.
+ */
+ override fun tokenize(str: String): List<Token> {
+ val tokens = mutableListOf<Token>()
+ //match line to '*/', check for anything after on same line
+ val matchToDocEnd = Regex("""(.*)\s+${Regex.escape(TokenGrammar.DOC_END.value)}(.*)$""")
+
+ //pad delimiter tokens so the scanner picks them up.
+ val paddedStr = ILexer.padDelimiters(str)
+
+ Scanner(paddedStr).use { scanner ->
+ while (scanner.hasNext()) {
+ val token = scanner.next()
+
+ when (token) {
+ //strip line comments
+ TokenGrammar.COMMENT_LINE.value -> scanner.nextLine()
+
+ //strip block comments -- jump to comment close
+ TokenGrammar.COMMENT_START.value -> {
+ if (scanner.findWithinHorizon(Regex.escape(TokenGrammar.DOC_END.value), 0) == null) {
+ throw ParseException("Unable to find closing comment marker", tokens.lastIndex)
+ }
+ }
+
+ //slurp text between /** and */ into a string,
+ //tokenize string using the doc comment lexer,
+ //append those tokens with the rest of the file tokens.
+ TokenGrammar.DOC_START.value -> {
+ tokens.add(TokenGrammar.newToken(token)) //doc_start
+ //slurp everything until doc_end into a string. but want to keep newline formatting
+ val sb = StringBuilder()
+ while (scanner.hasNextLine()) {
+ val line = scanner.nextLine()
+
+ val matches = matchToDocEnd.find(line)?.groups
+ if (matches != null) {
+ if (!matches[2]!!.value.isNullOrBlank()) {
+ throw ParseException("No text after '*/' on same line: ${line}", 0)
+ }
+ //found doc_end
+ sb.append(matches[1]!!.value)
+ break
+ } else {
+ sb.appendln(line)
+ }
+ }
+ //tokenize comment string and append all
+ tokens += DocLexer.tokenize(sb.toString())
+ tokens.add(TokenGrammar.newToken(TokenGrammar.DOC_END.value)) //doc_end
+ }
+
+ TokenGrammar.AT.value -> {
+ tokens.add(TokenGrammar.newToken(token)) //'@'
+ //determine if part of annotation tag
+ for (annotation in TokenGrammar.annotations()) {
+ if (scanner.hasNext(annotation.value)) {
+ scanner.next() //annotation tag
+ val annotationArgs = StringBuilder()
+ //capture any args: (...)
+ if (scanner.hasNext(Regex.escape(TokenGrammar.PAREN_OPEN.value))) {
+ while (!scanner.hasNext(Regex.escape(TokenGrammar.PAREN_CLOSE.value))) {
+ annotationArgs.append(scanner.next()).append(" ")
+ }
+ if (!scanner.hasNext()) {
+ throw ParseException("Unable to find closing annotation paren", tokens.lastIndex)
+ }
+ annotationArgs.append(scanner.next()) //')'
+ }
+ //all annotation args are embedded in the token's value
+ tokens.add(TokenGrammar.newToken(identifier = annotation, value = annotationArgs.toString()))
+ }
+ }
+ }
+
+ else -> tokens.add(TokenGrammar.newToken(token))
+ }
+ }
+ }
+ return tokens.toList()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/lexer/ILexer.kt b/docs/src/lexer/ILexer.kt
new file mode 100644
index 0000000..368ead7
--- /dev/null
+++ b/docs/src/lexer/ILexer.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package lexer
+
+import java.io.File
+
+interface ILexer {
+ fun tokenize(str: String): List<Token>
+
+ fun tokenize(file: File): List<Token> {
+ return this.tokenize(file.readText())
+ }
+
+ companion object {
+
+ fun padDelimiters(str: String): String {
+ val sb = StringBuilder()
+ val delimiters = TokenGrammar.values()
+ .filter { it.category == TokenCategory.Delimiter }
+ .filter { it != TokenGrammar.COMMENT_START } //don't convert '/**' to '/* *'
+ .map { it.value } //return string representation
+
+ str.lineSequence().forEach { line ->
+ var newLine = line
+ for (token in delimiters) {
+ newLine = newLine.replace(token, " $token ")
+ }
+ //delimiter corrections
+ newLine = unpadDecimal(newLine) //'nn . nn' => 'n.n'
+ newLine = newLine.replace(":\\s+:".toRegex(), TokenGrammar.PKG_SCOPE.value) //': :' => '::'
+ //squeeze multi-char ops with chevrons
+ newLine = newLine.replace("<\\s+<".toRegex(), TokenGrammar.LSHIFT.value)
+ newLine = newLine.replace(">\\s+>".toRegex(), TokenGrammar.RSHIFT.value)
+ newLine = newLine.replace("<\\s+=".toRegex(), TokenGrammar.LEQ.value)
+ newLine = newLine.replace(">\\s+=".toRegex(), TokenGrammar.GEQ.value)
+
+ sb.appendln(newLine)
+ }
+ return sb.toString()
+ }
+
+ /**
+ * Replace 'nn . nn' with 'n.n'
+ * Doesn't take into account decimals with missing a prefix or suffix, e.g. '9.' or '.9'
+ */
+ private fun unpadDecimal(str: String): String {
+ var newStr = str
+ Regex("(\\d+)\\s*\\.\\s*(\\d+)").findAll(newStr).forEach { matchResult ->
+ val n1 = matchResult.groups[1]?.value
+ val n2 = matchResult.groups[2]?.value
+ if (n1 != null && n2 != null) {
+ newStr = newStr.replace("${n1}\\s*\\.\\s*${n2}".toRegex(), "${n1}.${n2}")
+ }
+ }
+ return newStr
+ }
+
+ /**
+ * Clean up the padded and tokenized doc block (reverse padDelimiters)
+ */
+ fun unpadDelimiters(str: String): String {
+ var newStr = str
+ val delimiters = TokenGrammar.values()
+ .filter { it.category == TokenCategory.Delimiter }
+ .map { it.value } //return string representation
+
+ for (token in delimiters) {
+ newStr = newStr.replace(" $token ", token)
+ }
+ //special case
+ newStr = newStr.replace(Regex("\\s+\\.\\s*$"), ".") //end-of-line sentence periods
+ newStr = newStr.replace(",", ", ") //give comma some breathing room
+ return newStr
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/docs/src/lexer/Token.kt b/docs/src/lexer/Token.kt
new file mode 100644
index 0000000..3038b77
--- /dev/null
+++ b/docs/src/lexer/Token.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package lexer
+
+data class Token(val identifier: TokenGrammar, val value: String, val category: TokenCategory)
+
+enum class TokenCategory {
+ Annotation, Delimiter, Keyword, Number, Op, TypeDef, Word, DocComment, DocAnnotation, DocWord
+}
+
+enum class TokenGrammar(val value: String, val category: TokenCategory) {
+
+ WORD("", TokenCategory.Word),
+ INTEGER("", TokenCategory.Number),
+ DECIMAL("", TokenCategory.Number),
+
+ AT("@", TokenCategory.Delimiter),
+ EMPTY_LINE("EMPTY_LINE", TokenCategory.Delimiter), //sub in doc string, don't include value in token
+
+ DOC_START("/**", TokenCategory.Delimiter),
+ DOC_END("*/", TokenCategory.Delimiter),
+ COMMENT_LINE("//", TokenCategory.Delimiter),
+ COMMENT_START("/*", TokenCategory.Delimiter),
+
+ // HIDL grammar: {android}/system/tools/hidl/hidl-gen_l.ll
+ ENUM("enum", TokenCategory.TypeDef),
+ EXTENDS("extends", TokenCategory.Keyword),
+ GENERATES("generates", TokenCategory.Keyword),
+ IMPORT("import", TokenCategory.Keyword),
+ INTERFACE("interface", TokenCategory.TypeDef),
+ PACKAGE("package", TokenCategory.Keyword),
+ STRUCT("struct", TokenCategory.TypeDef),
+ TYPEDEF("typedef", TokenCategory.TypeDef),
+ UNION("union", TokenCategory.TypeDef),
+ BITFIELD("bitfield", TokenCategory.TypeDef),
+ VEC("vec", TokenCategory.TypeDef),
+ REF("ref", TokenCategory.TypeDef),
+ ONEWAY("oneway", TokenCategory.Keyword),
+
+ BOOL("bool", TokenCategory.TypeDef),
+ INT8_T("int8_t", TokenCategory.TypeDef),
+ UINT8_T("uint8_t", TokenCategory.TypeDef),
+ INT16_T("int16_t", TokenCategory.TypeDef),
+ UINT16_T("uint16_t", TokenCategory.TypeDef),
+ INT32_T("int32_t", TokenCategory.TypeDef),
+ UINT32_T("uint32_t", TokenCategory.TypeDef),
+ INT64_T("int64_t", TokenCategory.TypeDef),
+ UINT64_T("int8_t", TokenCategory.TypeDef),
+ FLOAT("float", TokenCategory.TypeDef),
+ DOUBLE("double", TokenCategory.TypeDef),
+
+ DEATH_RECIPIENT("death_recipient", TokenCategory.TypeDef),
+ HANDLE("handle", TokenCategory.TypeDef),
+ MEMORY("memory", TokenCategory.TypeDef),
+ POINTER("pointer", TokenCategory.TypeDef),
+ STRING("string", TokenCategory.TypeDef),
+
+ FMQ_SYNC("fmq_sync", TokenCategory.TypeDef),
+ FMQ_UNSYNC("fmq_unsync", TokenCategory.TypeDef),
+
+ PAREN_OPEN("(", TokenCategory.Delimiter),
+ PAREN_CLOSE(")", TokenCategory.Delimiter),
+ CHEVRON_OPEN("<", TokenCategory.Delimiter),
+ CHEVRON_CLOSE(">", TokenCategory.Delimiter),
+ BRACE_OPEN("{", TokenCategory.Delimiter),
+ BRACE_CLOSE("}", TokenCategory.Delimiter),
+ BRACKET_OPEN("[", TokenCategory.Delimiter),
+ BRACKET_CLOSE("]", TokenCategory.Delimiter),
+ COLON(":", TokenCategory.Delimiter),
+ SEMICOLON(";", TokenCategory.Delimiter),
+ COMMA(",", TokenCategory.Delimiter),
+ PERIOD(".", TokenCategory.Delimiter),
+ EQUAL("=", TokenCategory.Op),
+ PLUS("+", TokenCategory.Op),
+ MINUS("-", TokenCategory.Op),
+ MULTIPLY("*", TokenCategory.Op),
+ DIVIDE("/", TokenCategory.Op),
+ MOD("%", TokenCategory.Op),
+ BITWISE_AND("&", TokenCategory.Op),
+ BITWISE_OR("|", TokenCategory.Op),
+ BITWISE_XOR("^", TokenCategory.Op),
+ LSHIFT("<<", TokenCategory.Op),
+ RSHIFT(">>", TokenCategory.Op),
+ LOGICAL_AND("&&", TokenCategory.Op),
+ LOGICAL_OR("||", TokenCategory.Op),
+ NEGATE("!", TokenCategory.Op),
+ COMPLEMENT("~", TokenCategory.Op),
+ LEQ("<=", TokenCategory.Op),
+ GEQ(">=", TokenCategory.Op),
+ EQUALITY("==", TokenCategory.Op),
+ NEQUALITY("!=", TokenCategory.Op),
+ QUESTION("?", TokenCategory.Op),
+
+ PKG_SCOPE("::", TokenCategory.Delimiter),
+
+ // vts tags
+ ENTRY("entry", TokenCategory.Annotation),
+ EXIT("exit", TokenCategory.Annotation),
+ CALLFLOW("callflow", TokenCategory.Annotation),
+ EXPORT("export", TokenCategory.Annotation),
+
+ // javadoc tags. Not all supported in HIDL
+ // http://docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#javadoctags
+ // https://docs.google.com/document/d/1zHzOtvI9vIADPmI211F_tXQUd-w6lP5u-Y98lvxPvYQ/view#heading=h.aopuaezi1is6
+ AUTHOR("author", TokenCategory.DocAnnotation),
+ CODE("code", TokenCategory.DocAnnotation),
+ DOC_ROOT("docRoot", TokenCategory.DocAnnotation),
+ DEPRECATED("deprecated", TokenCategory.DocAnnotation),
+ EXCEPTION("exception", TokenCategory.DocAnnotation),
+ INHERIT_DOC("inheritDoc", TokenCategory.DocAnnotation),
+ LINK("link", TokenCategory.DocAnnotation),
+ LINK_PLAIN("linkplain", TokenCategory.DocAnnotation),
+ LITERAL("literal", TokenCategory.DocAnnotation),
+ PARAM("param", TokenCategory.DocAnnotation),
+ RETURN("return", TokenCategory.DocAnnotation),
+ SEE("see", TokenCategory.DocAnnotation),
+ SERIAL("serial", TokenCategory.DocAnnotation),
+ SERIAL_DATA("serialData", TokenCategory.DocAnnotation),
+ SERIAL_FIELD("serialField", TokenCategory.DocAnnotation),
+ SINCE("since", TokenCategory.DocAnnotation),
+ THROWS("throws", TokenCategory.DocAnnotation),
+ VALUE("value", TokenCategory.DocAnnotation),
+ VERSION("version", TokenCategory.DocAnnotation);
+
+ companion object {
+ private val map = TokenGrammar.values().associateBy(TokenGrammar::value)
+ private val matchInt = Regex("\\d+")
+ private val matchFloat = Regex("\\d+[.]\\d+")
+
+ fun getFromValue(value: String): TokenGrammar? {
+ return map[value]
+ }
+
+ fun getFromValueOrDefault(value: String): TokenGrammar {
+ return getFromValue(value) ?: when {
+ matchInt.matches(value) -> INTEGER
+ matchFloat.matches(value) -> DECIMAL
+ else -> WORD
+ }
+ }
+
+ fun newToken(value: String): Token {
+ val tokenGrammar = getFromValueOrDefault(value)
+ return Token(tokenGrammar, value, tokenGrammar.category)
+ }
+
+ fun newToken(value: String, category: TokenCategory): Token {
+ val tokenGrammar = getFromValueOrDefault(value)
+ return Token(tokenGrammar, value, category)
+ }
+
+ fun newToken(value: String, identifier: TokenGrammar): Token {
+ return Token(identifier, value, identifier.category)
+ }
+
+ fun annotations(): List<TokenGrammar> {
+ return TokenGrammar.values().filter { it.category == TokenCategory.Annotation }
+ }
+
+ fun docAnnotations(): List<TokenGrammar> {
+ return TokenGrammar.values().filter { it.category == TokenCategory.DocAnnotation }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/main.kt b/docs/src/main.kt
new file mode 100644
index 0000000..2069d2e
--- /dev/null
+++ b/docs/src/main.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import lexer.HidlLexer
+import lexer.Token
+import parser.LOG_NAME
+import parser.config
+import parser.files.AbstractFileParser
+import parser.files.InterfaceFileParser
+import parser.files.TypesFileParser
+import writer.files.*
+import java.nio.file.Paths
+import java.text.ParseException
+
+fun main(args: Array<String>) {
+ config.parseArgs(args)
+ if (config.verbose) println("$LOG_NAME args: ${config}")
+
+ val indexWriter = IndexFileWriter() //collects parser entries
+
+ /*
+ * parse and write HAL files
+ */
+ for (fp in config.files) {
+ println("$LOG_NAME Parsing input: $fp")
+
+ val tokens = HidlLexer.tokenize(fp)
+ val (parser, writer) = parseAndGetWriter(tokens)
+
+ indexWriter.addEntry(parser)
+
+ try {
+ //since lazily evaluated, check here for parse errors
+ if (writer.writeToFile()) println("$LOG_NAME Wrote file: ${writer.path}")
+
+ } catch (ex: ParseException) {
+ if (config.skipError) {
+ System.err.println("$LOG_NAME Error parsing file, skipping: $fp")
+ continue
+ } else {
+ System.err.println("$LOG_NAME Error parsing file: $fp")
+ throw ex
+ }
+ } finally {
+ if (config.verbose) writer.printInfo()
+ }
+ }
+
+ /*
+ * non-HAL file
+ */
+ if (indexWriter.writeToFile()) println("$LOG_NAME Wrote index: ${indexWriter.path}")
+
+ val cssPath = Paths.get("${config.outDir}/assets/style.css")
+ if (resources.copyToFile("/resources/assets/style.css", cssPath)) {
+ println("$LOG_NAME Copied resource file: $cssPath")
+ }
+}
+
+fun parseAndGetWriter(tokens: List<Token>): Pair<AbstractFileParser, AbstractParserFileWriter> {
+ val parser: AbstractFileParser
+ val writer: AbstractParserFileWriter
+ if (InterfaceFileParser.isInterface(tokens)) {
+ parser = InterfaceFileParser(tokens)
+ writer = InterfaceFileWriter(parser)
+ } else {
+ parser = TypesFileParser(tokens)
+ writer = TypesFileWriter(parser)
+ }
+ return Pair(parser, writer)
+}
\ No newline at end of file
diff --git a/docs/src/parser/config.kt b/docs/src/parser/config.kt
new file mode 100644
index 0000000..125b4ac
--- /dev/null
+++ b/docs/src/parser/config.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser
+
+import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+import kotlin.system.exitProcess
+
+const val LOG_NAME = "[hidl-doc]"
+
+fun printUsage() {
+ println("""
+Usage: hidl-doc [-i path]
+ -i=path Add input HAL file or directory to parse
+ -o=dir Output directory of generated HTML [${config.outDir}]
+ -x=path Exclude file or directory from files to parse
+ -v Verbose mode, print parsing info
+ -h Print this help and exit
+ Error modes:
+ -w Warn on errors instead of exiting
+ -l Lint. Warn-only and do not generate files
+ -e Error and exit on warnings
+ -s Skip files that encounter parse errors
+""".trim())
+}
+
+object config {
+ val files = mutableListOf<File>()
+ var outDir = toPath("~/out/hidl-doc/html")
+ var verbose = false
+ var lintMode = false
+ var warnOnly = false
+ var errorOnly = false
+ var skipError = false
+
+ override fun toString(): String {
+ return """
+verbose: $verbose
+warnOnly: $warnOnly
+errorOnly: $errorOnly
+skipError: $skipError
+outDir: $outDir
+files: $files
+"""
+ }
+
+ private const val HAL_EXTENSION = ".hal"
+
+ fun parseArgs(args: Array<String>) {
+ if (args.isEmpty()) {
+ printUsage()
+ exitProcess(1)
+ }
+
+ val dirPathArgs = mutableListOf<Path>()
+ val filePathArgs = mutableListOf<Path>()
+ val excludedPathArgs = mutableListOf<Path>()
+
+ val iter = args.iterator()
+ var arg: String
+
+ //parse command-line arguments
+ while (iter.hasNext()) {
+ arg = iter.next()
+
+ when (arg) {
+ "-i" -> {
+ val path = toPath(iter.next())
+ if (Files.isDirectory(path)) dirPathArgs.add(path) else filePathArgs.add(path)
+ }
+ "-x" -> excludedPathArgs.add(toPath(iter.next()).toAbsolutePath())
+ "-o" -> outDir = toPath(iter.next())
+ "-v" -> verbose = true
+ "-l" -> { lintMode = true; warnOnly = true }
+ "-w" -> warnOnly = true
+ "-e" -> errorOnly = true
+ "-s" -> skipError = true
+ "-h" -> {
+ printUsage()
+ exitProcess(0)
+ }
+ else -> {
+ System.err.println("Unknown option: $arg")
+ printUsage()
+ exitProcess(1)
+ }
+ }
+ }
+
+ //collect files (explicitly passed and search directories)
+ val allFiles = mutableListOf<File>()
+
+ //add individual files
+ filePathArgs.filterNot { excludedPathArgs.contains(it.toAbsolutePath()) }
+ .map { it.toFile() }.map { fp ->
+ if (!fp.isFile || !fp.canRead() || !fp.absolutePath.toLowerCase().endsWith(HAL_EXTENSION)) {
+ System.err.println("Error: Invalid $HAL_EXTENSION file: ${fp.path}")
+ exitProcess(1)
+ }
+ fp
+ }.map { allFiles.add(it) }
+
+ //check directory args
+ dirPathArgs.map { it.toFile() }
+ .map { findFiles(it, allFiles, HAL_EXTENSION, excludedPathArgs) }
+
+ //consolidate duplicates
+ allFiles.distinctBy { it.canonicalPath }
+ .forEach { files.add(it) }
+
+ if (files.isEmpty()) {
+ System.err.println("Error: Can't find any $HAL_EXTENSION files")
+ exitProcess(1)
+ }
+ }
+
+ /**
+ * Recursively search for files in a directory matching an extension and add to files.
+ */
+ private fun findFiles(dir: File, files: MutableList<File>, ext: String, excludedPaths: List<Path>) {
+ if (!dir.isDirectory || !dir.canRead()) {
+ System.err.println("Invalid directory: ${dir.path}, aborting")
+ exitProcess(1)
+ }
+ dir.listFiles()
+ .filterNot { excludedPaths.contains(it.toPath().toAbsolutePath()) }
+ .forEach { fp ->
+ if (fp.isDirectory) {
+ findFiles(fp, files, ext, excludedPaths)
+ } else if (fp.absolutePath.toLowerCase().endsWith(ext)) {
+ files.add(fp)
+ }
+ }
+ }
+
+ private fun toPath(string: String): Path {
+ //replace shell expansion for HOME
+ return Paths.get(string.replaceFirst("~", System.getProperty("user.home")))
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/AbstractParser.kt b/docs/src/parser/elements/AbstractParser.kt
new file mode 100644
index 0000000..45c1fc2
--- /dev/null
+++ b/docs/src/parser/elements/AbstractParser.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenGrammar
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+/**
+ * Start parsing at position in an iterator. Find the end, collect the results.
+ * @param iter An iterator of a list of tokens, starting at a position to parse.
+ */
+abstract class AbstractParser(iter: ListIterator<Token>) {
+
+ val indexStart: Int
+
+ init {
+ while (iter.hasNext() && peekToken(iter)?.identifier == TokenGrammar.EMPTY_LINE) {
+ iter.next() //skip over beginning empty lines
+ }
+ require(iter.hasNext()) { "Iterator is empty" }
+ indexStart = iter.nextIndex()
+ }
+
+ /**
+ * Do something with the tokens.
+ */
+ abstract fun parseTokens(tokens: List<Token>)
+
+ /**
+ * Determine end of token sequence, collect tokens from here to there.
+ */
+ abstract fun scanTokens(iter: ListIterator<Token>): List<Token>
+
+ protected fun scanDocTokens(iter: ListIterator<Token>): List<Token> {
+ val tokens = mutableListOf<Token>()
+ var token: Token
+
+ //ignore any empty lines that start the doc block (if called after doc_start)
+ while (peekPreviousToken(iter)?.identifier == TokenGrammar.EMPTY_LINE) iter.previous()
+
+ //queue up doc_start if called after
+ if (peekPreviousToken(iter)?.identifier == TokenGrammar.DOC_START) iter.previous()
+
+ if (peekToken(iter)!!.identifier != TokenGrammar.DOC_START)
+ throw ParseException("Doc comment blocks must begin with ${TokenGrammar.DOC_START.value}", this.indexStart)
+
+ tokens.add(iter.next()) //doc_start
+
+ while (iter.hasNext()) {
+ token = iter.next()
+ tokens.add(token)
+ if (token.identifier == TokenGrammar.DOC_END) {
+ break
+ } else if (token.identifier == TokenGrammar.DOC_START) {
+ throw ParseException("Nested doc comments not allowed", this.indexStart)
+ }
+ }
+
+ if (peekPreviousToken(iter)?.identifier != TokenGrammar.DOC_END) {
+ throw ParseException("Unable to find doc comment end", this.indexStart)
+ }
+ return tokens
+ }
+
+ /**
+ * Collect annotations (optional) to end of declaration (code body), may be nested.
+ */
+ protected fun scanDeclarationTokens(iter: ListIterator<Token>): List<Token> {
+ val tokens = mutableListOf<Token>()
+ var token: Token
+ var nestLevel = 0
+ var inDoc = false
+
+ while (iter.hasNext()) {
+ token = iter.next()
+ tokens.add(token)
+
+ if (token.identifier == TokenGrammar.DOC_START) {
+ inDoc = true
+ } else if (token.identifier == TokenGrammar.DOC_END) {
+ inDoc = false
+ }
+
+ if (inDoc) {
+ continue
+ } else if (token.identifier == TokenGrammar.BRACE_OPEN) {
+ nestLevel++
+ } else if (token.identifier == TokenGrammar.BRACE_CLOSE) {
+ nestLevel--
+ } else if (token.identifier == TokenGrammar.SEMICOLON && nestLevel == 0) {
+ break
+ }
+ }
+ assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+ return tokens
+ }
+
+ fun resetIterator(iter: ListIterator<Token>) {
+ while (iter.hasPrevious() && iter.previousIndex() >= indexStart) {
+ iter.previous()
+ }
+ assert(iter.nextIndex() == this.indexStart)
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/AnnotationParser.kt b/docs/src/parser/elements/AnnotationParser.kt
new file mode 100644
index 0000000..a1a32b4
--- /dev/null
+++ b/docs/src/parser/elements/AnnotationParser.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+class AnnotationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+ lateinit var name: TokenGrammar
+ lateinit var value: String
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+ val tokens = mutableListOf<Token>()
+ //depending how called, queue up annotation
+ if (peekToken(iter)?.identifier == TokenGrammar.AT) iter.next()
+ if (peekPreviousToken(iter)?.category == TokenCategory.Annotation) iter.previous()
+
+ if (peekToken(iter)!!.category != TokenCategory.Annotation)
+ throw ParseException("Doc token sequence must begin with an annotation", this.indexStart)
+
+ //just one token, info embedded
+ tokens.add(iter.next())
+ return tokens
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ val iter = tokens.listIterator()
+ assert(peekToken(iter)!!.category == TokenCategory.Annotation)
+ var token = iter.next()
+
+ this.name = token.identifier
+ this.value = parseAnnotationValue(token)
+ }
+
+ //capture text between parens
+ private fun parseAnnotationValue(token: Token): String {
+ return Regex(""".*\((.*)\).*""")
+ .matchEntire(token.value)
+ ?.groups?.get(1)
+ ?.value?.trim()
+ ?: ""
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/DocAnnotationParser.kt b/docs/src/parser/elements/DocAnnotationParser.kt
new file mode 100644
index 0000000..ee043da
--- /dev/null
+++ b/docs/src/parser/elements/DocAnnotationParser.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements
+
+import lexer.ILexer
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+
+class DocAnnotationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+ lateinit var tag: TokenGrammar
+ var arg: String? = null //some tags have arguments (eg. @param arg desc)
+ lateinit var description: String
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ private fun formatValue(tokens: List<Token>): String {
+ return if (tokens.isEmpty()) {
+ ""
+ } else {
+ tokens.map {
+ when (it.identifier) {
+ TokenGrammar.EMPTY_LINE -> "\n\n"
+ else -> it.value
+ }
+ }
+ .joinToString(" ")
+ .let { ILexer.unpadDelimiters(it) }
+ }
+ }
+
+ /**
+ * Scan until: doc end token, empty line, another @param
+ */
+ override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+ val tokens = mutableListOf<Token>()
+ var token: Token
+
+ //depending how invoked, queue up doc annotation token
+ if (peekToken(iter)?.identifier == TokenGrammar.AT) iter.next()
+ if (peekPreviousToken(iter)?.category == TokenCategory.DocAnnotation) iter.previous()
+
+ if (peekToken(iter)!!.category != TokenCategory.DocAnnotation)
+ throw ParseException("Token sequence must begin with a DocAnnotation", this.indexStart)
+
+ loop@ while (iter.hasNext()) {
+ token = iter.next()
+
+ when {
+ //descriptions don't span blank lines
+ token.identifier == TokenGrammar.EMPTY_LINE -> break@loop
+
+ //if doc block ends or found next annotation tag, back up and bail
+ token.identifier == TokenGrammar.DOC_END ||
+ token.identifier == TokenGrammar.AT && peekToken(iter)?.category == TokenCategory.DocAnnotation -> {
+ iter.previous()
+ break@loop
+ }
+
+ else -> tokens.add(token)
+ }
+ }
+ return tokens
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ val iter = tokens.listIterator()
+ assert(iter.hasNext())
+ val token = iter.next()
+
+ if (token.category != TokenCategory.DocAnnotation)
+ throw ParseException("Invalid doc anootation tag: ${token.value}", this.indexStart)
+
+ //annotation tag name (must be in TokenGrammar)
+ this.tag = token.identifier
+
+ //annotation tag can take an optional argument
+ //so can return (preferred). TODO: check HALs if mandatory
+ if (token.identifier == TokenGrammar.PARAM || token.identifier == TokenGrammar.RETURN) {
+ if (iter.hasNext()) this.arg = iter.next().value
+ }
+
+ //the rest is annotation description
+ val descTokens = mutableListOf<Token>()
+ while (iter.hasNext()) {
+ descTokens.add(iter.next())
+ }
+
+ this.description = if (descTokens.isEmpty()) {
+ ""
+ } else {
+ descTokens.map { if (it.identifier == TokenGrammar.EMPTY_LINE) "\n\n" else it.value }
+ .joinToString(" ")
+ .let { ILexer.unpadDelimiters(it) }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/DocParser.kt b/docs/src/parser/elements/DocParser.kt
new file mode 100644
index 0000000..fd1f8a1
--- /dev/null
+++ b/docs/src/parser/elements/DocParser.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements
+
+import lexer.ILexer
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.peekToken
+
+class DocParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+ val description: String by lazy { formatDescription(this.descTokens) }
+ var docAnnotationParsers = mutableListOf<DocAnnotationParser>()
+
+ private var descTokens = mutableListOf<Token>()
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ private fun formatDescription(tokens: List<Token>): String {
+ return tokens
+ .filterNot { it.identifier == TokenGrammar.DOC_START }
+ .filterNot { it.identifier == TokenGrammar.DOC_END }
+ .map {
+ when (it.identifier) {
+ TokenGrammar.EMPTY_LINE -> "\n\n"
+ else -> it.value
+ }
+ }
+ .joinToString(" ")
+ .let { ILexer.unpadDelimiters(it) }
+ }
+
+ override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+ //keep doc_start and doc_end tokens /** ... */
+ return scanDocTokens(iter)
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ val iter = tokens.listIterator()
+ var token = iter.next() //doc_start
+
+ assert(token.identifier == TokenGrammar.DOC_START)
+ assert(tokens.last().identifier == TokenGrammar.DOC_END)
+
+ loop@ while(iter.hasNext()) {
+ token = iter.next()
+
+ when {
+ token.identifier == TokenGrammar.AT && peekToken(iter)?.category == TokenCategory.DocAnnotation -> {
+ docAnnotationParsers.add(DocAnnotationParser(iter)) //increments iterator
+ }
+ token.identifier == TokenGrammar.DOC_END -> break@loop
+ else -> this.descTokens.add(token)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/EntryCollectionParser.kt b/docs/src/parser/elements/EntryCollectionParser.kt
new file mode 100644
index 0000000..493998c
--- /dev/null
+++ b/docs/src/parser/elements/EntryCollectionParser.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenGrammar
+import parser.config
+import writer.warn
+import java.text.ParseException
+
+/**
+ * Collection of doc entryParsers in a token stream.
+ * Typically, like entries are grouped together in a file (eg. Interface def),
+ * But could be useful for declarations with fields (enums, structs).
+ */
+class EntryCollectionParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+ var entryParsers = mutableListOf<EntryParser>()
+
+ constructor(tokens: List<Token>) : this(tokens.listIterator())
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) this.resetIterator(iter)
+ }
+
+ //use all the tokens
+ override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+ val tokens = mutableListOf<Token>()
+ while (iter.hasNext()) {
+ tokens.add(iter.next())
+ }
+ return tokens
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ val iter = tokens.listIterator()
+ var token: Token
+
+ //find entry parsers - must start with doc_start
+ while(iter.hasNext()) {
+ token = iter.next()
+
+ if (token.identifier == TokenGrammar.DOC_START) {
+ try {
+ entryParsers.add(EntryParser(iter)) //advances iterator past declaration
+ } catch (ex: IllegalEntryException) {
+ if (config.warnOnly) {
+ //bail on current entry but continue
+ warn("${ex.message}, skipping entry")
+ } else {
+ throw ParseException(ex.message, this.indexStart)
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/EntryParser.kt b/docs/src/parser/elements/EntryParser.kt
new file mode 100644
index 0000000..30ffc3b
--- /dev/null
+++ b/docs/src/parser/elements/EntryParser.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.config
+import parser.elements.declarations.*
+import parser.peekPreviousToken
+import parser.peekToken
+import writer.tokenValues
+import writer.warn
+import java.text.ParseException
+
+//an entry consists of: doc block, annotationParsers, declarationParser (code sig)
+//EntryParser contains:
+//- docParser
+// * description
+// * docAnnotationParsers
+// - tag (token)
+// - arg?
+// - description
+//- annotationParsers
+// * name
+// * value
+//- declarationParser
+// * name
+// * members [CompoundDeclarationParser]: type, name, tokens, typedef [CompoundMemberDeclaration]
+// * members [EnumDeclarationParser]: name, value?
+// * type [EnumDeclarationParser]
+// * type [TypedefDeclarationParser]
+// * extendsName [InterfaceDeclarationParser]
+// * extendsVersion [InterfaceDeclarationParser]
+// * prefix [MethodDeclarationParser]
+// * params [MethodDeclarationParser]: ArgEntry: type, name
+// * returns [[MethodDeclarationParser]: ArgEntry: type, name
+
+class IllegalEntryException(msg: String? = null, cause: Throwable? = null) : Exception(msg, cause)
+
+/**
+ * An entry inclused the doc comment block, code docAnnotationParsers, and method signature/interface
+ */
+class EntryParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractParser(iter) {
+
+ //doc description, summary, and doc annottions
+ lateinit var docParser: DocParser
+ //annotation
+ val annotationParsers = mutableListOf<AnnotationParser>()
+ //declaration - properties depend on declarationParser subclass
+ lateinit var declarationParser: AbstractDeclarationParser
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ //start at doc_start, collect until end of declaration
+ override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+ //queue up doc_start
+ //ignore any empty lines that start the doc block
+ while (peekPreviousToken(iter)?.identifier == TokenGrammar.EMPTY_LINE) iter.previous()
+ //if called after the doc_start was found
+ if (peekPreviousToken(iter)?.identifier == TokenGrammar.DOC_START) iter.previous()
+
+ val tokens = mutableListOf<Token>()
+ //collect doc block /** ... */
+ tokens += scanDocTokens(iter)
+ //collect annotations and declaration, nested to semicolon
+ tokens += scanDeclarationTokens(iter)
+ return tokens
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ val iter = tokens.listIterator()
+
+ /*
+ * doc comment block
+ */
+ do {
+ assert(peekToken(iter)!!.identifier == TokenGrammar.DOC_START)
+ this.docParser = DocParser(iter) //increments iterator
+ assert(peekPreviousToken(iter)!!.identifier == TokenGrammar.DOC_END)
+
+ //if there's consecutive doc blocks, use the last one found in warning mode, otherwise error
+ if (peekToken(iter)?.identifier != TokenGrammar.DOC_START) {
+ break //good to go
+ } else {
+ val msg = "Found consecutive doc block after: ${this.docParser.description}"
+ if (config.warnOnly) {
+ warn("${msg}\nUsing last found doc block.")
+ } else {
+ throw ParseException(msg, this.indexStart)
+ }
+ }
+ } while (true)
+
+ /*
+ * annotations (optional)
+ */
+ while (iter.hasNext() && peekToken(iter)!!.identifier == TokenGrammar.AT) {
+ iter.next()
+ if (peekToken(iter)?.category == TokenCategory.Annotation) {
+ this.annotationParsers.add(AnnotationParser(iter)) //increments iterator
+ } else {
+ throw ParseException("Unknown annotation tag: ${peekToken(iter)?.value}", this.indexStart)
+ }
+ }
+
+ /*
+ * declaration
+ */
+ val token = peekToken(iter) ?: throw ParseException("No declaration body available", this.indexStart)
+
+ //check we're not at an annotation
+ assert(token.identifier != TokenGrammar.AT && token.category != TokenCategory.Annotation)
+
+ /*
+ * known bad starts for a declaration
+ */
+
+ if (token.identifier == TokenGrammar.DOC_START) {
+ throw ParseException("Bad doc block location:\n${tokenValues(tokens)}", this.indexStart)
+ } else if (token.identifier == TokenGrammar.PACKAGE) {
+ //usually this means they've used a doc block for the license
+ throw IllegalEntryException("Don't document the package declaration") //handled in EntryCollectionParser
+ } else if (token.category != TokenCategory.Word
+ && token.category != TokenCategory.TypeDef
+ && token.category != TokenCategory.Keyword) {
+ //sanity check - skip entry or bail
+ throw IllegalEntryException("Invalid start for entry declaration: '${token.value}'\n" +
+ "tokens: ${tokenValues(tokens)}")
+ //throw ParseException("Invalid start for entry declaration: ${token}\ntoken: ${token.value}\n${tokenValues(tokens)}", this.indexStart)
+ }
+
+ this.declarationParser = when (token.identifier) {
+ TokenGrammar.INTERFACE -> {
+ this.shouldResetIterator = true
+ InterfaceDeclarationParser(iter)
+ }
+ TokenGrammar.ENUM -> EnumDeclarationParser(iter)
+ TokenGrammar.TYPEDEF -> TypedefDeclarationParser(iter)
+ TokenGrammar.STRUCT, TokenGrammar.UNION -> CompoundDeclarationParser(iter)
+ else -> MethodDeclarationParser(iter)
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/AbstractDeclarationParser.kt b/docs/src/parser/elements/declarations/AbstractDeclarationParser.kt
new file mode 100644
index 0000000..6d906a5
--- /dev/null
+++ b/docs/src/parser/elements/declarations/AbstractDeclarationParser.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.AbstractParser
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+//can be a method, struct, enum, typedef
+abstract class AbstractDeclarationParser(iter: ListIterator<Token>) : AbstractParser(iter) {
+
+ abstract var name: String //user-specified name declarationParser
+
+ //should be segmented in EntryParser, remaining tokens are the declarationParser, may be nested
+ override fun scanTokens(iter: ListIterator<Token>): List<Token> {
+ val token = peekPreviousToken(iter) ?: throw ParseException("No token before declaration", this.indexStart)
+
+ if (token.category != TokenCategory.Annotation && token.identifier != TokenGrammar.DOC_END)
+ throw ParseException("Invalid declaration start", this.indexStart)
+
+ return scanDeclarationTokens(iter)
+ }
+
+ /**
+ * Takes a delimited separated list and splits entries into list of list<token>.
+ * Ignore nested lists using same open/close delimiter.
+ * For method param lists, enum members, struct members, etc.
+ */
+ protected fun scanDelimitedList(iter: ListIterator<Token>,
+ delimiter: TokenGrammar = TokenGrammar.COMMA,
+ openDelimiter: TokenGrammar = TokenGrammar.PAREN_OPEN,
+ closeDelimiter: TokenGrammar = TokenGrammar.PAREN_CLOSE): List<List<Token>> {
+ val allFields = mutableListOf<List<Token>>() //top-level list
+ //queue up list open
+ if (iter.hasPrevious() && peekPreviousToken(iter)!!.identifier == openDelimiter) {
+ iter.previous()
+ }
+ var token = iter.next()
+ if (token.identifier != openDelimiter)
+ throw ParseException("Expected list start '${openDelimiter}', but got '${token.identifier}'", this.indexStart)
+
+ // collect tokens between open/close delimiters, fields separated by delimiter.
+ // ignore if nested, ignore in doc comment
+ while (iter.hasNext()) {
+ token = peekToken(iter)!! //iter.next()
+
+ if (token.identifier == closeDelimiter) {
+ iter.next()
+ break
+ } else if (token.identifier == delimiter) {
+ iter.next()
+ continue //skip
+ } else {
+ //start field entry
+ val fieldTokens = mutableListOf<Token>()
+ var inDoc = false
+ var nestLevel = 0
+
+ while (iter.hasNext()) {
+ token = iter.next()
+
+ if (token.identifier == TokenGrammar.DOC_START) {
+ inDoc = true
+ } else if (token.identifier == TokenGrammar.DOC_END) {
+ inDoc = false
+ }
+
+ //check for end of field
+ if ((token.identifier == delimiter || token.identifier == closeDelimiter) && nestLevel == 0 && !inDoc) {
+ break
+ } else {
+ fieldTokens.add(token)
+ }
+
+ if (token.identifier == openDelimiter) {
+ nestLevel++
+ } else if (token.identifier == closeDelimiter && nestLevel > 0) {
+ nestLevel--
+ }
+ }
+
+ //add entry
+ allFields.add(fieldTokens)
+ //check for end of list
+ if (token.identifier == closeDelimiter && nestLevel == 0) {
+ break
+ }
+ }
+ }
+
+ if (!iter.hasPrevious() || peekPreviousToken(iter)!!.identifier != closeDelimiter) {
+ throw ParseException("Didn't find closing '${closeDelimiter.value}' for list, found '${peekPreviousToken(iter)!!.value}'", this.indexStart)
+ }
+ return allFields
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/CompoundDeclarationParser.kt b/docs/src/parser/elements/declarations/CompoundDeclarationParser.kt
new file mode 100644
index 0000000..69f82d1
--- /dev/null
+++ b/docs/src/parser/elements/declarations/CompoundDeclarationParser.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.DocParser
+import writer.tokenValues
+import java.text.ParseException
+
+/**
+ * Used for Structs and Unions
+ */
+class CompoundDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+ lateinit var type: TokenGrammar
+ lateinit override var name: String
+ var members = mutableListOf<IMemberDeclaration>() //defined below
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ val iter = tokens.listIterator()
+ var token = iter.next()
+ assert(token.identifier == TokenGrammar.STRUCT || token.identifier == TokenGrammar.UNION)
+ assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+ //type - struct or union
+ this.type = token.identifier
+
+ //name
+ token = iter.next()
+ if (token.category != TokenCategory.Word)
+ throw ParseException("Invalid struct name: ${tokenValues(tokens)}", this.indexStart)
+ this.name = token.value
+
+ //parse each semicolon-delimited statement
+ scanDelimitedList(iter,
+ delimiter = TokenGrammar.SEMICOLON,
+ openDelimiter = TokenGrammar.BRACE_OPEN,
+ closeDelimiter = TokenGrammar.BRACE_CLOSE)
+ .forEach {
+ var docParser: DocParser? = null
+ var statementTokens = it.toMutableList()
+ if (statementTokens.isEmpty())
+ throw ParseException("Invalid statement in: ${tokenValues(tokens)}", this.indexStart)
+
+ //If doc, extract doc tokens and parse, and remove from statement tokens
+ if (statementTokens.first().identifier == TokenGrammar.DOC_START) {
+ val idx = statementTokens.indexOfFirst { it.identifier == TokenGrammar.DOC_END }
+ if (idx == -1) throw ParseException("Unable to find doc_end", this.indexStart)
+ val docTokens = statementTokens.subList(0, idx+1)
+ docParser = DocParser(docTokens.listIterator())
+ statementTokens = statementTokens.subList(idx+1, statementTokens.size)
+ }
+
+ if (statementTokens.isEmpty())
+ throw ParseException("Invalid statement in: ${tokenValues(tokens)}", this.indexStart)
+
+ when(statementTokens.first().identifier) {
+ TokenGrammar.STRUCT, TokenGrammar.UNION -> {
+ assert(statementTokens.first().category == TokenCategory.TypeDef)
+ this.members.add(CompoundMemberDeclaration(
+ typeDef = statementTokens.first().identifier,
+ type = statementTokens.get(1).value,
+ name = statementTokens.last().value,
+ docParser = docParser,
+ tokens = statementTokens.subList(2, statementTokens.size-1)
+ ))
+ }
+ TokenGrammar.ENUM -> {
+ assert(statementTokens.size > 1)
+ this.members.add(MemberDeclaration(
+ type = statementTokens.first().value,
+ name = statementTokens.get(1).value,
+ docParser = docParser,
+ tokens = statementTokens
+ ))
+ }
+ else -> {
+ this.members.add(MemberDeclaration(
+ type = statementTokens.first().value,
+ name = statementTokens.last().value,
+ docParser = docParser,
+ tokens = statementTokens
+ ))
+ }
+ }
+ }
+ }
+}
+
+interface IMemberDeclaration {
+ val type: String
+ val name: String
+ val docParser: DocParser?
+ val tokens: List<Token> //TODO: doesn't seem needed
+}
+
+class MemberDeclaration(override val type: String,
+ override val name: String,
+ override val docParser: DocParser?,
+ override val tokens: List<Token>) : IMemberDeclaration
+
+class CompoundMemberDeclaration(override val type: String,
+ override val name: String,
+ override val docParser: DocParser?,
+ override val tokens: List<Token>,
+ val typeDef: TokenGrammar) : IMemberDeclaration
diff --git a/docs/src/parser/elements/declarations/EnumDeclarationParser.kt b/docs/src/parser/elements/declarations/EnumDeclarationParser.kt
new file mode 100644
index 0000000..fb0fe64
--- /dev/null
+++ b/docs/src/parser/elements/declarations/EnumDeclarationParser.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.DocParser
+import parser.peekToken
+import writer.tokenValues
+import java.text.ParseException
+
+class EnumDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+
+ lateinit override var name: String
+ lateinit var type: String
+ var members = mutableListOf<EnumMember>()
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ val iter = tokens.listIterator()
+ var token = iter.next()
+ assert(token.identifier == TokenGrammar.ENUM)
+ assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+ //name
+ token = iter.next()
+ if (token.category != TokenCategory.Word)
+ throw ParseException("Invalid enum name: ${tokenValues(tokens)}", this.indexStart)
+ this.name = token.value
+
+ token = iter.next() //':'
+ if (token.identifier != TokenGrammar.COLON)
+ throw ParseException("Invalid enum type syntax: ${tokenValues(tokens)}", this.indexStart)
+
+ //type: can be type name or package
+ assert(iter.hasNext())
+ val sb = StringBuilder()
+ while (iter.hasNext() && peekToken(iter)!!.identifier != TokenGrammar.BRACE_OPEN) {
+ sb.append(iter.next().value)
+ }
+ this.type = sb.toString()
+
+ //members
+ //convert iterator sequence of comma separated tokens to list of lists
+ scanDelimitedList(iter, openDelimiter = TokenGrammar.BRACE_OPEN, closeDelimiter = TokenGrammar.BRACE_CLOSE)
+ .forEach {
+ var statementTokens = it.toMutableList()
+ var docParser: DocParser? = null
+ assert(statementTokens.isNotEmpty())
+
+ //If doc, extract doc tokens and parse, and remove from statement tokens
+ if (statementTokens.first().identifier == TokenGrammar.DOC_START) {
+ val idx = statementTokens.indexOfFirst { it.identifier == TokenGrammar.DOC_END }
+ if (idx == -1) throw ParseException("Unable to find doc_end", this.indexStart)
+
+ val docTokens = statementTokens.subList(0, idx+1)
+ docParser = DocParser(docTokens.listIterator())
+ statementTokens = statementTokens.subList(idx+1, statementTokens.size)
+ }
+ if (statementTokens.isEmpty())
+ throw ParseException("Invalid member in enum: ${tokenValues(tokens)}", this.indexStart)
+ val member = EnumMember(statementTokens)
+ member.docParser = docParser
+ this.members.add(member)
+ }
+ }
+}
+
+//split member: name [= value]
+class EnumMember(tokens: List<Token>) {
+ val name: String
+ var value: String? = null
+ var docParser: DocParser? = null
+
+ init {
+ assert(tokens.isNotEmpty())
+ this.name = tokens.first().value
+
+ //check for assignment, take right side
+ if (tokens.any { it.identifier == TokenGrammar.EQUAL }) {
+ this.value = tokens.takeLastWhile { it.identifier != TokenGrammar.EQUAL }
+ .map { it.value }
+ .joinToString(" ")
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/InterfaceDeclarationParser.kt b/docs/src/parser/elements/declarations/InterfaceDeclarationParser.kt
new file mode 100644
index 0000000..ac22c16
--- /dev/null
+++ b/docs/src/parser/elements/declarations/InterfaceDeclarationParser.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+
+class InterfaceDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+
+ lateinit override var name: String
+ var extendsName: String? = null
+ var extendsVersion: Float? = null
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ // example format: interface ITunerCallback extends @1.0::ITunerCallback
+ override fun parseTokens(tokens: List<Token>) {
+ assert(tokens.isNotEmpty())
+ assert(tokens.first().identifier == TokenGrammar.INTERFACE)
+ assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+ //grab first line of declarationParser
+ val sigToks = tokens.takeWhile { it.identifier != TokenGrammar.BRACE_OPEN }
+ assert(sigToks[1].category == TokenCategory.Word)
+ assert(sigToks.last().category == TokenCategory.Word) //either interface name or extends name
+
+ this.name = sigToks[1].value
+
+ //parse extends info (if exists)
+ if (sigToks.any { it.identifier == TokenGrammar.EXTENDS }) {
+ this.extendsName = sigToks.last().value
+ this.extendsVersion = sigToks.find { it.category == TokenCategory.Number }?.value?.toFloat()
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/MethodDeclarationParser.kt b/docs/src/parser/elements/declarations/MethodDeclarationParser.kt
new file mode 100644
index 0000000..beb80a7
--- /dev/null
+++ b/docs/src/parser/elements/declarations/MethodDeclarationParser.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import writer.tokenValues
+import java.text.ParseException
+
+data class ArgEntry(val type: String, val name: String)
+
+class MethodDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+ lateinit override var name: String
+
+ var prefix: String? = null
+ var params = mutableListOf<ArgEntry>()
+ var returns = mutableListOf<ArgEntry>()
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ // [prefix] <name> ( [typedef] [name], ...) [generates] ( [typedef], [name], ...);
+ override fun parseTokens(tokens: List<Token>) {
+ assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+ val iter = tokens.listIterator()
+ var token = iter.next()
+
+ //grab prefix (typedef or keyword), if there
+ if (token.category == TokenCategory.TypeDef || token.category == TokenCategory.Keyword) {
+ this.prefix = token.value
+ token = iter.next()
+ }
+
+ //name
+ if (token.category != TokenCategory.Word) {
+ throw ParseException("Invalid declarationParser name: '${token.value}'\nBad Tokens: ${tokenValues(tokens)}",
+ this.indexStart)
+ }
+ this.name = token.value
+
+ assert(iter.hasNext())
+ token = iter.next()
+
+ //parse arg list
+ if (token.identifier == TokenGrammar.PAREN_OPEN) {
+ addArgs(iter, this.params)
+ assert(iter.hasNext())
+ token = iter.next()
+ }
+
+ //parse return list
+ if (token.identifier == TokenGrammar.GENERATES) {
+ addArgs(iter, this.returns)
+ assert(iter.hasNext())
+ token = iter.next()
+ }
+
+ assert(token.identifier == TokenGrammar.SEMICOLON)
+ }
+
+ /**
+ * Arguments are provided in the form: (type1 name1, type2 name2, ...)
+ */
+ private fun addArgs(iter: ListIterator<Token>, list: MutableList<ArgEntry>) {
+ val argLists = this.scanDelimitedList(iter) //return list of list<token>
+ for (argList in argLists) {
+ list.add(formatArgEntry(argList)) //use tokens to create ArgEntry
+ }
+ }
+
+ private fun formatArgEntry(paramTokens: List<Token>): ArgEntry {
+ val type = paramTokens.takeWhile { it != paramTokens.last() } //but-last
+ .map { it.value }
+ .joinToString("")
+ val name = paramTokens.last().value
+ return ArgEntry(type, name)
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/elements/declarations/TypedefDeclarationParser.kt b/docs/src/parser/elements/declarations/TypedefDeclarationParser.kt
new file mode 100644
index 0000000..fa3574b
--- /dev/null
+++ b/docs/src/parser/elements/declarations/TypedefDeclarationParser.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.elements.declarations
+
+import lexer.Token
+import lexer.TokenGrammar
+
+class TypedefDeclarationParser(iter: ListIterator<Token>, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) {
+ lateinit override var name: String //synonym
+ lateinit var type: String //type declarationParser
+
+ init {
+ parseTokens(scanTokens(iter))
+ if (shouldResetIterator) resetIterator(iter)
+ }
+
+ override fun parseTokens(tokens: List<Token>) {
+ assert(tokens.isNotEmpty())
+ assert(tokens.first().identifier == TokenGrammar.TYPEDEF)
+ assert(tokens.last().identifier == TokenGrammar.SEMICOLON)
+
+ this.name = tokens.get(tokens.size - 2).value
+ this.type = tokens.subList(1, tokens.size - 2).map { it.value }.joinToString("")
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/files/AbstractFileParser.kt b/docs/src/parser/files/AbstractFileParser.kt
new file mode 100644
index 0000000..5348dfd
--- /dev/null
+++ b/docs/src/parser/files/AbstractFileParser.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.files
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.EntryCollectionParser
+import parser.elements.EntryParser
+import parser.elements.declarations.CompoundDeclarationParser
+import parser.elements.declarations.EnumDeclarationParser
+import parser.elements.declarations.TypedefDeclarationParser
+import parser.peekPreviousToken
+import parser.peekToken
+import java.text.ParseException
+
+/**
+ * Parses package info and all entries (determined by doc start/end tokens).
+ * Adds empty doc tokens for required types.
+ * This class shouldn't be instantiated on it's own.
+ */
+abstract class AbstractFileParser(tokens: List<Token>) {
+
+ private val packageInfo: PackageInfo by lazy { parsePackageInfo(tokens) }
+
+ abstract val name: String
+ val packageName: String get() = packageInfo.name
+ val packageVersion: Float get() = packageInfo.version
+
+ protected val entries: List<EntryParser> by lazy {
+ assert(tokens.isNotEmpty())
+ //add empty docblocks
+ EntryCollectionParser(insertDocsForRequiredTypes(tokens)).entryParsers
+ }
+
+ val enums: List<EntryParser> by lazy { getEntriesByDeclarationParser<EnumDeclarationParser>() }
+ val typedefs: List<EntryParser> by lazy { getEntriesByDeclarationParser<TypedefDeclarationParser>() }
+ val structs: List<EntryParser> by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.STRUCT) }
+ val unions: List<EntryParser> by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.UNION) }
+
+ protected inline fun <reified T> getEntriesByDeclarationParser(): List<EntryParser> {
+ return entries.filter { it.declarationParser is T }
+ }
+
+ private fun getEntriesByCompoundDeclarationParser(identifier: TokenGrammar): List<EntryParser> {
+ return getEntriesByDeclarationParser<CompoundDeclarationParser>()
+ .filter { (it.declarationParser as CompoundDeclarationParser).type == identifier }
+ }
+
+ private val REQUIRED_DOC_TYPES = listOf(
+ TokenGrammar.INTERFACE,
+ TokenGrammar.ENUM,
+ TokenGrammar.STRUCT,
+ TokenGrammar.UNION,
+ TokenGrammar.TYPEDEF)
+
+ /**
+ * Insert doc block before the undocumented types we want to show up.
+ */
+ private fun insertDocsForRequiredTypes(tokens: List<Token>): List<Token> {
+ val tokensCopy = mutableListOf<Token>()
+ val iter = tokens.listIterator()
+ var token: Token
+ var inDoc = false
+
+ while (iter.hasNext()) {
+ token = iter.next()
+ tokensCopy.add(token)
+
+ if (token.identifier == TokenGrammar.DOC_START) {
+ inDoc = true
+ continue
+ } else if (token.identifier == TokenGrammar.DOC_END) {
+ inDoc = false
+ continue
+
+ } else if (!inDoc && token.identifier in REQUIRED_DOC_TYPES) {
+ //make sure it's not a reference to a Generic: <name>
+ if (peekToken(iter)?.identifier == TokenGrammar.CHEVRON_CLOSE) {
+ continue
+ }
+
+ val idx = indexInsertionPointforDocTokens(tokensCopy)
+ if (idx != -1) {
+ val removedTokens = mutableListOf<Token>()
+ repeat(idx) {
+ removedTokens.add(tokensCopy.removeAt(tokensCopy.size-1))
+ }
+ tokensCopy.add(TokenGrammar.newToken(TokenGrammar.DOC_START.value))
+ tokensCopy.add(TokenGrammar.newToken(TokenGrammar.DOC_END.value))
+ removedTokens.reversed().forEach { tokensCopy.add(it) }
+ }
+ }
+ }
+ return tokensCopy.toList()
+ }
+
+ /**
+ * @return -1 if documented, otherwise the index count backwards for where
+ * to begin insertion of doc tokens.
+ */
+ private fun indexInsertionPointforDocTokens(tokens: List<Token>): Int {
+ val iter = tokens.reversed().listIterator()
+ var token: Token
+ var idx = 0
+
+ iter.next() //skip keyword token
+ while (iter.hasNext()) {
+ token = iter.next()
+ if (token.identifier == TokenGrammar.AT || token.category == TokenCategory.Annotation) {
+ idx++
+ continue //skip annotations
+ } else {
+ return if (token.identifier == TokenGrammar.DOC_END) -1 else idx+1
+ }
+ }
+ throw ParseException("Empty token list", 0)
+ }
+}
\ No newline at end of file
diff --git a/docs/src/parser/files/InterfaceFileParser.kt b/docs/src/parser/files/InterfaceFileParser.kt
new file mode 100644
index 0000000..d68b12c
--- /dev/null
+++ b/docs/src/parser/files/InterfaceFileParser.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.files
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import parser.elements.AnnotationParser
+import parser.elements.EntryParser
+import parser.elements.declarations.InterfaceDeclarationParser
+import parser.elements.declarations.MethodDeclarationParser
+
+class InterfaceFileParser(tokens: List<Token>) : AbstractFileParser(tokens) {
+
+ private val interfaceEntry: EntryParser by lazy {
+ //returns first matching element (i don't think there can be multiple interface declarations)
+ this.entries.find { it.declarationParser is InterfaceDeclarationParser }
+ ?: throw ClassNotFoundException("Unable to find an InterfaceDeclarationParser")
+ }
+ //need this for the cast
+ private val interfaceDecl: InterfaceDeclarationParser by lazy {
+ interfaceEntry.declarationParser as InterfaceDeclarationParser
+ }
+
+ /* top-level properties on the interface itself, ie. not its entries */
+
+ override val name: String get() = interfaceDecl.name
+ val extendsName: String? get() = interfaceDecl.extendsName
+ val extendsVersion: Float? get() = interfaceDecl.extendsVersion
+
+ //doc
+ val description get() = interfaceEntry.docParser.description
+ val docAnnotations get() = interfaceEntry.docParser.docAnnotationParsers //tag, arg?, description
+
+ val annotations: List<AnnotationParser> get() = interfaceEntry.annotationParsers //name, value
+ val methods: List<EntryParser> by lazy { getEntriesByDeclarationParser<MethodDeclarationParser>() }
+
+ companion object {
+ /**
+ * Searches tokens for an interface identifier.
+ * Maybe not the most accurate measurement, but good enough.
+ */
+ fun isInterface(tokens: List<Token>): Boolean {
+ val iter = tokens.listIterator()
+ var token: Token
+ var inDoc = false
+
+ while (iter.hasNext()) {
+ token = iter.next()
+ if (token.identifier == TokenGrammar.DOC_START) {
+ inDoc = true
+ continue
+ } else if (token.identifier == TokenGrammar.DOC_END) {
+ inDoc = false
+ continue
+
+ } else if (!inDoc && token.identifier == TokenGrammar.INTERFACE) {
+ if (iter.next().category != TokenCategory.Word) break //no, try again
+
+ return true
+ }
+ }
+ return false
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/docs/src/parser/files/TypesFileParser.kt
similarity index 79%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to docs/src/parser/files/TypesFileParser.kt
index d79e6ab..a903a6a 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/docs/src/parser/files/TypesFileParser.kt
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
-interface IFoo {
-};
\ No newline at end of file
+package parser.files
+
+import lexer.Token
+
+class TypesFileParser(tokens: List<Token>) : AbstractFileParser(tokens) {
+ override val name = "types"
+}
\ No newline at end of file
diff --git a/docs/src/parser/files/package.kt b/docs/src/parser/files/package.kt
new file mode 100644
index 0000000..5b1cb81
--- /dev/null
+++ b/docs/src/parser/files/package.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser.files
+
+import lexer.Token
+import lexer.TokenCategory
+import lexer.TokenGrammar
+import java.text.ParseException
+
+data class PackageInfo(val name: String, val version: Float)
+
+/**
+ * Find and parse package info. Throw error if it can't find a valid declarationParser format.
+ * Example format: package android.hardware.audio@2.0;
+ */
+fun parsePackageInfo(tokens: List<Token>): PackageInfo {
+ val iter: ListIterator<Token> = tokens.listIterator()
+ var token: Token
+
+ while (iter.hasNext()) {
+ token = iter.next()
+
+ if (token.identifier == TokenGrammar.PACKAGE) {
+ //collect namespace
+ val pkgNameToks = mutableListOf<Token>()
+ while (iter.hasNext()) {
+ token = iter.next()
+ if (token.identifier != TokenGrammar.AT && token.identifier != TokenGrammar.SEMICOLON) {
+ pkgNameToks.add(token)
+ } else {
+ break
+ }
+ }
+ val pkgName = pkgNameToks.map { it.value }.joinToString("")
+
+ //step through format and test syntax
+ if (token.identifier != TokenGrammar.AT) break
+ token = iter.next()
+ if (token.category != TokenCategory.Number) break //version
+ val pkgVer = token.value.toFloat()
+ token = iter.next()
+ if (token.identifier != TokenGrammar.SEMICOLON) break
+
+ //hooray, a proper package format
+ return PackageInfo(pkgName, pkgVer)
+ }
+ }
+ throw ParseException("Unable to find a valid package declaration", 0)
+}
\ No newline at end of file
diff --git a/docs/src/parser/utils.kt b/docs/src/parser/utils.kt
new file mode 100644
index 0000000..77fa3d3
--- /dev/null
+++ b/docs/src/parser/utils.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package parser
+
+import lexer.Token
+
+/**
+ * Returns the next token in the iteration, without advancing the iteration.
+ */
+fun peekToken(iter: ListIterator<Token>): Token? {
+ if (iter.hasNext()) {
+ val token = iter.next()
+ iter.previous()
+ return token
+ } else {
+ return null
+ }
+}
+
+fun peekPreviousToken(iter: ListIterator<Token>): Token? {
+ if (iter.hasPrevious()) {
+ val token = iter.previous()
+ iter.next()
+ return token
+ } else {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/AbstractElement.kt b/docs/src/writer/elements/AbstractElement.kt
new file mode 100644
index 0000000..8be0bae
--- /dev/null
+++ b/docs/src/writer/elements/AbstractElement.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.AbstractDeclarationParser
+import writer.formatTextasHTML
+import writer.htmlEscape
+
+abstract class AbstractElement(parser: EntryParser) {
+ protected val docParser = parser.docParser
+ protected val annotationParsers = parser.annotationParsers
+
+ abstract protected val declarationParser: AbstractDeclarationParser //specify in subclass
+ abstract fun declaration(): String
+ abstract fun detailsRows(): String
+
+ open fun toHTML(): String {
+ return """
+<div>
+ <h3>${declarationParser.name}</h3>
+ <pre class="prettyprint devsite-disable-click-to-copy">${declaration()}</pre>
+ <div>
+ ${formatTextasHTML(docParser.description)}
+ </div>
+ <table class="details responsive">
+ <thead>
+ <th colspan="2">Details</th>
+ </thead>
+ <tbody>
+ ${detailsRows()}
+ ${annotationRows()}
+ </tbody>
+ </table>
+</div>
+""".trim()
+ }
+
+ private fun annotationRows(): String {
+ val sb = StringBuilder()
+ if (annotationParsers.isNotEmpty()) {
+ sb.append("""
+ <tr>
+ <td>Annotations</td>
+ <td>
+ <table class="function param responsive">
+ <tbody>""")
+ //AnnotationParser => name:TokenGrammar, value:String
+ annotationParsers.forEach { arg ->
+ sb.append("""
+ <tr>
+ <td>
+ <code>${arg.name.value}</code>
+ </td>
+ <td>
+ <div>${htmlEscape(arg.value)}</div>
+ </td>
+ </tr>""")
+ }
+ sb.append("""
+ </tbody>
+ </table>
+ </td>
+ </tr>""")
+ }
+ return sb.toString().trim()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/CompoundElement.kt b/docs/src/writer/elements/CompoundElement.kt
new file mode 100644
index 0000000..d5fc180
--- /dev/null
+++ b/docs/src/writer/elements/CompoundElement.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.CompoundDeclarationParser
+import parser.elements.declarations.CompoundMemberDeclaration
+import writer.formatTextasHTML
+
+// used for structs and unions
+class CompoundElement(parser: EntryParser): AbstractElement(parser) {
+ //type, name, members [IMemberDeclaration]
+ override val declarationParser = parser.declarationParser as CompoundDeclarationParser
+
+ override fun declaration(): String {
+ val sb = StringBuilder()
+ sb.append("${declarationParser.type.value} ${declarationParser.name} {")
+ declarationParser.members.forEachIndexed { i, arg ->
+ val typedef = if (arg is CompoundMemberDeclaration) "${arg.typeDef.value} " else ""
+ sb.append("${typedef}${arg.type} ${arg.name}")
+ if (i < declarationParser.members.size-1) sb.append("; ")
+ }
+ sb.append("}")
+ return sb.toString()
+ }
+
+ override fun detailsRows(): String {
+ //build member rows
+ val sb = StringBuilder()
+ if (declarationParser.members.isNotEmpty()) {
+ sb.append("""
+ <tr>
+ <td>Members</td>
+ <td>
+ <table class="function param responsive">
+ <tbody>""")
+ //type, name, tokens, typedef
+ declarationParser.members.forEach { arg ->
+ val fieldDesc = arg.docParser?.description?.let { formatTextasHTML(it, useParagraphs = false) } ?: ""
+ sb.append("""
+ <tr>
+ <td>
+ <code>${arg.name}</code>
+ </td>
+ <td>
+ <div>${fieldDesc}</div>
+ </td>
+ </tr>""")
+ }
+ sb.append("""
+ </tbody>
+ </table>
+ </td>
+ </tr>""")
+ }
+ return sb.toString()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/EnumElement.kt b/docs/src/writer/elements/EnumElement.kt
new file mode 100644
index 0000000..42d03fe
--- /dev/null
+++ b/docs/src/writer/elements/EnumElement.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.EnumDeclarationParser
+import writer.formatTextasHTML
+import writer.htmlEscape
+
+class EnumElement(parser: EntryParser) : AbstractElement(parser) {
+ //name [String], type [String], members [EnumMember: name, value?]
+ override val declarationParser = parser.declarationParser as EnumDeclarationParser
+
+ override fun declaration(): String {
+ return "enum ${declarationParser.name}: ${declarationParser.type}"
+ }
+
+ override fun detailsRows(): String {
+ //build member rows
+ val sb = StringBuilder()
+ if (declarationParser.members.isNotEmpty()) {
+ sb.append("""
+ <tr>
+ <td>Members</td>
+ <td>
+ <table class="function param responsive">
+ <tbody>""")
+ //EnumMember => name, value?
+ declarationParser.members.forEach { arg ->
+ val fieldVal = arg.value?.let { " = ${htmlEscape(it)}" } ?: ""
+ val fieldDesc = arg.docParser?.description?.let { formatTextasHTML(it, useParagraphs = false) } ?: ""
+ sb.append("""
+ <tr>
+ <td>
+ <code>${htmlEscape(arg.name)}${fieldVal}</code>
+ </td>
+ <td>
+ <div>$fieldDesc</div>
+ </td>
+ </tr>""")
+ }
+ sb.append("""
+ </tbody>
+ </table>
+ </td>
+ </tr>""")
+ }
+ return sb.toString().trim()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/MethodElement.kt b/docs/src/writer/elements/MethodElement.kt
new file mode 100644
index 0000000..dcd6c2a
--- /dev/null
+++ b/docs/src/writer/elements/MethodElement.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.elements
+
+import lexer.TokenGrammar
+import parser.config
+import parser.elements.EntryParser
+import parser.elements.declarations.ArgEntry
+import parser.elements.declarations.MethodDeclarationParser
+import writer.formatTextasHTML
+import writer.htmlEscape
+import writer.warn
+import java.text.ParseException
+
+class MethodElement(parser: EntryParser) : AbstractElement(parser) {
+ override val declarationParser = parser.declarationParser as MethodDeclarationParser
+
+ //prefix, params, returns
+ override fun declaration(): String {
+ val sb = StringBuilder()
+ declarationParser.prefix?.let { sb.append("${it} ") }
+ sb.append("${declarationParser.name} (")
+ declarationParser.params.forEachIndexed { i, arg ->
+ sb.append(htmlEscape("${arg.type} ${arg.name}"))
+ if (i < declarationParser.params.size-1) sb.append(", ")
+ }
+ sb.append(")")
+ if (declarationParser.returns.isNotEmpty()) {
+ sb.append("\ngenerates (")
+ declarationParser.returns.forEachIndexed { i, arg ->
+ sb.append(htmlEscape("${arg.type} ${arg.name}"))
+ if (i < declarationParser.returns.size-1) sb.append(", ")
+ }
+ sb.append(")")
+ }
+ return sb.toString()
+ }
+
+ override fun detailsRows(): String {
+ return """
+${buildPrefixRows()}
+${buildParamRows()}
+${buildReturnRows()}
+""".trim()
+ }
+
+ private fun getDocAnnotationDesc(tag: TokenGrammar, arg: ArgEntry): String {
+ return docParser.docAnnotationParsers
+ .filter { it.tag == tag }
+ .firstOrNull { it.arg == arg.name }
+ ?.let { formatTextasHTML(it.description, useParagraphs = false) }
+ ?: run {
+ val msg = "Missing @${tag.value} doc annotation for '${arg.type} ${arg.name}'"
+ if (config.errorOnly) {
+ throw ParseException(msg, 0)
+ } else {
+ warn(msg)
+ "" //return empty string if it can't find it
+ }
+ }
+ }
+
+ private fun buildPrefixRows(): String {
+ val sb = StringBuilder()
+ if (declarationParser.prefix != null) {
+ sb.append("""
+ <tr>
+ <td>RPC mode</td>
+ <td>
+ <table class="function param responsive">
+ <tbody>
+ <tr>
+ <td>
+ <code>${declarationParser.prefix}</code>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>""")
+ }
+ return sb.toString()
+ }
+
+ private fun buildParamRows(): String {
+ //docParser.docAnnotationParsers //=> tag [TokenGrammer], arg?, description
+ val sb = StringBuilder()
+ if (declarationParser.params.isNotEmpty()) {
+ sb.append("""
+ <tr>
+ <td>Parameters</td>
+ <td>
+ <table class="function param responsive">
+ <tbody>""")
+ declarationParser.params.forEach { arg ->
+ sb.append("""
+ <tr>
+ <td>
+ <code>${htmlEscape(arg.name)}</code>
+ </td>
+ <td>
+ <div>${getDocAnnotationDesc(TokenGrammar.PARAM, arg)}</div>
+ </td>
+ </tr>""")
+ }
+ sb.append("""
+ </tbody>
+ </table>
+ </td>
+ </tr>""")
+ }
+ return sb.toString()
+ }
+
+ private fun buildReturnRows(): String {
+ val sb = StringBuilder()
+ if (declarationParser.returns.isNotEmpty()) {
+ sb.append("""
+ <tr>
+ <td>Generates</td>
+ <td>
+ <table class="function param responsive">
+ <tbody>""")
+ declarationParser.returns.forEach { arg ->
+ sb.append("""
+ <tr>
+ <td>
+ <code>${htmlEscape(arg.name)}</code>
+ </td>
+ <td>
+ <div>${getDocAnnotationDesc(TokenGrammar.RETURN, arg)}</div>
+ </td>
+ </tr>""")
+ }
+ sb.append("""
+ </tbody>
+ </table>
+ </td>
+ </tr>""")
+ }
+ return sb.toString()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/elements/TypedefElement.kt b/docs/src/writer/elements/TypedefElement.kt
new file mode 100644
index 0000000..9dea39a
--- /dev/null
+++ b/docs/src/writer/elements/TypedefElement.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.elements
+
+import parser.elements.EntryParser
+import parser.elements.declarations.TypedefDeclarationParser
+import writer.formatTextasHTML
+
+class TypedefElement(parser: EntryParser) : AbstractElement(parser) {
+ //name, type
+ override val declarationParser = parser.declarationParser as TypedefDeclarationParser
+
+ override fun declaration(): String {
+ return "typedef ${declarationParser.type} ${declarationParser.name}"
+ }
+
+ override fun detailsRows() = "" //not used
+
+ override fun toHTML(): String {
+ return """
+<div>
+ <h3>${declarationParser.name}</h3>
+ <pre class="prettyprint devsite-disable-click-to-copy">${declaration()}</pre>
+ <div>
+ ${formatTextasHTML(docParser.description)}
+ </div>
+</div>
+""".trim()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/AbstractFileWriter.kt b/docs/src/writer/files/AbstractFileWriter.kt
new file mode 100644
index 0000000..0c75688
--- /dev/null
+++ b/docs/src/writer/files/AbstractFileWriter.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.files
+
+import parser.config
+import java.nio.file.Path
+
+abstract class AbstractFileWriter() {
+ abstract val baseName: String
+ abstract val templateResource: String
+ abstract val path: Path
+
+ //init with template file contents on first use
+ var outStr: String = ""
+ get() = if (field.isEmpty()) resources.readResourceText(this.templateResource) else field
+
+ open fun replaceVars() {}
+
+ fun replaceVar(varName: String, newValue: String) {
+ outStr = outStr.replace(Regex("\\\$\\{?$varName}?"), newValue)
+ }
+
+ fun replaceVar(varName: String, newValue: () -> String) {
+ replaceVar(varName, newValue())
+ }
+
+ fun writeToFile(): Boolean {
+ replaceVars()
+ onWrite()
+
+ if (config.lintMode) {
+ return false
+ } else {
+ val dir = this.path.parent.toFile() //dir name
+ if (!dir.exists()) dir.mkdirs()
+ if (!dir.canWrite()) throw FileSystemException(dir, reason = "No write access to output directory")
+
+ val fp = this.path.toFile()
+ fp.bufferedWriter().use { it.write(this.outStr) }
+ if (!fp.isFile) throw FileSystemException(fp, reason = "Error writing file")
+ return true
+ }
+ }
+
+ open fun onWrite() {}
+
+ open fun printInfo() {
+ println("Name: ${this.baseName}")
+ println(" AbstractFileWriter:")
+ println(" class: ${javaClass.simpleName}")
+ println(" dest: ${this.path}")
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/AbstractParserFileWriter.kt b/docs/src/writer/files/AbstractParserFileWriter.kt
new file mode 100644
index 0000000..96a54d2
--- /dev/null
+++ b/docs/src/writer/files/AbstractParserFileWriter.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.files
+
+import parser.config
+import parser.files.AbstractFileParser
+import writer.elements.CompoundElement
+import writer.elements.EnumElement
+import writer.elements.TypedefElement
+import writer.getOutPath
+import java.nio.file.Path
+
+abstract class AbstractParserFileWriter(private val parser: AbstractFileParser) : AbstractFileWriter() {
+
+ override val baseName = parser.name
+ override val templateResource: String by lazy { "/resources/template/${this.baseName}.html" }
+ override val path: Path by lazy { getOutPath(parser, config.outDir) }
+
+ override fun replaceVars() {
+ replaceVar("name", parser.name)
+
+ replaceVar("propertyDefs") {
+ val sb = StringBuilder()
+
+ if (parser.typedefs.isNotEmpty()
+ || parser.enums.isNotEmpty()
+ || parser.structs.isNotEmpty()
+ || parser.unions.isNotEmpty()) {
+ sb.append("<section>\n")
+ sb.append("<h2>Properties</h2>\n")
+ //typedefs
+ sb.append(parser.typedefs.map { TypedefElement(it).toHTML() }.joinToString("\n"))
+ //enums
+ sb.append(parser.enums.map { EnumElement(it).toHTML() }.joinToString("\n"))
+ //structs
+ sb.append(parser.structs.map { CompoundElement(it).toHTML() }.joinToString("\n"))
+ //unions
+ sb.append(parser.unions.map { CompoundElement(it).toHTML() }.joinToString("\n"))
+ sb.append("\n</section>\n")
+ }
+ sb.toString()
+ }
+ }
+
+ override fun printInfo() {
+ super.printInfo()
+ println(" AbstractParserFileWriter:")
+ println(" package: ${parser.packageName}")
+ println(" package ver: ${parser.packageVersion}")
+ println(" enums count: ${parser.enums.size}")
+ println(" structs count: ${parser.structs.size}")
+ println(" unions count: ${parser.unions.size}")
+ println(" typedefs count: ${parser.typedefs.size}")
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/IndexFileWriter.kt b/docs/src/writer/files/IndexFileWriter.kt
new file mode 100644
index 0000000..3b686d3
--- /dev/null
+++ b/docs/src/writer/files/IndexFileWriter.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.files
+
+import parser.LOG_NAME
+import parser.config
+import parser.files.AbstractFileParser
+import parser.files.InterfaceFileParser
+import writer.getDescSummaryText
+import writer.getOutPath
+import java.io.File
+import java.nio.file.Path
+import java.nio.file.Paths
+
+data class EntryData(val fullName: String, //package.BaseName
+ val baseName: String,
+ val packageName: String,
+ val packageVersion: Float,
+ val summary: String,
+ val relPath: Path)
+
+class IndexFileWriter : AbstractFileWriter() {
+
+ override val baseName = "index"
+ override val templateResource = "/resources/template/${this.baseName}.html"
+ override val path: Path by lazy { Paths.get("${config.outDir}${File.separator}${this.baseName}.html") }
+
+ private val entries = mutableListOf<EntryData>()
+
+ fun addEntry(parser: AbstractFileParser) {
+ val summaryStr = when (parser) {
+ is InterfaceFileParser -> getDescSummaryText(parser.description)
+ else -> ""
+ }
+ entries.add(EntryData(
+ fullName = "${parser.packageName}.${parser.name}",
+ baseName = parser.name,
+ packageName = parser.packageName,
+ packageVersion = parser.packageVersion,
+ summary = summaryStr,
+ relPath = config.outDir.relativize(getOutPath(parser, config.outDir))
+ ))
+ }
+
+ override fun printInfo() {
+ super.printInfo()
+ println( "IndexFileWriter:")
+ println(" entries: ${this.entries.size}")
+ }
+
+ /*
+ * HTML index file
+ */
+
+ override fun replaceVars() {
+ replaceVar("title", "Index")
+
+ replaceVar("entries") {
+ val sb = StringBuilder()
+ if (entries.isNotEmpty()) {
+ entries.sortWith(EntryNameComparator())
+ sb.append(buildEntryTable())
+ }
+ sb.toString()
+ }
+ }
+
+ private fun buildEntryTable(): String {
+ return """
+<table>
+ <tr>
+ <th>Entry</th>
+ <th>Version</th>
+ <th>Summary</th>
+ </tr>
+${entries.map { buildClassEntry(it) }.joinToString("\n")}
+</table>
+""".trim()
+ }
+
+ private fun buildClassEntry(entry: EntryData): String {
+ return """
+<tr>
+ <td>
+<a href="${entry.relPath}">${entry.fullName}</a>
+ </td>
+ <td>
+${entry.packageVersion}
+ </td>
+ <td>
+${entry.summary}
+ </td>
+</tr>
+""".trim()
+ }
+
+ private class EntryNameComparator : Comparator<EntryData> {
+ override fun compare(entry1: EntryData, entry2: EntryData): Int {
+ return if (entry1.fullName != entry2.fullName) {
+ //sort on name first, alphabetic
+ when {
+ entry1.fullName < entry2.fullName -> -1
+ entry1.fullName > entry2.fullName -> 1
+ else -> 0
+ }
+ } else {
+ //if same name, reverse sort on pkg version (highest first)
+ when {
+ entry1.packageVersion < entry2.packageVersion -> 1
+ entry1.packageVersion > entry2.packageVersion -> -1
+ else -> 0
+ }
+ }
+ }
+ }
+
+ /*
+ * YAML toc file
+ */
+
+ private val tocFileName = "_book.yaml"
+ private val tocFileRelPath = "/reference/hidl"
+ private val tocOutPath: Path by lazy { Paths.get("${config.outDir}${File.separator}${this.tocFileName}") }
+
+ //write toc yaml file after html index
+ override fun onWrite() {
+ super.onWrite()
+ if (!config.lintMode) {
+ val fp = tocOutPath.toFile()
+ fp.bufferedWriter().use {
+ it.write(buildTocHeader())
+ it.write(buildTocEntries(collectPackages()))
+ }
+ if (!fp.isFile) throw FileSystemException(fp, reason = "Error writing file")
+ println("$LOG_NAME Wrote toc: $tocOutPath")
+ }
+ }
+
+ private fun buildTocHeader(): String {
+ return """
+# Generated by hidl-doc
+toc:
+- title: Index
+ path: $tocFileRelPath
+""".trimStart()
+ }
+
+ private fun buildTocEntries(pkgEntries: Map<String, List<EntryData>>): String {
+ val sb = StringBuilder()
+
+ for ((pkgName, entryList) in pkgEntries) {
+ sb.append("- title: $pkgName\n section:\n")
+
+ entryList.forEach { entry ->
+ sb.append(" - title: ${entry.baseName} @${entry.packageVersion}\n")
+ sb.append(" path: ${tocFileRelPath}/${entry.relPath}\n")
+ }
+ }
+ return sb.toString()
+ }
+
+ private fun collectPackages(): Map<String, List<EntryData>> {
+ val pkgEntries = mutableMapOf<String, MutableList<EntryData>>()
+
+ this.entries.forEach { entry ->
+ if (pkgEntries.containsKey(entry.packageName)) {
+ pkgEntries[entry.packageName]!!.add(entry)
+ } else {
+ pkgEntries[entry.packageName] = mutableListOf(entry)
+ }
+ }
+ //sort on package name. entries *should* already be sorted
+ return pkgEntries.toSortedMap()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/InterfaceFileWriter.kt b/docs/src/writer/files/InterfaceFileWriter.kt
new file mode 100644
index 0000000..a0fefa6
--- /dev/null
+++ b/docs/src/writer/files/InterfaceFileWriter.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.files
+
+import parser.files.InterfaceFileParser
+import writer.elements.MethodElement
+import writer.formatTextasHTML
+
+class InterfaceFileWriter(private val parser: InterfaceFileParser) : AbstractParserFileWriter(parser) {
+
+ override var templateResource = "/resources/template/interface.html"
+
+ override fun replaceVars() {
+ super.replaceVars()
+ replaceVar("header", buildHeader())
+
+ replaceVar("methodDefs") {
+ val sb = StringBuilder()
+ if (parser.methods.isNotEmpty()) {
+ sb.append("<section>\n")
+ sb.append("<h2>Methods</h2>\n")
+ sb.append(parser.methods.map { MethodElement(it).toHTML() }.joinToString("\n"))
+ sb.append("\n</section>\n")
+ }
+ sb.toString()
+ }
+ }
+
+ private fun buildHeader(): String {
+ return """
+<header>
+ <div id="api-info-block">
+ Package: <code>${parser.packageName}@${parser.packageVersion}</code>
+ </div>
+ <h1>${parser.name}</h1>
+ <code>${buildDeclaration()}</code>
+ ${formatTextasHTML(parser.description)}
+</header>
+""".trim()
+ }
+
+ private fun buildDeclaration(): String {
+ val sb = StringBuilder()
+ sb.append("interface ${parser.name}")
+ if (parser.extendsName != null) {
+ sb.append(" extends ")
+ if (parser.extendsVersion != null) sb.append("@${parser.extendsVersion}::")
+ sb.append(parser.extendsName)
+ }
+ return sb.toString()
+ }
+
+ override fun printInfo() {
+ super.printInfo()
+ println(" InterfaceFileWriter:")
+ println(" Interface: ${parser.name} extends ${parser.extendsName}::${parser.extendsVersion}")
+ val methodEntries = parser.methods.map { MethodElement(it) }
+ println(" method count: ${methodEntries.size}")
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/TypesFileWriter.kt b/docs/src/writer/files/TypesFileWriter.kt
new file mode 100644
index 0000000..85b9a60
--- /dev/null
+++ b/docs/src/writer/files/TypesFileWriter.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.files
+
+import parser.files.TypesFileParser
+
+class TypesFileWriter(private val parser: TypesFileParser) : AbstractParserFileWriter(parser) {
+
+ override var templateResource = "/resources/template/types.html"
+
+ override fun replaceVars() {
+ super.replaceVars()
+ replaceVar("header", buildHeader())
+ }
+
+ private fun buildHeader(): String {
+ return """
+<header>
+ <div id="api-info-block">
+ Package: <code>${parser.packageName}@${parser.packageVersion}</code>
+ </div>
+ <h1>${parser.name}</h1>
+</header>
+""".trim()
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/files/resources.kt b/docs/src/writer/files/resources.kt
new file mode 100644
index 0000000..25ba4b8
--- /dev/null
+++ b/docs/src/writer/files/resources.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer.files
+
+import parser.config
+import java.io.FileNotFoundException
+import java.nio.file.Path
+
+object resources {
+
+ private val resourceCache = mutableMapOf<String, String>() //name(path) => contents
+
+ fun readResourceText(name: String): String {
+ return resourceCache.getOrElse(name) {
+ val input = javaClass.getResourceAsStream(name)
+ ?: throw FileNotFoundException("Unable to locate file resource: $name")
+
+ val contents = input.bufferedReader().use { it.readText() }
+ resourceCache[name] = contents
+ contents
+ }
+ }
+
+ fun copyToFile(resourceName: String, outPath: Path): Boolean {
+ val contents = readResourceText(resourceName)
+
+ if (config.lintMode) {
+ return false
+ } else {
+ val dir = outPath.parent.toFile() //dir name
+ if (!dir.exists()) dir.mkdirs()
+ if (!dir.canWrite()) throw FileSystemException(dir, reason = "No write access to output directory")
+
+ val fp = outPath.toFile()
+ fp.bufferedWriter().use { it.write(contents) }
+ if (!fp.isFile) throw FileSystemException(fp, reason = "Error writing file")
+ return true
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/src/writer/formatutils.kt b/docs/src/writer/formatutils.kt
new file mode 100644
index 0000000..8afeb6d
--- /dev/null
+++ b/docs/src/writer/formatutils.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package writer
+
+import lexer.Token
+import parser.files.AbstractFileParser
+import java.io.File
+import java.nio.file.Path
+import java.nio.file.Paths
+
+fun warn(msg: String) {
+ System.err.println("WARNING: $msg")
+}
+
+/**
+ * Get values of tokens, useful for debugging.
+ */
+fun tokenValues(tokens: List<Token>): String {
+ return tokens.map { it.value }.joinToString("|")
+}
+
+/**
+ * Escape string for HTML.
+ */
+fun htmlEscape(string: String): String {
+ val out = StringBuilder(Math.max(16, string.length))
+ string.toCharArray().forEach { c ->
+ if (c.toInt() > 127 || c == '"' || c == '<' || c == '>' || c == '&' || c == '$' || c == '{' || c == '}') {
+ out.append("&#")
+ out.append(c.toInt())
+ out.append(';')
+ } else {
+ out.append(c)
+ }
+ }
+ return out.toString()
+}
+
+/**
+ * Used to display description text.
+ */
+fun formatTextasHTML(string: String, useParagraphs: Boolean = true): String {
+ if (string.isEmpty()) return string
+
+ val sb = StringBuilder()
+ if (useParagraphs) sb.append("<p>")
+ //match and replace empty lines
+ val replaceText = if (useParagraphs) "</p>\n<p>" else "<br>\n"
+ sb.append(htmlEscape(string.trim()).replace(Regex("\\s*\n\n\\s*"), replaceText))
+ if (useParagraphs) sb.append("</p>")
+ return sb.toString()
+}
+
+private val summaryRegex = Regex("\\.|\n\n") //match period or empty line
+
+/**
+ * Given a block of description text, return the first sentence.
+ */
+fun getDescSummaryText(string: String): String {
+ return if (string.isEmpty()) {
+ string
+ } else {
+ val s = string.trimStart() // remove any beginning empty lines/whitespace
+ val sb = StringBuilder(summaryRegex.split(s)[0])
+ if (sb[sb.length - 1] != '.') sb.append(".") // add period, if needed
+ formatTextasHTML(sb.toString())
+ }
+}
+
+/**
+ * Return the out file path for a given parser.
+ */
+fun getOutPath(parser: AbstractFileParser, outDir: Path): Path {
+ val pkgPath = parser.packageName.replace(".", File.separator)
+ val dirPath = "${outDir}${File.separator}${pkgPath}${File.separator}${parser.packageVersion}"
+ return Paths.get("${dirPath}${File.separator}${parser.name}.html")
+}
\ No newline at end of file
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 7292732..fa2a3ca 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -18,9 +18,10 @@
#include "Coordinator.h"
#include "EnumType.h"
-#include "Interface.h"
#include "HidlTypeAssertion.h"
+#include "Interface.h"
#include "Method.h"
+#include "Reference.h"
#include "ScalarType.h"
#include "Scope.h"
@@ -33,38 +34,6 @@
namespace android {
-status_t AST::generateCpp(const std::string &outputPath) const {
- status_t err = generateCppHeaders(outputPath);
-
- if (err == OK) {
- err = generateCppSources(outputPath);
- }
-
- return err;
-}
-
-status_t AST::generateCppHeaders(const std::string &outputPath) const {
- status_t err = generateInterfaceHeader(outputPath);
-
- if (err == OK) {
- err = generateStubHeader(outputPath);
- }
-
- if (err == OK) {
- err = generateHwBinderHeader(outputPath);
- }
-
- if (err == OK) {
- err = generateProxyHeader(outputPath);
- }
-
- if (err == OK) {
- err = generatePassthroughHeader(outputPath);
- }
-
- return err;
-}
-
void AST::getPackageComponents(
std::vector<std::string> *components) const {
mPackage.getPackageComponents(components);
@@ -175,173 +144,11 @@
<< "::android::sp<" << interfaceName << "> " << interfaceName << "::" << functionName << "("
<< "const std::string &serviceName, const bool getStub) ";
out.block([&] {
- out << "using ::android::hardware::defaultServiceManager;\n";
- out << "using ::android::hardware::details::waitForHwService;\n";
- out << "using ::android::hardware::getPassthroughServiceManager;\n";
- out << "using ::android::hardware::Return;\n";
- out << "using ::android::sp;\n";
- out << "using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;\n\n";
-
- out << "sp<" << interfaceName << "> iface = nullptr;\n";
-
- out.endl();
-
- out << "const sp<::android::hidl::manager::V1_0::IServiceManager> sm"
- << " = defaultServiceManager();\n";
-
- out.sIf("sm == nullptr", [&] {
- // hwbinder is not available on this device, so future tries
- // would also be null. I can only return nullptr.
- out << "ALOGE(\"getService: defaultServiceManager() is null\");\n"
- << "return nullptr;\n";
- }).endl().endl();
-
- out << "Return<Transport> transportRet = sm->getTransport("
- << interfaceName << "::descriptor, serviceName);\n\n";
-
- out.sIf("!transportRet.isOk()", [&] {
- out << "ALOGE(\"getService: defaultServiceManager()->getTransport returns %s\", "
- << "transportRet.description().c_str());\n";
- out << "return nullptr;\n";
- }).endl();
-
- out << "Transport transport = transportRet;\n";
- out << "const bool vintfHwbinder = (transport == Transport::HWBINDER);\n"
- << "const bool vintfPassthru = (transport == Transport::PASSTHROUGH);\n\n";
-
- // This means that you must set TREBLE_TESTING_OVERRIDE when running a test such
- // as hidl_test. Ideally these binaries set this value themselves. This allows
- // test modules to dynamically add and unset services even though they are not
- // declared in the device manifest. This prevents a problem where framework
- // changes are accidentally made in a way that is not backwards compatible. For
- // instance, consider the following situation for two devices developed in the
- // same tree:
- // A: serves @1.1::IFoo, declares @1.0::IFoo (incorrect)
- // B: serves @1.0::IFoo, declares @1.0::IFoo (correct configuration)
- // If development is done on device A, then framework code like: "V1_1::IFoo::
- // getService()->doV1_0Api()" will work. However, this will unintentionally break
- // the feature for devices like device B for which "V1_1::IFoo::getService()
- // will return nullptr. In order to prevent problems like this, we only allow
- // fetching an interface if it is declared in a VINTF manifest.
- out << "#ifdef __ANDROID_TREBLE__\n\n"
- << "#ifdef __ANDROID_DEBUGGABLE__\n"
- << "const char* env = std::getenv(\"TREBLE_TESTING_OVERRIDE\");\n"
- << "const bool trebleTestingOverride = env && !strcmp(env, \"true\");\n"
- << "const bool vintfLegacy = (transport == Transport::EMPTY) && trebleTestingOverride;\n"
- << "#else // __ANDROID_TREBLE__ but not __ANDROID_DEBUGGABLE__\n"
- << "const bool trebleTestingOverride = false;\n"
- << "const bool vintfLegacy = false;\n"
- << "#endif // __ANDROID_DEBUGGABLE__\n\n"
- << "#else // not __ANDROID_TREBLE__\n"
- << "const char* env = std::getenv(\"TREBLE_TESTING_OVERRIDE\");\n"
- << "const bool trebleTestingOverride = env && !strcmp(env, \"true\");\n"
- << "const bool vintfLegacy = (transport == Transport::EMPTY);\n\n"
- << "#endif // __ANDROID_TREBLE__\n\n";
-
- // if (getStub) {
- // getPassthroughServiceManager()->get only once.
- // } else {
- // if (vintfHwbinder) {
- // while (no alive service) {
- // if (have already tried to get service)
- // waitForHwService
- // defaultServiceManager()->get
- // }
- // } else if (vintfLegacy) {
- // defaultServiceManager()->get only once.
- // getPassthroughServiceManager()->get only once.
- // } else if (vintfPassthru) {
- // getPassthroughServiceManager()->get only once.
- // }
- // }
-
- out.sFor("int tries = 0; !getStub && (vintfHwbinder || (vintfLegacy && tries == 0)); tries++", [&] {
- if (!isTry) {
- out.sIf("tries > 1", [&] {
- // sleep only after the first time we've called waitForHwService.
- out << "ALOGI(\"" << functionName << ": Will do try %d for %s/%s in 1s...\", tries, "
- << interfaceName << "::descriptor, serviceName.c_str());\n"
- << "sleep(1);\n";
- }).endl();
-
- out.sIf("vintfHwbinder && tries > 0", [&] {
- out << "waitForHwService("
- << interfaceName << "::descriptor, serviceName);\n";
- }).endl();
- }
-
- out << "Return<sp<" << gIBaseFqName.cppName() << ">> ret = \n";
- out.indent(2, [&] {
- out << "sm->get(" << interfaceName << "::descriptor, serviceName);\n";
- });
-
- out.sIf("!ret.isOk()", [&] {
- // hwservicemanager fails, may be security issue
- out << "ALOGE(\"" << interfaceName << ": defaultServiceManager()->get returns %s\", "
- << "ret.description().c_str());\n"
- << "break;\n";
- }).endl();
-
- out << "sp<" << gIBaseFqName.cppName() << "> base = ret;\n";
- out.sIf("base == nullptr", [&] {
- // if tries > 0: race condition. hwservicemanager drops the service
- // from waitForHwService to here
- out.sIf("tries > 0", [&] {
- out << "ALOGW(\"" << interfaceName << ": found null hwbinder interface\");\n";
- });
- out << (isTry ? "break" : "continue")
- << ";\n";
- }).endl();
- out << "Return<sp<" << interfaceName
- << ">> castRet = " << interfaceName << "::castFrom(base, true /* emitError */);\n";
- out.sIf("!castRet.isOk()", [&] {
- out.sIf("castRet.isDeadObject()", [&] {
- // service is dead (castFrom cannot call interfaceChain)
- out << "ALOGW(\"" << interfaceName << ": found dead hwbinder service\");\n"
- << (isTry ? "break" : "continue")
- << ";\n";
- }).sElse([&] {
- out << "ALOGW(\"" << interfaceName << ": cannot call into hwbinder service: %s"
- << "; No permission? Check for selinux denials.\", "
- << "castRet.description().c_str());\n"
- << "break;\n";
- }).endl();
- }).endl();
- out << "iface = castRet;\n";
- out.sIf("iface == nullptr", [&] {
- // returned service isn't of correct type; this is a bug
- // to hwservicemanager or to the service itself (interfaceChain
- // is not consistent).
- out << "ALOGW(\"" << interfaceName << ": received incompatible service; bug in hwservicemanager?\");\n"
- << "break;\n";
- }).endl();
-
- out << "return iface;\n";
- }).endl();
-
- out.sIf("getStub || vintfPassthru || vintfLegacy", [&] {
- out << "const sp<::android::hidl::manager::V1_0::IServiceManager> pm"
- << " = getPassthroughServiceManager();\n";
-
- out.sIf("pm != nullptr", [&] () {
- out << "Return<sp<" << gIBaseFqName.cppName() << ">> ret = \n";
- out.indent(2, [&] {
- out << "pm->get(" << interfaceName << "::descriptor" << ", serviceName);\n";
- });
- out.sIf("ret.isOk()", [&] {
- out << "sp<" << gIBaseFqName.cppName()
- << "> baseInterface = ret;\n";
- out.sIf("baseInterface != nullptr", [&]() {
- out << "iface = " << interfaceName << "::castFrom(baseInterface);\n";
- out.sIf("!getStub || trebleTestingOverride", [&] () {
- out << "iface = new " << fqName.getInterfacePassthroughName() << "(iface);\n";
- }).endl();
- }).endl();
- }).endl();
- }).endl();
- }).endl();
-
- out << "return iface;\n";
+ out << "return ::android::hardware::details::getServiceInternal<"
+ << fqName.getInterfaceProxyName()
+ << ">(serviceName, "
+ << (!isTry ? "true" : "false") // retry
+ << ", getStub);\n";
}).endl().endl();
}
@@ -397,25 +204,9 @@
}).endl().endl();
}
-status_t AST::generateInterfaceHeader(const std::string &outputPath) const {
+void AST::generateInterfaceHeader(Formatter& out) const {
const Interface *iface = getInterface();
std::string ifaceName = iface ? iface->localName() : "types";
-
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
- path.append(ifaceName);
- path.append(".h");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
const std::string guard = makeHeaderGuard(ifaceName);
out << "#ifndef " << guard << "\n";
@@ -467,13 +258,10 @@
out.indent();
+ generateCppTag(out, "android::hardware::details::i_tag");
}
- status_t err = emitTypeDeclarations(out);
-
- if (err != OK) {
- return err;
- }
+ emitTypeDeclarations(out);
if (iface) {
out << "virtual bool isRemote() const ";
@@ -488,7 +276,7 @@
out << "\n";
const bool returnsValue = !method->results().empty();
- const TypedVar *elidedReturn = method->canElideCallback();
+ const NamedReference<Type>* elidedReturn = method->canElideCallback();
if (elidedReturn == nullptr && returnsValue) {
out << "using "
@@ -500,6 +288,8 @@
method->dumpAnnotations(out);
+ method->emitDocComment(out);
+
if (elidedReturn) {
out << "virtual ::android::hardware::Return<";
out << elidedReturn->type().getCppResultType() << "> ";
@@ -548,37 +338,20 @@
out << "};\n\n";
}
- err = mRootScope.emitGlobalTypeDeclarations(out);
-
- if (err != OK) {
- return err;
- }
+ mRootScope.emitPackageTypeDeclarations(out);
out << "\n";
enterLeaveNamespace(out, false /* enter */);
- out << "\n#endif // " << guard << "\n";
+ mRootScope.emitGlobalTypeDeclarations(out);
- return OK;
+ out << "\n#endif // " << guard << "\n";
}
-status_t AST::generateHwBinderHeader(const std::string &outputPath) const {
+void AST::generateHwBinderHeader(Formatter& out) const {
const Interface *iface = getInterface();
std::string klassName = iface ? iface->getHwName() : "hwtypes";
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
- path.append(klassName + ".h");
-
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
const std::string guard = makeHeaderGuard(klassName);
out << "#ifndef " << guard << "\n";
@@ -607,25 +380,19 @@
enterLeaveNamespace(out, true /* enter */);
- status_t err = mRootScope.emitGlobalHwDeclarations(out);
- if (err != OK) {
- return err;
- }
+ mRootScope.emitPackageHwDeclarations(out);
enterLeaveNamespace(out, false /* enter */);
out << "\n#endif // " << guard << "\n";
-
- return OK;
}
-status_t AST::emitTypeDeclarations(Formatter &out) const {
+void AST::emitTypeDeclarations(Formatter& out) const {
return mRootScope.emitTypeDeclarations(out);
}
-static void wrapPassthroughArg(Formatter &out,
- const TypedVar *arg, bool addPrefixToName,
- std::function<void(void)> handleError) {
+static void wrapPassthroughArg(Formatter& out, const NamedReference<Type>* arg,
+ bool addPrefixToName, std::function<void(void)> handleError) {
if (!arg->type().isInterface()) {
return;
}
@@ -638,11 +405,9 @@
out.sIf(name + " != nullptr && !" + name + "->isRemote()", [&] {
out << wrappedName
<< " = "
- << iface.fqName().cppName()
- << "::castFrom(::android::hardware::details::wrapPassthrough<"
- << iface.fqName().cppName()
- << ">("
- << name << "));\n";
+ << "::android::hardware::details::wrapPassthrough("
+ << name
+ << ");\n";
out.sIf(wrappedName + " == nullptr", [&] {
// Fatal error. Happens when the BsFoo class is not found in the binary
// or any dynamic libraries.
@@ -653,8 +418,7 @@
}).endl().endl();
}
-status_t AST::generatePassthroughMethod(Formatter &out,
- const Method *method) const {
+void AST::generatePassthroughMethod(Formatter& out, const Method* method) const {
method->generateCppSignature(out);
out << " {\n";
@@ -665,11 +429,11 @@
method->cppImpl(IMPL_PASSTHROUGH, out);
out.unindent();
out << "}\n\n";
- return OK;
+ return;
}
const bool returnsValue = !method->results().empty();
- const TypedVar *elidedReturn = method->canElideCallback();
+ const NamedReference<Type>* elidedReturn = method->canElideCallback();
if (returnsValue && elidedReturn == nullptr) {
generateCheckNonNull(out, "_hidl_cb");
@@ -779,11 +543,9 @@
out.unindent();
out << "}\n";
-
- return OK;
}
-status_t AST::generateMethods(Formatter &out, MethodGenerator gen, bool includeParent) const {
+void AST::generateMethods(Formatter& out, const MethodGenerator& gen, bool includeParent) const {
const Interface* iface = mRootScope.getInterface();
const Interface *prevIterface = nullptr;
@@ -804,52 +566,32 @@
<< " follow.\n";
prevIterface = superInterface;
}
- status_t err = gen(method, superInterface);
-
- if (err != OK) {
- return err;
- }
+ gen(method, superInterface);
}
out << "\n";
-
- return OK;
}
void AST::generateTemplatizationLink(Formatter& out) const {
out << "typedef " << mRootScope.getInterface()->localName() << " Pure;\n\n";
}
-status_t AST::generateStubHeader(const std::string &outputPath) const {
- if (!AST::isInterface()) {
- // types.hal does not get a stub header.
- return OK;
- }
+void AST::generateCppTag(Formatter& out, const std::string& tag) const {
+ out << "typedef " << tag << " _hidl_tag;\n\n";
+}
+
+void AST::generateStubHeader(Formatter& out) const {
+ CHECK(AST::isInterface());
const Interface* iface = mRootScope.getInterface();
const std::string klassName = iface->getStubName();
-
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
- path.append(klassName);
- path.append(".h");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
const std::string guard = makeHeaderGuard(klassName);
out << "#ifndef " << guard << "\n";
out << "#define " << guard << "\n\n";
generateCppPackageInclude(out, mPackage, iface->getHwName());
+
out << "\n";
enterLeaveNamespace(out, true /* enter */);
@@ -891,41 +633,40 @@
out.endl();
generateTemplatizationLink(out);
+ generateCppTag(out, "android::hardware::details::bnhw_tag");
- out << "::android::sp<" << iface->localName() << "> getImpl() { return _hidl_mImpl; };\n";
+ out << "::android::sp<" << iface->localName() << "> getImpl() { return _hidl_mImpl; }\n";
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
- if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
- return OK;
- }
+ generateMethods(out,
+ [&](const Method* method, const Interface*) {
+ if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
+ return;
+ }
- out << "static ::android::status_t _hidl_" << method->name() << "(\n";
+ out << "static ::android::status_t _hidl_" << method->name() << "(\n";
- out.indent(2, [&] {
- out << "::android::hidl::base::V1_0::BnHwBase* _hidl_this,\n"
- << "const ::android::hardware::Parcel &_hidl_data,\n"
- << "::android::hardware::Parcel *_hidl_reply,\n"
- << "TransactCallback _hidl_cb);\n";
- }).endl().endl();
-
- return OK;
- }, false /* include parents */);
-
- if (err != OK) {
- return err;
- }
+ out.indent(2,
+ [&] {
+ out << "::android::hidl::base::V1_0::BnHwBase* _hidl_this,\n"
+ << "const ::android::hardware::Parcel &_hidl_data,\n"
+ << "::android::hardware::Parcel *_hidl_reply,\n"
+ << "TransactCallback _hidl_cb);\n";
+ })
+ .endl()
+ .endl();
+ },
+ false /* include parents */);
out.unindent();
out << "private:\n";
out.indent();
-
- err = generateMethods(out, [&](const Method *method, const Interface *iface) {
+ generateMethods(out, [&](const Method* method, const Interface* iface) {
if (!method->isHidlReserved() || !method->overridesCppImpl(IMPL_STUB_IMPL)) {
- return OK;
+ return;
}
const bool returnsValue = !method->results().empty();
- const TypedVar *elidedReturn = method->canElideCallback();
+ const NamedReference<Type>* elidedReturn = method->canElideCallback();
if (elidedReturn == nullptr && returnsValue) {
out << "using " << method->name() << "_cb = "
@@ -934,11 +675,7 @@
}
method->generateCppSignature(out);
out << ";\n";
- return OK;
});
- if (err != OK) {
- return err;
- }
out << "::android::sp<" << iface->localName() << "> _hidl_mImpl;\n";
out.unindent();
@@ -947,34 +684,16 @@
enterLeaveNamespace(out, false /* enter */);
out << "\n#endif // " << guard << "\n";
-
- return OK;
}
-status_t AST::generateProxyHeader(const std::string &outputPath) const {
+void AST::generateProxyHeader(Formatter& out) const {
if (!AST::isInterface()) {
// types.hal does not get a proxy header.
- return OK;
+ return;
}
const Interface* iface = mRootScope.getInterface();
const std::string proxyName = iface->getProxyName();
-
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
- path.append(proxyName);
- path.append(".h");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
const std::string guard = makeHeaderGuard(proxyName);
out << "#ifndef " << guard << "\n";
@@ -1006,44 +725,36 @@
<< "\n\n";
generateTemplatizationLink(out);
+ generateCppTag(out, "android::hardware::details::bphw_tag");
out << "virtual bool isRemote() const override { return true; }\n\n";
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
- if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
- return OK;
- }
+ generateMethods(
+ out,
+ [&](const Method* method, const Interface*) {
+ if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
+ return;
+ }
- out << "static ";
- method->generateCppReturnType(out);
- out << " _hidl_"
- << method->name()
- << "("
- << "::android::hardware::IInterface* _hidl_this, "
- << "::android::hardware::details::HidlInstrumentor *_hidl_this_instrumentor";
+ out << "static ";
+ method->generateCppReturnType(out);
+ out << " _hidl_" << method->name() << "("
+ << "::android::hardware::IInterface* _hidl_this, "
+ << "::android::hardware::details::HidlInstrumentor *_hidl_this_instrumentor";
- if (!method->hasEmptyCppArgSignature()) {
- out << ", ";
- }
- method->emitCppArgSignature(out);
- out << ");\n";
- return OK;
- }, false /* include parents */);
+ if (!method->hasEmptyCppArgSignature()) {
+ out << ", ";
+ }
+ method->emitCppArgSignature(out);
+ out << ");\n";
+ },
+ false /* include parents */);
- if (err != OK) {
- return err;
- }
-
- err = generateMethods(out, [&](const Method *method, const Interface *) {
+ generateMethods(out, [&](const Method* method, const Interface*) {
method->generateCppSignature(out);
out << " override;\n";
- return OK;
});
- if (err != OK) {
- return err;
- }
-
out.unindent();
out << "private:\n";
out.indent();
@@ -1056,33 +767,13 @@
enterLeaveNamespace(out, false /* enter */);
out << "\n#endif // " << guard << "\n";
-
- return OK;
}
-status_t AST::generateCppSources(const std::string &outputPath) const {
+void AST::generateCppSource(Formatter& out) const {
std::string baseName = getBaseName();
const Interface *iface = getInterface();
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
- path.append(baseName);
-
- if (baseName != "types") {
- path.append("All");
- }
-
- path.append(".cpp");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
+ const std::string klassName = baseName + (baseName == "types" ? "" : "All");
out << "#define LOG_TAG \""
<< mPackage.string() << "::" << baseName
@@ -1116,9 +807,9 @@
enterLeaveNamespace(out, true /* enter */);
out << "\n";
- status_t err = generateTypeSource(out, iface ? iface->localName() : "");
+ generateTypeSource(out, iface ? iface->localName() : "");
- if (err == OK && iface) {
+ if (iface) {
const Interface* iface = mRootScope.getInterface();
// need to be put here, generateStubSource is using this.
@@ -1127,10 +818,10 @@
<< "::descriptor(\""
<< iface->fqName().string()
<< "\");\n\n";
- out << "__attribute__((constructor))";
+ out << "__attribute__((constructor)) ";
out << "static void static_constructor() {\n";
out.indent([&] {
- out << "::android::hardware::details::gBnConstructorMap.set("
+ out << "::android::hardware::details::getBnConstructorMap().set("
<< iface->localName()
<< "::descriptor,\n";
out.indent(2, [&] {
@@ -1144,7 +835,7 @@
});
out << "});\n";
});
- out << "::android::hardware::details::gBsConstructorMap.set("
+ out << "::android::hardware::details::getBsConstructorMap().set("
<< iface->localName()
<< "::descriptor,\n";
out.indent(2, [&] {
@@ -1165,32 +856,19 @@
out << "__attribute__((destructor))";
out << "static void static_destructor() {\n";
out.indent([&] {
- out << "::android::hardware::details::gBnConstructorMap.erase("
+ out << "::android::hardware::details::getBnConstructorMap().erase("
<< iface->localName()
<< "::descriptor);\n";
- out << "::android::hardware::details::gBsConstructorMap.erase("
+ out << "::android::hardware::details::getBsConstructorMap().erase("
<< iface->localName()
<< "::descriptor);\n";
});
out << "};\n\n";
- err = generateInterfaceSource(out);
- }
-
- if (err == OK && iface) {
- err = generateProxySource(out, iface->fqName());
- }
-
- if (err == OK && iface) {
- err = generateStubSource(out, iface);
- }
-
- if (err == OK && iface) {
- err = generatePassthroughSource(out);
- }
-
- if (err == OK && iface) {
- const Interface* iface = mRootScope.getInterface();
+ generateInterfaceSource(out);
+ generateProxySource(out, iface->fqName());
+ generateStubSource(out, iface);
+ generatePassthroughSource(out);
if (isIBase()) {
out << "// skipped getService, registerAsService, registerForNotifications\n";
@@ -1206,8 +884,6 @@
out << "\n";
enterLeaveNamespace(out, false /* enter */);
-
- return err;
}
void AST::generateCheckNonNull(Formatter &out, const std::string &nonNull) {
@@ -1220,15 +896,12 @@
}).endl().endl();
}
-status_t AST::generateTypeSource(
- Formatter &out, const std::string &ifaceName) const {
- return mRootScope.emitTypeDefinitions(out, ifaceName);
+void AST::generateTypeSource(Formatter& out, const std::string& ifaceName) const {
+ mRootScope.emitTypeDefinitions(out, ifaceName);
}
-void AST::declareCppReaderLocals(
- Formatter &out,
- const std::vector<TypedVar *> &args,
- bool forResults) const {
+void AST::declareCppReaderLocals(Formatter& out, const std::vector<NamedReference<Type>*>& args,
+ bool forResults) const {
if (args.empty()) {
return;
}
@@ -1245,14 +918,9 @@
out << "\n";
}
-void AST::emitCppReaderWriter(
- Formatter &out,
- const std::string &parcelObj,
- bool parcelObjIsPointer,
- const TypedVar *arg,
- bool isReader,
- Type::ErrorMode mode,
- bool addPrefixToName) const {
+void AST::emitCppReaderWriter(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer,
+ const NamedReference<Type>* arg, bool isReader, Type::ErrorMode mode,
+ bool addPrefixToName) const {
const Type &type = arg->type();
type.emitReaderWriter(
@@ -1264,14 +932,10 @@
mode);
}
-void AST::emitCppResolveReferences(
- Formatter &out,
- const std::string &parcelObj,
- bool parcelObjIsPointer,
- const TypedVar *arg,
- bool isReader,
- Type::ErrorMode mode,
- bool addPrefixToName) const {
+void AST::emitCppResolveReferences(Formatter& out, const std::string& parcelObj,
+ bool parcelObjIsPointer, const NamedReference<Type>* arg,
+ bool isReader, Type::ErrorMode mode,
+ bool addPrefixToName) const {
const Type &type = arg->type();
if(type.needsResolveReferences()) {
type.emitResolveReferences(
@@ -1285,10 +949,8 @@
}
}
-status_t AST::generateProxyMethodSource(Formatter &out,
- const std::string &klassName,
- const Method *method,
- const Interface *superInterface) const {
+void AST::generateProxyMethodSource(Formatter& out, const std::string& klassName,
+ const Method* method, const Interface* superInterface) const {
method->generateCppSignature(out,
klassName,
true /* specify namespaces */);
@@ -1297,14 +959,12 @@
out.block([&] {
method->cppImpl(IMPL_PROXY, out);
}).endl().endl();
- return OK;
+ return;
}
- status_t err = OK;
-
out.block([&] {
const bool returnsValue = !method->results().empty();
- const TypedVar *elidedReturn = method->canElideCallback();
+ const NamedReference<Type>* elidedReturn = method->canElideCallback();
method->generateCppReturnType(out);
@@ -1335,15 +995,12 @@
out << "return _hidl_out;\n";
}).endl().endl();
-
- return err;
}
-status_t AST::generateStaticProxyMethodSource(Formatter &out,
- const std::string &klassName,
- const Method *method) const {
+void AST::generateStaticProxyMethodSource(Formatter& out, const std::string& klassName,
+ const Method* method) const {
if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
- return OK;
+ return;
}
method->generateCppReturnType(out);
@@ -1372,7 +1029,7 @@
out << "#endif // __ANDROID_DEBUGGABLE__\n";
const bool returnsValue = !method->results().empty();
- const TypedVar *elidedReturn = method->canElideCallback();
+ const NamedReference<Type>* elidedReturn = method->canElideCallback();
if (returnsValue && elidedReturn == nullptr) {
generateCheckNonNull(out, "_hidl_cb");
}
@@ -1434,7 +1091,7 @@
<< " */, _hidl_data, &_hidl_reply";
if (method->isOneway()) {
- out << ", ::android::hardware::IBinder::FLAG_ONEWAY";
+ out << ", " << Interface::FLAG_ONEWAY << " /* oneway */";
}
out << ");\n";
@@ -1513,11 +1170,9 @@
out.unindent();
out << "}\n\n";
- return OK;
}
-status_t AST::generateProxySource(
- Formatter &out, const FQName &fqName) const {
+void AST::generateProxySource(Formatter& out, const FQName& fqName) const {
const std::string klassName = fqName.getInterfaceProxyName();
out << klassName
@@ -1542,24 +1197,18 @@
out.unindent();
out << "}\n\n";
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
- return generateStaticProxyMethodSource(out, klassName, method);
- }, false /* include parents */);
+ generateMethods(out,
+ [&](const Method* method, const Interface*) {
+ generateStaticProxyMethodSource(out, klassName, method);
+ },
+ false /* include parents */);
- if (err != OK) {
- return err;
- }
-
- err = generateMethods(out, [&](const Method *method, const Interface *superInterface) {
- return generateProxyMethodSource(out, klassName, method, superInterface);
+ generateMethods(out, [&](const Method* method, const Interface* superInterface) {
+ generateProxyMethodSource(out, klassName, method, superInterface);
});
-
- return err;
}
-status_t AST::generateStubSource(
- Formatter &out,
- const Interface *iface) const {
+void AST::generateStubSource(Formatter& out, const Interface* iface) const {
const std::string interfaceName = iface->localName();
const std::string klassName = iface->getStubName();
@@ -1625,24 +1274,22 @@
out << "::android::hardware::details::gBnMap.eraseIfEqual(_hidl_mImpl.get(), this);\n";
}).endl().endl();
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
- return generateStaticStubMethodSource(out, klassName, method);
- }, false /* include parents */);
+ generateMethods(out,
+ [&](const Method* method, const Interface*) {
+ return generateStaticStubMethodSource(out, iface->fqName(), method);
+ },
+ false /* include parents */);
- err = generateMethods(out, [&](const Method *method, const Interface *) {
+ generateMethods(out, [&](const Method* method, const Interface*) {
if (!method->isHidlReserved() || !method->overridesCppImpl(IMPL_STUB_IMPL)) {
- return OK;
+ return;
}
method->generateCppSignature(out, iface->getStubName());
out << " ";
out.block([&] {
method->cppImpl(IMPL_STUB_IMPL, out);
}).endl();
- return OK;
});
- if (err != OK) {
- return err;
- }
out << "::android::status_t " << klassName << "::onTransact(\n";
@@ -1665,6 +1312,9 @@
const Method *method = tuple.method();
const Interface *superInterface = tuple.interface();
+ if (!isIBase() && method->isHidlReserved()) {
+ continue;
+ }
out << "case "
<< method->getSerialId()
<< " /* "
@@ -1673,15 +1323,12 @@
out.indent();
- out << "bool _hidl_is_oneway = _hidl_flags & ::android::hardware::IBinder::FLAG_ONEWAY;\n";
+ out << "bool _hidl_is_oneway = _hidl_flags & " << Interface::FLAG_ONEWAY
+ << " /* oneway */;\n";
out << "if (_hidl_is_oneway != " << (method->isOneway() ? "true" : "false") << ") ";
out.block([&] { out << "return ::android::UNKNOWN_ERROR;\n"; }).endl().endl();
- status_t err = generateStubSourceForMethod(out, method, superInterface);
-
- if (err != OK) {
- return err;
- }
+ generateStubSourceForMethod(out, method, superInterface);
out.unindent();
out << "}\n\n";
@@ -1726,19 +1373,14 @@
out.unindent();
out << "}\n\n";
-
- return OK;
}
-status_t AST::generateStubSourceForMethod(
- Formatter &out,
- const Method *method,
- const Interface* superInterface) const {
-
+void AST::generateStubSourceForMethod(Formatter& out, const Method* method,
+ const Interface* superInterface) const {
if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB)) {
method->cppImpl(IMPL_STUB, out);
out << "break;\n";
- return OK;
+ return;
}
out << "_hidl_err = "
@@ -1749,17 +1391,16 @@
<< method->name()
<< "(this, _hidl_data, _hidl_reply, _hidl_cb);\n";
out << "break;\n";
-
- return OK;
}
-status_t AST::generateStaticStubMethodSource(Formatter &out,
- const std::string &klassName,
- const Method *method) const {
+void AST::generateStaticStubMethodSource(Formatter& out, const FQName& fqName,
+ const Method* method) const {
if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB)) {
- return OK;
+ return;
}
+ const std::string& klassName = fqName.getInterfaceStubName();
+
out << "::android::status_t " << klassName << "::_hidl_" << method->name() << "(\n";
out.indent();
@@ -1821,14 +1462,14 @@
method);
const bool returnsValue = !method->results().empty();
- const TypedVar *elidedReturn = method->canElideCallback();
+ const NamedReference<Type>* elidedReturn = method->canElideCallback();
std::string callee;
if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB_IMPL)) {
callee = "_hidl_this";
} else {
- callee = "static_cast<" + klassName + "*>(_hidl_this)->_hidl_mImpl";
+ callee = "static_cast<" + fqName.getInterfaceName() + "*>(_hidl_this->getImpl().get())";
}
if (elidedReturn != nullptr) {
@@ -1973,14 +1614,12 @@
out << "return _hidl_err;\n";
out.unindent();
out << "}\n\n";
-
- return OK;
}
-status_t AST::generatePassthroughHeader(const std::string &outputPath) const {
+void AST::generatePassthroughHeader(Formatter& out) const {
if (!AST::isInterface()) {
// types.hal does not get a stub header.
- return OK;
+ return;
}
const Interface* iface = mRootScope.getInterface();
@@ -1990,21 +1629,6 @@
bool supportOneway = iface->hasOnewayMethods();
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
- path.append(klassName);
- path.append(".h");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
const std::string guard = makeHeaderGuard(klassName);
out << "#ifndef " << guard << "\n";
@@ -2043,15 +1667,12 @@
out.endl();
generateTemplatizationLink(out);
+ generateCppTag(out, "android::hardware::details::bs_tag");
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
- return generatePassthroughMethod(out, method);
+ generateMethods(out, [&](const Method* method, const Interface*) {
+ generatePassthroughMethod(out, method);
});
- if (err != OK) {
- return err;
- }
-
out.unindent();
out << "private:\n";
out.indent();
@@ -2073,17 +1694,15 @@
enterLeaveNamespace(out, false /* enter */);
out << "\n#endif // " << guard << "\n";
-
- return OK;
}
-status_t AST::generateInterfaceSource(Formatter &out) const {
+void AST::generateInterfaceSource(Formatter& out) const {
const Interface* iface = mRootScope.getInterface();
// generate castFrom functions
std::string childTypeResult = iface->getCppResultType();
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+ generateMethods(out, [&](const Method* method, const Interface*) {
bool reserved = method->isHidlReserved();
if (!reserved) {
@@ -2098,11 +1717,8 @@
out << "\n";
- return OK;
+ return;
});
- if (err != OK) {
- return err;
- }
for (const Interface *superType : iface->typeChain()) {
out << "// static \n::android::hardware::Return<"
@@ -2134,11 +1750,9 @@
out.unindent();
out << "}\n\n";
}
-
- return OK;
}
-status_t AST::generatePassthroughSource(Formatter &out) const {
+void AST::generatePassthroughSource(Formatter& out) const {
const Interface* iface = mRootScope.getInterface();
const std::string klassName = iface->getPassthroughName();
@@ -2185,8 +1799,6 @@
}
-
- return OK;
}
void AST::generateCppAtraceCall(Formatter &out,
@@ -2222,7 +1834,7 @@
}
default:
{
- LOG(FATAL) << "Unsupported instrumentation event: " << event;
+ CHECK(false) << "Unsupported instrumentation event: " << event;
}
}
}
@@ -2304,7 +1916,7 @@
}
default:
{
- LOG(FATAL) << "Unsupported instrumentation event: " << event;
+ CHECK(false) << "Unsupported instrumentation event: " << event;
}
}
diff --git a/generateCppAdapter.cpp b/generateCppAdapter.cpp
new file mode 100644
index 0000000..1fbfb57
--- /dev/null
+++ b/generateCppAdapter.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2017 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 "HidlTypeAssertion.h"
+#include "Interface.h"
+#include "Method.h"
+#include "Reference.h"
+#include "ScalarType.h"
+#include "Scope.h"
+
+#include <android-base/logging.h>
+#include <hidl-util/Formatter.h>
+#include <hidl-util/StringHelper.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+namespace android {
+
+void AST::generateCppAdapterHeader(Formatter& out) const {
+ const std::string klassName = AST::isInterface() ? getInterface()->getAdapterName() : "Atypes";
+ const std::string guard = makeHeaderGuard(klassName, true /* indicateGenerated */);
+
+ out << "#ifndef " << guard << "\n";
+ out << "#define " << guard << "\n\n";
+
+ if (AST::isInterface()) {
+ generateCppPackageInclude(out, mPackage, getInterface()->localName());
+
+ enterLeaveNamespace(out, true /* enter */);
+ out.endl();
+
+ const std::string mockName = getInterface()->fqName().cppName();
+
+ out << "class " << klassName << " : public " << mockName << " ";
+ out.block([&] {
+ out << "public:\n";
+ out << "typedef " << mockName << " Pure;\n";
+
+ out << klassName << "(::android::sp<" << mockName << "> impl);\n";
+
+ generateMethods(out, [&](const Method* method, const Interface* /* interface */) {
+ if (method->isHidlReserved()) {
+ return;
+ }
+
+ out << "virtual ";
+ method->generateCppSignature(out);
+ out << " override;\n";
+ });
+ out << "private:\n";
+ out << "::android::sp<" << mockName << "> mImpl;\n";
+
+ }) << ";\n\n";
+
+ enterLeaveNamespace(out, false /* enter */);
+ } else {
+ out << "// no adapters for types.hal\n";
+ }
+
+ out << "#endif // " << guard << "\n";
+}
+
+void AST::generateCppAdapterSource(Formatter& out) const {
+ const std::string klassName = AST::isInterface() ? getInterface()->getAdapterName() : "Atypes";
+
+ generateCppPackageInclude(out, mPackage, klassName);
+
+ if (AST::isInterface()) {
+ out << "#include <hidladapter/HidlBinderAdapter.h>\n";
+ generateCppPackageInclude(out, mPackage, getInterface()->localName());
+
+ std::set<FQName> allImportedNames;
+ getAllImportedNames(&allImportedNames);
+ for (const auto& item : allImportedNames) {
+ if (item.name() == "types") {
+ continue;
+ }
+ generateCppPackageInclude(out, item, item.getInterfaceAdapterName());
+ }
+
+ out.endl();
+
+ enterLeaveNamespace(out, true /* enter */);
+ out.endl();
+
+ const std::string mockName = getInterface()->fqName().cppName();
+
+ out << klassName << "::" << klassName << "(::android::sp<" << mockName
+ << "> impl) : mImpl(impl) {}";
+
+ generateMethods(out, [&](const Method* method, const Interface* /* interface */) {
+ generateAdapterMethod(out, method);
+ });
+
+ enterLeaveNamespace(out, false /* enter */);
+ out.endl();
+ } else {
+ out << "// no adapters for types.hal\n";
+ }
+}
+
+void AST::generateAdapterMethod(Formatter& out, const Method* method) const {
+ if (method->isHidlReserved()) {
+ return;
+ }
+
+ const auto adapt = [](Formatter& out, const std::string& var, const Type* type) {
+ if (!type->isInterface()) {
+ out << var;
+ return;
+ }
+
+ // TODO(b/66900959): if we are creating the adapter for a 1.1 IFoo
+ // and we are using a method that takes/returns a 1.0 Callback, but
+ // there exists a 1.1 Callback (or other subclass that is depended
+ // on by this module), then wrap with the adapter subclass adapter
+ // IFF that callback is a subclass. However, if the callback
+ // is 1.0 ICallback, then wrap with a 1.0 adapter.
+
+ const Interface* interface = static_cast<const Interface*>(type);
+ out << "static_cast<::android::sp<" << interface->fqName().cppName() << ">>("
+ << interface->fqName().cppName() << "::castFrom("
+ << "::android::hardware::details::adaptWithDefault("
+ << "static_cast<::android::sp<" << interface->fqName().cppName() << ">>(" << var
+ << "), [&] { return new " << interface->fqName().getInterfaceAdapterFqName().cppName()
+ << "(" << var << "); })))";
+ };
+
+ const std::string klassName = getInterface()->getAdapterName();
+
+ method->generateCppSignature(out, klassName);
+ out.block([&] {
+ bool hasCallback = !method->canElideCallback() && !method->results().empty();
+
+ if (hasCallback) {
+ out << method->name() << "_cb _hidl_cb_wrapped = [&](";
+ method->emitCppResultSignature(out);
+ out << ") ";
+ out.block([&] {
+ out << "return _hidl_cb(\n";
+ out.indent([&]() {
+ out.join(method->results().begin(), method->results().end(), ",\n",
+ [&](auto arg) { adapt(out, arg->name(), arg->get()); });
+ });
+ out << ");\n";
+ });
+ out << ";\n";
+ }
+
+ out << "auto _hidl_out = mImpl->" << method->name() << "(\n";
+ out.indent([&]() {
+ out.join(method->args().begin(), method->args().end(), ",\n",
+ [&](auto arg) { adapt(out, arg->name(), arg->get()); });
+ if (hasCallback) {
+ if (!method->args().empty()) {
+ out << ",\n";
+ }
+ out << "_hidl_cb_wrapped";
+ }
+ });
+ out << ");\n";
+
+ const auto elidedCallback = method->canElideCallback();
+ if (elidedCallback) {
+ out.sIf("!_hidl_out.isOkUnchecked()", [&] { out << "return _hidl_out;\n"; });
+ out << "return ";
+ adapt(out, "_hidl_out", elidedCallback->get());
+ out << ";\n";
+ } else {
+ out << "return _hidl_out;\n";
+ }
+ }).endl();
+}
+
+} // namespace android
diff --git a/generateCppImpl.cpp b/generateCppImpl.cpp
index ae95590..18fac6b 100644
--- a/generateCppImpl.cpp
+++ b/generateCppImpl.cpp
@@ -20,6 +20,7 @@
#include "EnumType.h"
#include "Interface.h"
#include "Method.h"
+#include "Reference.h"
#include "ScalarType.h"
#include "Scope.h"
@@ -32,27 +33,15 @@
namespace android {
-status_t AST::generateCppImpl(const std::string &outputPath) const {
- status_t err = generateStubImplHeader(outputPath);
-
- if (err == OK) {
- err = generateStubImplSource(outputPath);
- }
-
- return err;
-}
-
void AST::generateFetchSymbol(Formatter &out, const std::string& ifaceName) const {
out << "HIDL_FETCH_" << ifaceName;
}
-status_t AST::generateStubImplMethod(Formatter &out,
- const std::string &className,
- const Method *method) const {
-
+void AST::generateStubImplMethod(Formatter& out, const std::string& className,
+ const Method* method) const {
// ignore HIDL reserved methods -- implemented in IFoo already.
if (method->isHidlReserved()) {
- return OK;
+ return;
}
method->generateCppSignature(out, className, false /* specifyNamespaces */);
@@ -62,7 +51,7 @@
out.indent();
out << "// TODO implement\n";
- const TypedVar *elidedReturn = method->canElideCallback();
+ const NamedReference<Type>* elidedReturn = method->canElideCallback();
if (elidedReturn == nullptr) {
out << "return Void();\n";
@@ -76,31 +65,18 @@
out << "}\n\n";
- return OK;
+ return;
}
-status_t AST::generateStubImplHeader(const std::string &outputPath) const {
+void AST::generateCppImplHeader(Formatter& out) const {
if (!AST::isInterface()) {
// types.hal does not get a stub header.
- return OK;
+ return;
}
const Interface* iface = mRootScope.getInterface();
const std::string baseName = iface->getBaseName();
- std::string path = outputPath;
- path.append(baseName);
- path.append(".h");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
const std::string guard = makeHeaderGuard(baseName, false /* indicateGenerated */);
out << "#ifndef " << guard << "\n";
@@ -132,21 +108,16 @@
out.indent();
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
+ generateMethods(out, [&](const Method* method, const Interface*) {
// ignore HIDL reserved methods -- implemented in IFoo already.
if (method->isHidlReserved()) {
- return OK;
+ return;
}
method->generateCppSignature(out, "" /* className */,
false /* specifyNamespaces */);
out << " override;\n";
- return OK;
});
- if (err != OK) {
- return err;
- }
-
out.unindent();
out << "};\n\n";
@@ -162,45 +133,26 @@
enterLeaveNamespace(out, false /* leave */);
out << "\n#endif // " << guard << "\n";
-
- return OK;
}
-status_t AST::generateStubImplSource(const std::string &outputPath) const {
+void AST::generateCppImplSource(Formatter& out) const {
if (!AST::isInterface()) {
// types.hal does not get a stub header.
- return OK;
+ return;
}
const Interface* iface = mRootScope.getInterface();
const std::string baseName = iface->getBaseName();
- std::string path = outputPath;
- path.append(baseName);
- path.append(".cpp");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
out << "#include \"" << baseName << ".h\"\n\n";
enterLeaveNamespace(out, true /* enter */);
out << "namespace implementation {\n\n";
- status_t err = generateMethods(out, [&](const Method *method, const Interface *) {
- return generateStubImplMethod(out, baseName, method);
+ generateMethods(out, [&](const Method* method, const Interface*) {
+ generateStubImplMethod(out, baseName, method);
});
- if (err != OK) {
- return err;
- }
-
out.setLinePrefix("//");
out << iface->localName()
<< "* ";
@@ -214,8 +166,6 @@
out << "} // namespace implementation\n";
enterLeaveNamespace(out, false /* leave */);
-
- return OK;
}
} // namespace android
diff --git a/generateJava.cpp b/generateJava.cpp
index f463b7f..bdbe25a 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -19,6 +19,7 @@
#include "Coordinator.h"
#include "Interface.h"
#include "Method.h"
+#include "Reference.h"
#include "Scope.h"
#include <hidl-util/Formatter.h>
@@ -26,12 +27,9 @@
namespace android {
-void AST::emitJavaReaderWriter(
- Formatter &out,
- const std::string &parcelObj,
- const TypedVar *arg,
- bool isReader,
- bool addPrefixToName) const {
+void AST::emitJavaReaderWriter(Formatter& out, const std::string& parcelObj,
+ const NamedReference<Type>* arg, bool isReader,
+ bool addPrefixToName) const {
if (isReader) {
out << arg->type().getJavaType()
<< " "
@@ -45,94 +43,81 @@
isReader);
}
-status_t AST::generateJavaTypes(
- const std::string &outputPath, const std::string &limitToType) const {
+void AST::generateJavaTypes(Formatter& out, const std::string& limitToType) const {
// Splits types.hal up into one java file per declared type.
+ CHECK(!limitToType.empty()) << getFilename();
for (const auto& type : mRootScope.getSubTypes()) {
std::string typeName = type->localName();
- if (type->isTypeDef()) {
- continue;
- }
-
- if (!limitToType.empty() && typeName != limitToType) {
- continue;
- }
-
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */,
- true /* sanitized */));
- path.append(typeName);
- path.append(".java");
-
- CHECK(Coordinator::MakeParentHierarchy(path)) << path;
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
+ if (type->isTypeDef()) continue;
+ if (typeName != limitToType) continue;
std::vector<std::string> packageComponents;
getPackageAndVersionComponents(
&packageComponents, true /* cpp_compatible */);
- out << "package " << mPackage.javaPackage() << ";\n\n";
+ out << "package " << mPackage.javaPackage() << ";\n\n\n";
- out << "\n";
-
- status_t err =
- type->emitJavaTypeDeclarations(out, true /* atTopLevel */);
-
- if (err != OK) {
- return err;
- }
+ type->emitJavaTypeDeclarations(out, true /* atTopLevel */);
+ return;
}
- return OK;
+ CHECK(false) << "generateJavaTypes could not find limitToType type";
}
-status_t AST::generateJava(
- const std::string &outputPath, const std::string &limitToType) const {
- if (!isJavaCompatible()) {
- fprintf(stderr,
- "ERROR: This interface is not Java compatible. The Java backend"
- " does NOT support union types nor native handles. "
- "In addition, vectors of arrays are limited to at most "
- "one-dimensional arrays and vectors of {vectors,interfaces} are"
- " not supported.\n");
-
- return UNKNOWN_ERROR;
+void emitGetService(
+ Formatter& out,
+ const std::string& ifaceName,
+ const std::string& fqName,
+ bool isRetry) {
+ out << "public static "
+ << ifaceName
+ << " getService(String serviceName";
+ if (isRetry) {
+ out << ", boolean retry";
}
+ out << ") throws android.os.RemoteException ";
+ out.block([&] {
+ out << "return "
+ << ifaceName
+ << ".asInterface(android.os.HwBinder.getService(\""
+ << fqName
+ << "\", serviceName";
+ if (isRetry) {
+ out << ", retry";
+ }
+ out << "));\n";
+ }).endl().endl();
+
+ out << "public static "
+ << ifaceName
+ << " getService(";
+ if (isRetry) {
+ out << "boolean retry";
+ }
+ out << ") throws android.os.RemoteException ";
+ out.block([&] {
+ out << "return getService(\"default\"";
+ if (isRetry) {
+ out << ", retry";
+ }
+ out <<");\n";
+ }).endl().endl();
+}
+
+void AST::generateJava(Formatter& out, const std::string& limitToType) const {
+ CHECK(isJavaCompatible()) << getFilename();
if (!AST::isInterface()) {
- return generateJavaTypes(outputPath, limitToType);
+ generateJavaTypes(out, limitToType);
+ return;
}
const Interface* iface = mRootScope.getInterface();
- std::string ifaceName = iface->localName();
-
+ const std::string ifaceName = iface->localName();
const std::string baseName = iface->getBaseName();
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */,
- true /* sanitized */));
- path.append(ifaceName);
- path.append(".java");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
std::vector<std::string> packageComponents;
getPackageAndVersionComponents(
&packageComponents, true /* cpp_compatible */);
@@ -225,43 +210,10 @@
out << "@Override\npublic android.os.IHwBinder asBinder();\n\n";
- out << "public static "
- << ifaceName
- << " getService(String serviceName) throws android.os.RemoteException {\n";
+ emitGetService(out, ifaceName, iface->fqName().string(), true /* isRetry */);
+ emitGetService(out, ifaceName, iface->fqName().string(), false /* isRetry */);
- out.indent();
-
- out << "return "
- << ifaceName
- << ".asInterface(android.os.HwBinder.getService(\""
- << iface->fqName().string()
- << "\",serviceName));\n";
-
- out.unindent();
-
- out << "}\n\n";
-
- out << "public static "
- << ifaceName
- << " getService() throws android.os.RemoteException {\n";
-
- out.indent();
-
- out << "return "
- << ifaceName
- << ".asInterface(android.os.HwBinder.getService(\""
- << iface->fqName().string()
- << "\",\"default\"));\n";
-
- out.unindent();
-
- out << "}\n\n";
-
- status_t err = emitJavaTypeDeclarations(out);
-
- if (err != OK) {
- return err;
- }
+ emitJavaTypeDeclarations(out);
for (const auto &method : iface->methods()) {
if (method->isHiddenFromJava()) {
@@ -285,6 +237,8 @@
out << "}\n\n";
}
+ method->emitDocComment(out);
+
if (returnsValue && !needsCallback) {
out << method->results()[0]->type().getJavaType();
} else {
@@ -342,6 +296,18 @@
<< ifaceName << ".kInterfaceName + \"]@Proxy\";\n";
}).endl().endl();
+ // Equals when internal binder object is equal (even if the interface Proxy object
+ // itself is different). This is similar to interfacesEqual in C++.
+ out << "@Override\npublic final boolean equals(java.lang.Object other) ";
+ out.block([&] {
+ out << "return android.os.HidlSupport.interfacesEqual(this, other);\n";
+ }).endl().endl();
+
+ out << "@Override\npublic final int hashCode() ";
+ out.block([&] {
+ out << "return this.asBinder().hashCode();\n";
+ }).endl().endl();
+
const Interface *prevInterface = nullptr;
for (const auto &tuple : iface->allMethodsFromRoot()) {
const Method *method = tuple.method();
@@ -417,7 +383,7 @@
<< " */, _hidl_request, _hidl_reply, ";
if (method->isOneway()) {
- out << "android.os.IHwBinder.FLAG_ONEWAY";
+ out << Interface::FLAG_ONEWAY << " /* oneway */";
} else {
out << "0 /* flags */";
}
@@ -484,6 +450,8 @@
out << "@Override\npublic android.os.IHwBinder asBinder() {\n";
out.indent();
+ // If we change this behavior in the future and asBinder does not return "this",
+ // equals and hashCode should also be overridden.
out << "return this;\n";
out.unindent();
out << "}\n\n";
@@ -567,6 +535,15 @@
out.indent();
+ out << "boolean _hidl_is_oneway = (_hidl_flags & " << Interface::FLAG_ONEWAY
+ << " /* oneway */) != 0\n;";
+ out << "if (_hidl_is_oneway != " << (method->isOneway() ? "true" : "false") << ") ";
+ out.block([&] {
+ out << "_hidl_reply.writeStatus(" << UNKNOWN_ERROR << ");\n";
+ out << "_hidl_reply.send();\n";
+ out << "break;\n";
+ });
+
if (method->isHidlReserved() && method->overridesJavaImpl(IMPL_STUB)) {
method->javaImpl(IMPL_STUB, out);
out.unindent();
@@ -602,7 +579,7 @@
}
if (!needsCallback && returnsValue) {
- const TypedVar *returnArg = method->results()[0];
+ const NamedReference<Type>* returnArg = method->results()[0];
out << returnArg->type().getJavaType()
<< " _hidl_out_"
@@ -663,7 +640,7 @@
out << "_hidl_reply.writeStatus(android.os.HwParcel.STATUS_SUCCESS);\n";
if (returnsValue) {
- const TypedVar *returnArg = method->results()[0];
+ const NamedReference<Type>* returnArg = method->results()[0];
emitJavaReaderWriter(
out,
@@ -692,12 +669,10 @@
out.unindent();
out << "}\n";
-
- return OK;
}
-status_t AST::emitJavaTypeDeclarations(Formatter &out) const {
- return mRootScope.emitJavaTypeDeclarations(out, false /* atTopLevel */);
+void AST::emitJavaTypeDeclarations(Formatter& out) const {
+ mRootScope.emitJavaTypeDeclarations(out, false /* atTopLevel */);
}
} // namespace android
diff --git a/generateVts.cpp b/generateVts.cpp
index 5fd8670..8a37ae3 100644
--- a/generateVts.cpp
+++ b/generateVts.cpp
@@ -29,7 +29,7 @@
namespace android {
-status_t AST::emitVtsTypeDeclarations(Formatter &out) const {
+void AST::emitVtsTypeDeclarations(Formatter& out) const {
if (AST::isInterface()) {
const Interface* iface = mRootScope.getInterface();
return iface->emitVtsAttributeDeclaration(out);
@@ -42,36 +42,16 @@
}
out << "attribute: {\n";
out.indent();
- status_t status = type->emitVtsTypeDeclarations(out);
- if (status != OK) {
- return status;
- }
+ type->emitVtsTypeDeclarations(out);
out.unindent();
out << "}\n\n";
}
-
- return OK;
}
-status_t AST::generateVts(const std::string &outputPath) const {
+void AST::generateVts(Formatter& out) const {
std::string baseName = AST::getBaseName();
const Interface *iface = AST::getInterface();
- std::string path = outputPath;
- path.append(mCoordinator->convertPackageRootToPath(mPackage));
- path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
- path.append(baseName);
- path.append(".vts");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
out << "component_class: HAL_HIDL\n";
out << "component_type_version: " << mPackage.version()
<< "\n";
@@ -101,28 +81,19 @@
std::vector<const Interface *> chain = iface->typeChain();
// Generate all the attribute declarations first.
- status_t status = emitVtsTypeDeclarations(out);
- if (status != OK) {
- return status;
- }
+ emitVtsTypeDeclarations(out);
+
// Generate all the method declarations.
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
const Interface *superInterface = *it;
- status_t status = superInterface->emitVtsMethodDeclaration(out);
- if (status != OK) {
- return status;
- }
+ superInterface->emitVtsMethodDeclaration(out);
}
out.unindent();
out << "}\n";
} else {
- status_t status = emitVtsTypeDeclarations(out);
- if (status != OK) {
- return status;
- }
+ emitVtsTypeDeclarations(out);
}
- return OK;
}
} // namespace android
diff --git a/hidl-gen_l.h b/hidl-gen_l.h
new file mode 100644
index 0000000..52237e4
--- /dev/null
+++ b/hidl-gen_l.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <AST.h>
+
+#include <utils/Errors.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// entry-point for file parsing
+// - contents of file are added to the AST
+// - expects file to already be open
+status_t parseFile(AST* ast, std::unique_ptr<FILE, std::function<void(FILE*)>> file);
+
+} // namespace android
\ No newline at end of file
diff --git a/hidl-gen_l.ll b/hidl-gen_l.ll
index 13b69bf..c94df01 100644
--- a/hidl-gen_l.ll
+++ b/hidl-gen_l.ll
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-D [0-9]
-L [a-zA-Z_]
-H [a-fA-F0-9]
-E [Ee][+-]?{D}+
-FS (f|F|l|L)
-IS (u|U|l|L)*
+D [0-9]
+L [a-zA-Z_]
+H [a-fA-F0-9]
+E [Ee][+-]?{D}+
+FS (f|F|l|L)
+IS (u|U|l|L)*
-COMPONENT {L}({L}|{D})*
-DOT [.]
-AT [@]
-VERSION {AT}{D}+{DOT}{D}+
-FQNAME ({COMPONENT}|{VERSION})(({DOT}|":"+){COMPONENT}|{VERSION})*
+COMPONENT {L}({L}|{D})*
+DOT [.]
+AT [@]
+VERSION {AT}{D}+{DOT}{D}+
+FQNAME ({COMPONENT}|{VERSION})(({DOT}|":"+){COMPONENT}|{VERSION})*
%{
@@ -35,12 +35,14 @@
#include "CompoundType.h"
#include "ConstantExpression.h"
#include "DeathRecipientType.h"
+#include "DocComment.h"
#include "EnumType.h"
#include "HandleType.h"
#include "MemoryType.h"
#include "Method.h"
#include "PointerType.h"
#include "ScalarType.h"
+#include "Scope.h"
#include "StringType.h"
#include "VectorType.h"
#include "RefType.h"
@@ -53,17 +55,23 @@
using namespace android;
using token = yy::parser::token;
-#define SCALAR_TYPE(kind) \
- do { \
- yylval->type = new ScalarType(ScalarType::kind); \
- return token::TYPE; \
- } while (0)
+static std::string gCurrentComment;
+
+#define SCALAR_TYPE(kind) \
+ { \
+ yylval->type = new ScalarType(ScalarType::kind, *scope); \
+ return token::TYPE; \
+ }
+
+#define YY_DECL int yylex(YYSTYPE* yylval_param, YYLTYPE* yylloc_param, \
+ yyscan_t yyscanner, android::Scope** const scope)
#define YY_USER_ACTION yylloc->step(); yylloc->columns(yyleng);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wdeprecated-register"
+#pragma clang diagnostic ignored "-Wregister"
%}
@@ -76,127 +84,135 @@
%option bison-locations
%x COMMENT_STATE
+%x DOC_COMMENT_STATE
%%
-"/*" { BEGIN(COMMENT_STATE); }
-<COMMENT_STATE>"*/" { BEGIN(INITIAL); }
-<COMMENT_STATE>[\n] { yylloc->lines(); }
-<COMMENT_STATE>. { }
+"/**" { gCurrentComment.clear(); BEGIN(DOC_COMMENT_STATE); }
+<DOC_COMMENT_STATE>"*/" {
+ BEGIN(INITIAL);
+ yylval->docComment = new DocComment(gCurrentComment);
+ return token::DOC_COMMENT;
+ }
+<DOC_COMMENT_STATE>[^*\n]* { gCurrentComment += yytext; }
+<DOC_COMMENT_STATE>[\n] { gCurrentComment += yytext; yylloc->lines(); }
+<DOC_COMMENT_STATE>[*] { gCurrentComment += yytext; }
-"//"[^\r\n]* { /* skip C++ style comment */ }
+"/*" { BEGIN(COMMENT_STATE); }
+<COMMENT_STATE>"*/" { BEGIN(INITIAL); }
+<COMMENT_STATE>[\n] { yylloc->lines(); }
+<COMMENT_STATE>. { }
-"enum" { return token::ENUM; }
-"extends" { return token::EXTENDS; }
-"generates" { return token::GENERATES; }
-"import" { return token::IMPORT; }
-"interface" { return token::INTERFACE; }
-"package" { return token::PACKAGE; }
-"struct" { return token::STRUCT; }
-"typedef" { return token::TYPEDEF; }
-"union" { return token::UNION; }
-"bitfield" { yylval->templatedType = new BitFieldType; return token::TEMPLATED; }
-"vec" { yylval->templatedType = new VectorType; return token::TEMPLATED; }
-"ref" { yylval->templatedType = new RefType; return token::TEMPLATED; }
-"oneway" { return token::ONEWAY; }
+"//"[^\r\n]* { /* skip C++ style comment */ }
-"bool" { SCALAR_TYPE(KIND_BOOL); }
-"int8_t" { SCALAR_TYPE(KIND_INT8); }
-"uint8_t" { SCALAR_TYPE(KIND_UINT8); }
-"int16_t" { SCALAR_TYPE(KIND_INT16); }
-"uint16_t" { SCALAR_TYPE(KIND_UINT16); }
-"int32_t" { SCALAR_TYPE(KIND_INT32); }
-"uint32_t" { SCALAR_TYPE(KIND_UINT32); }
-"int64_t" { SCALAR_TYPE(KIND_INT64); }
-"uint64_t" { SCALAR_TYPE(KIND_UINT64); }
-"float" { SCALAR_TYPE(KIND_FLOAT); }
-"double" { SCALAR_TYPE(KIND_DOUBLE); }
+"enum" { return token::ENUM; }
+"extends" { return token::EXTENDS; }
+"generates" { return token::GENERATES; }
+"import" { return token::IMPORT; }
+"interface" { return token::INTERFACE; }
+"package" { return token::PACKAGE; }
+"struct" { return token::STRUCT; }
+"typedef" { return token::TYPEDEF; }
+"union" { return token::UNION; }
+"bitfield" { yylval->templatedType = new BitFieldType(*scope); return token::TEMPLATED; }
+"vec" { yylval->templatedType = new VectorType(*scope); return token::TEMPLATED; }
+"ref" { yylval->templatedType = new RefType(*scope); return token::TEMPLATED; }
+"oneway" { return token::ONEWAY; }
-"death_recipient" { yylval->type = new DeathRecipientType; return token::TYPE; }
-"handle" { yylval->type = new HandleType; return token::TYPE; }
-"memory" { yylval->type = new MemoryType; return token::TYPE; }
-"pointer" { yylval->type = new PointerType; return token::TYPE; }
-"string" { yylval->type = new StringType; return token::TYPE; }
+"bool" { SCALAR_TYPE(KIND_BOOL); }
+"int8_t" { SCALAR_TYPE(KIND_INT8); }
+"uint8_t" { SCALAR_TYPE(KIND_UINT8); }
+"int16_t" { SCALAR_TYPE(KIND_INT16); }
+"uint16_t" { SCALAR_TYPE(KIND_UINT16); }
+"int32_t" { SCALAR_TYPE(KIND_INT32); }
+"uint32_t" { SCALAR_TYPE(KIND_UINT32); }
+"int64_t" { SCALAR_TYPE(KIND_INT64); }
+"uint64_t" { SCALAR_TYPE(KIND_UINT64); }
+"float" { SCALAR_TYPE(KIND_FLOAT); }
+"double" { SCALAR_TYPE(KIND_DOUBLE); }
-"fmq_sync" { yylval->type = new FmqType("::android::hardware", "MQDescriptorSync"); return token::TEMPLATED; }
-"fmq_unsync" { yylval->type = new FmqType("::android::hardware", "MQDescriptorUnsync"); return token::TEMPLATED; }
+"death_recipient" { yylval->type = new DeathRecipientType(*scope); return token::TYPE; }
+"handle" { yylval->type = new HandleType(*scope); return token::TYPE; }
+"memory" { yylval->type = new MemoryType(*scope); return token::TYPE; }
+"pointer" { yylval->type = new PointerType(*scope); return token::TYPE; }
+"string" { yylval->type = new StringType(*scope); return token::TYPE; }
-"(" { return('('); }
-")" { return(')'); }
-"<" { return('<'); }
-">" { return('>'); }
-"{" { return('{'); }
-"}" { return('}'); }
-"[" { return('['); }
-"]" { return(']'); }
-":" { return(':'); }
-";" { return(';'); }
-"," { return(','); }
-"." { return('.'); }
-"=" { return('='); }
-"+" { return('+'); }
-"-" { return('-'); }
-"*" { return('*'); }
-"/" { return('/'); }
-"%" { return('%'); }
-"&" { return('&'); }
-"|" { return('|'); }
-"^" { return('^'); }
-"<<" { return(token::LSHIFT); }
-">>" { return(token::RSHIFT); }
-"&&" { return(token::LOGICAL_AND); }
-"||" { return(token::LOGICAL_OR); }
-"!" { return('!'); }
-"~" { return('~'); }
-"<=" { return(token::LEQ); }
-">=" { return(token::GEQ); }
-"==" { return(token::EQUALITY); }
-"!=" { return(token::NEQ); }
-"?" { return('?'); }
-"@" { return('@'); }
+"fmq_sync" { yylval->type = new FmqType("::android::hardware", "MQDescriptorSync", *scope); return token::TEMPLATED; }
+"fmq_unsync" { yylval->type = new FmqType("::android::hardware", "MQDescriptorUnsync", *scope); return token::TEMPLATED; }
-{COMPONENT} { yylval->str = strdup(yytext); return token::IDENTIFIER; }
-{FQNAME} { yylval->str = strdup(yytext); return token::FQNAME; }
+"(" { return('('); }
+")" { return(')'); }
+"<" { return('<'); }
+">" { return('>'); }
+"{" { return('{'); }
+"}" { return('}'); }
+"[" { return('['); }
+"]" { return(']'); }
+":" { return(':'); }
+";" { return(';'); }
+"," { return(','); }
+"." { return('.'); }
+"=" { return('='); }
+"+" { return('+'); }
+"-" { return('-'); }
+"*" { return('*'); }
+"/" { return('/'); }
+"%" { return('%'); }
+"&" { return('&'); }
+"|" { return('|'); }
+"^" { return('^'); }
+"<<" { return(token::LSHIFT); }
+">>" { return(token::RSHIFT); }
+"&&" { return(token::LOGICAL_AND); }
+"||" { return(token::LOGICAL_OR); }
+"!" { return('!'); }
+"~" { return('~'); }
+"<=" { return(token::LEQ); }
+">=" { return(token::GEQ); }
+"==" { return(token::EQUALITY); }
+"!=" { return(token::NEQ); }
+"?" { return('?'); }
+"@" { return('@'); }
-0[xX]{H}+{IS}? { yylval->str = strdup(yytext); return token::INTEGER; }
-0{D}+{IS}? { yylval->str = strdup(yytext); return token::INTEGER; }
-{D}+{IS}? { yylval->str = strdup(yytext); return token::INTEGER; }
-L?\"(\\.|[^\\"])*\" { yylval->str = strdup(yytext); return token::STRING_LITERAL; }
+{COMPONENT} { yylval->str = strdup(yytext); return token::IDENTIFIER; }
+{FQNAME} { yylval->str = strdup(yytext); return token::FQNAME; }
-{D}+{E}{FS}? { yylval->str = strdup(yytext); return token::FLOAT; }
-{D}+\.{E}?{FS}? { yylval->str = strdup(yytext); return token::FLOAT; }
-{D}*\.{D}+{E}?{FS}? { yylval->str = strdup(yytext); return token::FLOAT; }
+0[xX]{H}+{IS}? { yylval->str = strdup(yytext); return token::INTEGER; }
+0{D}+{IS}? { yylval->str = strdup(yytext); return token::INTEGER; }
+{D}+{IS}? { yylval->str = strdup(yytext); return token::INTEGER; }
+L?\"(\\.|[^\\"])*\" { yylval->str = strdup(yytext); return token::STRING_LITERAL; }
-[\n] { yylloc->lines(); }
-. { /* ignore bad characters */ }
+{D}+{E}{FS}? { yylval->str = strdup(yytext); return token::FLOAT; }
+{D}+\.{E}?{FS}? { yylval->str = strdup(yytext); return token::FLOAT; }
+{D}*\.{D}+{E}?{FS}? { yylval->str = strdup(yytext); return token::FLOAT; }
+
+\n|\r\n { yylloc->lines(); }
+[ \t\f\v] { /* ignore all other whitespace */ }
+
+. { yylval->str = strdup(yytext); return token::UNKNOWN; }
%%
#pragma clang diagnostic pop
-status_t parseFile(AST *ast) {
- FILE *file = fopen(ast->getFilename().c_str(), "rb");
+namespace android {
- if (file == nullptr) {
- return -errno;
- }
-
+status_t parseFile(AST* ast, std::unique_ptr<FILE, std::function<void(FILE *)>> file) {
yyscan_t scanner;
yylex_init(&scanner);
- yyset_in(file, scanner);
+ yyset_in(file.get(), scanner);
Scope* scopeStack = ast->getRootScope();
int res = yy::parser(scanner, ast, &scopeStack).parse();
yylex_destroy(scanner);
- fclose(file);
- file = nullptr;
-
if (res != 0 || ast->syntaxErrors() != 0) {
return UNKNOWN_ERROR;
}
return OK;
}
+
+} // namespace android
diff --git a/hidl-gen_y.yy b/hidl-gen_y.yy
index d67638e..f445199 100644
--- a/hidl-gen_y.yy
+++ b/hidl-gen_y.yy
@@ -16,18 +16,20 @@
%{
-#include "Annotation.h"
#include "AST.h"
+#include "Annotation.h"
#include "ArrayType.h"
#include "CompoundType.h"
#include "ConstantExpression.h"
+#include "DocComment.h"
#include "EnumType.h"
#include "Interface.h"
#include "Location.h"
#include "Method.h"
-#include "Scope.h"
-#include "VectorType.h"
#include "RefType.h"
+#include "Scope.h"
+#include "TypeDef.h"
+#include "VectorType.h"
#include "hidl-gen_y.h"
@@ -38,7 +40,7 @@
using namespace android;
-extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void*);
+extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void*, Scope** const);
void enterScope(AST* /* ast */, Scope** scope, Scope* container) {
CHECK(container->parent() == (*scope));
@@ -205,6 +207,7 @@
%parse-param { android::AST* const ast }
%parse-param { android::Scope** const scope }
%lex-param { void* scanner }
+%lex-param { android::Scope** const scope }
%pure-parser
%glr-parser
%skeleton "glr.cc"
@@ -212,23 +215,28 @@
%expect-rr 0
%error-verbose
-%token<str> ENUM "keyword `enum`"
-%token<str> EXTENDS "keyword `extends`"
+%debug
+
+%token<docComment> DOC_COMMENT "doc comment"
+
+%token<void> ENUM "keyword `enum`"
+%token<void> EXTENDS "keyword `extends`"
%token<str> FQNAME "fully-qualified name"
-%token<str> GENERATES "keyword `generates`"
+%token<void> GENERATES "keyword `generates`"
%token<str> IDENTIFIER "identifier"
-%token<str> IMPORT "keyword `import`"
+%token<void> IMPORT "keyword `import`"
%token<str> INTEGER "integer value"
%token<str> FLOAT "float value"
-%token<str> INTERFACE "keyword `interface`"
+%token<void> INTERFACE "keyword `interface`"
%token<str> PACKAGE "keyword `package`"
%token<type> TYPE "type"
-%token<str> STRUCT "keyword `struct`"
+%token<void> STRUCT "keyword `struct`"
%token<str> STRING_LITERAL "string literal"
-%token<str> TYPEDEF "keyword `typedef`"
-%token<str> UNION "keyword `union`"
+%token<void> TYPEDEF "keyword `typedef`"
+%token<void> UNION "keyword `union`"
%token<templatedType> TEMPLATED "templated type"
%token<void> ONEWAY "keyword `oneway`"
+%token<str> UNKNOWN "unknown character"
/* Operator precedence and associativity, as per
* http://en.cppreference.com/w/cpp/language/operator_precedence */
@@ -254,28 +262,31 @@
/* Precedence level 3, RTL; but we have to use %left here */
%left UNARY_MINUS UNARY_PLUS '!' '~'
+%type<docComment> doc_comments
+
%type<str> error_stmt error
%type<str> package
%type<fqName> fqname
-%type<type> fqtype
+%type<referenceToType> fqtype
%type<str> valid_identifier valid_type_name
-%type<type> type enum_storage_type
-%type<type> array_type_base
+%type<referenceToType> type enum_storage_type type_or_inplace_compound_declaration
+%type<referenceToType> array_type_base
%type<arrayType> array_type
-%type<type> opt_extends
-%type<type> type_declaration type_declaration_body interface_declaration typedef_declaration
+%type<referenceToType> opt_extends
+%type<type> type_declaration commentable_type_declaration type_declaration_body
+%type<type> interface_declaration typedef_declaration
%type<type> named_struct_or_union_declaration named_enum_declaration
%type<type> compound_declaration annotated_compound_declaration
-%type<field> field_declaration
+%type<field> field_declaration commentable_field_declaration
%type<fields> field_declarations struct_or_union_body
%type<constantExpression> const_expr
-%type<enumValue> enum_value
+%type<enumValue> enum_value commentable_enum_value
%type<enumValues> enum_values enum_declaration_body
%type<typedVars> typed_vars
%type<typedVar> typed_var
-%type<method> method_declaration
+%type<method> method_declaration commentable_method_declaration
%type<compoundStyle> struct_or_union_keyword
%type<stringVec> annotation_string_values annotation_string_value
%type<constExprVec> annotation_const_expr_values annotation_const_expr_value
@@ -288,17 +299,18 @@
%union {
const char *str;
- android::Type *type;
+ android::Type* type;
+ android::Reference<android::Type>* referenceToType;
android::ArrayType *arrayType;
android::TemplatedType *templatedType;
android::FQName *fqName;
android::CompoundType *compoundType;
- android::CompoundField *field;
- std::vector<android::CompoundField *> *fields;
+ android::NamedReference<android::Type>* field;
+ std::vector<android::NamedReference<android::Type>*>* fields;
android::EnumValue *enumValue;
android::ConstantExpression *constantExpression;
std::vector<android::EnumValue *> *enumValues;
- android::TypedVar *typedVar;
+ android::NamedReference<android::Type>* typedVar;
android::TypedVarVector *typedVars;
android::Method *method;
android::CompoundType::Style compoundStyle;
@@ -308,14 +320,29 @@
android::AnnotationParamVector *annotationParams;
android::Annotation *annotation;
std::vector<android::Annotation *> *annotations;
+ android::DocComment* docComment;
}
%%
program
- : package
- imports
- type_declarations
+ // Don't care if license header is a doc comment or not
+ : DOC_COMMENT package imports type_declarations
+ | package imports type_declarations
+ ;
+
+doc_comments
+ : DOC_COMMENT { $$ = $1; }
+ | doc_comments DOC_COMMENT
+ {
+ $1->merge($2);
+ $$ = $1;
+ }
+ | doc_comments '}'
+ {
+ std::cerr << "ERROR: Doc comments must preceed what they describe at " << @1 << "\n";
+ YYERROR;
+ }
;
valid_identifier
@@ -388,11 +415,11 @@
annotation_param
: IDENTIFIER '=' annotation_string_value
{
- $$ = new AnnotationParam($1, $3);
+ $$ = new StringAnnotationParam($1, $3);
}
| IDENTIFIER '=' annotation_const_expr_value
{
- $$ = new AnnotationParam($1, $3);
+ $$ = new ConstantExpressionAnnotationParam($1, $3);
}
;
@@ -460,8 +487,8 @@
fqname
: FQNAME
{
- $$ = new FQName($1);
- if(!$$->isValid()) {
+ $$ = new FQName();
+ if(!FQName::parse($1, $$)) {
std::cerr << "ERROR: FQName '" << $1 << "' is not valid at "
<< @1
<< ".\n";
@@ -470,8 +497,8 @@
}
| valid_type_name
{
- $$ = new FQName($1);
- if(!$$->isValid()) {
+ $$ = new FQName();
+ if(!FQName::parse($1, $$)) {
std::cerr << "ERROR: FQName '" << $1 << "' is not valid at "
<< @1
<< ".\n";
@@ -483,16 +510,12 @@
fqtype
: fqname
{
- $$ = ast->lookupType(*($1), *scope);
- if ($$ == NULL) {
- std::cerr << "ERROR: Failed to lookup type '" << $1->string() << "' at "
- << @1
- << "\n";
-
- YYERROR;
- }
+ $$ = new Reference<Type>(*$1, convertYYLoc(@1));
}
| TYPE
+ {
+ $$ = new Reference<Type>($1, convertYYLoc(@1));
+ }
;
package
@@ -543,25 +566,29 @@
;
opt_extends
- : /* empty */ { $$ = NULL; }
+ : /* empty */ { $$ = nullptr; }
| EXTENDS fqtype { $$ = $2; }
+ ;
interface_declarations
: /* empty */
- | interface_declarations type_declaration
+ | interface_declarations commentable_type_declaration
{
+ CHECK((*scope)->isInterface());
+
std::string errorMsg;
- if ($2 != nullptr &&
- $2->isNamedType() &&
- !isValidInterfaceField(static_cast<NamedType *>($2)->localName().c_str(),
+ if ($2 != nullptr && $2->isNamedType() &&
+ !isValidInterfaceField(static_cast<NamedType*>($2)->localName().c_str(),
&errorMsg)) {
std::cerr << "ERROR: " << errorMsg << " at "
<< @2 << "\n";
YYERROR;
}
}
- | interface_declarations method_declaration
+ | interface_declarations commentable_method_declaration
{
+ CHECK((*scope)->isInterface());
+
std::string errorMsg;
if ($2 != nullptr &&
!isValidInterfaceField($2->name().c_str(), &errorMsg)) {
@@ -571,13 +598,7 @@
}
if ($2 != nullptr) {
- if (!(*scope)->isInterface()) {
- std::cerr << "ERROR: unknown error in interface declaration at "
- << @2 << "\n";
- YYERROR;
- }
-
- Interface *iface = static_cast<Interface *>(*scope);
+ Interface *iface = static_cast<Interface*>(*scope);
if (!iface->addMethod($2)) {
std::cerr << "ERROR: Unable to add method '" << $2->name()
<< "' at " << @2 << "\n";
@@ -592,19 +613,29 @@
type_declarations
: /* empty */
| error_stmt
- | type_declarations type_declaration
+ | type_declarations commentable_type_declaration
+ ;
+
+commentable_type_declaration
+ : doc_comments type_declaration
+ {
+ $2->setDocComment($1);
+ $$ = $2;
+ }
+ | type_declaration { $$ = $1; }
;
type_declaration
: opt_annotations type_declaration_body
{
- if ($2 != nullptr) {
- $2->setAnnotations($1);
+ if (!$2->isTypeDef()) {
+ CHECK($2->isScope());
+ static_cast<Scope*>($2)->setAnnotations($1);
} else if (!$1->empty()) {
// Since typedefs are always resolved to their target it makes
// little sense to annotate them and have their annotations
// impose semantics other than their target type.
- std::cerr << "ERROR: typedefs cannot be annotated. at " << @2
+ std::cerr << "ERROR: typedefs cannot be annotated at " << @2
<< "\n";
YYERROR;
@@ -623,9 +654,18 @@
interface_declaration
: INTERFACE valid_type_name opt_extends
{
- Type *parent = $3;
+ Reference<Type>* superType = $3;
+ bool isIBase = ast->package().package() == gIBaseFqName.package();
- if (ast->package().package() != gIBasePackageFqName.string()) {
+ if (isIBase) {
+ if (superType != nullptr) {
+ std::cerr << "ERROR: IBase must not extend any interface at " << @3
+ << "\n";
+
+ YYERROR;
+ }
+ superType = new Reference<Type>();
+ } else {
if (!ast->addImport(gIBaseFqName.string().c_str())) {
std::cerr << "ERROR: Unable to automatically import '"
<< gIBaseFqName.string()
@@ -633,63 +673,41 @@
<< "\n";
YYERROR;
}
- if (parent == nullptr) {
- parent = ast->lookupType(gIBaseFqName, *scope);
+
+ if (superType == nullptr) {
+ superType = new Reference<Type>(gIBaseFqName, convertYYLoc(@$));
}
}
- if (parent != NULL && !parent->isInterface()) {
- std::cerr << "ERROR: You can only extend interfaces. at " << @3
- << "\n";
-
- YYERROR;
- }
-
if ($2[0] != 'I') {
std::cerr << "ERROR: All interface names must start with an 'I' "
- << "prefix. at " << @2 << "\n";
+ << "prefix at " << @2 << "\n";
YYERROR;
}
if (*scope != ast->getRootScope()) {
std::cerr << "ERROR: All interface must declared in "
- << "global scope. at " << @2 << "\n";
+ << "global scope at " << @2 << "\n";
YYERROR;
}
Interface* iface = new Interface(
- $2, convertYYLoc(@2), *scope,
- static_cast<Interface *>(parent));
-
- // Register interface immediately so it can be referenced inside
- // definition.
- std::string errorMsg;
- if (!ast->addScopedType(iface, &errorMsg, *scope)) {
- std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n";
- YYERROR;
- }
+ $2, ast->makeFullName($2, *scope), convertYYLoc(@2),
+ *scope, *superType, ast->getFileHash());
enterScope(ast, scope, iface);
}
'{' interface_declarations '}'
{
- if (!(*scope)->isInterface()) {
- std::cerr << "ERROR: unknown error in interface declaration at "
- << @5 << "\n";
- YYERROR;
- }
+ CHECK((*scope)->isInterface());
Interface *iface = static_cast<Interface *>(*scope);
- if (!iface->addAllReservedMethods()) {
- std::cerr << "ERROR: unknown error in adding reserved methods at "
- << @5 << "\n";
- YYERROR;
- }
+ CHECK(iface->addAllReservedMethods());
leaveScope(ast, scope);
-
+ ast->addScopedType(iface, *scope);
$$ = iface;
}
;
@@ -697,18 +715,26 @@
typedef_declaration
: TYPEDEF type valid_type_name
{
- std::string errorMsg;
- if (!ast->addTypeDef($3, $2, convertYYLoc(@3), &errorMsg, *scope)) {
- std::cerr << "ERROR: " << errorMsg << " at " << @3 << "\n";
- YYERROR;
- }
-
- $$ = nullptr;
+ // 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.
+ TypeDef* typeDef = new TypeDef(
+ $3, ast->makeFullName($3, *scope), convertYYLoc(@2), *scope, *$2);
+ ast->addScopedType(typeDef, *scope);
+ $$ = typeDef;
}
;
const_expr
- : INTEGER { $$ = new ConstantExpression($1); }
+ : INTEGER {
+ $$ = LiteralConstantExpression::tryParse($1);
+
+ if ($$ == nullptr) {
+ std::cerr << "ERROR: Could not parse literal: "
+ << $1 << " at " << @1 << ".\n";
+ YYERROR;
+ }
+ }
| fqname
{
if(!$1->isValidValueName()) {
@@ -717,79 +743,89 @@
<< @1 << ".\n";
YYERROR;
}
- if($1->isIdentifier()) {
- std::string identifier = $1->name();
- LocalIdentifier *iden = (*scope)->lookupIdentifier(identifier);
- if(!iden) {
- std::cerr << "ERROR: identifier " << $1->string()
- << " could not be found at " << @1 << ".\n";
- YYERROR;
- }
- if(!iden->isEnumValue()) {
- std::cerr << "ERROR: identifier " << $1->string()
- << " is not an enum value at " << @1 << ".\n";
- YYERROR;
- }
- $$ = new ConstantExpression(
- *(static_cast<EnumValue *>(iden)->constExpr()), $1->string());
- } else {
- std::string errorMsg;
- EnumValue *v = ast->lookupEnumValue(*($1), &errorMsg, *scope);
- if(v == nullptr) {
- std::cerr << "ERROR: " << errorMsg << " at " << @1 << ".\n";
- YYERROR;
- }
- $$ = new ConstantExpression(*(v->constExpr()), $1->string());
- }
+
+ $$ = new ReferenceConstantExpression(
+ Reference<LocalIdentifier>(*$1, convertYYLoc(@1)), $1->string());
}
| const_expr '?' const_expr ':' const_expr
{
- $$ = new ConstantExpression($1, $3, $5);
+ $$ = new TernaryConstantExpression($1, $3, $5);
}
- | const_expr LOGICAL_OR const_expr { $$ = new ConstantExpression($1, "||", $3); }
- | const_expr LOGICAL_AND const_expr { $$ = new ConstantExpression($1, "&&", $3); }
- | const_expr '|' const_expr { $$ = new ConstantExpression($1, "|" , $3); }
- | const_expr '^' const_expr { $$ = new ConstantExpression($1, "^" , $3); }
- | const_expr '&' const_expr { $$ = new ConstantExpression($1, "&" , $3); }
- | const_expr EQUALITY const_expr { $$ = new ConstantExpression($1, "==", $3); }
- | const_expr NEQ const_expr { $$ = new ConstantExpression($1, "!=", $3); }
- | const_expr '<' const_expr { $$ = new ConstantExpression($1, "<" , $3); }
- | const_expr '>' const_expr { $$ = new ConstantExpression($1, ">" , $3); }
- | const_expr LEQ const_expr { $$ = new ConstantExpression($1, "<=", $3); }
- | const_expr GEQ const_expr { $$ = new ConstantExpression($1, ">=", $3); }
- | const_expr LSHIFT const_expr { $$ = new ConstantExpression($1, "<<", $3); }
- | const_expr RSHIFT const_expr { $$ = new ConstantExpression($1, ">>", $3); }
- | const_expr '+' const_expr { $$ = new ConstantExpression($1, "+" , $3); }
- | const_expr '-' const_expr { $$ = new ConstantExpression($1, "-" , $3); }
- | const_expr '*' const_expr { $$ = new ConstantExpression($1, "*" , $3); }
- | const_expr '/' const_expr { $$ = new ConstantExpression($1, "/" , $3); }
- | const_expr '%' const_expr { $$ = new ConstantExpression($1, "%" , $3); }
- | '+' const_expr %prec UNARY_PLUS { $$ = new ConstantExpression("+", $2); }
- | '-' const_expr %prec UNARY_MINUS { $$ = new ConstantExpression("-", $2); }
- | '!' const_expr { $$ = new ConstantExpression("!", $2); }
- | '~' const_expr { $$ = new ConstantExpression("~", $2); }
+ | const_expr LOGICAL_OR const_expr { $$ = new BinaryConstantExpression($1, "||", $3); }
+ | const_expr LOGICAL_AND const_expr { $$ = new BinaryConstantExpression($1, "&&", $3); }
+ | const_expr '|' const_expr { $$ = new BinaryConstantExpression($1, "|" , $3); }
+ | const_expr '^' const_expr { $$ = new BinaryConstantExpression($1, "^" , $3); }
+ | const_expr '&' const_expr { $$ = new BinaryConstantExpression($1, "&" , $3); }
+ | const_expr EQUALITY const_expr { $$ = new BinaryConstantExpression($1, "==", $3); }
+ | const_expr NEQ const_expr { $$ = new BinaryConstantExpression($1, "!=", $3); }
+ | const_expr '<' const_expr { $$ = new BinaryConstantExpression($1, "<" , $3); }
+ | const_expr '>' const_expr { $$ = new BinaryConstantExpression($1, ">" , $3); }
+ | const_expr LEQ const_expr { $$ = new BinaryConstantExpression($1, "<=", $3); }
+ | const_expr GEQ const_expr { $$ = new BinaryConstantExpression($1, ">=", $3); }
+ | const_expr LSHIFT const_expr { $$ = new BinaryConstantExpression($1, "<<", $3); }
+ | const_expr RSHIFT const_expr { $$ = new BinaryConstantExpression($1, ">>", $3); }
+ | const_expr '+' const_expr { $$ = new BinaryConstantExpression($1, "+" , $3); }
+ | const_expr '-' const_expr { $$ = new BinaryConstantExpression($1, "-" , $3); }
+ | const_expr '*' const_expr { $$ = new BinaryConstantExpression($1, "*" , $3); }
+ | const_expr '/' const_expr { $$ = new BinaryConstantExpression($1, "/" , $3); }
+ | const_expr '%' const_expr { $$ = new BinaryConstantExpression($1, "%" , $3); }
+ | '+' const_expr %prec UNARY_PLUS { $$ = new UnaryConstantExpression("+", $2); }
+ | '-' const_expr %prec UNARY_MINUS { $$ = new UnaryConstantExpression("-", $2); }
+ | '!' const_expr { $$ = new UnaryConstantExpression("!", $2); }
+ | '~' const_expr { $$ = new UnaryConstantExpression("~", $2); }
| '(' const_expr ')' { $$ = $2; }
| '(' error ')'
{
ast->addSyntaxError();
// to avoid segfaults
- $$ = new ConstantExpression(ConstantExpression::Zero(ScalarType::KIND_INT32));
+ $$ = ConstantExpression::Zero(ScalarType::KIND_INT32).release();
}
;
+commentable_method_declaration
+ : doc_comments method_declaration
+ {
+ if ($2 != nullptr) $2->setDocComment($1);
+ $$ = $2;
+ }
+ | method_declaration
+ {
+ $$ = $1;
+ }
+
method_declaration
: error_stmt { $$ = nullptr; }
| opt_annotations valid_identifier '(' typed_vars ')' require_semicolon
{
- $$ = new Method($2, $4, new std::vector<TypedVar *>, false, $1);
+ $$ = new Method($2 /* name */,
+ $4 /* args */,
+ new std::vector<NamedReference<Type>*> /* results */,
+ false /* oneway */,
+ $1 /* annotations */,
+ convertYYLoc(@$));
}
| opt_annotations ONEWAY valid_identifier '(' typed_vars ')' require_semicolon
{
- $$ = new Method($3, $5, new std::vector<TypedVar *>, true, $1);
+ $$ = new Method($3 /* name */,
+ $5 /* args */,
+ new std::vector<NamedReference<Type>*> /* results */,
+ true /* oneway */,
+ $1 /* annotations */,
+ convertYYLoc(@$));
}
| opt_annotations valid_identifier '(' typed_vars ')' GENERATES '(' typed_vars ')' require_semicolon
{
- $$ = new Method($2, $4, $8, false, $1);
+ if ($8->empty()) {
+ std::cerr << "ERROR: generates clause used without result at " << @1 << "\n";
+ ast->addSyntaxError();
+ }
+
+ $$ = new Method($2 /* name */,
+ $4 /* args */,
+ $8 /* results */,
+ false /* oneway */,
+ $1 /* annotations */,
+ convertYYLoc(@$));
}
;
@@ -818,7 +854,22 @@
}
;
-typed_var : type valid_identifier { $$ = new TypedVar($2, $1); }
+typed_var
+ : type valid_identifier
+ {
+ $$ = new NamedReference<Type>($2, *$1, convertYYLoc(@2));
+ }
+ | type
+ {
+ $$ = new NamedReference<Type>("", *$1, convertYYLoc(@1));
+
+ const std::string typeName = $$->isResolved()
+ ? $$->get()->typeName() : $$->getLookupFqName().string();
+
+ std::cerr << "ERROR: variable of type " << typeName
+ << " is missing a variable name at " << @1 << "\n";
+ ast->addSyntaxError();
+ }
;
@@ -830,31 +881,18 @@
named_struct_or_union_declaration
: struct_or_union_keyword valid_type_name
{
- CompoundType *container = new CompoundType($1, $2, convertYYLoc(@2), *scope);
+ CompoundType *container = new CompoundType(
+ $1, $2, ast->makeFullName($2, *scope), convertYYLoc(@2), *scope);
enterScope(ast, scope, container);
}
struct_or_union_body
{
- if (!(*scope)->isCompoundType()) {
- std::cerr << "ERROR: unknown error in struct or union declaration at "
- << @4 << "\n";
- YYERROR;
- }
+ CHECK((*scope)->isCompoundType());
CompoundType *container = static_cast<CompoundType *>(*scope);
-
- std::string errorMsg;
- if (!container->setFields($4, &errorMsg)) {
- std::cerr << "ERROR: " << errorMsg << " at " << @4 << "\n";
- YYERROR;
- }
+ container->setFields($4);
leaveScope(ast, scope);
-
- if (!ast->addScopedType(container, &errorMsg, *scope)) {
- std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n";
- YYERROR;
- }
-
+ ast->addScopedType(container, *scope);
$$ = container;
}
;
@@ -864,51 +902,63 @@
;
field_declarations
- : /* empty */ { $$ = new std::vector<CompoundField *>; }
- | field_declarations field_declaration
+ : /* empty */ { $$ = new std::vector<NamedReference<Type>*>; }
+ | field_declarations commentable_field_declaration
{
$$ = $1;
- if ($2 != NULL) {
+ // Compound declaration or error
+ if ($2 != nullptr) {
$$->push_back($2);
}
}
;
+commentable_field_declaration
+ : doc_comments field_declaration
+ {
+ if ($2 != nullptr) $2->setDocComment($1);
+ $$ = $2;
+ }
+ | field_declaration { $$ = $1; }
+
field_declaration
: error_stmt { $$ = nullptr; }
- | type valid_identifier require_semicolon
+ | type_or_inplace_compound_declaration valid_identifier require_semicolon
{
- std::string errorMsg;
- if ((*scope)->isCompoundType() &&
- static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
- !isValidStructField($2, &errorMsg)) {
- std::cerr << "ERROR: " << errorMsg << " at "
- << @2 << "\n";
- YYERROR;
- }
- $$ = new CompoundField($2, $1);
+ CHECK((*scope)->isCompoundType());
+
+ std::string errorMsg;
+ if (static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
+ !isValidStructField($2, &errorMsg)) {
+ std::cerr << "ERROR: " << errorMsg << " at "
+ << @2 << "\n";
+ YYERROR;
+ }
+ $$ = new NamedReference<Type>($2, *$1, convertYYLoc(@2));
}
| annotated_compound_declaration ';'
{
- std::string errorMsg;
- if ((*scope)->isCompoundType() &&
- static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
- $1 != nullptr &&
- $1->isNamedType() &&
- !isValidStructField(static_cast<NamedType *>($1)->localName().c_str(), &errorMsg)) {
- std::cerr << "ERROR: " << errorMsg << " at "
- << @2 << "\n";
- YYERROR;
- }
- $$ = NULL;
+ CHECK((*scope)->isCompoundType());
+
+ std::string errorMsg;
+ if (static_cast<CompoundType *>(*scope)->style() == CompoundType::STYLE_STRUCT &&
+ $1 != nullptr && $1->isNamedType() &&
+ !isValidStructField(static_cast<NamedType*>($1)->localName().c_str(), &errorMsg)) {
+ std::cerr << "ERROR: " << errorMsg << " at "
+ << @2 << "\n";
+ YYERROR;
+ }
+ // Returns fields only
+ $$ = nullptr;
}
;
annotated_compound_declaration
: opt_annotations compound_declaration
{
- $2->setAnnotations($1);
+ CHECK($2->isScope());
+ static_cast<Scope*>($2)->setAnnotations($1);
$$ = $2;
}
;
@@ -919,19 +969,8 @@
;
enum_storage_type
- : ':' fqtype
- {
- $$ = $2;
-
- if ($$ != NULL && !$$->isValidEnumStorageType()) {
- std::cerr << "ERROR: Invalid enum storage type ("
- << $2->typeName()
- << ") specified. at "
- << @2 << "\n";
-
- YYERROR;
- }
- }
+ : ':' fqtype { $$ = $2; }
+ | /* empty */ { $$ = nullptr; }
;
opt_comma
@@ -942,25 +981,27 @@
named_enum_declaration
: ENUM valid_type_name enum_storage_type
{
- enterScope(ast, scope, new EnumType($2, convertYYLoc(@2), $3, *scope));
+ auto storageType = $3;
+
+ if (storageType == nullptr) {
+ std::cerr << "ERROR: Must explicitly specify enum storage type for "
+ << $2 << " at " << @2 << "\n";
+ ast->addSyntaxError();
+ storageType = new Reference<Type>(
+ new ScalarType(ScalarType::KIND_INT64, *scope), convertYYLoc(@2));
+ }
+
+ EnumType* enumType = new EnumType(
+ $2, ast->makeFullName($2, *scope), convertYYLoc(@2), *storageType, *scope);
+ enterScope(ast, scope, enumType);
}
enum_declaration_body
{
- if (!(*scope)->isEnum()) {
- std::cerr << "ERROR: unknown error in enum declaration at "
- << @5 << "\n";
- YYERROR;
- }
+ CHECK((*scope)->isEnum());
+ EnumType* enumType = static_cast<EnumType*>(*scope);
- EnumType *enumType = static_cast<EnumType *>(*scope);
leaveScope(ast, scope);
-
- std::string errorMsg;
- if (!ast->addScopedType(enumType, &errorMsg, *scope)) {
- std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n";
- YYERROR;
- }
-
+ ast->addScopedType(enumType, *scope);
$$ = enumType;
}
;
@@ -969,83 +1010,74 @@
: '{' enum_values opt_comma '}' { $$ = $2; }
;
+commentable_enum_value
+ : doc_comments enum_value
+ {
+ $2->setDocComment($1);
+ $$ = $2;
+ }
+ | enum_value { $$ = $1; }
+ ;
+
enum_value
- : valid_identifier { $$ = new EnumValue($1); }
- | valid_identifier '=' const_expr { $$ = new EnumValue($1, $3); }
+ : valid_identifier
+ {
+ $$ = new EnumValue($1 /* name */, nullptr /* value */, convertYYLoc(@$));
+ }
+ | valid_identifier '=' const_expr
+ {
+ $$ = new EnumValue($1 /* name */, $3 /* value */, convertYYLoc(@$));
+ }
;
enum_values
: /* empty */
{ /* do nothing */ }
- | enum_value
+ | commentable_enum_value
{
- if (!(*scope)->isEnum()) {
- std::cerr << "ERROR: unknown error in enum declaration at "
- << @1 << "\n";
- YYERROR;
- }
-
+ CHECK((*scope)->isEnum());
static_cast<EnumType *>(*scope)->addValue($1);
}
- | enum_values ',' enum_value
+ | enum_values ',' commentable_enum_value
{
- if (!(*scope)->isEnum()) {
- std::cerr << "ERROR: unknown error in enum declaration at "
- << @3 << "\n";
- YYERROR;
- }
-
+ CHECK((*scope)->isEnum());
static_cast<EnumType *>(*scope)->addValue($3);
}
+ | error ',' commentable_enum_value
+ {
+ ast->addSyntaxError();
+
+ CHECK((*scope)->isEnum());
+ static_cast<EnumType *>(*scope)->addValue($3);
+ }
+ | enum_values ',' error ',' commentable_enum_value
+ {
+ ast->addSyntaxError();
+
+ CHECK((*scope)->isEnum());
+ static_cast<EnumType *>(*scope)->addValue($5);
+ }
;
array_type_base
: fqtype { $$ = $1; }
| TEMPLATED '<' type '>'
{
- if (!$1->isCompatibleElementType($3)) {
- std::cerr << "ERROR: " << $1->typeName() << " of " << $3->typeName()
- << " is not supported. at " << @3 << "\n";
-
- YYERROR;
- }
- $1->setElementType($3);
- $$ = $1;
+ $1->setElementType(*$3);
+ $$ = new Reference<Type>($1, convertYYLoc(@1));
}
| TEMPLATED '<' TEMPLATED '<' type RSHIFT
{
- if (!$3->isCompatibleElementType($5)) {
- std::cerr << "ERROR: " << $3->typeName() << " of " << $5->typeName()
- << " is not supported. at " << @3 << "\n";
-
- YYERROR;
- }
- $3->setElementType($5);
- if (!$1->isCompatibleElementType($3)) {
- std::cerr << "ERROR: " << $1->typeName() << " of " << $3->typeName()
- << " is not supported. at " << @3 << "\n";
-
- YYERROR;
- }
- $1->setElementType($3);
- $$ = $1;
+ $3->setElementType(*$5);
+ $1->setElementType(Reference<Type>($3, convertYYLoc(@3)));
+ $$ = new Reference<Type>($1, convertYYLoc(@1));
}
;
array_type
: array_type_base '[' const_expr ']'
{
- if ($1->isBinder()) {
- std::cerr << "ERROR: Arrays of interface types are not supported."
- << " at " << @1 << "\n";
-
- YYERROR;
- }
- if ($1->isArray()) {
- $$ = new ArrayType(static_cast<ArrayType *>($1), $3);
- } else {
- $$ = new ArrayType($1, $3);
- }
+ $$ = new ArrayType(*$1, $3, *scope);
}
| array_type '[' const_expr ']'
{
@@ -1056,26 +1088,24 @@
type
: array_type_base { $$ = $1; }
- | array_type { $$ = $1; }
- | annotated_compound_declaration { $$ = $1; }
+ | array_type { $$ = new Reference<Type>($1, convertYYLoc(@1)); }
| INTERFACE
{
// "interface" is a synonym of android.hidl.base@1.0::IBase
- $$ = ast->lookupType(gIBaseFqName, *scope);
- if ($$ == nullptr) {
- std::cerr << "ERROR: Cannot find "
- << gIBaseFqName.string()
- << " at " << @1 << "\n";
-
- YYERROR;
+ $$ = new Reference<Type>(gIBaseFqName, convertYYLoc(@1));
}
- }
+ ;
+
+type_or_inplace_compound_declaration
+ : type { $$ = $1; }
+ | annotated_compound_declaration
+ {
+ $$ = new Reference<Type>($1, convertYYLoc(@1));
+ }
;
%%
-#include <android-base/logging.h>
-
void yy::parser::error(
const yy::parser::location_type &where,
const std::string &errstr) {
diff --git a/include_hash/hidl-hash/Hash.h b/include_hash/hidl-hash/Hash.h
index 71a96e8..e443ef5 100644
--- a/include_hash/hidl-hash/Hash.h
+++ b/include_hash/hidl-hash/Hash.h
@@ -23,15 +23,18 @@
namespace android {
struct Hash {
+ static const std::vector<uint8_t> kEmptyHash;
+
// path to .hal file
static const Hash &getHash(const std::string &path);
+ static void clearHash(const std::string& path);
// returns matching hashes of interfaceName in path
// path is something like hardware/interfaces/current.txt
// interfaceName is something like android.hardware.foo@1.0::IFoo
- static std::vector<std::string> lookupHash(const std::string &path,
- const std::string &interfaceName,
- std::string *err);
+ static std::vector<std::string> lookupHash(const std::string& path,
+ const std::string& interfaceName, std::string* err,
+ bool* fileExists = nullptr);
static std::string hexString(const std::vector<uint8_t> &hash);
std::string hexString() const;
@@ -42,8 +45,10 @@
private:
Hash(const std::string &path);
+ static Hash& getMutableHash(const std::string& path);
+
const std::string mPath;
- const std::vector<uint8_t> mHash;
+ std::vector<uint8_t> mHash;
};
} // namespace android
diff --git a/main.cpp b/main.cpp
index 1b43e1e..dabbdaa 100644
--- a/main.cpp
+++ b/main.cpp
@@ -18,58 +18,277 @@
#include "Coordinator.h"
#include "Scope.h"
-#include <hidl-hash/Hash.h>
-#include <hidl-util/Formatter.h>
-#include <hidl-util/FQName.h>
-#include <hidl-util/StringHelper.h>
#include <android-base/logging.h>
-#include <set>
+#include <hidl-hash/Hash.h>
+#include <hidl-util/FQName.h>
+#include <hidl-util/Formatter.h>
+#include <hidl-util/StringHelper.h>
#include <stdio.h>
-#include <string>
+#include <sys/stat.h>
#include <unistd.h>
+#include <iostream>
+#include <set>
+#include <string>
#include <vector>
using namespace android;
-struct OutputHandler {
- std::string mKey;
- std::string mDescription;
- enum OutputMode {
- NEEDS_DIR,
- NEEDS_FILE,
- NEEDS_SRC, // for changes inside the source tree itself
- NOT_NEEDED
- } mOutputMode;
-
- const std::string& name() { return mKey; }
- const std::string& description() { return mDescription; }
-
- using ValidationFunction = std::function<bool(const FQName &, const std::string &language)>;
- using GenerationFunction = std::function<status_t(const FQName &fqName,
- const char *hidl_gen,
- Coordinator *coordinator,
- const std::string &outputDir)>;
-
- ValidationFunction validate;
- GenerationFunction generate;
+enum class OutputMode {
+ NEEDS_DIR, // -o output option expects a directory
+ NEEDS_FILE, // -o output option expects a file
+ NEEDS_SRC, // for changes inside the source tree itself
+ NOT_NEEDED // does not create files
};
-static bool generateForTest = false;
+enum class GenerationGranularity {
+ PER_PACKAGE, // Files generated for each package
+ PER_FILE, // Files generated for each hal file
+ PER_TYPE, // Files generated for each hal file + each type in HAL files
+};
-static status_t generateSourcesForFile(
- const FQName &fqName,
- const char *,
- Coordinator *coordinator,
- const std::string &outputDir,
- const std::string &lang) {
+// Represents a file that is generated by an -L option for an FQName
+struct FileGenerator {
+ using ShouldGenerateFunction = std::function<bool(const FQName& fqName)>;
+ using FileNameForFQName = std::function<std::string(const FQName& fqName)>;
+ using GenerationFunction = std::function<status_t(Formatter& out, const FQName& fqName,
+ const Coordinator* coordinator)>;
+
+ ShouldGenerateFunction mShouldGenerateForFqName; // If generate function applies to this target
+ FileNameForFQName mFileNameForFqName; // Target -> filename
+ GenerationFunction mGenerationFunction; // Function to generate output for file
+
+ std::string getFileName(const FQName& fqName) const {
+ return mFileNameForFqName ? mFileNameForFqName(fqName) : "";
+ }
+
+ status_t getOutputFile(const FQName& fqName, const Coordinator* coordinator,
+ Coordinator::Location location, std::string* file) const {
+ if (!mShouldGenerateForFqName(fqName)) {
+ return OK;
+ }
+
+ return coordinator->getFilepath(fqName, location, getFileName(fqName), file);
+ }
+
+ status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
+ Coordinator::Location location,
+ std::vector<std::string>* outputFiles) const {
+ if (location == Coordinator::Location::STANDARD_OUT) {
+ return OK;
+ }
+
+ if (mShouldGenerateForFqName(fqName)) {
+ std::string fileName;
+ status_t err = getOutputFile(fqName, coordinator, location, &fileName);
+ if (err != OK) return err;
+
+ if (!fileName.empty()) {
+ outputFiles->push_back(fileName);
+ }
+ }
+ return OK;
+ }
+
+ status_t generate(const FQName& fqName, const Coordinator* coordinator,
+ Coordinator::Location location) const {
+ CHECK(mShouldGenerateForFqName != nullptr);
+ CHECK(mGenerationFunction != nullptr);
+
+ if (!mShouldGenerateForFqName(fqName)) {
+ return OK;
+ }
+
+ Formatter out = coordinator->getFormatter(fqName, location, getFileName(fqName));
+ if (!out.isValid()) {
+ return UNKNOWN_ERROR;
+ }
+
+ return mGenerationFunction(out, fqName, coordinator);
+ }
+
+ // Helper methods for filling out this struct
+ static bool generateForTypes(const FQName& fqName) {
+ const auto names = fqName.names();
+ return names.size() > 0 && names[0] == "types";
+ }
+ static bool generateForInterfaces(const FQName& fqName) { return !generateForTypes(fqName); }
+ static bool alwaysGenerate(const FQName&) { return true; }
+};
+
+// Represents a -L option, takes a fqName and generates files
+struct OutputHandler {
+ using ValidationFunction = std::function<bool(
+ const FQName& fqName, const Coordinator* coordinator, const std::string& language)>;
+
+ std::string mKey; // -L in Android.bp
+ std::string mDescription; // for display in help menu
+ OutputMode mOutputMode; // how this option interacts with -o
+ Coordinator::Location mLocation; // how to compute location relative to the output directory
+ GenerationGranularity mGenerationGranularity; // what to run generate function on
+ ValidationFunction mValidate; // if a given fqName is allowed for this option
+ std::vector<FileGenerator> mGenerateFunctions; // run for each target at this granularity
+
+ const std::string& name() const { return mKey; }
+ const std::string& description() const { return mDescription; }
+
+ status_t generate(const FQName& fqName, const Coordinator* coordinator) const;
+ status_t validate(const FQName& fqName, const Coordinator* coordinator,
+ const std::string& language) const {
+ return mValidate(fqName, coordinator, language);
+ }
+
+ status_t writeDepFile(const FQName& fqName, const Coordinator* coordinator) const;
+
+ private:
+ status_t appendTargets(const FQName& fqName, const Coordinator* coordinator,
+ std::vector<FQName>* targets) const;
+ status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
+ std::vector<std::string>* outputFiles) const;
+};
+
+// Helper method for GenerationGranularity::PER_TYPE
+// IFoo -> IFoo, types.hal (containing Bar, Baz) -> types.Bar, types.Baz
+static status_t appendPerTypeTargets(const FQName& fqName, const Coordinator* coordinator,
+ std::vector<FQName>* exportedPackageInterfaces) {
CHECK(fqName.isFullyQualified());
+ if (fqName.name() != "types") {
+ exportedPackageInterfaces->push_back(fqName);
+ return OK;
+ }
- AST *ast;
+ AST* typesAST = coordinator->parse(fqName);
+ if (typesAST == nullptr) {
+ fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
+ return UNKNOWN_ERROR;
+ }
+
+ std::vector<NamedType*> rootTypes = typesAST->getRootScope()->getSubTypes();
+ for (const NamedType* rootType : rootTypes) {
+ if (rootType->isTypeDef()) continue;
+
+ FQName rootTypeName(fqName.package(), fqName.version(), "types." + rootType->localName());
+ exportedPackageInterfaces->push_back(rootTypeName);
+ }
+ return OK;
+}
+
+status_t OutputHandler::appendTargets(const FQName& fqName, const Coordinator* coordinator,
+ std::vector<FQName>* targets) const {
+ switch (mGenerationGranularity) {
+ case GenerationGranularity::PER_PACKAGE: {
+ targets->push_back(fqName.getPackageAndVersion());
+ } break;
+ case GenerationGranularity::PER_FILE: {
+ if (fqName.isFullyQualified()) {
+ targets->push_back(fqName);
+ break;
+ }
+ status_t err = coordinator->appendPackageInterfacesToVector(fqName, targets);
+ if (err != OK) return err;
+ } break;
+ case GenerationGranularity::PER_TYPE: {
+ if (fqName.isFullyQualified()) {
+ status_t err = appendPerTypeTargets(fqName, coordinator, targets);
+ if (err != OK) return err;
+ }
+
+ std::vector<FQName> packageInterfaces;
+ status_t err = coordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces);
+ if (err != OK) return err;
+ for (const FQName& packageInterface : packageInterfaces) {
+ err = appendPerTypeTargets(packageInterface, coordinator, targets);
+ if (err != OK) return err;
+ }
+ } break;
+ default:
+ CHECK(!"Should be here");
+ }
+
+ return OK;
+}
+
+status_t OutputHandler::generate(const FQName& fqName, const Coordinator* coordinator) const {
+ std::vector<FQName> targets;
+ status_t err = appendTargets(fqName, coordinator, &targets);
+ if (err != OK) return err;
+
+ for (const FQName& fqName : targets) {
+ for (const FileGenerator& file : mGenerateFunctions) {
+ status_t err = file.generate(fqName, coordinator, mLocation);
+ if (err != OK) return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t OutputHandler::appendOutputFiles(const FQName& fqName, const Coordinator* coordinator,
+ std::vector<std::string>* outputFiles) const {
+ std::vector<FQName> targets;
+ status_t err = appendTargets(fqName, coordinator, &targets);
+ if (err != OK) return err;
+
+ for (const FQName& fqName : targets) {
+ for (const FileGenerator& file : mGenerateFunctions) {
+ err = file.appendOutputFiles(fqName, coordinator, mLocation, outputFiles);
+ if (err != OK) return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t OutputHandler::writeDepFile(const FQName& fqName, const Coordinator* coordinator) const {
+ std::vector<std::string> outputFiles;
+ status_t err = appendOutputFiles(fqName, coordinator, &outputFiles);
+ if (err != OK) return err;
+
+ // No need for dep files
+ if (outputFiles.empty()) {
+ return OK;
+ }
+
+ // Depfiles in Android for genrules should be for the 'main file'. Because hidl-gen doesn't have
+ // a main file for most targets, we are just outputting a depfile for one single file only.
+ const std::string forFile = outputFiles[0];
+
+ return coordinator->writeDepFile(forFile);
+}
+
+// Use an AST function as a OutputHandler GenerationFunction
+static FileGenerator::GenerationFunction astGenerationFunction(void (AST::*generate)(Formatter&)
+ const = nullptr) {
+ return [generate](Formatter& out, const FQName& fqName,
+ const Coordinator* coordinator) -> status_t {
+ AST* ast = coordinator->parse(fqName);
+ if (ast == nullptr) {
+ fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
+ return UNKNOWN_ERROR;
+ }
+
+ if (generate == nullptr) return OK; // just parsing AST
+ (ast->*generate)(out);
+
+ return OK;
+ };
+}
+
+// Common pattern: single file for package or standard out
+static FileGenerator singleFileGenerator(
+ const std::string& fileName, const FileGenerator::GenerationFunction& generationFunction) {
+ return {
+ FileGenerator::alwaysGenerate, [fileName](const FQName&) { return fileName; },
+ generationFunction,
+ };
+}
+
+static status_t generateJavaForPackage(Formatter& out, const FQName& fqName,
+ const Coordinator* coordinator) {
+ AST* ast;
std::string limitToType;
+ // Required for legacy -Lmakefile files
if (fqName.name().find("types.") == 0) {
- CHECK(lang == "java"); // Already verified in validate().
-
limitToType = fqName.name().substr(strlen("types."));
FQName typesName = fqName.getTypesForPackage();
@@ -77,265 +296,49 @@
} else {
ast = coordinator->parse(fqName);
}
-
- if (ast == NULL) {
- fprintf(stderr,
- "ERROR: Could not parse %s. Aborting.\n",
- fqName.string().c_str());
-
+ if (ast == nullptr) {
+ fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
return UNKNOWN_ERROR;
}
+ ast->generateJava(out, limitToType);
+ return OK;
+};
- if (lang == "check") {
- return OK; // only parsing, not generating
- }
- if (lang == "c++") {
- return ast->generateCpp(outputDir);
- }
- if (lang == "c++-headers") {
- return ast->generateCppHeaders(outputDir);
- }
- if (lang == "c++-sources") {
- return ast->generateCppSources(outputDir);
- }
- if (lang == "c++-impl") {
- return ast->generateCppImpl(outputDir);
- }
- if (lang == "c++-impl-headers") {
- return ast->generateStubImplHeader(outputDir);
- }
- if (lang == "c++-impl-sources") {
- return ast->generateStubImplSource(outputDir);
- }
- if (lang == "java") {
- return ast->generateJava(outputDir, limitToType);
- }
- if (lang == "vts") {
- return ast->generateVts(outputDir);
- }
- // Unknown language.
- return UNKNOWN_ERROR;
-}
-
-static status_t generateSourcesForPackage(
- const FQName &packageFQName,
- const char *hidl_gen,
- Coordinator *coordinator,
- const std::string &outputDir,
- const std::string &lang) {
- CHECK(packageFQName.isValid() &&
- !packageFQName.isFullyQualified() &&
- packageFQName.name().empty());
-
+static status_t dumpDefinedButUnreferencedTypeNames(const FQName& packageFQName,
+ const Coordinator* coordinator) {
std::vector<FQName> packageInterfaces;
+ status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces);
+ if (err != OK) return err;
- status_t err =
- coordinator->appendPackageInterfacesToVector(packageFQName,
- &packageInterfaces);
+ std::set<FQName> unreferencedDefinitions;
+ std::set<FQName> unreferencedImports;
+ err = coordinator->addUnreferencedTypes(packageInterfaces, &unreferencedDefinitions,
+ &unreferencedImports);
+ if (err != OK) return err;
- if (err != OK) {
- return err;
+ for (const auto& fqName : unreferencedDefinitions) {
+ std::cerr
+ << "VERBOSE: DEFINED-BUT-NOT-REFERENCED "
+ << fqName.string()
+ << std::endl;
}
- for (const auto &fqName : packageInterfaces) {
- err = generateSourcesForFile(
- fqName, hidl_gen, coordinator, outputDir, lang);
- if (err != OK) {
- return err;
- }
+ for (const auto& fqName : unreferencedImports) {
+ std::cerr
+ << "VERBOSE: IMPORTED-BUT-NOT-REFERENCED "
+ << fqName.string()
+ << std::endl;
}
return OK;
}
-OutputHandler::GenerationFunction generationFunctionForFileOrPackage(const std::string &language) {
- return [language](const FQName &fqName,
- const char *hidl_gen, Coordinator *coordinator,
- const std::string &outputDir) -> status_t {
- if (fqName.isFullyQualified()) {
- return generateSourcesForFile(fqName,
- hidl_gen,
- coordinator,
- outputDir,
- language);
- } else {
- return generateSourcesForPackage(fqName,
- hidl_gen,
- coordinator,
- outputDir,
- language);
- }
- };
-}
-
static std::string makeLibraryName(const FQName &packageFQName) {
return packageFQName.string();
}
-static std::string makeJavaLibraryName(const FQName &packageFQName) {
- std::string out;
- out = packageFQName.package();
- out += "-V";
- out += packageFQName.version();
- return out;
-}
-
-static void generatePackagePathsSection(
- Formatter &out,
- Coordinator *coordinator,
- const FQName &packageFQName,
- const std::set<FQName> &importedPackages,
- bool forMakefiles = false) {
- std::set<std::string> options{};
- for (const auto &interface : importedPackages) {
- options.insert(coordinator->getPackageRootOption(interface));
- }
- options.insert(coordinator->getPackageRootOption(packageFQName));
- options.insert(coordinator->getPackageRootOption(gIBaseFqName));
- for (const auto &option : options) {
- out << "-r"
- << option
- << " ";
- if (forMakefiles) {
- out << "\\\n";
- }
- }
-}
-
-static void generateMakefileSectionForType(
- Formatter &out,
- Coordinator *coordinator,
- const FQName &packageFQName,
- const FQName &fqName,
- const std::set<FQName> &importedPackages,
- const char *typeName) {
- out << "\n"
- << "\n#"
- << "\n# Build " << fqName.name() << ".hal";
-
- if (typeName != nullptr) {
- out << " (" << typeName << ")";
- }
-
- out << "\n#"
- << "\nGEN := $(intermediates)/"
- << coordinator->convertPackageRootToPath(packageFQName)
- << coordinator->getPackagePath(packageFQName, true /* relative */,
- true /* sanitized */);
- if (typeName == nullptr) {
- out << fqName.name() << ".java";
- } else {
- out << typeName << ".java";
- }
-
- out << "\n$(GEN): $(HIDL)";
- out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
- out << "\n$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/"
- << fqName.name() << ".hal";
-
- {
- AST *ast = coordinator->parse(fqName);
- CHECK(ast != nullptr);
- const std::set<FQName>& refs = ast->getImportedNames();
- for (auto depFQName : refs) {
- // If the package of depFQName is the same as this fqName's package,
- // then add it explicitly as a .hal dependency within the same
- // package.
- if (fqName.package() == depFQName.package() &&
- fqName.version() == depFQName.version()) {
- // PRIVATE_DEPS is not actually being used in the
- // auto-generated file, but is necessary if the build rule
- // ever needs to use the dependency information, since the
- // built-in Make variables are not supported in the Android
- // build system.
- out << "\n$(GEN): PRIVATE_DEPS += $(LOCAL_PATH)/"
- << depFQName.name() << ".hal";
- // This is the actual dependency.
- out << "\n$(GEN): $(LOCAL_PATH)/"
- << depFQName.name() << ".hal";
- }
- }
- }
-
- out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
- << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
- out.indent();
- out.indent();
- out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
- << "\n-Ljava \\\n";
-
- generatePackagePathsSection(out, coordinator, packageFQName, importedPackages, true /* forJava */);
-
- out << packageFQName.string()
- << "::"
- << fqName.name();
-
- if (typeName != nullptr) {
- out << "." << typeName;
- }
-
- out << "\n";
-
- out.unindent();
- out.unindent();
-
- out << "\n$(GEN): $(LOCAL_PATH)/" << fqName.name() << ".hal";
- out << "\n\t$(transform-generated-source)";
- out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
-}
-
-static void generateMakefileSection(
- Formatter &out,
- Coordinator *coordinator,
- const FQName &packageFQName,
- const std::vector<FQName> &packageInterfaces,
- const std::set<FQName> &importedPackages,
- AST *typesAST) {
- for (const auto &fqName : packageInterfaces) {
- if (fqName.name() == "types") {
- CHECK(typesAST != nullptr);
-
- Scope* rootScope = typesAST->getRootScope();
-
- std::vector<NamedType *> subTypes = rootScope->getSubTypes();
- std::sort(
- subTypes.begin(),
- subTypes.end(),
- [](const NamedType *a, const NamedType *b) -> bool {
- return a->fqName() < b->fqName();
- });
-
- for (const auto &type : subTypes) {
- if (type->isTypeDef()) {
- continue;
- }
-
- generateMakefileSectionForType(
- out,
- coordinator,
- packageFQName,
- fqName,
- importedPackages,
- type->localName().c_str());
- }
-
- continue;
- }
-
- generateMakefileSectionForType(
- out,
- coordinator,
- packageFQName,
- fqName,
- importedPackages,
- nullptr /* typeName */);
- }
-}
-
-static status_t isPackageJavaCompatible(
- const FQName &packageFQName,
- Coordinator *coordinator,
- bool *compatible) {
+static status_t isPackageJavaCompatible(const FQName& packageFQName, const Coordinator* coordinator,
+ bool* compatible) {
std::vector<FQName> todo;
status_t err =
coordinator->appendPackageInterfacesToVector(packageFQName, &todo);
@@ -395,6 +398,10 @@
static bool packageNeedsJavaCode(
const std::vector<FQName> &packageInterfaces, AST *typesAST) {
+ if (packageInterfaces.size() == 0) {
+ return false;
+ }
+
// If there is more than just a types.hal file to this package we'll
// definitely need to generate Java code.
if (packageInterfaces.size() > 1
@@ -419,219 +426,8 @@
return false;
}
-static void generateMakefileSectionForJavaConstants(
- Formatter &out,
- Coordinator *coordinator,
- const FQName &packageFQName,
- const std::vector<FQName> &packageInterfaces,
- const std::set<FQName> &importedPackages) {
- out << "\n#"
- << "\nGEN := $(intermediates)/"
- << coordinator->convertPackageRootToPath(packageFQName)
- << coordinator->getPackagePath(packageFQName, true /* relative */, true /* sanitized */)
- << "Constants.java";
-
- out << "\n$(GEN): $(HIDL)\n";
- for (const auto &iface : packageInterfaces) {
- out << "$(GEN): $(LOCAL_PATH)/" << iface.name() << ".hal\n";
- }
- out << "\n$(GEN): PRIVATE_HIDL := $(HIDL)";
- out << "\n$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)"
- << "\n$(GEN): PRIVATE_CUSTOM_TOOL = \\";
- out.indent();
- out.indent();
- out << "\n$(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \\"
- << "\n-Ljava-constants \\\n";
-
- generatePackagePathsSection(out, coordinator, packageFQName, importedPackages, true /* forJava */);
-
- out << packageFQName.string();
- out << "\n";
-
- out.unindent();
- out.unindent();
-
- out << "\n$(GEN):";
- out << "\n\t$(transform-generated-source)";
- out << "\nLOCAL_GENERATED_SOURCES += $(GEN)";
-}
-
-static status_t generateMakefileForPackage(
- const FQName &packageFQName,
- const char *hidl_gen,
- Coordinator *coordinator,
- const std::string &outputPath) {
-
- CHECK(packageFQName.isValid() &&
- !packageFQName.isFullyQualified() &&
- packageFQName.name().empty());
-
- std::vector<FQName> packageInterfaces;
-
- status_t err =
- coordinator->appendPackageInterfacesToVector(packageFQName,
- &packageInterfaces);
-
- if (err != OK) {
- return err;
- }
-
- std::set<FQName> importedPackages;
- AST *typesAST = nullptr;
- std::vector<const Type *> exportedTypes;
-
- for (const auto &fqName : packageInterfaces) {
- AST *ast = coordinator->parse(fqName);
-
- if (ast == NULL) {
- fprintf(stderr,
- "ERROR: Could not parse %s. Aborting.\n",
- fqName.string().c_str());
-
- return UNKNOWN_ERROR;
- }
-
- if (fqName.name() == "types") {
- typesAST = ast;
- }
-
- ast->getImportedPackagesHierarchy(&importedPackages);
- ast->appendToExportedTypesVector(&exportedTypes);
- }
-
- bool packageIsJavaCompatible;
- err = isPackageJavaCompatible(
- packageFQName, coordinator, &packageIsJavaCompatible);
-
- if (err != OK) {
- return err;
- }
-
- bool haveJavaConstants = !exportedTypes.empty();
-
- if (!packageIsJavaCompatible && !haveJavaConstants) {
- // TODO(b/33420795)
- fprintf(stderr,
- "WARNING: %s is not java compatible. No java makefile created.\n",
- packageFQName.string().c_str());
- return OK;
- }
-
- if (!packageNeedsJavaCode(packageInterfaces, typesAST)) {
- return OK;
- }
-
- std::string path = outputPath;
- path.append(coordinator->getPackagePath(packageFQName, false /* relative */));
- path.append("Android.mk");
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- const std::string libraryName = makeJavaLibraryName(packageFQName);
-
- Formatter out(file);
-
- out << "# This file is autogenerated by hidl-gen. Do not edit manually.\n\n";
- out << "LOCAL_PATH := $(call my-dir)\n";
-
- enum LibraryStyle {
- LIBRARY_STYLE_REGULAR,
- LIBRARY_STYLE_STATIC,
- LIBRARY_STYLE_END,
- };
-
- for (int style = LIBRARY_STYLE_REGULAR;
- (packageIsJavaCompatible && style != LIBRARY_STYLE_END);
- ++style) {
- const std::string staticSuffix =
- (style == LIBRARY_STYLE_STATIC) ? "-static" : "";
-
- out << "\n"
- << "########################################"
- << "########################################\n\n";
-
- out << "include $(CLEAR_VARS)\n"
- << "LOCAL_MODULE := "
- << libraryName
- << "-java"
- << staticSuffix
- << "\nLOCAL_MODULE_CLASS := JAVA_LIBRARIES\n\n"
- << "intermediates := $(call local-generated-sources-dir, COMMON)"
- << "\n\n"
- << "HIDL := $(HOST_OUT_EXECUTABLES)/"
- << hidl_gen
- << "$(HOST_EXECUTABLE_SUFFIX)";
-
- if (!importedPackages.empty()) {
- out << "\n"
- << "\nLOCAL_"
- << ((style == LIBRARY_STYLE_STATIC) ? "STATIC_" : "")
- << "JAVA_LIBRARIES := \\";
-
- out.indent();
- for (const auto &importedPackage : importedPackages) {
- out << "\n"
- << makeJavaLibraryName(importedPackage)
- << "-java"
- << staticSuffix
- << " \\";
- }
- out << "\n";
- out.unindent();
- }
-
- generateMakefileSection(
- out,
- coordinator,
- packageFQName,
- packageInterfaces,
- importedPackages,
- typesAST);
-
- out << "\ninclude $(BUILD_"
- << ((style == LIBRARY_STYLE_STATIC) ? "STATIC_" : "")
- << "JAVA_LIBRARY)\n\n";
- }
-
- if (haveJavaConstants) {
- out << "\n"
- << "########################################"
- << "########################################\n\n";
-
- out << "include $(CLEAR_VARS)\n"
- << "LOCAL_MODULE := "
- << libraryName
- << "-java-constants"
- << "\nLOCAL_MODULE_CLASS := JAVA_LIBRARIES\n\n"
- << "intermediates := $(call local-generated-sources-dir, COMMON)"
- << "\n\n"
- << "HIDL := $(HOST_OUT_EXECUTABLES)/"
- << hidl_gen
- << "$(HOST_EXECUTABLE_SUFFIX)";
-
- generateMakefileSectionForJavaConstants(
- out, coordinator, packageFQName, packageInterfaces, importedPackages);
-
- out << "\n# Avoid dependency cycle of framework.jar -> this-library "
- << "-> framework.jar\n"
- << "LOCAL_NO_STANDARD_LIBRARIES := true\n"
- << "LOCAL_JAVA_LIBRARIES := core-oj\n\n"
- << "include $(BUILD_STATIC_JAVA_LIBRARY)\n\n";
- }
-
- out << "\n\n"
- << "include $(call all-makefiles-under,$(LOCAL_PATH))\n";
-
- return OK;
-}
-
-bool validateIsPackage(
- const FQName &fqName, const std::string & /* language */) {
+bool validateIsPackage(const FQName& fqName, const Coordinator*,
+ const std::string& /* language */) {
if (fqName.package().empty()) {
fprintf(stderr, "ERROR: Expecting package name\n");
return false;
@@ -652,17 +448,19 @@
}
bool isHidlTransportPackage(const FQName& fqName) {
- return fqName.package() == gIBasePackageFqName.string() ||
- fqName.package() == gIManagerPackageFqName.string();
+ return fqName.package() == gIBaseFqName.package() ||
+ fqName.package() == gIManagerFqName.package();
}
bool isSystemProcessSupportedPackage(const FQName& fqName) {
// Technically, so is hidl IBase + IServiceManager, but
// these are part of libhidltransport.
- return fqName.string() == "android.hardware.graphics.allocator@2.0" ||
- fqName.string() == "android.hardware.graphics.common@1.0" ||
+ return fqName.string() == "android.hardware.graphics.common@1.0" ||
+ fqName.string() == "android.hardware.graphics.common@1.1" ||
fqName.string() == "android.hardware.graphics.mapper@2.0" ||
+ fqName.string() == "android.hardware.graphics.mapper@2.1" ||
fqName.string() == "android.hardware.renderscript@1.0" ||
+ fqName.string() == "android.hidl.memory.token@1.0" ||
fqName.string() == "android.hidl.memory@1.0";
}
@@ -673,155 +471,89 @@
package.inPackage("android.hardware");
}
-static void generateAndroidBpGenSection(
- Formatter &out,
- const FQName &packageFQName,
- const char *hidl_gen,
- Coordinator *coordinator,
- const std::string &halFilegroupName,
- const std::string &genName,
- const char *language,
- const std::vector<FQName> &packageInterfaces,
- const std::set<FQName> &importedPackages,
- const std::function<void(Formatter&, const FQName)> outputFn) {
+// TODO(b/69862859): remove special case
+status_t isTestPackage(const FQName& fqName, const Coordinator* coordinator, bool* isTestPackage) {
+ const auto fileExists = [](const std::string& file) {
+ struct stat buf;
+ return stat(file.c_str(), &buf) == 0;
+ };
- out << "genrule {\n";
- out.indent();
- out << "name: \"" << genName << "\",\n"
- << "tools: [\"" << hidl_gen << "\"],\n";
+ std::string path;
+ status_t err = coordinator->getFilepath(fqName, Coordinator::Location::PACKAGE_ROOT,
+ ".hidl_for_test", &path);
+ if (err != OK) return err;
- out << "cmd: \"$(location " << hidl_gen << ") -o $(genDir)"
- << " -L" << language << " ";
+ const bool exists = fileExists(path);
- generatePackagePathsSection(out, coordinator, packageFQName, importedPackages);
-
- out << packageFQName.string() << "\",\n";
-
- out << "srcs: [\n";
- out.indent();
- out << "\":" << halFilegroupName << "\",\n";
- out.unindent();
- out << "],\n";
-
- out << "out: [\n";
- out.indent();
- for (const auto &fqName : packageInterfaces) {
- outputFn(out, fqName);
+ if (exists) {
+ coordinator->onFileAccess(path, "r");
}
- out.unindent();
- out << "],\n";
- out.unindent();
- out << "}\n\n";
+ *isTestPackage = exists;
+ return OK;
}
-static void generateAndroidBpDependencyList(
- Formatter &out,
- const std::set<FQName> &importedPackagesHierarchy,
- bool generateVendor) {
- for (const auto &importedPackage : importedPackagesHierarchy) {
- if (isHidlTransportPackage(importedPackage)) {
+static status_t generateAdapterMainSource(Formatter& out, const FQName& packageFQName,
+ const Coordinator* coordinator) {
+ std::vector<FQName> packageInterfaces;
+ status_t err =
+ coordinator->appendPackageInterfacesToVector(packageFQName,
+ &packageInterfaces);
+ if (err != OK) {
+ return err;
+ }
+
+ out << "#include <hidladapter/HidlBinderAdapter.h>\n";
+
+ for (auto &interface : packageInterfaces) {
+ if (interface.name() == "types") {
continue;
}
-
- out << "\"" << makeLibraryName(importedPackage);
- if (generateVendor && !isSystemPackage(importedPackage)) {
- out << "_vendor";
- }
- out << "\",\n";
+ AST::generateCppPackageInclude(out, interface, interface.getInterfaceAdapterName());
}
+
+ out << "int main(int argc, char** argv) ";
+ out.block([&] {
+ out << "return ::android::hardware::adapterMain<\n";
+ out.indent();
+ for (auto &interface : packageInterfaces) {
+ if (interface.name() == "types") {
+ continue;
+ }
+ out << interface.getInterfaceAdapterFqName().cppName();
+
+ if (&interface != &packageInterfaces.back()) {
+ out << ",\n";
+ }
+ }
+ out << ">(\"" << packageFQName.string() << "\", argc, argv);\n";
+ out.unindent();
+ }).endl();
+ return OK;
}
-static void generateAndroidBpLibSection(
- Formatter &out,
- bool generateVendor,
- const FQName &packageFQName,
- const std::string &libraryName,
- const std::string &genSourceName,
- const std::string &genHeaderName,
- const std::set<FQName> &importedPackagesHierarchy) {
-
- // C++ library definition
- out << "cc_library {\n";
- out.indent();
- out << "name: \"" << libraryName << (generateVendor ? "_vendor" : "") << "\",\n"
- << "defaults: [\"hidl-module-defaults\"],\n"
- << "generated_sources: [\"" << genSourceName << "\"],\n"
- << "generated_headers: [\"" << genHeaderName << "\"],\n"
- << "export_generated_headers: [\"" << genHeaderName << "\"],\n";
-
- if (generateVendor) {
- out << "vendor: true,\n";
- } else {
- out << "vendor_available: true,\n";
- if (!generateForTest) {
- out << "vndk: ";
- out.block([&]() {
- out << "enabled: true,\n";
- if (isSystemProcessSupportedPackage(packageFQName)) {
- out << "support_system_process: true,\n";
- }
- }) << ",\n";
- }
- }
- out << "shared_libs: [\n";
-
- out.indent();
- out << "\"libhidlbase\",\n"
- << "\"libhidltransport\",\n"
- << "\"libhwbinder\",\n"
- << "\"liblog\",\n"
- << "\"libutils\",\n"
- << "\"libcutils\",\n";
- generateAndroidBpDependencyList(out, importedPackagesHierarchy, generateVendor);
- out.unindent();
-
- out << "],\n";
-
- out << "export_shared_lib_headers: [\n";
- out.indent();
- out << "\"libhidlbase\",\n"
- << "\"libhidltransport\",\n"
- << "\"libhwbinder\",\n"
- << "\"libutils\",\n";
- generateAndroidBpDependencyList(out, importedPackagesHierarchy, generateVendor);
- out.unindent();
- out << "],\n";
- out.unindent();
-
- out << "}\n";
-}
-
-static status_t generateAndroidBpForPackage(
- const FQName &packageFQName,
- const char *hidl_gen,
- Coordinator *coordinator,
- const std::string &outputPath) {
-
- CHECK(packageFQName.isValid() &&
- !packageFQName.isFullyQualified() &&
+static status_t generateAndroidBpForPackage(Formatter& out, const FQName& packageFQName,
+ const Coordinator* coordinator) {
+ CHECK(packageFQName.isValid() && !packageFQName.isFullyQualified() &&
packageFQName.name().empty());
std::vector<FQName> packageInterfaces;
- status_t err =
- coordinator->appendPackageInterfacesToVector(packageFQName,
- &packageInterfaces);
+ status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces);
if (err != OK) {
return err;
}
std::set<FQName> importedPackagesHierarchy;
- AST *typesAST = nullptr;
+ std::vector<const Type *> exportedTypes;
+ AST* typesAST = nullptr;
- for (const auto &fqName : packageInterfaces) {
- AST *ast = coordinator->parse(fqName);
+ for (const auto& fqName : packageInterfaces) {
+ AST* ast = coordinator->parse(fqName);
if (ast == NULL) {
- fprintf(stderr,
- "ERROR: Could not parse %s. Aborting.\n",
- fqName.string().c_str());
+ fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
return UNKNOWN_ERROR;
}
@@ -831,134 +563,93 @@
}
ast->getImportedPackagesHierarchy(&importedPackagesHierarchy);
+ ast->appendToExportedTypesVector(&exportedTypes);
}
- std::string path = outputPath;
- path.append(coordinator->getPackagePath(packageFQName, false /* relative */));
- path.append("Android.bp");
+ bool needsJavaCode = packageNeedsJavaCode(packageInterfaces, typesAST);
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
+ bool genJavaConstants = needsJavaCode && !exportedTypes.empty();
- if (file == NULL) {
- return -errno;
- }
+ bool isJavaCompatible;
+ err = isPackageJavaCompatible(packageFQName, coordinator, &isJavaCompatible);
+ if (err != OK) return err;
+ bool genJavaLibrary = needsJavaCode && isJavaCompatible;
- const std::string libraryName = makeLibraryName(packageFQName);
- const std::string halFilegroupName = libraryName + "_hal";
- const std::string genSourceName = libraryName + "_genc++";
- const std::string genHeaderName = libraryName + "_genc++_headers";
- const std::string pathPrefix =
- coordinator->convertPackageRootToPath(packageFQName) +
- coordinator->getPackagePath(packageFQName, true /* relative */);
+ bool generateForTest;
+ err = isTestPackage(packageFQName, coordinator, &generateForTest);
+ if (err != OK) return err;
- Formatter out(file);
+ bool isVndk = !generateForTest && isSystemPackage(packageFQName);
+ bool isVndkSp = isVndk && isSystemProcessSupportedPackage(packageFQName);
- out << "// This file is autogenerated by hidl-gen. Do not edit manually.\n\n";
+ std::string packageRoot;
+ err = coordinator->getPackageRoot(packageFQName, &packageRoot);
+ if (err != OK) return err;
- // Rule to generate .hal filegroup
- out << "filegroup {\n";
- out.indent();
- out << "name: \"" << halFilegroupName << "\",\n";
- out << "srcs: [\n";
- out.indent();
- for (const auto &fqName : packageInterfaces) {
- out << "\"" << fqName.name() << ".hal\",\n";
- }
- out.unindent();
- out << "],\n";
- out.unindent();
- out << "}\n\n";
+ out << "// This file is autogenerated by hidl-gen -Landroidbp.\n\n";
- // Rule to generate the C++ source files
- generateAndroidBpGenSection(
- out,
- packageFQName,
- hidl_gen,
- coordinator,
- halFilegroupName,
- genSourceName,
- "c++-sources",
- packageInterfaces,
- importedPackagesHierarchy,
- [&pathPrefix](Formatter &out, const FQName &fqName) {
- if (fqName.name() == "types") {
- out << "\"" << pathPrefix << "types.cpp\",\n";
- } else {
- out << "\"" << pathPrefix << fqName.name().substr(1) << "All.cpp\",\n";
- }
- });
-
- // Rule to generate the C++ header files
- generateAndroidBpGenSection(
- out,
- packageFQName,
- hidl_gen,
- coordinator,
- halFilegroupName,
- genHeaderName,
- "c++-headers",
- packageInterfaces,
- importedPackagesHierarchy,
- [&pathPrefix](Formatter &out, const FQName &fqName) {
- out << "\"" << pathPrefix << fqName.name() << ".h\",\n";
- if (fqName.name() != "types") {
- out << "\"" << pathPrefix << fqName.getInterfaceHwName() << ".h\",\n";
- out << "\"" << pathPrefix << fqName.getInterfaceStubName() << ".h\",\n";
- out << "\"" << pathPrefix << fqName.getInterfaceProxyName() << ".h\",\n";
- out << "\"" << pathPrefix << fqName.getInterfacePassthroughName() << ".h\",\n";
- } else {
- out << "\"" << pathPrefix << "hwtypes.h\",\n";
- }
- });
-
- if (isHidlTransportPackage(packageFQName)) {
- out << "// " << packageFQName.string() << " is exported from libhidltransport\n";
- } else {
- generateAndroidBpLibSection(
- out,
- false /* generateVendor */,
- packageFQName,
- libraryName,
- genSourceName,
- genHeaderName,
- importedPackagesHierarchy);
-
- // TODO(b/35813011): make all libraries vendor_available
- // Explicitly create '_vendor' copies of libraries so that
- // vendor code can link against the extensions. When this is
- // used, framework code should link against vendor.awesome.foo@1.0
- // and code on the vendor image should link against
- // vendor.awesome.foo@1.0_vendor. For libraries with the below extensions,
- // they will be available even on the generic system image.
- // Because of this, they should always be referenced without the
- // '_vendor' name suffix.
- if (!isSystemPackage(packageFQName)) {
-
- // Note, not using cc_defaults here since it's already not used and
- // because generating this libraries will be removed when the VNDK
- // is enabled (done by the build system itself).
- out.endl();
- generateAndroidBpLibSection(
- out,
- true /* generateVendor */,
- packageFQName,
- libraryName,
- genSourceName,
- genHeaderName,
- importedPackagesHierarchy);
+ out << "hidl_interface ";
+ out.block([&] {
+ out << "name: \"" << makeLibraryName(packageFQName) << "\",\n";
+ if (!coordinator->getOwner().empty()) {
+ out << "owner: \"" << coordinator->getOwner() << "\",\n";
}
- }
+ out << "root: \"" << packageRoot << "\",\n";
+ if (isHidlTransportPackage(packageFQName)) {
+ out << "core_interface: true,\n";
+ }
+ if (isVndk) {
+ out << "vndk: ";
+ out.block([&]() {
+ out << "enabled: true,\n";
+ if (isVndkSp) {
+ out << "support_system_process: true,\n";
+ }
+ }) << ",\n";
+ }
+ (out << "srcs: [\n").indent([&] {
+ for (const auto& fqName : packageInterfaces) {
+ out << "\"" << fqName.name() << ".hal\",\n";
+ }
+ }) << "],\n";
+ if (!importedPackagesHierarchy.empty()) {
+ (out << "interfaces: [\n").indent([&] {
+ for (const auto& fqName : importedPackagesHierarchy) {
+ out << "\"" << fqName.string() << "\",\n";
+ }
+ }) << "],\n";
+ }
+ if (typesAST != nullptr) {
+ (out << "types: [\n").indent([&] {
+ std::vector<NamedType *> subTypes = typesAST->getRootScope()->getSubTypes();
+ std::sort(
+ subTypes.begin(),
+ subTypes.end(),
+ [](const NamedType *a, const NamedType *b) -> bool {
+ return a->fqName() < b->fqName();
+ });
+
+ for (const auto &type : subTypes) {
+ if (type->isTypeDef()) {
+ continue;
+ }
+
+ out << "\"" << type->localName() << "\",\n";
+ }
+ }) << "],\n";
+ }
+ // Explicity call this out for developers.
+ out << "gen_java: " << (genJavaLibrary ? "true" : "false") << ",\n";
+ if (genJavaConstants) {
+ out << "gen_java_constants: true,\n";
+ }
+ }).endl().endl();
return OK;
}
-static status_t generateAndroidBpImplForPackage(
- const FQName &packageFQName,
- const char *,
- Coordinator *coordinator,
- const std::string &outputDir) {
-
+static status_t generateAndroidBpImplForPackage(Formatter& out, const FQName& packageFQName,
+ const Coordinator* coordinator) {
const std::string libraryName = makeLibraryName(packageFQName) + "-impl";
std::vector<FQName> packageInterfaces;
@@ -987,23 +678,28 @@
ast->getImportedPackages(&importedPackages);
}
- std::string path = outputDir + "Android.bp";
-
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == NULL) {
- return -errno;
- }
-
- Formatter out(file);
-
out << "cc_library_shared {\n";
out.indent([&] {
- out << "name: \"" << libraryName << "\",\n"
- << "relative_install_path: \"hw\",\n"
- << "proprietary: true,\n"
- << "srcs: [\n";
+ out << "// FIXME: this should only be -impl for a passthrough hal.\n"
+ << "// In most cases, to convert this to a binderized implementation, you should:\n"
+ << "// - change '-impl' to '-service' here and make it a cc_binary instead of a\n"
+ << "// cc_library_shared.\n"
+ << "// - add a *.rc file for this module.\n"
+ << "// - delete HIDL_FETCH_I* functions.\n"
+ << "// - call configureRpcThreadpool and registerAsService on the instance.\n"
+ << "// You may also want to append '-impl/-service' with a specific identifier like\n"
+ << "// '-vendor' or '-<hardware identifier>' etc to distinguish it.\n";
+ out << "name: \"" << libraryName << "\",\n";
+ if (!coordinator->getOwner().empty()) {
+ out << "owner: \"" << coordinator->getOwner() << "\",\n";
+ }
+ out << "relative_install_path: \"hw\",\n";
+ if (coordinator->getOwner().empty()) {
+ out << "// FIXME: this should be 'vendor: true' for modules that will eventually be\n"
+ "// on AOSP.\n";
+ }
+ out << "proprietary: true,\n";
+ out << "srcs: [\n";
out.indent([&] {
for (const auto &fqName : packageInterfaces) {
if (fqName.name() == "types") {
@@ -1035,8 +731,8 @@
return OK;
}
-bool validateForSource(
- const FQName &fqName, const std::string &language) {
+bool validateForSource(const FQName& fqName, const Coordinator* coordinator,
+ const std::string& language) {
if (fqName.package().empty()) {
fprintf(stderr, "ERROR: Expecting package name\n");
return false;
@@ -1066,14 +762,29 @@
return true;
}
+ if (language == "java") {
+ bool isJavaCompatible;
+ status_t err = isPackageJavaCompatible(fqName, coordinator, &isJavaCompatible);
+ if (err != OK) return false;
+
+ if (!isJavaCompatible) {
+ fprintf(stderr,
+ "ERROR: %s is not Java compatible. The Java backend"
+ " does NOT support union types nor native handles. "
+ "In addition, vectors of arrays are limited to at most "
+ "one-dimensional arrays and vectors of {vectors,interfaces} are"
+ " not supported.\n",
+ fqName.string().c_str());
+ return false;
+ }
+ }
+
return true;
}
-OutputHandler::GenerationFunction generateExportHeaderForPackage(bool forJava) {
- return [forJava](const FQName &packageFQName,
- const char * /* hidl_gen */,
- Coordinator *coordinator,
- const std::string &outputPath) -> status_t {
+FileGenerator::GenerationFunction generateExportHeaderForPackage(bool forJava) {
+ return [forJava](Formatter& out, const FQName& packageFQName,
+ const Coordinator* coordinator) -> status_t {
CHECK(packageFQName.isValid()
&& !packageFQName.package().empty()
&& !packageFQName.version().empty()
@@ -1108,29 +819,18 @@
return OK;
}
- std::string path = outputPath;
-
- if (forJava) {
- path.append(coordinator->convertPackageRootToPath(packageFQName));
-
- path.append(coordinator->getPackagePath(
- packageFQName, true /* relative */, true /* sanitized */));
-
- path.append("Constants.java");
+ if (!out.isValid()) {
+ return UNKNOWN_ERROR;
}
- CHECK(Coordinator::MakeParentHierarchy(path));
- FILE *file = fopen(path.c_str(), "w");
-
- if (file == nullptr) {
- return -errno;
- }
-
- Formatter out(file);
+ std::string packagePath;
+ err = coordinator->getPackagePath(packageFQName, false /* relative */,
+ false /* sanitized */, &packagePath);
+ if (err != OK) return err;
out << "// This file is autogenerated by hidl-gen. Do not edit manually.\n"
<< "// Source: " << packageFQName.string() << "\n"
- << "// Root: " << coordinator->getPackageRootOption(packageFQName) << "\n\n";
+ << "// Location: " << packagePath << "\n\n";
std::string guard;
if (forJava) {
@@ -1167,164 +867,336 @@
};
}
-static status_t generateHashOutput(const FQName &fqName,
- const char* /*hidl_gen*/,
- Coordinator *coordinator,
- const std::string & /*outputDir*/) {
+static status_t generateHashOutput(Formatter& out, const FQName& fqName,
+ const Coordinator* coordinator) {
+ CHECK(fqName.isFullyQualified());
- status_t err;
- std::vector<FQName> packageInterfaces;
+ AST* ast = coordinator->parse(fqName, {} /* parsed */,
+ Coordinator::Enforce::NO_HASH /* enforcement */);
- if (fqName.isFullyQualified()) {
- packageInterfaces = {fqName};
- } else {
- err = coordinator->appendPackageInterfacesToVector(
- fqName, &packageInterfaces);
- if (err != OK) {
- return err;
- }
+ if (ast == NULL) {
+ fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str());
+
+ return UNKNOWN_ERROR;
}
- for (const auto ¤tFqName : packageInterfaces) {
- AST* ast = coordinator->parse(currentFqName, {} /* parsed */,
- Coordinator::Enforce::NO_HASH /* enforcement */);
-
- if (ast == NULL) {
- fprintf(stderr,
- "ERROR: Could not parse %s. Aborting.\n",
- currentFqName.string().c_str());
-
- return UNKNOWN_ERROR;
- }
-
- printf("%s %s\n",
- Hash::getHash(ast->getFilename()).hexString().c_str(),
- currentFqName.string().c_str());
- }
+ out << Hash::getHash(ast->getFilename()).hexString() << " " << fqName.string() << "\n";
return OK;
}
-static std::vector<OutputHandler> formats = {
- {"check",
- "Parses the interface to see if valid but doesn't write any files.",
- OutputHandler::NOT_NEEDED /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("check")
- },
+template <typename T>
+std::vector<T> operator+(const std::vector<T>& lhs, const std::vector<T>& rhs) {
+ std::vector<T> ret;
+ ret.reserve(lhs.size() + rhs.size());
+ ret.insert(ret.begin(), lhs.begin(), lhs.end());
+ ret.insert(ret.end(), rhs.begin(), rhs.end());
+ return ret;
+}
- {"c++",
- "(internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("c++")
+// clang-format off
+static const std::vector<FileGenerator> kCppHeaderFormats = {
+ {
+ FileGenerator::alwaysGenerate,
+ [](const FQName& fqName) { return fqName.name() + ".h"; },
+ astGenerationFunction(&AST::generateInterfaceHeader),
},
-
- {"c++-headers",
- "(internal) Generates C++ headers for interface files for talking to HIDL interfaces.",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("c++-headers")
+ {
+ FileGenerator::alwaysGenerate,
+ [](const FQName& fqName) {
+ return fqName.isInterfaceName() ? fqName.getInterfaceHwName() + ".h" : "hwtypes.h";
+ },
+ astGenerationFunction(&AST::generateHwBinderHeader),
},
-
- {"c++-sources",
- "(internal) Generates C++ sources for interface files for talking to HIDL interfaces.",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("c++-sources")
+ {
+ FileGenerator::generateForInterfaces,
+ [](const FQName& fqName) { return fqName.getInterfaceStubName() + ".h"; },
+ astGenerationFunction(&AST::generateStubHeader),
},
-
- {"export-header",
- "Generates a header file from @export enumerations to help maintain legacy code.",
- OutputHandler::NEEDS_FILE /* mOutputMode */,
- validateIsPackage,
- generateExportHeaderForPackage(false /* forJava */)
+ {
+ FileGenerator::generateForInterfaces,
+ [](const FQName& fqName) { return fqName.getInterfaceProxyName() + ".h"; },
+ astGenerationFunction(&AST::generateProxyHeader),
},
-
- {"c++-impl",
- "Generates boilerplate implementation of a hidl interface in C++ (for convenience).",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("c++-impl")
- },
- {"c++-impl-headers",
- "c++-impl but headers only",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("c++-impl-headers")
- },
- {"c++-impl-sources",
- "c++-impl but sources only",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("c++-impl-sources")
- },
-
- {"java",
- "(internal) Generates Java library for talking to HIDL interfaces in Java.",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("java")
- },
-
- {"java-constants",
- "(internal) Like export-header but for Java (always created by -Lmakefile if @export exists).",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateIsPackage,
- generateExportHeaderForPackage(true /* forJava */)
- },
-
- {"vts",
- "(internal) Generates vts proto files for use in vtsd.",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateForSource,
- generationFunctionForFileOrPackage("vts")
- },
-
- {"makefile",
- "(internal) Generates makefiles for -Ljava and -Ljava-constants.",
- OutputHandler::NEEDS_SRC /* mOutputMode */,
- validateIsPackage,
- generateMakefileForPackage,
- },
-
- {"androidbp",
- "(internal) Generates Soong bp files for -Lc++-headers and -Lc++-sources.",
- OutputHandler::NEEDS_SRC /* mOutputMode */,
- validateIsPackage,
- generateAndroidBpForPackage,
- },
-
- {"androidbp-impl",
- "Generates boilerplate bp files for implementation created with -Lc++-impl.",
- OutputHandler::NEEDS_DIR /* mOutputMode */,
- validateIsPackage,
- generateAndroidBpImplForPackage,
- },
-
- {"hash",
- "Prints hashes of interface in `current.txt` format to standard out.",
- OutputHandler::NOT_NEEDED /* mOutputMode */,
- validateForSource,
- generateHashOutput,
+ {
+ FileGenerator::generateForInterfaces,
+ [](const FQName& fqName) { return fqName.getInterfacePassthroughName() + ".h"; },
+ astGenerationFunction(&AST::generatePassthroughHeader),
},
};
+static const std::vector<FileGenerator> kCppSourceFormats = {
+ {
+ FileGenerator::alwaysGenerate,
+ [](const FQName& fqName) {
+ return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + "All.cpp" : "types.cpp";
+ },
+ astGenerationFunction(&AST::generateCppSource),
+ },
+};
+
+static const std::vector<FileGenerator> kCppImplHeaderFormats = {
+ {
+ FileGenerator::generateForInterfaces,
+ [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".h"; },
+ astGenerationFunction(&AST::generateCppImplHeader),
+ },
+};
+
+static const std::vector<FileGenerator> kCppImplSourceFormats = {
+ {
+ FileGenerator::generateForInterfaces,
+ [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".cpp"; },
+ astGenerationFunction(&AST::generateCppImplSource),
+ },
+};
+
+static const std::vector<FileGenerator> kCppAdapterHeaderFormats = {
+ {
+ FileGenerator::alwaysGenerate,
+ [](const FQName& fqName) {
+ return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".h" : "Atypes.h";
+ },
+ astGenerationFunction(&AST::generateCppAdapterHeader),
+ },
+};
+
+static const std::vector<FileGenerator> kCppAdapterSourceFormats = {
+ {
+ FileGenerator::alwaysGenerate,
+ [](const FQName& fqName) {
+ return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".cpp" : "Atypes.cpp";
+ },
+ astGenerationFunction(&AST::generateCppAdapterSource),
+ },
+};
+
+static const std::vector<OutputHandler> kFormats = {
+ {
+ "check",
+ "Parses the interface to see if valid but doesn't write any files.",
+ OutputMode::NOT_NEEDED,
+ Coordinator::Location::STANDARD_OUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ {
+ {
+ FileGenerator::alwaysGenerate,
+ nullptr /* filename for fqname */,
+ astGenerationFunction(),
+ },
+ },
+ },
+ {
+ "c++",
+ "(internal) (deprecated) Generates C++ interface files for talking to HIDL interfaces.",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_OUTPUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppHeaderFormats + kCppSourceFormats,
+ },
+ {
+ "c++-headers",
+ "(internal) Generates C++ headers for interface files for talking to HIDL interfaces.",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_OUTPUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppHeaderFormats,
+ },
+ {
+ "c++-sources",
+ "(internal) Generates C++ sources for interface files for talking to HIDL interfaces.",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_OUTPUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppSourceFormats,
+ },
+ {
+ "export-header",
+ "Generates a header file from @export enumerations to help maintain legacy code.",
+ OutputMode::NEEDS_FILE,
+ Coordinator::Location::DIRECT,
+ GenerationGranularity::PER_PACKAGE,
+ validateIsPackage,
+ {singleFileGenerator("", generateExportHeaderForPackage(false /* forJava */))}
+ },
+ {
+ "c++-impl",
+ "Generates boilerplate implementation of a hidl interface in C++ (for convenience).",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::DIRECT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppImplHeaderFormats + kCppImplSourceFormats,
+ },
+ {
+ "c++-impl-headers",
+ "c++-impl but headers only",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::DIRECT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppImplHeaderFormats,
+ },
+ {
+ "c++-impl-sources",
+ "c++-impl but sources only",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::DIRECT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppImplSourceFormats,
+ },
+ {
+ "c++-adapter",
+ "Takes a x.(y+n) interface and mocks an x.y interface.",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_OUTPUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppAdapterHeaderFormats + kCppAdapterSourceFormats,
+ },
+ {
+ "c++-adapter-headers",
+ "c++-adapter but helper headers only",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_OUTPUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppAdapterHeaderFormats,
+ },
+ {
+ "c++-adapter-sources",
+ "c++-adapter but helper sources only",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_OUTPUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ kCppAdapterSourceFormats,
+ },
+ {
+ "c++-adapter-main",
+ "c++-adapter but the adapter binary source only",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::DIRECT,
+ GenerationGranularity::PER_PACKAGE,
+ validateIsPackage,
+ {singleFileGenerator("main.cpp", generateAdapterMainSource)},
+ },
+ {
+ "java",
+ "(internal) Generates Java library for talking to HIDL interfaces in Java.",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_SANITIZED,
+ GenerationGranularity::PER_TYPE,
+ validateForSource,
+ {
+ {
+ FileGenerator::alwaysGenerate,
+ [](const FQName& fqName) {
+ return StringHelper::LTrim(fqName.name(), "types.") + ".java";
+ },
+ generateJavaForPackage,
+ },
+ }
+ },
+ {
+ "java-constants",
+ "(internal) Like export-header but for Java (always created by -Lmakefile if @export exists).",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_SANITIZED,
+ GenerationGranularity::PER_PACKAGE,
+ validateIsPackage,
+ {singleFileGenerator("Constants.java", generateExportHeaderForPackage(true /* forJava */))}
+ },
+ {
+ "vts",
+ "(internal) Generates vts proto files for use in vtsd.",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::GEN_OUTPUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ {
+ {
+ FileGenerator::alwaysGenerate,
+ [](const FQName& fqName) {
+ return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + ".vts" : "types.vts";
+ },
+ astGenerationFunction(&AST::generateVts),
+ },
+ }
+ },
+ {
+ "makefile",
+ "(removed) Used to generate makefiles for -Ljava and -Ljava-constants.",
+ OutputMode::NEEDS_SRC,
+ Coordinator::Location::PACKAGE_ROOT,
+ GenerationGranularity::PER_PACKAGE,
+ [](const FQName &, const Coordinator*, const std::string &) {
+ fprintf(stderr, "ERROR: makefile output is not supported. Use -Landroidbp for all build file generation.\n");
+ return false;
+ },
+ {},
+ },
+ {
+ "androidbp",
+ "(internal) Generates Soong bp files for -Lc++-headers, -Lc++-sources, -Ljava, -Ljava-constants, and -Lc++-adapter.",
+ OutputMode::NEEDS_SRC,
+ Coordinator::Location::PACKAGE_ROOT,
+ GenerationGranularity::PER_PACKAGE,
+ validateIsPackage,
+ {singleFileGenerator("Android.bp", generateAndroidBpForPackage)},
+ },
+ {
+ "androidbp-impl",
+ "Generates boilerplate bp files for implementation created with -Lc++-impl.",
+ OutputMode::NEEDS_DIR,
+ Coordinator::Location::DIRECT,
+ GenerationGranularity::PER_PACKAGE,
+ validateIsPackage,
+ {singleFileGenerator("Android.bp", generateAndroidBpImplForPackage)},
+ },
+ {
+ "hash",
+ "Prints hashes of interface in `current.txt` format to standard out.",
+ OutputMode::NOT_NEEDED,
+ Coordinator::Location::STANDARD_OUT,
+ GenerationGranularity::PER_FILE,
+ validateForSource,
+ {
+ {
+ FileGenerator::alwaysGenerate,
+ nullptr /* file name for fqName */,
+ generateHashOutput,
+ },
+ }
+ },
+};
+// clang-format on
+
static void usage(const char *me) {
fprintf(stderr,
- "usage: %s [-p <root path>] -o <output path> -L <language> (-r <interface root>)+ [-t] "
- "fqname+\n",
+ "usage: %s [-p <root path>] -o <output path> -L <language> [-O <owner>] (-r <interface "
+ "root>)+ [-v] [-d <depfile>] FQNAME...\n\n",
me);
+ fprintf(stderr,
+ "Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, to create output.\n\n");
+
fprintf(stderr, " -h: Prints this menu.\n");
fprintf(stderr, " -L <language>: The following options are available:\n");
- for (auto &e : formats) {
+ for (auto& e : kFormats) {
fprintf(stderr, " %-16s: %s\n", e.name().c_str(), e.description().c_str());
}
+ fprintf(stderr, " -O <owner>: The owner of the module for -Landroidbp(-impl)?.\n");
fprintf(stderr, " -o <output path>: Location to output files.\n");
fprintf(stderr, " -p <root path>: Android build root, defaults to $ANDROID_BUILD_TOP or pwd.\n");
fprintf(stderr, " -r <package:path root>: E.g., android.hardware:hardware/interfaces.\n");
- fprintf(stderr, " -t: generate build scripts (Android.bp) for tests.\n");
+ fprintf(stderr, " -v: verbose output.\n");
+ fprintf(stderr, " -d <depfile>: location of depfile to write to.\n");
}
// hidl is intentionally leaky. Turn off LeakSanitizer by default.
@@ -1333,36 +1205,57 @@
}
int main(int argc, char **argv) {
- std::string outputPath;
- std::string rootPath;
- std::vector<std::string> packageRootPaths;
- std::vector<std::string> packageRoots;
-
const char *me = argv[0];
- OutputHandler *outputFormat = nullptr;
-
if (argc == 1) {
usage(me);
exit(1);
}
+ const OutputHandler* outputFormat = nullptr;
+ Coordinator coordinator;
+ std::string outputPath;
+
int res;
- while ((res = getopt(argc, argv, "hp:o:r:L:t")) >= 0) {
+ while ((res = getopt(argc, argv, "hp:o:O:r:L:vd:")) >= 0) {
switch (res) {
- case 'p':
- {
- rootPath = optarg;
+ case 'p': {
+ if (!coordinator.getRootPath().empty()) {
+ fprintf(stderr, "ERROR: -p <root path> can only be specified once.\n");
+ exit(1);
+ }
+ coordinator.setRootPath(optarg);
break;
}
- case 'o':
- {
+ case 'v': {
+ coordinator.setVerbose(true);
+ break;
+ }
+
+ case 'd': {
+ coordinator.setDepFile(optarg);
+ break;
+ }
+
+ case 'o': {
+ if (!outputPath.empty()) {
+ fprintf(stderr, "ERROR: -o <output path> can only be specified once.\n");
+ exit(1);
+ }
outputPath = optarg;
break;
}
- case 'r':
- {
+ case 'O': {
+ if (!coordinator.getOwner().empty()) {
+ fprintf(stderr, "ERROR: -O <owner> can only be specified once.\n");
+ exit(1);
+ }
+ coordinator.setOwner(optarg);
+ break;
+ }
+
+ case 'r': {
std::string val(optarg);
auto index = val.find_first_of(':');
if (index == std::string::npos) {
@@ -1370,22 +1263,27 @@
exit(1);
}
- auto package = val.substr(0, index);
+ auto root = val.substr(0, index);
auto path = val.substr(index + 1);
- packageRootPaths.push_back(path);
- packageRoots.push_back(package);
+
+ std::string error;
+ status_t err = coordinator.addPackagePath(root, path, &error);
+ if (err != OK) {
+ fprintf(stderr, "%s\n", error.c_str());
+ exit(1);
+ }
+
break;
}
- case 'L':
- {
+ case 'L': {
if (outputFormat != nullptr) {
fprintf(stderr,
"ERROR: only one -L option allowed. \"%s\" already specified.\n",
outputFormat->name().c_str());
exit(1);
}
- for (auto &e : formats) {
+ for (auto& e : kFormats) {
if (e.name() == optarg) {
outputFormat = &e;
break;
@@ -1400,15 +1298,9 @@
break;
}
- case 't': {
- generateForTest = true;
- break;
- }
-
case '?':
case 'h':
- default:
- {
+ default: {
usage(me);
exit(1);
break;
@@ -1416,17 +1308,19 @@
}
}
+ if (coordinator.getRootPath().empty()) {
+ const char* ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP");
+ if (ANDROID_BUILD_TOP != nullptr) {
+ coordinator.setRootPath(ANDROID_BUILD_TOP);
+ }
+ }
+
if (outputFormat == nullptr) {
fprintf(stderr,
"ERROR: no -L option provided.\n");
exit(1);
}
- if (generateForTest && outputFormat->name() != "androidbp") {
- fprintf(stderr, "ERROR: -t option is for -Landroidbp only.\n");
- exit(1);
- }
-
argc -= optind;
argv += optind;
@@ -1436,43 +1330,29 @@
exit(1);
}
- if (rootPath.empty()) {
- const char *ANDROID_BUILD_TOP = getenv("ANDROID_BUILD_TOP");
-
- if (ANDROID_BUILD_TOP != nullptr) {
- rootPath = ANDROID_BUILD_TOP;
- }
-
- // else default to pwd
- }
-
- if (!rootPath.empty() && !StringHelper::EndsWith(rootPath, "/")) {
- rootPath += "/";
- }
-
// Valid options are now in argv[0] .. argv[argc - 1].
switch (outputFormat->mOutputMode) {
- case OutputHandler::NEEDS_DIR:
- case OutputHandler::NEEDS_FILE:
- {
+ case OutputMode::NEEDS_DIR:
+ case OutputMode::NEEDS_FILE: {
if (outputPath.empty()) {
usage(me);
exit(1);
}
- if (outputFormat->mOutputMode == OutputHandler::NEEDS_DIR) {
- const size_t len = outputPath.size();
- if (outputPath[len - 1] != '/') {
+ if (outputFormat->mOutputMode == OutputMode::NEEDS_DIR) {
+ if (outputPath.back() != '/') {
outputPath += "/";
}
}
break;
}
- case OutputHandler::NEEDS_SRC:
- {
+ case OutputMode::NEEDS_SRC: {
if (outputPath.empty()) {
- outputPath = rootPath;
+ outputPath = coordinator.getRootPath();
+ }
+ if (outputPath.back() != '/') {
+ outputPath += "/";
}
break;
@@ -1483,33 +1363,38 @@
break;
}
- Coordinator coordinator(packageRootPaths, packageRoots, rootPath);
+ coordinator.setOutputPath(outputPath);
+
coordinator.addDefaultPackagePath("android.hardware", "hardware/interfaces");
coordinator.addDefaultPackagePath("android.hidl", "system/libhidl/transport");
coordinator.addDefaultPackagePath("android.frameworks", "frameworks/hardware/interfaces");
coordinator.addDefaultPackagePath("android.system", "system/hardware/interfaces");
for (int i = 0; i < argc; ++i) {
- FQName fqName(argv[i]);
-
- if (!fqName.isValid()) {
- fprintf(stderr,
- "ERROR: Invalid fully-qualified name.\n");
+ FQName fqName;
+ if (!FQName::parse(argv[i], &fqName)) {
+ fprintf(stderr, "ERROR: Invalid fully-qualified name as argument: %s.\n", argv[i]);
exit(1);
}
- if (!outputFormat->validate(fqName, outputFormat->name())) {
+ // Dump extra verbose output
+ if (coordinator.isVerbose()) {
+ status_t err =
+ dumpDefinedButUnreferencedTypeNames(fqName.getPackageAndVersion(), &coordinator);
+ if (err != OK) return err;
+ }
+
+ if (!outputFormat->validate(fqName, &coordinator, outputFormat->name())) {
fprintf(stderr,
"ERROR: output handler failed.\n");
exit(1);
}
- status_t err =
- outputFormat->generate(fqName, me, &coordinator, outputPath);
+ status_t err = outputFormat->generate(fqName, &coordinator);
+ if (err != OK) exit(1);
- if (err != OK) {
- exit(1);
- }
+ err = outputFormat->writeDepFile(fqName, &coordinator);
+ if (err != OK) exit(1);
}
return 0;
diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh
new file mode 100755
index 0000000..bb6f7d7
--- /dev/null
+++ b/scripts/run-tests.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+# See hal_hidl_gtest.py
+
+THREADS=
+CHECKER=vts_testability_checker
+CHECKER_DEVICE_PATH="/data/local/tmp/${CHECKER}"
+PRINT_COMMANDS=
+
+function run() {
+ if [ "${PRINT_COMMANDS}" = true ] ; then
+ >&2 echo "*** $@"
+ fi
+ $@
+}
+
+function make_modules() {
+ if [ "${THREADS}" != "0" ] ; then
+ run make -j${THREADS} -C ${ANDROID_BUILD_TOP} -f build/core/main.mk $@
+ fi
+}
+
+function push_checker() {
+ run adb push ${OUT}/system/bin/${CHECKER} ${CHECKER_DEVICE_PATH}
+}
+
+function push_test() {
+ local module=$1
+ for test_dir in nativetest nativetest64 ; do
+ local test_file=/data/${test_dir}/${module}/${module}
+ run adb push ${OUT}${test_file} ${test_file}
+ done
+}
+
+function read_checker_output() {
+ python -c 'import json,sys;obj=json.load(sys.stdin);sys.stdout.write("%s\n"%obj["Testable"]);map(lambda i:sys.stdout.write("%s\n"%i),obj["instances"])'
+}
+
+function run_test() {
+ local module=$1
+ local status=0
+
+ for test_dir in nativetest nativetest64 ; do
+ local test_file=/data/${test_dir}/${module}/${module}
+ local interfaces=$(run adb shell ${test_file} --list_registered_services \
+ | sed -n 's/^hal_service: \(.*\)$/\1/p')
+ if [ -z "$interfaces" ]; then
+ run adb shell ${test_file} || status=$?
+ else
+ for interface in ${interfaces} ; do
+ local output=$(run adb shell ${CHECKER_DEVICE_PATH} -c ${interface} | read_checker_output)
+ local testable=$(echo "${output}" | head -n1)
+ local instances=$(echo "${output}" | tail -n+2)
+
+ if [ "${testable}" == "True" ] ; then
+ for instance in ${instances} ; do
+ run adb shell ${test_file} --hal_service_instance="${interface}/${instance}" || status=$?
+ done
+ fi
+ done
+ fi
+ done
+ return ${status}
+}
+
+function usage() {
+ echo "usage: $0 -m <module_name> [-m <module_name>[...]] [-j <jobs>] [-p]"
+ echo " -m <module_name>: name of test (e.g. VtsHalHealthV2_0TargetTest)"
+ echo " -p: print commands"
+ echo " -j <jobs>: # jobs in make. "
+ echo " -j0 skips making any modules."
+ echo " If not present, use infinite number of jobs."
+
+ exit 1
+}
+
+function main() {
+ local modules=
+
+ while getopts "m:j:p" option ; do
+ case "${option}" in
+ m)
+ [ ! -z ${OPTARG} ] || usage
+ modules="${modules} ${OPTARG}"
+ ;;
+ j)
+ THREADS=${OPTARG}
+ ;;
+ p)
+ PRINT_COMMANDS=true
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ done
+
+ set -e
+ make_modules ${CHECKER} ${modules}
+ run adb root
+ push_checker
+ for module in ${modules} ; do
+ push_test ${module}
+ done
+
+ set +e
+ local status=0
+ for module in ${modules} ; do
+ run_test ${module} || status=$?
+ done
+ return ${status}
+}
+
+main $@
diff --git a/test/Android.bp b/test/Android.bp
deleted file mode 100644
index 08aed77..0000000
--- a/test/Android.bp
+++ /dev/null
@@ -1,80 +0,0 @@
-subdirs = [
- "error_test",
- "hash_test",
- "impl_test",
- "java_test",
- "vendor/1.0",
- "vendor/1.1",
- "version_test",
-]
-
-cc_test {
- name: "hidl_test_client",
- cflags: hidl_flags,
- srcs: ["hidl_test_client.cpp", "FooCallback.cpp"],
-
- shared_libs: [
- "libbase",
- "liblog",
- "libcutils",
- "libhidlbase",
- "libhidltransport",
- "libhidlmemory",
- "libfootest",
- "libhwbinder",
- "libpointertest",
- "libutils",
- "android.hardware.tests.foo@1.0",
- "android.hardware.tests.bar@1.0",
- "android.hardware.tests.baz@1.0",
- "android.hardware.tests.hash@1.0",
- "android.hardware.tests.inheritance@1.0",
- "android.hardware.tests.pointer@1.0",
- "android.hardware.tests.memory@1.0",
- "android.hardware.tests.multithread@1.0",
- "android.hidl.allocator@1.0",
- "android.hidl.memory@1.0",
- "android.hidl.token@1.0",
- ],
-
- required: [
- // Warning, these should never be specified explicitly for anything else
- // as for a given device, what hal implementation is used is determined
- // by what is specified/available in a device.mk. However, this test
- // actually depends on these test implementations (which should never be
- // included on a device.mk).
- "android.hardware.tests.foo@1.0-impl",
- "android.hardware.tests.bar@1.0-impl",
- "android.hardware.tests.baz@1.0-impl",
- "android.hardware.tests.hash@1.0-impl",
- "android.hardware.tests.inheritance@1.0-impl",
- "android.hardware.tests.pointer@1.0-impl",
- "android.hardware.tests.memory@1.0-impl",
- "android.hardware.tests.multithread@1.0-impl",
- ],
-}
-
-cc_test {
- name: "hidl_test_servers",
- srcs: ["hidl_test_servers.cpp"],
- gtest: false,
-
- shared_libs: [
- "libbase",
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libutils",
- "android.hardware.tests.foo@1.0",
- "android.hardware.tests.bar@1.0",
- "android.hardware.tests.baz@1.0",
- "android.hardware.tests.hash@1.0",
- "android.hardware.tests.inheritance@1.0",
- "android.hardware.tests.pointer@1.0",
- "android.hardware.tests.memory@1.0",
- "android.hardware.tests.multithread@1.0",
- "android.hidl.memory@1.0",
- "android.hidl.token@1.0",
- ],
-}
diff --git a/test/AndroidTest.xml b/test/AndroidTest.xml
deleted file mode 100644
index e78aac5..0000000
--- a/test/AndroidTest.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Config for VTS HIDL unit tests">
- <option name="config-descriptor:metadata" key="plan" value="vts-library" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
- <option name="push-group" value="HostDrivenTest.push" />
- <option name="cleanup" value="true" />
- <option name="push" value="DATA/nativetest/hidl_test_servers/hidl_test_servers->/data/nativetest/hidl_test_servers/hidl_test_servers" />
- <option name="push" value="DATA/nativetest64/hidl_test_servers/hidl_test_servers->/data/nativetest64/hidl_test_servers/hidl_test_servers" />
- <option name="push" value="DATA/nativetest/hidl_test_client/hidl_test_client->/data/nativetest/hidl_test_client/hidl_test_client" />
- <option name="push" value="DATA/nativetest64/hidl_test_client/hidl_test_client->/data/nativetest64/hidl_test_client/hidl_test_client" />
- <option name="push" value="DATA/nativetest64/hidl_test_helper->/data/nativetest64/hidl_test_helper" />
- <option name="push" value="DATA/nativetest64/hidl_test->/data/nativetest64/hidl_test" />
- <option name="push" value="DATA/lib/libfootest.so->/data/local/tmp/system/lib/libfootest.so" />
- <option name="push" value="DATA/lib64/libfootest.so->/data/local/tmp/system/lib64/libfootest.so" />
- <option name="push" value="DATA/lib/libpointertest.so->/data/local/tmp/system/lib/libpointertest.so" />
- <option name="push" value="DATA/lib64/libpointertest.so->/data/local/tmp/system/lib64/libpointertest.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.foo@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.foo@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.foo@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.foo@1.0.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.bar@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.bar@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.bar@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.bar@1.0.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.baz@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.baz@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.baz@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.baz@1.0.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.hash@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.hash@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.hash@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.hash@1.0.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.inheritance@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.inheritance@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.inheritance@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.inheritance@1.0.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.pointer@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.pointer@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.pointer@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.pointer@1.0.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.memory@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.memory@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.memory@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.memory@1.0.so" />
- <option name="push" value="DATA/lib/android.hardware.tests.multithread@1.0.so->/data/local/tmp/system/lib/android.hardware.tests.multithread@1.0.so" />
- <option name="push" value="DATA/lib64/android.hardware.tests.multithread@1.0.so->/data/local/tmp/system/lib64/android.hardware.tests.multithread@1.0.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.foo@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.foo@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.foo@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.foo@1.0-impl.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.bar@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.bar@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.bar@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.bar@1.0-impl.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.baz@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.baz@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.baz@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.baz@1.0-impl.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.hash@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.hash@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.hash@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.hash@1.0-impl.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.inheritance@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.inheritance@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.inheritance@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.inheritance@1.0-impl.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.pointer@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.pointer@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.pointer@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.pointer@1.0-impl.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.memory@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.memory@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.memory@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.memory@1.0-impl.so" />
- <option name="push" value="DATA/lib/hw/android.hardware.tests.multithread@1.0-impl.so->/data/local/tmp/system/lib/hw/android.hardware.tests.multithread@1.0-impl.so" />
- <option name="push" value="DATA/lib64/hw/android.hardware.tests.multithread@1.0-impl.so->/data/local/tmp/system/lib64/hw/android.hardware.tests.multithread@1.0-impl.so" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer" />
- <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
- <option name="test-module-name" value="VtsHidlUnitTests" />
- <option name="binary-test-type" value="binary_test" />
- <option name="binary-test-source" value="DATA/nativetest64/hidl_test->/data/nativetest64/hidl_test" />
- <option name="binary-test-envp" value="VTS_ROOT_PATH=/data/local/tmp" />
- <option name="binary-test-args" value="-x /data/local/tmp/system/lib" />
- <option name="binary-test-args" value="-y /data/local/tmp/system/lib64" />
- </test>
-</configuration>
diff --git a/test/error_test/Android.bp b/test/error_test/Android.bp
index bd5c076..42345b2 100644
--- a/test/error_test/Android.bp
+++ b/test/error_test/Android.bp
@@ -1,51 +1,20 @@
genrule {
name: "hidl_error_test_gen",
- tools: [
- "hidl-gen",
- ],
- cmd: "!($(location hidl-gen) -L check " +
- " -r test.error:system/tools/hidl/test/error_test/interface_semicolon" +
- " test.error.error@1.0 >/dev/null 2>&1)" +
- "&&" +
- "!($(location hidl-gen) -L check " +
- " -r test.error:system/tools/hidl/test/error_test/method_ends_in_semicolon" +
- " test.error.error@1.0 >/dev/null 2>&1)" +
- "&&" +
- "!($(location hidl-gen) -L check " +
- " -r test.error:system/tools/hidl/test/error_test/no_data_outside_interface" +
- " test.error.error@1.0 >/dev/null 2>&1)" +
- "&&" +
- "!($(location hidl-gen) -L check " +
- " -r test.error:system/tools/hidl/test/error_test/no_interface_in_types" +
- " test.error.error@1.0 >/dev/null 2>&1)" +
- "&&" +
- "!($(location hidl-gen) -L check " +
- " -r test.error:system/tools/hidl/test/error_test/no_two_interfaces" +
- " test.error.error@1.0 >/dev/null 2>&1)" +
- "&&" +
- "!($(location hidl-gen) -L check " +
- " -r test.error:system/tools/hidl/test/error_test/same_name_interface" +
- " test.error.error@1.0 >/dev/null 2>&1)" +
- "&&" +
- "!($(location hidl-gen) -L check " +
- " -r test.error:system/tools/hidl/test/error_test/same_package_name" +
- " test.error.error@1.0 >/dev/null 2>&1)" +
- "&&" +
+ tools: ["hidl-gen"],
+ tool_files: ["hidl_error_test.sh"],
+ cmd: "$(location hidl_error_test.sh) $(location hidl-gen) &&" +
"echo 'int main(){return 0;}' > $(genDir)/TODO_b_37575883.cpp",
out: ["TODO_b_37575883.cpp"],
srcs: [
- "interface_semicolon/error/1.0/IFoo.hal",
- "method_ends_in_semicolon/error/1.0/IFoo.hal",
- "no_data_outside_interface/error/1.0/IFoo.hal",
- "no_interface_in_types/error/1.0/IFoo.hal",
- "no_interface_in_types/error/1.0/types.hal",
- "no_two_interfaces/error/1.0/IFoo.hal",
- "same_name_interface/error/1.0/IFoo.hal",
- "same_package_name/error/1.0/IFoo.hal",
+ "hidl_error_test.sh",
+
+ "**/*.hal",
+ "**/required_error",
],
}
cc_test_host {
name: "hidl_error_test",
+ cflags: ["-Wall", "-Werror"],
generated_sources: ["hidl_error_test_gen"],
}
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/bad_character/1.0/IFoo.hal
similarity index 93%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/bad_character/1.0/IFoo.hal
index d79e6ab..9164ee5 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/bad_character/1.0/IFoo.hal
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.bad_character@1.0;
+
+😄
interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/bad_character/1.0/required_error b/test/error_test/bad_character/1.0/required_error
new file mode 100644
index 0000000..ab3b3e7
--- /dev/null
+++ b/test/error_test/bad_character/1.0/required_error
@@ -0,0 +1 @@
+syntax error, unexpected unknown character
\ No newline at end of file
diff --git a/test/error_test/cpp_forward_decl_restriction/1.0/required_error b/test/error_test/cpp_forward_decl_restriction/1.0/required_error
new file mode 100644
index 0000000..0426995
--- /dev/null
+++ b/test/error_test/cpp_forward_decl_restriction/1.0/required_error
@@ -0,0 +1 @@
+C++ forward declaration doesn't support inner types
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/cpp_forward_decl_restriction/1.0/types.hal
similarity index 74%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/cpp_forward_decl_restriction/1.0/types.hal
index d79e6ab..17d2aa9 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/cpp_forward_decl_restriction/1.0/types.hal
@@ -13,7 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.cpp_forward_decl_restriction@1.0;
-interface IFoo {
-};
\ No newline at end of file
+struct A {
+ // Requires A.B.C to be forward declared in A,
+ // which is not supported in C++.
+ A.B.C foo;
+
+ struct B {
+ struct C {};
+ };
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/doc_comment_ends_block/1.0/IFoo.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/doc_comment_ends_block/1.0/IFoo.hal
index d79e6ab..26c768e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/doc_comment_ends_block/1.0/IFoo.hal
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.empty_generates@1.0;
interface IFoo {
-};
\ No newline at end of file
+ doFoo();
+ /**
+ * Does foo!
+ */
+};
diff --git a/test/error_test/doc_comment_ends_block/1.0/required_error b/test/error_test/doc_comment_ends_block/1.0/required_error
new file mode 100644
index 0000000..94c947a
--- /dev/null
+++ b/test/error_test/doc_comment_ends_block/1.0/required_error
@@ -0,0 +1 @@
+Doc comments must preceed what they describe at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/empty_generates/1.0/IFoo.hal
similarity index 91%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/empty_generates/1.0/IFoo.hal
index d79e6ab..1ad23e0 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/empty_generates/1.0/IFoo.hal
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.empty_generates@1.0;
interface IFoo {
-};
\ No newline at end of file
+ foo() generates ();
+};
diff --git a/test/error_test/empty_generates/1.0/required_error b/test/error_test/empty_generates/1.0/required_error
new file mode 100644
index 0000000..ce24040
--- /dev/null
+++ b/test/error_test/empty_generates/1.0/required_error
@@ -0,0 +1 @@
+generates clause used without result at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/enum_storage/1.0/IFoo.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/enum_storage/1.0/IFoo.hal
index d79e6ab..68f17c9 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/enum_storage/1.0/IFoo.hal
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.bad_character@1.0;
interface IFoo {
-};
\ No newline at end of file
+
+ enum A /* missing storage */ {
+ };
+
+};
+
diff --git a/test/error_test/enum_storage/1.0/required_error b/test/error_test/enum_storage/1.0/required_error
new file mode 100644
index 0000000..aeb0e84
--- /dev/null
+++ b/test/error_test/enum_storage/1.0/required_error
@@ -0,0 +1 @@
+Must explicitly specify enum storage type
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/enum_unique_field_names/1.0/IFoo.hal
similarity index 82%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/enum_unique_field_names/1.0/IFoo.hal
index d79e6ab..69818e8 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/enum_unique_field_names/1.0/IFoo.hal
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.enum_unique_field_names@1.0;
interface IFoo {
-};
\ No newline at end of file
+ enum E : int32_t {
+ A,
+ B,
+ A, // duplicates first value name
+ };
+};
diff --git a/test/error_test/enum_unique_field_names/1.0/required_error b/test/error_test/enum_unique_field_names/1.0/required_error
new file mode 100644
index 0000000..26d6385
--- /dev/null
+++ b/test/error_test/enum_unique_field_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of value 'A'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/enum_unique_field_names_extends/1.0/IFoo.hal
similarity index 76%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/enum_unique_field_names_extends/1.0/IFoo.hal
index d79e6ab..a2e4642 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/enum_unique_field_names_extends/1.0/IFoo.hal
@@ -13,7 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.enum_unique_field_names_extends@1.0;
interface IFoo {
-};
\ No newline at end of file
+ enum E1 : int32_t {
+ A,
+ B,
+ C,
+ };
+
+ enum E2 : E1 {
+ D,
+ B, // duplicates E1.B name
+ F,
+ };
+};
diff --git a/test/error_test/enum_unique_field_names_extends/1.0/required_error b/test/error_test/enum_unique_field_names_extends/1.0/required_error
new file mode 100644
index 0000000..dbb4cdb
--- /dev/null
+++ b/test/error_test/enum_unique_field_names_extends/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of value 'B' defined in enum
\ No newline at end of file
diff --git a/test/error_test/hidl_error_test.sh b/test/error_test/hidl_error_test.sh
new file mode 100755
index 0000000..2f5d522
--- /dev/null
+++ b/test/error_test/hidl_error_test.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+if [ $# -ne 1 ]; then
+ echo "usage: hidl_error_test.sh hidl-gen_path"
+ exit 1
+fi
+
+readonly HIDL_GEN_PATH=$1
+readonly HIDL_ERROR_TEST_DIR="$ANDROID_BUILD_TOP/system/tools/hidl/test/error_test"
+
+for dir in $(ls -d $HIDL_ERROR_TEST_DIR/*/); do
+ package=$(basename $dir)
+ output=$($HIDL_GEN_PATH -L check -r test:$HIDL_ERROR_TEST_DIR test.$package@1.0 2>&1)
+ command_fails=$?
+ error=$(cat $HIDL_ERROR_TEST_DIR/$package/1.0/required_error)
+
+ if [[ $error == "" ]]; then
+ echo "error: No required error message specified for $package."
+ echo "$output" | while read line; do echo "test output: $line"; done
+ exit 1
+ fi
+
+ if [ $command_fails -ne 1 ]; then
+ echo "error: $package test did not fail"
+ exit 1
+ fi
+
+ if [[ $output != *$error* ]]; then
+ echo "error: error output for $package does not contain '$error':"
+ echo "$output" | while read line; do echo "test output: $line"; done
+ exit 1
+ fi
+
+done
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_extends_only_interface/1.0/IFoo.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_extends_only_interface/1.0/IFoo.hal
index d79e6ab..6b18b73 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_extends_only_interface/1.0/IFoo.hal
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.interface_extends_only_interface@1.0;
-interface IFoo {
-};
\ No newline at end of file
+interface IFoo extends IStruct /* struct, not interface */ {
+};
diff --git a/test/error_test/interface_extends_only_interface/1.0/required_error b/test/error_test/interface_extends_only_interface/1.0/required_error
new file mode 100644
index 0000000..718544b
--- /dev/null
+++ b/test/error_test/interface_extends_only_interface/1.0/required_error
@@ -0,0 +1 @@
+You can only extend interfaces
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_extends_only_interface/1.0/types.hal
similarity index 89%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_extends_only_interface/1.0/types.hal
index d79e6ab..dffd41e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_extends_only_interface/1.0/types.hal
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.interface_extends_only_interface@1.0;
-interface IFoo {
-};
\ No newline at end of file
+struct IStruct {
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_no_using_reserved_method_names/1.0/IFoo.hal
similarity index 85%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_no_using_reserved_method_names/1.0/IFoo.hal
index d79e6ab..9b68217 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_no_using_reserved_method_names/1.0/IFoo.hal
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.interface_no_using_reserved_method_names@1.0;
interface IFoo {
-};
\ No newline at end of file
+ debug(); // duplicates reserved method name
+};
diff --git a/test/error_test/interface_no_using_reserved_method_names/1.0/required_error b/test/error_test/interface_no_using_reserved_method_names/1.0/required_error
new file mode 100644
index 0000000..ec1dc30
--- /dev/null
+++ b/test/error_test/interface_no_using_reserved_method_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of reserved method 'debug'
\ No newline at end of file
diff --git a/test/error_test/interface_semicolon/error/1.0/IFoo.hal b/test/error_test/interface_semicolon/1.0/IFoo.hal
similarity index 94%
rename from test/error_test/interface_semicolon/error/1.0/IFoo.hal
rename to test/error_test/interface_semicolon/1.0/IFoo.hal
index 8222665..f334052 100644
--- a/test/error_test/interface_semicolon/error/1.0/IFoo.hal
+++ b/test/error_test/interface_semicolon/1.0/IFoo.hal
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.interface_semicolon@1.0;
interface IFoo {
-} // <- missing semicolon
\ No newline at end of file
+} // <- missing semicolon
diff --git a/test/error_test/interface_semicolon/1.0/required_error b/test/error_test/interface_semicolon/1.0/required_error
new file mode 100644
index 0000000..4301bae
--- /dev/null
+++ b/test/error_test/interface_semicolon/1.0/required_error
@@ -0,0 +1 @@
+missing ; at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_unique_method_names/1.0/IFoo.hal
similarity index 83%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_unique_method_names/1.0/IFoo.hal
index d79e6ab..9a39b97 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_unique_method_names/1.0/IFoo.hal
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.interface_unique_method_names@1.0;
interface IFoo {
-};
\ No newline at end of file
+ foo(int32_t arg);
+ foo(IFoo arg); // duplicates first method name
+};
diff --git a/test/error_test/interface_unique_method_names/1.0/required_error b/test/error_test/interface_unique_method_names/1.0/required_error
new file mode 100644
index 0000000..42c134b
--- /dev/null
+++ b/test/error_test/interface_unique_method_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of method 'foo' at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/types.hal b/test/error_test/interface_unique_method_names_inheritance/1.0/IBar.hal
similarity index 87%
copy from test/error_test/no_interface_in_types/error/1.0/types.hal
copy to test/error_test/interface_unique_method_names_inheritance/1.0/IBar.hal
index a246ef8..25ac710 100644
--- a/test/error_test/no_interface_in_types/error/1.0/types.hal
+++ b/test/error_test/interface_unique_method_names_inheritance/1.0/IBar.hal
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.interface_unique_method_names_inheritance@1.0;
-// can't have interface in types file
interface IBar {
-};
\ No newline at end of file
+ foo(int32_t arg);
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/interface_unique_method_names_inheritance/1.0/IFoo.hal
similarity index 78%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/interface_unique_method_names_inheritance/1.0/IFoo.hal
index d79e6ab..6e47cef 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/interface_unique_method_names_inheritance/1.0/IFoo.hal
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.interface_unique_method_names_inheritance@1.0;
-interface IFoo {
-};
\ No newline at end of file
+import IBar;
+
+interface IFoo extends IBar {
+ foo(IFoo arg); // duplicates method defined in IBar name
+};
diff --git a/test/error_test/interface_unique_method_names_inheritance/1.0/required_error b/test/error_test/interface_unique_method_names_inheritance/1.0/required_error
new file mode 100644
index 0000000..a76d410
--- /dev/null
+++ b/test/error_test/interface_unique_method_names_inheritance/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of method 'foo'
\ No newline at end of file
diff --git a/test/error_test/method_ends_in_semicolon/error/1.0/IFoo.hal b/test/error_test/method_ends_in_semicolon/1.0/IFoo.hal
similarity index 93%
rename from test/error_test/method_ends_in_semicolon/error/1.0/IFoo.hal
rename to test/error_test/method_ends_in_semicolon/1.0/IFoo.hal
index e8ebca6..1814314 100644
--- a/test/error_test/method_ends_in_semicolon/error/1.0/IFoo.hal
+++ b/test/error_test/method_ends_in_semicolon/1.0/IFoo.hal
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.method_ends_in_semicolon@1.0;
interface IFoo {
foo() generates (int32_t bar)
-};
\ No newline at end of file
+};
diff --git a/test/error_test/method_ends_in_semicolon/1.0/required_error b/test/error_test/method_ends_in_semicolon/1.0/required_error
new file mode 100644
index 0000000..4301bae
--- /dev/null
+++ b/test/error_test/method_ends_in_semicolon/1.0/required_error
@@ -0,0 +1 @@
+missing ; at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/missing_variable_type/1.0/IFoo.hal
similarity index 85%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/missing_variable_type/1.0/IFoo.hal
index d79e6ab..b73bc84 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/missing_variable_type/1.0/IFoo.hal
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.missing_variable_type@1.0;
interface IFoo {
-};
\ No newline at end of file
+ struct Struct { uint32_t a; };
+ foo() generates (Struct);
+};
diff --git a/test/error_test/missing_variable_type/1.0/required_error b/test/error_test/missing_variable_type/1.0/required_error
new file mode 100644
index 0000000..54573c6
--- /dev/null
+++ b/test/error_test/missing_variable_type/1.0/required_error
@@ -0,0 +1 @@
+is missing a variable name
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_1/1.0/IFoo.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_1/1.0/IFoo.hal
index d79e6ab..80b4938 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_1/1.0/IFoo.hal
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_cyclic_declaration_1@1.0;
-interface IFoo {
-};
\ No newline at end of file
+interface IFoo extends IFoo /* extends itself */ {
+};
diff --git a/test/error_test/no_cyclic_declaration_1/1.0/required_error b/test/error_test/no_cyclic_declaration_1/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_1/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_2/1.0/IFoo.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_2/1.0/IFoo.hal
index d79e6ab..9477305 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_2/1.0/IFoo.hal
@@ -13,7 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_cyclic_declaration_2@1.0;
interface IFoo {
-};
\ No newline at end of file
+ struct A {
+ B b;
+ };
+ struct B {
+ A a;
+ };
+};
diff --git a/test/error_test/no_cyclic_declaration_2/1.0/required_error b/test/error_test/no_cyclic_declaration_2/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_2/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_cyclic_declaration_3/1.0/required_error b/test/error_test/no_cyclic_declaration_3/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_3/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_3/1.0/types.hal
similarity index 89%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_3/1.0/types.hal
index d79e6ab..3f78c51 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_3/1.0/types.hal
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_cyclic_declaration_3@1.0;
-interface IFoo {
-};
\ No newline at end of file
+typedef A B;
+typedef B A;
diff --git a/test/error_test/no_cyclic_declaration_4/1.0/required_error b/test/error_test/no_cyclic_declaration_4/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_declaration_4/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_declaration_4/1.0/types.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_declaration_4/1.0/types.hal
index d79e6ab..32a7683 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_declaration_4/1.0/types.hal
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_cyclic_declaration_4@1.0;
-interface IFoo {
-};
\ No newline at end of file
+typedef S SS;
+struct S {
+ SS foo;
+};
diff --git a/test/error_test/no_cyclic_expressions_1/1.0/required_error b/test/error_test/no_cyclic_expressions_1/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_expressions_1/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_expressions_1/1.0/types.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_expressions_1/1.0/types.hal
index d79e6ab..e46a150 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_expressions_1/1.0/types.hal
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_cyclic_expressions_1@1.0;
-interface IFoo {
-};
\ No newline at end of file
+enum E : int32_t {
+ A = D,
+ B,
+ C,
+ D
+};
diff --git a/test/error_test/no_cyclic_expressions_2/1.0/required_error b/test/error_test/no_cyclic_expressions_2/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_expressions_2/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_expressions_2/1.0/types.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_expressions_2/1.0/types.hal
index d79e6ab..e67a774 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_expressions_2/1.0/types.hal
@@ -13,7 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_cyclic_expressions_2@1.0;
-interface IFoo {
-};
\ No newline at end of file
+enum X : int32_t {
+ A = Y:D,
+ B,
+};
+
+enum Y : X {
+ C,
+ D,
+};
diff --git a/test/error_test/no_cyclic_expressions_3/1.0/required_error b/test/error_test/no_cyclic_expressions_3/1.0/required_error
new file mode 100644
index 0000000..340a6a5
--- /dev/null
+++ b/test/error_test/no_cyclic_expressions_3/1.0/required_error
@@ -0,0 +1 @@
+Cyclic declaration
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_cyclic_expressions_3/1.0/types.hal
similarity index 89%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_cyclic_expressions_3/1.0/types.hal
index d79e6ab..c544609 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_cyclic_expressions_3/1.0/types.hal
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_cyclic_expressions_3@1.0;
-interface IFoo {
-};
\ No newline at end of file
+enum E : int32_t {
+ A = A
+};
diff --git a/test/error_test/no_data_outside_interface/error/1.0/IFoo.hal b/test/error_test/no_data_outside_interface/1.0/IFoo.hal
similarity index 93%
rename from test/error_test/no_data_outside_interface/error/1.0/IFoo.hal
rename to test/error_test/no_data_outside_interface/1.0/IFoo.hal
index 4a5ab96..73681b2 100644
--- a/test/error_test/no_data_outside_interface/error/1.0/IFoo.hal
+++ b/test/error_test/no_data_outside_interface/1.0/IFoo.hal
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_data_outside_interface@1.0;
struct Bar { // can only have this in types.hal files
};
interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/no_data_outside_interface/1.0/required_error b/test/error_test/no_data_outside_interface/1.0/required_error
new file mode 100644
index 0000000..497ccd4
--- /dev/null
+++ b/test/error_test/no_data_outside_interface/1.0/required_error
@@ -0,0 +1 @@
+declares types rather than the expected interface type 'IFoo'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_interface_in_types/1.0/IFoo.hal
similarity index 93%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_interface_in_types/1.0/IFoo.hal
index d79e6ab..a994968 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_interface_in_types/1.0/IFoo.hal
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_interface_in_types@1.0;
interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/no_interface_in_types/1.0/required_error b/test/error_test/no_interface_in_types/1.0/required_error
new file mode 100644
index 0000000..3fe7eaf
--- /dev/null
+++ b/test/error_test/no_interface_in_types/1.0/required_error
@@ -0,0 +1 @@
+declares an interface 'IBar' instead of the expected types common to the package
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/types.hal b/test/error_test/no_interface_in_types/1.0/types.hal
similarity index 94%
rename from test/error_test/no_interface_in_types/error/1.0/types.hal
rename to test/error_test/no_interface_in_types/1.0/types.hal
index a246ef8..a2c49f8 100644
--- a/test/error_test/no_interface_in_types/error/1.0/types.hal
+++ b/test/error_test/no_interface_in_types/1.0/types.hal
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_interface_in_types@1.0;
// can't have interface in types file
interface IBar {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/no_two_interfaces/1.0/IFoo.hal
similarity index 91%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/no_two_interfaces/1.0/IFoo.hal
index d79e6ab..eb416d3 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/no_two_interfaces/1.0/IFoo.hal
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.no_two_interfaces@1.0;
interface IFoo {
-};
\ No newline at end of file
+};
+
+interface IBar {
+};
diff --git a/test/error_test/no_two_interfaces/1.0/required_error b/test/error_test/no_two_interfaces/1.0/required_error
new file mode 100644
index 0000000..497ccd4
--- /dev/null
+++ b/test/error_test/no_two_interfaces/1.0/required_error
@@ -0,0 +1 @@
+declares types rather than the expected interface type 'IFoo'
\ No newline at end of file
diff --git a/test/error_test/no_two_interfaces/error/1.0/IFoo.hal b/test/error_test/no_two_interfaces/error/1.0/IFoo.hal
deleted file mode 100644
index db27507..0000000
--- a/test/error_test/no_two_interfaces/error/1.0/IFoo.hal
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package test.error.error@1.0;
-
-interface IFoo {
-};
-
-// can only have one interface per file
-interface IBar {
-};
\ No newline at end of file
diff --git a/test/error_test/same_name_interface/error/1.0/IFoo.hal b/test/error_test/references_broken_package/1.0/IBar.hal
similarity index 78%
copy from test/error_test/same_name_interface/error/1.0/IFoo.hal
copy to test/error_test/references_broken_package/1.0/IBar.hal
index df7806c..27dbdeb 100644
--- a/test/error_test/same_name_interface/error/1.0/IFoo.hal
+++ b/test/error_test/references_broken_package/1.0/IBar.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.references_broken_package@1.0;
-// Must be called IFoo
+import test.references_broken_package.bad_package@1.0::IFoo;
+
interface IBar {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/references_broken_package/1.0/required_error b/test/error_test/references_broken_package/1.0/required_error
new file mode 100644
index 0000000..4301bae
--- /dev/null
+++ b/test/error_test/references_broken_package/1.0/required_error
@@ -0,0 +1 @@
+missing ; at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/references_broken_package/bad_package/1.0/IFoo.hal
similarity index 80%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/references_broken_package/bad_package/1.0/IFoo.hal
index d79e6ab..14d4189 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/references_broken_package/bad_package/1.0/IFoo.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.references_broken_package.bad_package@1.0;
interface IFoo {
-};
\ No newline at end of file
+} // no semicolon -> error
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/references_broken_package/bad_package/1.0/types.hal
similarity index 81%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/references_broken_package/bad_package/1.0/types.hal
index d79e6ab..ea7ed6e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/references_broken_package/bad_package/1.0/types.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.references_broken_package.bad_package@1.0;
-interface IFoo {
-};
\ No newline at end of file
+struct IFoo {};
\ No newline at end of file
diff --git a/test/error_test/same_name_interface/error/1.0/IFoo.hal b/test/error_test/same_name_interface/1.0/IFoo.hal
similarity index 94%
rename from test/error_test/same_name_interface/error/1.0/IFoo.hal
rename to test/error_test/same_name_interface/1.0/IFoo.hal
index df7806c..71a0671 100644
--- a/test/error_test/same_name_interface/error/1.0/IFoo.hal
+++ b/test/error_test/same_name_interface/1.0/IFoo.hal
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.same_name_interface@1.0;
// Must be called IFoo
interface IBar {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/same_name_interface/1.0/required_error b/test/error_test/same_name_interface/1.0/required_error
new file mode 100644
index 0000000..1a46ce9
--- /dev/null
+++ b/test/error_test/same_name_interface/1.0/required_error
@@ -0,0 +1 @@
+does not declare interface type 'IFoo'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/same_package_name/1.0/IFoo.hal
similarity index 95%
rename from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
rename to test/error_test/same_package_name/1.0/IFoo.hal
index d79e6ab..60702d9 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/same_package_name/1.0/IFoo.hal
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.notpackage@1.0;
interface IFoo {
-};
\ No newline at end of file
+};
diff --git a/test/error_test/same_package_name/1.0/required_error b/test/error_test/same_package_name/1.0/required_error
new file mode 100644
index 0000000..d18a773
--- /dev/null
+++ b/test/error_test/same_package_name/1.0/required_error
@@ -0,0 +1 @@
+does not match expected package and/or version
\ No newline at end of file
diff --git a/test/error_test/same_package_name/error/1.0/IFoo.hal b/test/error_test/same_package_name/error/1.0/IFoo.hal
deleted file mode 100644
index efc71b2..0000000
--- a/test/error_test/same_package_name/error/1.0/IFoo.hal
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package test.error.notpackage@1.0;
-
-interface IFoo {
-};
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/scope_unique_type_names/1.0/IFoo.hal
similarity index 84%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/scope_unique_type_names/1.0/IFoo.hal
index d79e6ab..815c8a2 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/scope_unique_type_names/1.0/IFoo.hal
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.scope_unique_type_names@1.0;
interface IFoo {
-};
\ No newline at end of file
+ struct S {};
+ enum S : int32_t {}; // duplicates first struct name
+};
diff --git a/test/error_test/scope_unique_type_names/1.0/required_error b/test/error_test/scope_unique_type_names/1.0/required_error
new file mode 100644
index 0000000..f876888
--- /dev/null
+++ b/test/error_test/scope_unique_type_names/1.0/required_error
@@ -0,0 +1 @@
+A type named 'S' is already declared in the scope at
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/error_test/struct_unique_field_names/1.0/IFoo.hal
similarity index 82%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/error_test/struct_unique_field_names/1.0/IFoo.hal
index d79e6ab..d5c07cd 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/error_test/struct_unique_field_names/1.0/IFoo.hal
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package test.struct_unique_field_names@1.0;
interface IFoo {
-};
\ No newline at end of file
+ struct S {
+ int32_t a;
+ int32_t a; // duplicates first field name
+ };
+};
diff --git a/test/error_test/struct_unique_field_names/1.0/required_error b/test/error_test/struct_unique_field_names/1.0/required_error
new file mode 100644
index 0000000..0aca5dc
--- /dev/null
+++ b/test/error_test/struct_unique_field_names/1.0/required_error
@@ -0,0 +1 @@
+Redefinition of field 'a'
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/export_test/1.0/IFoo.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/export_test/1.0/IFoo.hal
index d79e6ab..adde1da 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/export_test/1.0/IFoo.hal
@@ -13,7 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package export@1.0;
interface IFoo {
+ @export
+ enum S : uint32_t {
+ X,
+ Y,
+ Z,
+ };
};
\ No newline at end of file
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/export_test/1.0/types.hal
similarity index 69%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/export_test/1.0/types.hal
index d79e6ab..d58d097 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/export_test/1.0/types.hal
@@ -13,7 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+package export@1.0;
-interface IFoo {
+@export
+enum NoArgs : uint32_t {
+ A = 1,
+};
+
+@export(name="")
+enum NoName : uint32_t {
+ B = 2,
+};
+
+@export(value_prefix="prefix")
+enum ValueSuffix : uint32_t {
+ C = 3,
+};
+
+@export(value_suffix="suffix")
+enum ValuePrefix : uint32_t {
+ D = 4,
};
\ No newline at end of file
diff --git a/test/export_test/Android.bp b/test/export_test/Android.bp
new file mode 100644
index 0000000..5d7bead
--- /dev/null
+++ b/test/export_test/Android.bp
@@ -0,0 +1,22 @@
+genrule {
+ name: "hidl_export_test_gen-headers",
+ tools: [
+ "hidl-gen",
+ ],
+ srcs: [
+ "1.0/IFoo.hal",
+ "1.0/types.hal"
+ ],
+ cmd: "$(location hidl-gen) -o $(genDir)/export-base.h -Lexport-header " +
+ "-rexport:system/tools/hidl/test/export_test export@1.0",
+ out: [
+ "export-base.h"
+ ],
+}
+
+cc_test_library {
+ name: "hidl_export_test",
+ cflags: ["-Wall", "-Werror"],
+ generated_headers: ["hidl_export_test_gen-headers"],
+ srcs: ["test.cpp"],
+}
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/export_test/test.cpp
similarity index 60%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/export_test/test.cpp
index d79e6ab..91cebff 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/export_test/test.cpp
@@ -13,7 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
-interface IFoo {
-};
\ No newline at end of file
+#include <export-base.h>
+
+// From IFoo.hal
+
+static_assert(X == 0, "");
+static_assert(Y == 1, "");
+static_assert(Z == 2, "");
+
+typedef S SExists;
+
+// From types.hal
+
+static_assert(B == 2, "");
+enum NoName {}; // causes error if declared already
+
+static_assert(prefixC == 3, "");
+typedef ValueSuffix ValueSuffixExists;
+
+static_assert(Dsuffix == 4, "");
+typedef ValuePrefix ValuePrefixExists;
diff --git a/test/hash_test/Android.bp b/test/hash_test/Android.bp
index 89c403b..5f9f248 100644
--- a/test/hash_test/Android.bp
+++ b/test/hash_test/Android.bp
@@ -31,5 +31,6 @@
cc_test_host {
name: "hidl_hash_test",
+ cflags: ["-Wall", "-Werror"],
generated_sources: ["hidl_hash_test_gen"],
}
diff --git a/test/hash_test/good/current.txt b/test/hash_test/good/current.txt
index b6e7514..ff4b669 100644
--- a/test/hash_test/good/current.txt
+++ b/test/hash_test/good/current.txt
@@ -1 +1 @@
-b19939ecb4f877820df49b684f3164f0a3f9aa18743a3521f3bd04e4a06fed64 test.hash.hash@1.0::IHash
+b19939ecb4f877820df49b684f3164f0a3f9aa18743a3521f3bd04e4a06fed64 test.hash.hash@1.0::IHash # comments are fine too
diff --git a/test/hidl_test.h b/test/hidl_test.h
deleted file mode 100644
index 3eeebaf..0000000
--- a/test/hidl_test.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef HIDL_TEST_H_
-#define HIDL_TEST_H_
-
-#define EACH_SERVER(THING) \
- do { \
- THING<IMemoryTest>("memory"); \
- THING<IChild>("child"); \
- THING<IParent>("parent"); \
- THING<IFetcher>("fetcher"); \
- THING<IBar>("foo"); \
- THING<IHash>("default"); \
- THING<IGraph>("graph"); \
- THING<IPointer>("pointer"); \
- THING<IMultithread>("multithread"); \
- } while (false)
-
-#endif // HIDL_TEST_H_
\ No newline at end of file
diff --git a/test/hidl_test/Android.bp b/test/hidl_test/Android.bp
new file mode 100644
index 0000000..73110a6
--- /dev/null
+++ b/test/hidl_test/Android.bp
@@ -0,0 +1,77 @@
+cc_defaults {
+ name: "hidl_test_client-defaults",
+ defaults: ["hidl-gen-defaults"],
+
+ shared_libs: [
+ "android.hidl.memory@1.0",
+ "android.hidl.memory.token@1.0",
+ "android.hidl.token@1.0",
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ ],
+
+ // Allow dlsym'ing self for statically linked passthrough implementations
+ ldflags: ["-rdynamic"],
+
+ // These are static libs only for testing purposes and portability. Shared
+ // libs should be used on device.
+ static_libs: [
+ "libfootest",
+ "libpointertest",
+ "android.hardware.tests.expression@1.0",
+ "android.hardware.tests.foo@1.0",
+ "android.hardware.tests.bar@1.0",
+ "android.hardware.tests.baz@1.0",
+ "android.hardware.tests.hash@1.0",
+ "android.hardware.tests.inheritance@1.0",
+ "android.hardware.tests.pointer@1.0",
+ "android.hardware.tests.memory@1.0",
+ "android.hardware.tests.multithread@1.0",
+ "android.hardware.tests.trie@1.0",
+ ],
+
+ // impls should never be static, these are used only for testing purposes
+ // and test portability since this test pairs with specific hal
+ // implementations
+ whole_static_libs: [
+ "android.hardware.tests.foo@1.0-impl",
+ "android.hardware.tests.bar@1.0-impl",
+ "android.hardware.tests.baz@1.0-impl",
+ "android.hardware.tests.hash@1.0-impl",
+ "android.hardware.tests.inheritance@1.0-impl",
+ "android.hardware.tests.pointer@1.0-impl",
+ "android.hardware.tests.memory@1.0-impl",
+ "android.hardware.tests.multithread@1.0-impl",
+ "android.hardware.tests.trie@1.0-impl",
+ ],
+
+ group_static_libs: true,
+}
+
+cc_test {
+ name: "hidl_test_client",
+ defaults: ["hidl_test_client-defaults"],
+
+ srcs: [
+ "hidl_test_client.cpp",
+ "FooCallback.cpp",
+ "static_test.cpp"
+ ],
+
+ shared_libs: [
+ "android.hidl.allocator@1.0",
+ ],
+}
+
+cc_test {
+ name: "hidl_test_servers",
+ defaults: ["hidl_test_client-defaults"],
+ srcs: ["hidl_test_servers.cpp"],
+ gtest: false,
+}
diff --git a/test/Android.mk b/test/hidl_test/Android.mk
similarity index 77%
rename from test/Android.mk
rename to test/hidl_test/Android.mk
index ff0001c..f328223 100644
--- a/test/Android.mk
+++ b/test/hidl_test/Android.mk
@@ -25,12 +25,15 @@
LOCAL_MODULE := hidl_test
LOCAL_MODULE_CLASS := NATIVE_TESTS
LOCAL_SRC_FILES := hidl_test
-LOCAL_REQUIRED_MODULES := \
- hidl_test_client \
- hidl_test_servers \
- hidl_test_client_32 \
- hidl_test_servers_32 \
- hidl_test_helper
+
+LOCAL_REQUIRED_MODULES := \
+ hidl_test_client \
+ hidl_test_helper \
+ hidl_test_servers
+
+LOCAL_REQUIRED_MODULES_arm64 := hidl_test_servers_32 hidl_test_client_32
+LOCAL_REQUIRED_MODULES_mips64 := hidl_test_servers_32 hidl_test_client_32
+LOCAL_REQUIRED_MODULES_x86_64 := hidl_test_servers_32 hidl_test_client_32
include $(BUILD_PREBUILT)
@@ -39,5 +42,3 @@
LOCAL_MODULE := VtsHidlUnitTests
VTS_CONFIG_SRC_DIR := system/tools/hidl/tests
-include test/vts/tools/build/Android.host_config.mk
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test/hidl_test/AndroidTest.xml b/test/hidl_test/AndroidTest.xml
new file mode 100644
index 0000000..6633942
--- /dev/null
+++ b/test/hidl_test/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for VTS HIDL unit tests">
+ <option name="config-descriptor:metadata" key="plan" value="vts-library" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
+ <option name="push-group" value="HostDrivenTest.push" />
+ <option name="cleanup" value="true" />
+ <option name="push" value="DATA/nativetest/hidl_test_servers/hidl_test_servers->/data/nativetest/hidl_test_servers/hidl_test_servers" />
+ <option name="push" value="DATA/nativetest64/hidl_test_servers/hidl_test_servers->/data/nativetest64/hidl_test_servers/hidl_test_servers" />
+ <option name="push" value="DATA/nativetest/hidl_test_client/hidl_test_client->/data/nativetest/hidl_test_client/hidl_test_client" />
+ <option name="push" value="DATA/nativetest64/hidl_test_client/hidl_test_client->/data/nativetest64/hidl_test_client/hidl_test_client" />
+ <option name="push" value="DATA/nativetest64/hidl_test_helper->/data/nativetest64/hidl_test_helper" />
+ </target_preparer>
+ <multi_target_preparer class="com.android.tradefed.targetprep.VtsPythonVirtualenvPreparer" />
+ <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
+ <option name="test-module-name" value="VtsHidlUnitTests" />
+ <option name="binary-test-type" value="binary_test" />
+ <option name="binary-test-source" value="DATA/nativetest64/hidl_test->/data/nativetest64/hidl_test" />
+ <option name="test-timeout" value="1m" />
+ </test>
+</configuration>
diff --git a/test/FooCallback.cpp b/test/hidl_test/FooCallback.cpp
similarity index 100%
rename from test/FooCallback.cpp
rename to test/hidl_test/FooCallback.cpp
diff --git a/test/FooCallback.h b/test/hidl_test/FooCallback.h
similarity index 100%
rename from test/FooCallback.h
rename to test/hidl_test/FooCallback.h
diff --git a/test/hidl_test b/test/hidl_test/hidl_test
similarity index 100%
rename from test/hidl_test
rename to test/hidl_test/hidl_test
diff --git a/test/hidl_test/hidl_test.h b/test/hidl_test/hidl_test.h
new file mode 100644
index 0000000..0ad17ef
--- /dev/null
+++ b/test/hidl_test/hidl_test.h
@@ -0,0 +1,40 @@
+#ifndef HIDL_TEST_H_
+#define HIDL_TEST_H_
+
+#include <android/hardware/tests/bar/1.0/IBar.h>
+#include <android/hardware/tests/hash/1.0/IHash.h>
+#include <android/hardware/tests/inheritance/1.0/IChild.h>
+#include <android/hardware/tests/inheritance/1.0/IFetcher.h>
+#include <android/hardware/tests/inheritance/1.0/IParent.h>
+#include <android/hardware/tests/memory/1.0/IMemoryTest.h>
+#include <android/hardware/tests/multithread/1.0/IMultithread.h>
+#include <android/hardware/tests/pointer/1.0/IGraph.h>
+#include <android/hardware/tests/pointer/1.0/IPointer.h>
+#include <android/hardware/tests/trie/1.0/ITrie.h>
+
+template <template <typename Type> class Service>
+void runOnEachServer(void) {
+ using ::android::hardware::tests::bar::V1_0::IBar;
+ using ::android::hardware::tests::hash::V1_0::IHash;
+ using ::android::hardware::tests::inheritance::V1_0::IChild;
+ using ::android::hardware::tests::inheritance::V1_0::IFetcher;
+ using ::android::hardware::tests::inheritance::V1_0::IParent;
+ using ::android::hardware::tests::memory::V1_0::IMemoryTest;
+ using ::android::hardware::tests::multithread::V1_0::IMultithread;
+ using ::android::hardware::tests::pointer::V1_0::IGraph;
+ using ::android::hardware::tests::pointer::V1_0::IPointer;
+ using ::android::hardware::tests::trie::V1_0::ITrie;
+
+ Service<IMemoryTest>::run("memory");
+ Service<IChild>::run("child");
+ Service<IParent>::run("parent");
+ Service<IFetcher>::run("fetcher");
+ Service<IBar>::run("foo");
+ Service<IHash>::run("default");
+ Service<IGraph>::run("graph");
+ Service<IPointer>::run("pointer");
+ Service<IMultithread>::run("multithread");
+ Service<ITrie>::run("trie");
+}
+
+#endif // HIDL_TEST_H_
diff --git a/test/hidl_test_client.cpp b/test/hidl_test/hidl_test_client.cpp
similarity index 80%
rename from test/hidl_test_client.cpp
rename to test/hidl_test/hidl_test_client.cpp
index a8e7793..718a32c 100644
--- a/test/hidl_test_client.cpp
+++ b/test/hidl_test/hidl_test_client.cpp
@@ -10,11 +10,11 @@
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMemory.h>
-
+#include <android/hidl/memory/token/1.0/IMemoryToken.h>
#include <android/hidl/token/1.0/ITokenManager.h>
-#include <android/hardware/tests/bar/1.0/BpHwBar.h>
#include <android/hardware/tests/bar/1.0/BnHwBar.h>
+#include <android/hardware/tests/bar/1.0/BpHwBar.h>
#include <android/hardware/tests/bar/1.0/IBar.h>
#include <android/hardware/tests/bar/1.0/IComplicated.h>
#include <android/hardware/tests/bar/1.0/IImportRules.h>
@@ -32,6 +32,7 @@
#include <android/hardware/tests/multithread/1.0/IMultithread.h>
#include <android/hardware/tests/pointer/1.0/IGraph.h>
#include <android/hardware/tests/pointer/1.0/IPointer.h>
+#include <android/hardware/tests/trie/1.0/ITrie.h>
#include <gtest/gtest.h>
#if GTEST_IS_THREADSAFE
@@ -51,9 +52,12 @@
#include <fstream>
#include <future>
#include <mutex>
+#include <random>
#include <set>
#include <sstream>
#include <thread>
+#include <type_traits>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -62,6 +66,7 @@
#include <hidl/ServiceManagement.h>
#include <hidl/Status.h>
+#include <hidlmemory/HidlMemoryToken.h>
#include <hidlmemory/mapping.h>
#include <utils/Condition.h>
@@ -102,11 +107,14 @@
using ::android::hardware::tests::pointer::V1_0::IPointer;
using ::android::hardware::tests::memory::V1_0::IMemoryTest;
using ::android::hardware::tests::multithread::V1_0::IMultithread;
+using ::android::hardware::tests::trie::V1_0::ITrie;
+using ::android::hardware::tests::trie::V1_0::TrieNode;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_death_recipient;
using ::android::hardware::hidl_memory;
+using ::android::hardware::HidlMemory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hidl::allocator::V1_0::IAllocator;
@@ -114,6 +122,8 @@
using ::android::hidl::manager::V1_1::IServiceManager;
using ::android::hidl::manager::V1_0::IServiceNotification;
using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hidl::memory::token::V1_0::IMemoryToken;
+using ::android::hidl::memory::block::V1_0::MemoryBlock;
using ::android::hidl::token::V1_0::ITokenManager;
using ::android::sp;
using ::android::wp;
@@ -127,16 +137,8 @@
using ::android::ONEWAY_TOLERANCE_NS;
using std::to_string;
-bool isLibraryOpen(const std::string &lib) {
- std::ifstream ifs("/proc/self/maps");
- for (std::string line; std::getline(ifs, line);) {
- if (line.size() >= lib.size() && line.substr(line.size() - lib.size()) == lib) {
- return true;
- }
- }
-
- return false;
-}
+template <typename T>
+using hidl_enum_iterator = ::android::hardware::hidl_enum_iterator<T>;
template <typename T>
static inline ::testing::AssertionResult isOk(const ::android::hardware::Return<T> &ret) {
@@ -268,67 +270,6 @@
int32_t mCookie;
};
-// Ensure (statically) that the types in IImportRules resolves to the correct types by
-// overriding the methods with fully namespaced types as arguments.
-struct MyImportRules : public ::android::hardware::tests::bar::V1_0::IImportRules {
- Return<void> rule0a(
- const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
- return Void();
- }
-
- Return<void> rule0a1(
- const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
- return Void();
- }
-
- Return<void> rule0b(
- const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
- return Void();
- }
-
- Return<void> rule0c(const ::android::hardware::tests::foo::V1_0::Outer&) override {
- return Void();
- }
-
- Return<void> rule0d(const ::android::hardware::tests::foo::V1_0::Outer&) override {
- return Void();
- }
-
- Return<void> rule0e(
- const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
- return Void();
- }
-
- Return<void> rule0f(
- const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
- return Void();
- }
-
- Return<void> rule0g(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
- return Void();
- }
-
- Return<void> rule0h(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
- return Void();
- }
-
- Return<void> rule1a(const ::android::hardware::tests::bar::V1_0::Def&) override {
- return Void();
- }
-
- Return<void> rule1b(const ::android::hardware::tests::foo::V1_0::Def&) override {
- return Void();
- }
-
- Return<void> rule2a(const ::android::hardware::tests::foo::V1_0::Unrelated&) override {
- return Void();
- }
-
- Return<void> rule2b(const sp<::android::hardware::tests::foo::V1_0::IFooCallback>&) override {
- return Void();
- }
-};
-
struct ServiceNotification : public IServiceNotification {
std::mutex mutex;
std::condition_variable condition;
@@ -373,6 +314,7 @@
sp<IPointer> pointerInterface;
sp<IPointer> validationPointerInterface;
sp<IMultithread> multithreadInterface;
+ sp<ITrie> trieInterface;
TestMode mode;
bool enableDelayMeasurementTests;
HidlEnvironment(TestMode mode, bool enableDelayMeasurementTests) :
@@ -434,6 +376,10 @@
IMultithread::getService("multithread", mode == PASSTHROUGH /* getStub */);
ASSERT_NE(multithreadInterface, nullptr);
ASSERT_EQ(multithreadInterface->isRemote(), mode == BINDERIZED);
+
+ trieInterface = ITrie::getService("trie", mode == PASSTHROUGH /* getStub */);
+ ASSERT_NE(trieInterface, nullptr);
+ ASSERT_EQ(trieInterface->isRemote(), mode == BINDERIZED);
}
virtual void SetUp() {
@@ -456,6 +402,7 @@
sp<IGraph> graphInterface;
sp<IPointer> pointerInterface;
sp<IPointer> validationPointerInterface;
+ sp<ITrie> trieInterface;
TestMode mode = TestMode::PASSTHROUGH;
virtual void SetUp() override {
@@ -471,24 +418,12 @@
graphInterface = gHidlEnvironment->graphInterface;
pointerInterface = gHidlEnvironment->pointerInterface;
validationPointerInterface = gHidlEnvironment->validationPointerInterface;
+ trieInterface = gHidlEnvironment->trieInterface;
mode = gHidlEnvironment->mode;
ALOGI("Test setup complete");
}
};
-TEST_F(HidlTest, PreloadTest) {
- // in passthrough mode, this will already be opened
- if (mode == BINDERIZED) {
- using android::hardware::preloadPassthroughService;
-
- static const std::string kLib = "android.hardware.tests.inheritance@1.0-impl.so";
-
- EXPECT_FALSE(isLibraryOpen(kLib));
- preloadPassthroughService<IParent>();
- EXPECT_TRUE(isLibraryOpen(kLib));
- }
-}
-
TEST_F(HidlTest, ToStringTest) {
using namespace android::hardware;
@@ -531,6 +466,38 @@
EXPECT_NE(nullptr, IFoo::getService("\n", true /* getStub */).get());
}
+TEST_F(HidlTest, EnumIteratorTest) {
+ using Empty = ::android::hardware::tests::foo::V1_0::EnumIterators::Empty;
+ using Grandchild = ::android::hardware::tests::foo::V1_0::EnumIterators::Grandchild;
+ using SkipsValues = ::android::hardware::tests::foo::V1_0::EnumIterators::SkipsValues;
+ using MultipleValues = ::android::hardware::tests::foo::V1_0::EnumIterators::MultipleValues;
+
+ for (const auto value : hidl_enum_iterator<Empty>()) {
+ (void)value;
+ EXPECT_TRUE(false) << "Empty iterator should not iterate";
+ }
+
+ auto it1 = hidl_enum_iterator<Grandchild>().begin();
+ EXPECT_EQ(Grandchild::A, *it1++);
+ EXPECT_EQ(Grandchild::B, *it1++);
+ EXPECT_EQ(hidl_enum_iterator<Grandchild>().end(), it1);
+
+ auto it2 = hidl_enum_iterator<SkipsValues>().begin();
+ EXPECT_EQ(SkipsValues::A, *it2++);
+ EXPECT_EQ(SkipsValues::B, *it2++);
+ EXPECT_EQ(SkipsValues::C, *it2++);
+ EXPECT_EQ(SkipsValues::D, *it2++);
+ EXPECT_EQ(SkipsValues::E, *it2++);
+ EXPECT_EQ(hidl_enum_iterator<SkipsValues>().end(), it2);
+
+ auto it3 = hidl_enum_iterator<MultipleValues>().begin();
+ EXPECT_EQ(MultipleValues::A, *it3++);
+ EXPECT_EQ(MultipleValues::B, *it3++);
+ EXPECT_EQ(MultipleValues::C, *it3++);
+ EXPECT_EQ(MultipleValues::D, *it3++);
+ EXPECT_EQ(hidl_enum_iterator<MultipleValues>().end(), it3);
+}
+
TEST_F(HidlTest, EnumToStringTest) {
using namespace std::string_literals;
using ::android::hardware::tests::foo::V1_0::toString;
@@ -541,10 +508,20 @@
EXPECT_EQ(toString(static_cast<IFoo::BitField>(IFoo::BitField::V0 | IFoo::BitField::V2)), "0x5"s)
<< "Invalid enum isn't stringified correctly.";
// dump bitfields
- EXPECT_EQ(toString<IFoo::BitField>(0 | IFoo::BitField::V0), "V0 (0x1)"s);
- EXPECT_EQ(toString<IFoo::BitField>(0 | IFoo::BitField::V0 | IFoo::BitField::V2), "V0 | V2 (0x5)"s);
- EXPECT_EQ(toString<IFoo::BitField>(0xF), "V0 | V1 | V2 | V3 | VALL (0xf)"s);
- EXPECT_EQ(toString<IFoo::BitField>(0xFF), "V0 | V1 | V2 | V3 | VALL | 0xf0 (0xff)"s);
+ EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0 | IFoo::BitField::V0), "V0 (0x1)"s);
+ EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0 | IFoo::BitField::V0 | IFoo::BitField::V2),
+ "V0 | V2 (0x5)"s);
+ EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0xF), "V0 | V1 | V2 | V3 | VALL (0xf)"s);
+ EXPECT_EQ(toString<IFoo::BitField>((uint8_t)0xFF), "V0 | V1 | V2 | V3 | VALL | 0xf0 (0xff)"s);
+
+ // inheritance
+ using Parent = ::android::hardware::tests::foo::V1_0::EnumIterators::Parent;
+ using EmptyChild = ::android::hardware::tests::foo::V1_0::EnumIterators::EmptyChild;
+ using Grandchild = ::android::hardware::tests::foo::V1_0::EnumIterators::Grandchild;
+ EXPECT_EQ(toString(Parent::A), "A"s);
+ EXPECT_EQ(toString(EmptyChild::A), "A"s);
+ EXPECT_EQ(toString(Grandchild::A), "A"s);
+ EXPECT_EQ(toString(Grandchild::B), "B"s);
}
TEST_F(HidlTest, PingTest) {
@@ -560,17 +537,24 @@
}
TEST_F(HidlTest, HashTest) {
- uint8_t ihash[32] = {74,38,204,105,102,117,11,15,207,7,238,198,29,35,30,62,100,
- 216,131,182,3,61,162,241,215,211,6,20,251,143,125,161};
+ static constexpr uint64_t kHashSize = 32u;
+ // unreleased interface has an empty hash
+ uint8_t ihash[kHashSize] = {0};
+ uint8_t ibase[kHashSize] = {189, 218, 182, 24, 77, 122, 52, 109, 166, 160, 125,
+ 192, 130, 140, 241, 154, 105, 111, 76, 170, 54, 17,
+ 197, 31, 46, 20, 86, 90, 20, 180, 15, 217};
auto service = IHash::getService(mode == PASSTHROUGH /* getStub */);
- EXPECT_OK(service->getHashChain([&] (const auto &chain) {
- EXPECT_EQ(chain[0].size(), 32u);
- EXPECT_ARRAYEQ(ihash, chain[0], 32);
- EXPECT_OK(manager->getHashChain([&] (const auto &managerChain) {
- EXPECT_EQ(chain[chain.size() - 1].size(), managerChain[managerChain.size() - 1].size());
- EXPECT_ARRAYEQ(chain[chain.size() - 1], managerChain[managerChain.size() - 1],
- chain[chain.size() - 1].size()) << "Hash for IBase doesn't match!";
- }));
+ EXPECT_OK(service->getHashChain([&](const auto& chain) {
+ ASSERT_EQ(chain.size(), 2u);
+ EXPECT_EQ(chain[0].size(), kHashSize);
+ EXPECT_ARRAYEQ(ihash, chain[0], kHashSize);
+ EXPECT_EQ(chain[1].size(), kHashSize);
+ EXPECT_ARRAYEQ(ibase, chain[1], kHashSize);
+ }));
+ EXPECT_OK(manager->getHashChain([&](const auto& managerChain) {
+ EXPECT_EQ(managerChain[managerChain.size() - 1].size(), kHashSize);
+ EXPECT_ARRAYEQ(ibase, managerChain[managerChain.size() - 1], kHashSize)
+ << "Hash for IBase doesn't match!";
}));
}
@@ -624,137 +608,165 @@
}));
}
-// passthrough TODO(b/31959402)
TEST_F(HidlTest, ServiceListByInterfaceTest) {
- if (mode == BINDERIZED) {
- EXPECT_OK(manager->listByInterface(IParent::descriptor,
- [](const hidl_vec<hidl_string> ®istered) {
- std::set<std::string> registeredSet;
-
- for (size_t i = 0; i < registered.size(); i++) {
- registeredSet.insert(registered[i]);
- }
-
- std::set<std::string> activeSet = {
- "parent", "child"
- };
- std::set<std::string> difference;
- std::set_difference(activeSet.begin(), activeSet.end(),
- registeredSet.begin(), registeredSet.end(),
- std::inserter(difference, difference.begin()));
-
- EXPECT_EQ(difference.size(), 0u) << "service(s) not registered " << to_string(difference);
- }));
+ if (mode != BINDERIZED) {
+ // passthrough service manager does not know about services
+ return;
}
+
+ EXPECT_OK(
+ manager->listByInterface(IParent::descriptor, [](const hidl_vec<hidl_string>& registered) {
+ std::set<std::string> registeredSet;
+
+ for (size_t i = 0; i < registered.size(); i++) {
+ registeredSet.insert(registered[i]);
+ }
+
+ std::set<std::string> activeSet = {"parent", "child"};
+ std::set<std::string> difference;
+ std::set_difference(activeSet.begin(), activeSet.end(), registeredSet.begin(),
+ registeredSet.end(), std::inserter(difference, difference.begin()));
+
+ EXPECT_EQ(difference.size(), 0u)
+ << "service(s) not registered " << to_string(difference);
+ }));
}
-// passthrough TODO(b/31959402)
-TEST_F(HidlTest, ServiceParentTest) {
- if (mode == BINDERIZED) {
- sp<IParent> parent = IParent::getService("child");
+TEST_F(HidlTest, SubInterfaceServiceRegistrationTest) {
+ using ::android::hardware::interfacesEqual;
- EXPECT_NE(parent, nullptr);
- }
+ const std::string kInstanceName = "no-matter-what-it-is";
+ const std::string kOtherName = "something-different";
+
+ sp<IChild> child = new SimpleChild();
+ sp<IParent> parent = new SimpleParent();
+
+ EXPECT_EQ(::android::OK, child->registerAsService(kInstanceName));
+ EXPECT_EQ(::android::OK, child->registerAsService(kOtherName));
+
+ EXPECT_TRUE(interfacesEqual(child, IChild::getService(kInstanceName)));
+ EXPECT_TRUE(interfacesEqual(child, IParent::getService(kInstanceName)));
+
+ EXPECT_EQ(::android::OK, parent->registerAsService(kInstanceName));
+
+ // FALSE since passthrough HAL will return an instance
+ // since binderized instance is nullptr
+ EXPECT_FALSE(interfacesEqual(parent, IChild::getService(kInstanceName)));
+ EXPECT_TRUE(interfacesEqual(parent, IParent::getService(kInstanceName)));
+
+ // other instance name is unchanged
+ EXPECT_TRUE(interfacesEqual(child, IChild::getService(kOtherName)));
+ EXPECT_TRUE(interfacesEqual(child, IParent::getService(kOtherName)));
}
-// passthrough TODO(b/31959402)
TEST_F(HidlTest, ServiceNotificationTest) {
- if (mode == BINDERIZED) {
- ServiceNotification *notification = new ServiceNotification();
-
- std::string instanceName = "test-instance";
- EXPECT_TRUE(IParent::registerForNotifications(instanceName, notification));
-
- EXPECT_EQ(::android::OK, (new SimpleChild())->registerAsService(instanceName));
- EXPECT_EQ(::android::OK, (new SimpleParent())->registerAsService(instanceName));
-
- std::unique_lock<std::mutex> lock(notification->mutex);
-
- notification->condition.wait_for(
- lock,
- std::chrono::milliseconds(2),
- [¬ification]() {
- return notification->getRegistrations().size() >= 2;
- });
-
- std::vector<std::string> registrations = notification->getRegistrations();
-
- EXPECT_EQ(registrations.size(), 2u);
-
- EXPECT_EQ(to_string(registrations.data(), registrations.size()),
- std::string("['") + IParent::descriptor + "/" + instanceName +
- "', '" + IParent::descriptor + "/" + instanceName + "']");
+ if (mode != BINDERIZED) {
+ // service notifications aren't supported in passthrough mode
+ return;
}
+
+ ServiceNotification* notification = new ServiceNotification();
+
+ std::string instanceName = "test-instance";
+ EXPECT_TRUE(IParent::registerForNotifications(instanceName, notification));
+
+ EXPECT_EQ(::android::OK, (new SimpleChild())->registerAsService(instanceName));
+ EXPECT_EQ(::android::OK, (new SimpleParent())->registerAsService(instanceName));
+
+ std::unique_lock<std::mutex> lock(notification->mutex);
+
+ notification->condition.wait_for(lock, std::chrono::milliseconds(2), [¬ification]() {
+ return notification->getRegistrations().size() >= 2;
+ });
+
+ std::vector<std::string> registrations = notification->getRegistrations();
+
+ EXPECT_EQ(registrations.size(), 2u);
+
+ EXPECT_EQ(to_string(registrations.data(), registrations.size()),
+ std::string("['") + IParent::descriptor + "/" + instanceName + "', '" +
+ IParent::descriptor + "/" + instanceName + "']");
}
TEST_F(HidlTest, ServiceUnregisterTest) {
- if (mode == BINDERIZED) {
- const std::string instance = "some-instance-name";
+ const std::string instance = "some-instance-name";
- sp<ServiceNotification> sNotification = new ServiceNotification();
+ sp<ServiceNotification> sNotification = new ServiceNotification();
- // unregister all
- EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
- EXPECT_TRUE(manager->unregisterForNotifications("", "", sNotification));
+ // unregister all
+ EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
+ EXPECT_TRUE(manager->unregisterForNotifications("", "", sNotification));
- // unregister all with instance name
- EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
- EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor,
- "", sNotification));
+ // unregister all with instance name
+ EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
+ EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor, "", sNotification));
- // unregister package listener
- EXPECT_TRUE(IParent::registerForNotifications("", sNotification));
- EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor,
- "", sNotification));
+ // unregister package listener
+ EXPECT_TRUE(IParent::registerForNotifications("", sNotification));
+ EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor, "", sNotification));
- // unregister listener for specific service and name
- EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
- EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor,
- instance, sNotification));
+ // unregister listener for specific service and name
+ EXPECT_TRUE(IParent::registerForNotifications(instance, sNotification));
+ EXPECT_TRUE(manager->unregisterForNotifications(IParent::descriptor, instance, sNotification));
- EXPECT_FALSE(manager->unregisterForNotifications("", "", sNotification));
+ EXPECT_FALSE(manager->unregisterForNotifications("", "", sNotification));
- // TODO(b/32837397): remote destructor is lazy
- // wp<ServiceNotification> wNotification = sNotification;
- // sNotification = nullptr;
- // EXPECT_EQ(nullptr, wNotification.promote().get());
- }
+ // TODO(b/32837397): remote destructor is lazy
+ // wp<ServiceNotification> wNotification = sNotification;
+ // sNotification = nullptr;
+ // EXPECT_EQ(nullptr, wNotification.promote().get());
}
-// passthrough TODO(b/31959402)
TEST_F(HidlTest, ServiceAllNotificationTest) {
+ ServiceNotification* notification = new ServiceNotification();
+
+ std::string instanceOne = "test-instance-one";
+ std::string instanceTwo = "test-instance-two";
+ EXPECT_TRUE(ISimple::registerForNotifications("", notification));
+
+ Simple* instanceA = new Simple(1);
+ EXPECT_EQ(::android::OK, instanceA->registerAsService(instanceOne));
+ Simple* instanceB = new Simple(2);
+ EXPECT_EQ(::android::OK, instanceB->registerAsService(instanceTwo));
+
+ std::unique_lock<std::mutex> lock(notification->mutex);
+
+ notification->condition.wait_for(lock, std::chrono::milliseconds(2), [¬ification]() {
+ return notification->getRegistrations().size() >= 2;
+ });
+
+ std::vector<std::string> registrations = notification->getRegistrations();
+ std::sort(registrations.begin(), registrations.end());
+
+ EXPECT_EQ(registrations.size(), 2u);
+
+ std::string descriptor = ISimple::descriptor;
+
+ EXPECT_EQ(
+ to_string(registrations.data(), registrations.size()),
+ "['" + descriptor + "/" + instanceOne + "', '" + descriptor + "/" + instanceTwo + "']");
+}
+
+TEST_F(HidlTest, InterfacesEqualTest) {
+ using android::hardware::interfacesEqual;
+
+ sp<IParent> service1 = IParent::getService("child", mode == PASSTHROUGH /* getStub */);
+ sp<IParent> service2 = service1;
+
+ // Passthrough services are reinstantiated whenever getService is called.
if (mode == BINDERIZED) {
- ServiceNotification *notification = new ServiceNotification();
-
- std::string instanceOne = "test-instance-one";
- std::string instanceTwo = "test-instance-two";
- EXPECT_TRUE(ISimple::registerForNotifications("", notification));
-
- Simple* instanceA = new Simple(1);
- EXPECT_EQ(::android::OK, instanceA->registerAsService(instanceOne));
- Simple* instanceB = new Simple(2);
- EXPECT_EQ(::android::OK, instanceB->registerAsService(instanceTwo));
-
- std::unique_lock<std::mutex> lock(notification->mutex);
-
- notification->condition.wait_for(
- lock,
- std::chrono::milliseconds(2),
- [¬ification]() {
- return notification->getRegistrations().size() >= 2;
- });
-
- std::vector<std::string> registrations = notification->getRegistrations();
- std::sort(registrations.begin(), registrations.end());
-
- EXPECT_EQ(registrations.size(), 2u);
-
- std::string descriptor = ISimple::descriptor;
-
- EXPECT_EQ(to_string(registrations.data(), registrations.size()),
- "['" + descriptor + "/" + instanceOne + "', '"
- + descriptor + "/" + instanceTwo + "']");
+ service2 = IParent::getService("child");
}
+
+ EXPECT_NE(nullptr, service1.get());
+ EXPECT_NE(nullptr, service2.get());
+ EXPECT_TRUE(interfacesEqual(service1, service2));
+
+ sp<IChild> child = IChild::castFrom(service1);
+ EXPECT_NE(nullptr, child.get()); // it is actually a child
+
+ EXPECT_TRUE(interfacesEqual(service1, child));
+ EXPECT_TRUE(interfacesEqual(service2, child));
}
TEST_F(HidlTest, TestToken) {
@@ -889,19 +901,57 @@
}
}
-inline uint64_t operator""_GB(unsigned long long num) {
- return num * 1024 * 1024 * 1024;
-}
+TEST_F(HidlTest, MemoryBlock) {
+ const uint8_t kValue = 0xCA;
+ using ::android::hardware::IBinder;
+ using ::android::hardware::interfacesEqual;
+ using ::android::hardware::toBinder;
-TEST_F(HidlTest, FailedBatchSharedMemory) {
- EXPECT_OK(ashmemAllocator->batchAllocate(1024, UINT64_MAX, [&](bool success, const auto& v) {
- EXPECT_FALSE(success);
- EXPECT_EQ(0u, v.size());
+ sp<HidlMemory> mem;
+ EXPECT_OK(ashmemAllocator->allocate(1024, [&](bool success, const hidl_memory& _mem) {
+ ASSERT_TRUE(success);
+ mem = HidlMemory::getInstance(_mem);
}));
- EXPECT_OK(ashmemAllocator->batchAllocate(1_GB, 1024, [&](bool success, const auto& v) {
- EXPECT_FALSE(success);
- EXPECT_EQ(0u, v.size());
+ memoryTest->set(*mem);
+ Return<sp<IMemoryToken>> tokenRet = memoryTest->get();
+ EXPECT_OK(tokenRet);
+ sp<IMemoryToken> token = tokenRet;
+ EXPECT_NE(nullptr, token.get());
+ EXPECT_OK(token->get([&](const hidl_memory& mem) {
+ sp<IMemory> memory = mapMemory(mem);
+
+ EXPECT_NE(nullptr, memory.get());
+
+ uint8_t* data = static_cast<uint8_t*>(static_cast<void*>(memory->getPointer()));
+ EXPECT_NE(data, nullptr);
+
+ EXPECT_EQ(memory->getSize(), mem.size());
+
+ memory->update();
+ memset(data, 0, memory->getSize());
+ memory->commit();
+
+ memoryTest->fillMemory(mem, kValue);
+ memory->commit();
}));
+ MemoryBlock blk = {token, 0x200 /* size */, 0x100 /* offset */};
+ EXPECT_OK(memoryTest->haveSomeMemoryBlock(blk, [&](const MemoryBlock& blkBack) {
+ sp<IMemoryToken> tokenBack = blkBack.token;
+ EXPECT_TRUE(interfacesEqual(token, tokenBack));
+ EXPECT_EQ(blkBack.size, 0x200ULL);
+ EXPECT_EQ(blkBack.offset, 0x100ULL);
+ blk = blkBack;
+ }));
+
+ sp<IMemoryToken> mtoken = blk.token;
+ mtoken->get([&](const hidl_memory& mem) {
+ sp<IMemory> memory = mapMemory(mem);
+ uint8_t* data = static_cast<uint8_t*>(static_cast<void*>(memory->getPointer()));
+ EXPECT_NE(data, nullptr);
+ for (size_t i = 0; i < mem.size(); i++) {
+ EXPECT_EQ(kValue, data[i]);
+ }
+ });
}
TEST_F(HidlTest, NullSharedMemory) {
@@ -922,6 +972,23 @@
}));
}
+TEST_F(HidlTest, FooConvertToBoolIfSmallTest) {
+ hidl_vec<IFoo::Union> u = {
+ {.intValue = 7}, {.intValue = 0}, {.intValue = 1}, {.intValue = 8},
+ };
+ EXPECT_OK(foo->convertToBoolIfSmall(IFoo::Discriminator::INT, u, [&](const auto& res) {
+ ASSERT_EQ(4u, res.size());
+ EXPECT_EQ(IFoo::Discriminator::INT, res[0].discriminator);
+ EXPECT_EQ(u[0].intValue, res[0].value.intValue);
+ EXPECT_EQ(IFoo::Discriminator::BOOL, res[1].discriminator);
+ EXPECT_EQ(static_cast<bool>(u[1].intValue), res[1].value.boolValue);
+ EXPECT_EQ(IFoo::Discriminator::BOOL, res[2].discriminator);
+ EXPECT_EQ(static_cast<bool>(u[2].intValue), res[2].value.boolValue);
+ EXPECT_EQ(IFoo::Discriminator::INT, res[3].discriminator);
+ EXPECT_EQ(u[3].intValue, res[3].value.intValue);
+ }));
+}
+
TEST_F(HidlTest, FooDoThisTest) {
ALOGI("CLIENT call doThis.");
EXPECT_OK(foo->doThis(1.0f));
@@ -1669,6 +1736,72 @@
EXPECT_OK(bar->thisIsNew());
}
+TEST_F(HidlTest, TrieSimpleTest) {
+ trieInterface->newTrie([&](const TrieNode& trie) {
+ trieInterface->addStrings(trie, {"a", "ba"}, [&](const TrieNode& trie) {
+ trieInterface->containsStrings(
+ trie, {"", "a", "b", "ab", "ba", "c"}, [](const hidl_vec<bool>& response) {
+ EXPECT_EQ(response,
+ std::vector<bool>({false, true, false, false, true, false}));
+ });
+
+ trieInterface->addStrings(trie, {"", "ab", "bab"}, [&](const TrieNode& trie) {
+ trieInterface->containsStrings(
+ trie, {"", "a", "b", "ab", "ba", "c"}, [](const hidl_vec<bool>& response) {
+ EXPECT_EQ(response,
+ std::vector<bool>({true, true, false, true, true, false}));
+ });
+ });
+ });
+ });
+}
+
+struct RandomString {
+ std::string next() {
+ std::string ret(lengthDist(rng), 0);
+ std::generate(ret.begin(), ret.end(), [&]() { return charDist(rng); });
+ return ret;
+ }
+
+ RandomString() : rng(std::random_device{}()), lengthDist(5, 10), charDist('a', 'a' + 10) {}
+
+ private:
+ std::default_random_engine rng;
+ std::uniform_int_distribution<> lengthDist;
+ std::uniform_int_distribution<> charDist;
+};
+
+TEST_F(HidlTest, TrieStressTest) {
+ const size_t REQUEST_NUM = 1000;
+ RandomString stringGenerator;
+
+ trieInterface->newTrie([&](const TrieNode& trie) {
+ std::vector<std::string> strings(REQUEST_NUM);
+ for (auto& str : strings) {
+ str = stringGenerator.next();
+ }
+
+ trieInterface->addStrings(
+ trie, hidl_vec<hidl_string>(strings.begin(), strings.end()), [&](const TrieNode& trie) {
+ std::unordered_set<std::string> addedStrings(strings.begin(), strings.end());
+
+ for (size_t i = 0; i != REQUEST_NUM; ++i) {
+ strings.push_back(stringGenerator.next());
+ }
+
+ std::vector<bool> trueResponse(strings.size());
+ std::transform(strings.begin(), strings.end(), trueResponse.begin(),
+ [&](const std::string& str) {
+ return addedStrings.find(str) != addedStrings.end();
+ });
+
+ trieInterface->containsStrings(
+ trie, hidl_vec<hidl_string>(strings.begin(), strings.end()),
+ [&](const hidl_vec<bool>& response) { EXPECT_EQ(response, trueResponse); });
+ });
+ });
+}
+
class HidlMultithreadTest : public ::testing::Test {
public:
sp<IMultithread> multithreadInterface;
@@ -2010,9 +2143,11 @@
#endif
template <class T>
-static void waitForServer(const std::string &serviceName) {
- ::android::hardware::details::waitForHwService(T::descriptor, serviceName);
-}
+struct WaitForServer {
+ static void run(const std::string& serviceName) {
+ ::android::hardware::details::waitForHwService(T::descriptor, serviceName);
+ }
+};
int forkAndRunTests(TestMode mode, bool enableDelayMeasurementTests) {
pid_t child;
@@ -2122,7 +2257,7 @@
handleStatus(pStatus, "PASSTHROUGH");
}
if (b) {
- EACH_SERVER(waitForServer);
+ runOnEachServer<WaitForServer>();
ALOGI("BINDERIZED Test result = %d", bStatus);
handleStatus(bStatus, "BINDERIZED ");
}
diff --git a/test/hidl_test_helper b/test/hidl_test/hidl_test_helper
similarity index 100%
rename from test/hidl_test_helper
rename to test/hidl_test/hidl_test_helper
diff --git a/test/hidl_test/hidl_test_servers.cpp b/test/hidl_test/hidl_test_servers.cpp
new file mode 100644
index 0000000..2862002
--- /dev/null
+++ b/test/hidl_test/hidl_test_servers.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "hidl_test_servers"
+
+#include "hidl_test.h"
+
+#include <android-base/logging.h>
+
+#include <android/hardware/tests/baz/1.0/IBaz.h>
+
+#include <hidl/LegacySupport.h>
+
+#include <hwbinder/IPCThreadState.h>
+
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+using ::android::hardware::tests::baz::V1_0::IBaz;
+
+using ::android::hardware::defaultPassthroughServiceImplementation;
+using ::android::hardware::IPCThreadState;
+
+static std::vector<std::pair<std::string, pid_t>> gPidList;
+
+void signal_handler_server(int signal) {
+ if (signal == SIGTERM) {
+ IPCThreadState::shutdown();
+ exit(0);
+ }
+}
+
+template <class T>
+struct ForkServer {
+ static void run(const std::string& serviceName) {
+ pid_t pid;
+
+ if ((pid = fork()) == 0) {
+ // in child process
+ signal(SIGTERM, signal_handler_server);
+ int status = defaultPassthroughServiceImplementation<T>(serviceName);
+ exit(status);
+ }
+
+ gPidList.push_back({serviceName, pid});
+ }
+};
+
+static void killServer(pid_t pid, const char *serverName) {
+ if (kill(pid, SIGTERM)) {
+ ALOGE("Could not kill %s; errno = %d", serverName, errno);
+ } else {
+ int status;
+ ALOGE("Waiting for %s to exit...", serverName);
+ waitpid(pid, &status, 0);
+ if (status != 0) {
+ ALOGE("%s terminates abnormally with status %d", serverName, status);
+ } else {
+ ALOGE("%s killed successfully", serverName);
+ }
+ ALOGE("Continuing...");
+ }
+}
+
+void signal_handler(int signal) {
+ if (signal == SIGTERM) {
+ for (auto p : gPidList) {
+ killServer(p.second, p.first.c_str());
+ }
+ exit(0);
+ }
+}
+
+int main(int /* argc */, char* /* argv */ []) {
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+
+ runOnEachServer<ForkServer>();
+
+ ForkServer<IBaz>::run("dyingBaz");
+
+ signal(SIGTERM, signal_handler);
+ // Parent process should not exit before the forked child processes.
+ pause();
+
+ return 0;
+}
diff --git a/test/hidl_test/static_test.cpp b/test/hidl_test/static_test.cpp
new file mode 100644
index 0000000..3222f03
--- /dev/null
+++ b/test/hidl_test/static_test.cpp
@@ -0,0 +1,265 @@
+
+#include <android/hardware/tests/bar/1.0/IImportRules.h>
+#include <android/hardware/tests/expression/1.0/IExpression.h>
+#include <android/hardware/tests/foo/1.0/IFoo.h>
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_array;
+using ::android::hardware::tests::bar::V1_0::IImportRules;
+using ::android::hardware::tests::expression::V1_0::IExpression;
+using ::android::hardware::tests::foo::V1_0::IFoo;
+using ::android::sp;
+
+namespace {
+
+// Check array dimension order
+static_assert(std::is_same<IFoo::multidimArrayOne, hidl_array<int32_t, 5, 6, 7>>::value,
+ "hidl-gen output array dimention order is incorrect");
+static_assert(std::is_same<IFoo::multidimArrayTwo, hidl_array<int32_t, 8, 9, 10, 5, 6, 7>>::value,
+ "hidl-gen output array dimention order is incorrect");
+static_assert(
+ std::is_same<IFoo::multidimArrayThree, hidl_array<int32_t, 2, 3, 4, 8, 9, 10, 5, 6, 7>>::value,
+ "hidl-gen output array dimention order is incorrect");
+
+// Check correct type in ambiguous case
+static_assert(std::is_same<decltype(IFoo::S1::foo), IFoo::InnerTestStruct>::value,
+ "hidl-gen wrong (inner) type in output");
+static_assert(!std::is_same<decltype(IFoo::S1::foo), IFoo::S1::InnerTestStruct>::value,
+ "hidl-gen wrong (inner) type in output");
+static_assert((int32_t) decltype(IFoo::S2::foo)::VALUE == 0,
+ "hidl-gen wrong (inner) type in output");
+
+// Ensure (statically) that the types in IImportRules resolves to the correct types by
+// overriding the methods with fully namespaced types as arguments.
+struct MyImportRules : public IImportRules {
+ Return<void> rule0a(
+ const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+ return Void();
+ }
+
+ Return<void> rule0a1(
+ const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+ return Void();
+ }
+
+ Return<void> rule0b(
+ const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+ return Void();
+ }
+
+ Return<void> rule0b1(
+ const ::android::hardware::tests::bar::V1_0::IImportRules::Outer&) override {
+ return Void();
+ }
+
+ Return<void> rule0c(const ::android::hardware::tests::foo::V1_0::Outer&) override {
+ return Void();
+ }
+
+ Return<void> rule0d(const ::android::hardware::tests::foo::V1_0::Outer&) override {
+ return Void();
+ }
+
+ Return<void> rule0e(
+ const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
+ return Void();
+ }
+
+ Return<void> rule0f(
+ const ::android::hardware::tests::bar::V1_0::IImportRules::Outer::Inner&) override {
+ return Void();
+ }
+
+ Return<void> rule0g(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
+ return Void();
+ }
+
+ Return<void> rule0h(const ::android::hardware::tests::foo::V1_0::Outer::Inner&) override {
+ return Void();
+ }
+
+ Return<void> rule1a(const ::android::hardware::tests::bar::V1_0::Def&) override {
+ return Void();
+ }
+
+ Return<void> rule1b(const ::android::hardware::tests::foo::V1_0::Def&) override {
+ return Void();
+ }
+
+ Return<void> rule2a(const ::android::hardware::tests::foo::V1_0::Unrelated&) override {
+ return Void();
+ }
+
+ Return<void> rule2b(const sp<::android::hardware::tests::foo::V1_0::IFooCallback>&) override {
+ return Void();
+ }
+};
+
+template <typename E>
+constexpr bool operator==(E actual, typename std::underlying_type<E>::type expected) {
+ return expected == static_cast<typename std::underlying_type<E>::type>(actual);
+}
+
+// See IExpression.hal
+// Use the C++ compiler to compute these expressions and make sure hidl-gen emits the same result.
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec1 == 0, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec2 == 1, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec3 == -1, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec4 == ~0, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec5 == 2147483647, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec6 == -2147483648, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec7 == 2147483648, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec8 == -2147483649, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixDec9 == ~(-1), "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixHex1 == 0x7fffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixHex2 == 0x80000000, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::noSuffixHex3 == 0xffffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex1 == 0xffffffffl, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex2 == 0Xfffffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex3 == 0x7fffffffffffffff, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex4 == 0x8000000000000000, "");
+static_assert(IExpression::UInt64LiteralTypeGuessing::longHex5 == 0xFFFFFFFFFFFFFFFF, "");
+
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt32_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt32_2 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_2 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_3 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::decInt64_4 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexInt32_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexUInt32_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexUInt32_2 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexInt64_1 == 1, "");
+static_assert(IExpression::SuffixedLiteralTypeGuessing::hexUInt64_1 == 1, "");
+
+static_assert(IExpression::Int64LiteralTypeGuessing::noSuffixDec11 == -2147483648,
+ "1 + 0x7fffffff");
+static_assert(IExpression::Int64LiteralTypeGuessing::noSuffixDec12 == 2147483647, "0x80000000 - 1");
+
+static_assert(IExpression::Int32BitShifting::int32BitShift1 == 1 << 31, "");
+static_assert(IExpression::UInt32BitShifting::uint32BitShift1 == 1 << 31, "");
+static_assert(IExpression::Int64BitShifting::int64BitShift1 == 1LL << 63, "1l << 63");
+static_assert(IExpression::UInt64BitShifting::uint64BitShift1 == 1LL << 63, "1l << 63");
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconstant-logical-operand"
+#pragma clang diagnostic ignored "-Wlogical-op-parentheses"
+#pragma clang diagnostic ignored "-Wbitwise-op-parentheses"
+
+static_assert(IExpression::Precedence::literal == (4), "");
+static_assert(IExpression::Precedence::neg == (-4), "");
+static_assert(IExpression::Precedence::literalL == (-4L), "");
+static_assert(IExpression::Precedence::hex == (0xffffffff), "");
+static_assert(IExpression::Precedence::hexLong == (0xffffffffl), "");
+static_assert(IExpression::Precedence::hexLong2 == (0xfffffffff), "");
+static_assert(IExpression::Precedence::simpleArithmetic == (4 + 1), "");
+static_assert(IExpression::Precedence::simpleArithmetic2 == (2 + 3 - 4), "");
+static_assert(IExpression::Precedence::simpleArithmetic3 == (2 - 3 + 4), "");
+static_assert(IExpression::Precedence::simpleBoolExpr == (1 == 4), "");
+static_assert(IExpression::Precedence::simpleLogical == (1 && 1), "");
+static_assert(IExpression::Precedence::simpleLogical2 == (1 || 1 && 0),
+ "&& higher than || in HIDL");
+static_assert(IExpression::Precedence::simpleComp == (1 < 2), "");
+static_assert(IExpression::Precedence::boolExpr1 == (!((3 != 4 || (2 < 3 <= 3 > 4)) >= 0)), "");
+static_assert(IExpression::Precedence::boolExpr == (1 == 7 && !((3 != 4 || (2 < 3 <= 3 > 4)) >= 0)),
+ "");
+static_assert(IExpression::Precedence::simpleBitShift == (1 << 2), "");
+static_assert(IExpression::Precedence::simpleBitShift2 == (4 >> 1), "");
+// Shifting negative number of bits is UB in HIDL, so the value is not tested.
+// static_assert(IExpression::Precedence::simpleBitShiftNeg == (4 << -1), "");
+static_assert(IExpression::Precedence::simpleArithmeticRightShift == (1 << 31 >> 31), "");
+static_assert(IExpression::Precedence::simpleBitExpr == (1 | 16 >> 2), "");
+static_assert(IExpression::Precedence::simpleBitExpr2 == (0x0f ^ 0x33 & 0x99),
+ "& higher than ^ in HIDL");
+static_assert(IExpression::Precedence::bitExpr == (~42 & (1 << 3 | 16 >> 2) ^ 7),
+ "& higher than ^ in HIDL");
+static_assert(IExpression::Precedence::arithmeticExpr == (2 + 3 - 4 * -7 / (10 % 3)), "");
+static_assert(IExpression::Precedence::messyExpr == (2 + (-3 & 4 / 7)), "");
+static_assert(IExpression::Precedence::paranExpr == ((((((1 + 1)))))), "");
+static_assert(IExpression::Precedence::ternary == (1 ? 2 : 3), "");
+static_assert(IExpression::Precedence::ternary2 == (1 && 2 ? 3 : 4), "");
+static_assert(IExpression::Precedence::complicatedTernary2 == (1 - 1 && 2 + 3 || 5 ? 7 * 8 : -3),
+ "&& higher than || in HIDL");
+
+#pragma clang diagnostic pop
+
+static_assert(IExpression::OperatorSanityCheck::plus == 1, "");
+static_assert(IExpression::OperatorSanityCheck::minus == 1, "");
+static_assert(IExpression::OperatorSanityCheck::product == 1, "");
+static_assert(IExpression::OperatorSanityCheck::division == 1, "");
+static_assert(IExpression::OperatorSanityCheck::mod == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_or == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_or2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_and == 1, "");
+static_assert(IExpression::OperatorSanityCheck::bit_xor == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lt1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lt2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gt1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gt2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gte1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::gte2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lte1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lte2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::ne1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::ne2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::lshift == 1, "");
+static_assert(IExpression::OperatorSanityCheck::rshift == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor3 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logor4 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand1 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand2 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand3 == 1, "");
+static_assert(IExpression::OperatorSanityCheck::logand4 == 1, "");
+
+static_assert(IExpression::Grayscale::WHITE == 126, "");
+static_assert(IExpression::Grayscale::GRAY == 127, "");
+static_assert(IExpression::Grayscale::DARK_GRAY == -128, "");
+static_assert(IExpression::Grayscale::BLACK == -127, "");
+
+static_assert(IExpression::Color::RED == -126, "");
+static_assert(IExpression::Color::RUBY == 0, "");
+static_assert(IExpression::Color::GREEN == 1, "");
+static_assert(IExpression::Color::BLUE == 5, "");
+static_assert(IExpression::Color::CYAN == 6, "");
+static_assert(IExpression::Color::ORANGE == 7, "");
+static_assert(IExpression::Color::ROSE == 126, "");
+
+static_assert(std::is_same<std::underlying_type<IExpression::Foo1>::type,
+ std::underlying_type<IExpression::Foo2>::type>::value,
+ "");
+static_assert(std::is_same<std::underlying_type<IExpression::Foo1>::type,
+ std::underlying_type<IExpression::Foo3>::type>::value,
+ "");
+static_assert(std::is_same<std::underlying_type<IExpression::Foo1>::type,
+ std::underlying_type<IExpression::Foo4>::type>::value,
+ "");
+static_assert(IExpression::Foo3::BAR1 == 0, "");
+static_assert(IExpression::Foo3::BAR2 == 10, "");
+static_assert(IExpression::Foo4::BAR3 == 11, "");
+static_assert(IExpression::Foo4::BAR4 == 21, "");
+
+static_assert(IExpression::Number::MAX == 255, "");
+static_assert(IExpression::Number::MAX_PLUS_1 == 0, "");
+static_assert(IExpression::Number::MAX_PLUS_2 == 1, "");
+
+static_assert(IExpression::Constants::CONST_FOO == 0, "");
+static_assert(IExpression::Constants::CONST_BAR == 70, "");
+static_assert(IExpression::Constants::MAX_ARRAY_SIZE == 20, "");
+static_assert(IExpression::Constants::MAX_ARRAY_SIZE2 == 21, "");
+static_assert(IExpression::Constants::MAX_ARRAY_SIZE3 == 40, "");
+static_assert(IExpression::Constants::MY_INT32_MAX_MINUS_1 == 0x7FFFFFFE, "");
+static_assert(IExpression::Constants::MY_INT32_MAX == 0x7FFFFFFF, "");
+static_assert(IExpression::Constants::MY_INT32_MIN == 0x80000000, "");
+static_assert(IExpression::Constants::MY_INT32_MIN_PLUS_1 == 0x80000001, "");
+
+// Ensure (statically) that the array sizes in function arguments are correct.
+struct Expression : public IExpression {
+ Return<void> foo1(const hidl_array<int32_t, 1>&) override { return Void(); }
+ Return<void> foo2(const hidl_array<int32_t, 13>&) override { return Void(); }
+ Return<void> foo3(const hidl_array<int32_t, 20>&) override { return Void(); }
+};
+
+} // anonymous namespace
diff --git a/test/hidl_test_servers.cpp b/test/hidl_test_servers.cpp
deleted file mode 100644
index cd14d30..0000000
--- a/test/hidl_test_servers.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "hidl_test_servers"
-
-#include "hidl_test.h"
-
-#include <android-base/logging.h>
-
-#include <android/hardware/tests/bar/1.0/IBar.h>
-#include <android/hardware/tests/baz/1.0/IBaz.h>
-#include <android/hardware/tests/foo/1.0/BnHwSimple.h>
-#include <android/hardware/tests/foo/1.0/BpHwSimple.h>
-#include <android/hardware/tests/foo/1.0/BsSimple.h>
-#include <android/hardware/tests/hash/1.0/IHash.h>
-#include <android/hardware/tests/inheritance/1.0/IChild.h>
-#include <android/hardware/tests/inheritance/1.0/IFetcher.h>
-#include <android/hardware/tests/inheritance/1.0/IParent.h>
-#include <android/hardware/tests/memory/1.0/IMemoryTest.h>
-#include <android/hardware/tests/multithread/1.0/IMultithread.h>
-#include <android/hardware/tests/pointer/1.0/IGraph.h>
-#include <android/hardware/tests/pointer/1.0/IPointer.h>
-
-#include <hidl/LegacySupport.h>
-
-#include <hwbinder/IPCThreadState.h>
-
-#include <sys/wait.h>
-#include <signal.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-using ::android::hardware::tests::bar::V1_0::IBar;
-using ::android::hardware::tests::baz::V1_0::IBaz;
-using ::android::hardware::tests::hash::V1_0::IHash;
-using ::android::hardware::tests::inheritance::V1_0::IFetcher;
-using ::android::hardware::tests::inheritance::V1_0::IParent;
-using ::android::hardware::tests::inheritance::V1_0::IChild;
-using ::android::hardware::tests::pointer::V1_0::IGraph;
-using ::android::hardware::tests::pointer::V1_0::IPointer;
-using ::android::hardware::tests::memory::V1_0::IMemoryTest;
-using ::android::hardware::tests::multithread::V1_0::IMultithread;
-
-using ::android::hardware::defaultPassthroughServiceImplementation;
-using ::android::hardware::IPCThreadState;
-
-static std::vector<std::pair<std::string, pid_t>> gPidList;
-
-void signal_handler_server(int signal) {
- if (signal == SIGTERM) {
- IPCThreadState::shutdown();
- exit(0);
- }
-}
-
-template <class T>
-static void forkServer(const std::string &serviceName) {
- pid_t pid;
-
- if ((pid = fork()) == 0) {
- // in child process
- signal(SIGTERM, signal_handler_server);
- int status = defaultPassthroughServiceImplementation<T>(serviceName);
- exit(status);
- }
-
- gPidList.push_back({serviceName, pid});
- return;
-}
-
-static void killServer(pid_t pid, const char *serverName) {
- if (kill(pid, SIGTERM)) {
- ALOGE("Could not kill %s; errno = %d", serverName, errno);
- } else {
- int status;
- ALOGE("Waiting for %s to exit...", serverName);
- waitpid(pid, &status, 0);
- if (status != 0) {
- ALOGE("%s terminates abnormally with status %d", serverName, status);
- } else {
- ALOGE("%s killed successfully", serverName);
- }
- ALOGE("Continuing...");
- }
-}
-
-void signal_handler(int signal) {
- if (signal == SIGTERM) {
- for (auto p : gPidList) {
- killServer(p.second, p.first.c_str());
- }
- exit(0);
- }
-}
-
-int main(int /* argc */, char* /* argv */ []) {
- setenv("TREBLE_TESTING_OVERRIDE", "true", true);
-
- EACH_SERVER(forkServer);
-
- forkServer<IBaz>("dyingBaz");
-
- signal(SIGTERM, signal_handler);
- // Parent process should not exit before the forked child processes.
- pause();
-
- return 0;
-}
diff --git a/test/host_test/Android.bp b/test/host_test/Android.bp
new file mode 100644
index 0000000..d5e6511
--- /dev/null
+++ b/test/host_test/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2017 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.
+
+cc_test_host {
+ name: "hidl-gen-host_test",
+ defaults: ["hidl-gen-defaults"],
+
+ shared_libs: [
+ "libhidl-gen",
+ "libhidl-gen-ast",
+ "libhidl-gen-utils",
+ ],
+
+ srcs: ["main.cpp"],
+}
\ No newline at end of file
diff --git a/test/host_test/main.cpp b/test/host_test/main.cpp
new file mode 100644
index 0000000..9377306
--- /dev/null
+++ b/test/host_test/main.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libhidl-gen-utils"
+
+#include <gtest/gtest.h>
+
+#include <ConstantExpression.h>
+#include <Coordinator.h>
+#include <hidl-util/FQName.h>
+
+#define EXPECT_EQ_OK(expectResult, call, ...) \
+ do { \
+ std::string result; \
+ status_t err = (call)(__VA_ARGS__, &result); \
+ EXPECT_EQ(err, ::android::OK); \
+ EXPECT_EQ(expectResult, result); \
+ } while (false)
+
+namespace android {
+
+class HidlGenHostTest : public ::testing::Test {};
+
+TEST_F(HidlGenHostTest, CoordinatorTest) {
+ Coordinator coordinator;
+
+ std::string error;
+ EXPECT_EQ(OK, coordinator.addPackagePath("a.b", "a1/b1", &error));
+ EXPECT_TRUE(error.empty());
+ EXPECT_NE(OK, coordinator.addPackagePath("a.b", "a2/b2/", &error));
+ EXPECT_FALSE(error.empty());
+
+ coordinator.addDefaultPackagePath("a.b", "a3/b3/"); // should take path above
+ coordinator.addDefaultPackagePath("a.c", "a4/b4/"); // should succeed
+
+ EXPECT_EQ_OK("a.b", coordinator.getPackageRoot, FQName("a.b.foo@1.0"));
+ EXPECT_EQ_OK("a.c", coordinator.getPackageRoot, FQName("a.c.foo.bar@1.0::IFoo"));
+
+ // getPackagePath(fqname, relative, sanitized, ...)
+ EXPECT_EQ_OK("a1/b1/foo/1.0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), false, false);
+ EXPECT_EQ_OK("a4/b4/foo/bar/1.0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"),
+ false, false);
+ EXPECT_EQ_OK("a1/b1/foo/V1_0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), false, true);
+ EXPECT_EQ_OK("a4/b4/foo/bar/V1_0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"),
+ false, true);
+ EXPECT_EQ_OK("foo/1.0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), true, false);
+ EXPECT_EQ_OK("foo/bar/1.0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"), true,
+ false);
+ EXPECT_EQ_OK("foo/V1_0/", coordinator.getPackagePath, FQName("a.b.foo@1.0"), true, true);
+ EXPECT_EQ_OK("foo/bar/V1_0/", coordinator.getPackagePath, FQName("a.c.foo.bar@1.0::IFoo"), true,
+ true);
+}
+
+TEST_F(HidlGenHostTest, CoordinatorFilepathTest) {
+ using Location = Coordinator::Location;
+
+ Coordinator coordinator;
+ coordinator.setOutputPath("foo/");
+ coordinator.setRootPath("bar/");
+
+ std::string error;
+ EXPECT_EQ(OK, coordinator.addPackagePath("a.b", "a1/b1", &error));
+ EXPECT_TRUE(error.empty());
+
+ const static FQName kName = FQName("a.b.c@1.2");
+
+ // get file names
+ EXPECT_EQ_OK("foo/x.y", coordinator.getFilepath, kName, Location::DIRECT, "x.y");
+ EXPECT_EQ_OK("foo/a1/b1/c/1.2/x.y", coordinator.getFilepath, kName, Location::PACKAGE_ROOT,
+ "x.y");
+ EXPECT_EQ_OK("foo/a/b/c/1.2/x.y", coordinator.getFilepath, kName, Location::GEN_OUTPUT, "x.y");
+ EXPECT_EQ_OK("foo/a/b/c/V1_2/x.y", coordinator.getFilepath, kName, Location::GEN_SANITIZED,
+ "x.y");
+
+ // get directories
+ EXPECT_EQ_OK("foo/", coordinator.getFilepath, kName, Location::DIRECT, "");
+ EXPECT_EQ_OK("foo/a1/b1/c/1.2/", coordinator.getFilepath, kName, Location::PACKAGE_ROOT, "");
+ EXPECT_EQ_OK("foo/a/b/c/1.2/", coordinator.getFilepath, kName, Location::GEN_OUTPUT, "");
+ EXPECT_EQ_OK("foo/a/b/c/V1_2/", coordinator.getFilepath, kName, Location::GEN_SANITIZED, "");
+}
+
+TEST_F(HidlGenHostTest, LocationTest) {
+ Location a{{"file", 3, 4}, {"file", 3, 5}};
+ Location b{{"file", 3, 6}, {"file", 3, 7}};
+ Location c{{"file", 4, 4}, {"file", 4, 5}};
+
+ Location other{{"other", 0, 0}, {"other", 0, 1}};
+
+ EXPECT_LT(a, b);
+ EXPECT_LT(b, c);
+ EXPECT_LT(a, c);
+ EXPECT_FALSE(Location::inSameFile(a, other));
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/test/impl_test/Android.bp b/test/impl_test/Android.bp
index b220f24..a440eb9 100644
--- a/test/impl_test/Android.bp
+++ b/test/impl_test/Android.bp
@@ -42,4 +42,9 @@
"libutils",
"android.hardware.tests.foo@1.0",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
}
diff --git a/test/java_test/Android.bp b/test/java_test/Android.bp
index 23bdf08..f7b731d 100644
--- a/test/java_test/Android.bp
+++ b/test/java_test/Android.bp
@@ -2,6 +2,8 @@
name: "hidl_test_java_native",
srcs: ["hidl_test_java_native.cpp"],
+ cflags: ["-Wall", "-Werror"],
+
shared_libs: [
"libbase",
"libhidlbase",
diff --git a/test/java_test/Android.mk b/test/java_test/Android.mk
index 1f2e868..a41fa5d 100644
--- a/test/java_test/Android.mk
+++ b/test/java_test/Android.mk
@@ -7,6 +7,7 @@
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_JAVA_LIBRARIES := \
android.hidl.base-V1.0-java \
+ android.hidl.manager-V1.0-java \
android.hardware.tests.baz-V1.0-java \
android.hardware.tests.expression-V1.0-java \
android.hardware.tests.inheritance-V1.0-java
@@ -20,13 +21,15 @@
LOCAL_MODULE_CLASS := NATIVE_TESTS
LOCAL_SRC_FILES := hidl_test_java
-LOCAL_ADDITIONAL_DEPENDENCIES := \
- hidl_test_java_lib \
- hidl_test_java_native \
-
LOCAL_REQUIRED_MODULES := \
hidl_test_java_lib \
+ hidl_test_java_native \
android.hidl.base-V1.0-java \
+ android.hidl.manager-V1.0-java \
android.hardware.tests.baz-V1.0-java
+LOCAL_REQUIRED_MODULES_arm64 := hidl_test_java_native_32
+LOCAL_REQUIRED_MODULES_x86_64 := hidl_test_java_native_32
+LOCAL_REQUIRED_MODULES_mips64 := hidl_test_java_native_32
+
include $(BUILD_PREBUILT)
diff --git a/test/java_test/hidl_test_java b/test/java_test/hidl_test_java
index 2076d00..1cdfb3f 100644
--- a/test/java_test/hidl_test_java
+++ b/test/java_test/hidl_test_java
@@ -22,7 +22,6 @@
# Test Java server with native client
app_process $base/bin com.android.commands.hidl_test_java.HidlTestJava -s &
NATIVE_PID=$!
- sleep 1
$native -c \
&& echo "native client => Java server PASSED" \
|| (echo "native client => Java server FAILED" && false) || e=1
@@ -38,4 +37,4 @@
echo "Summary: $e"
[ $e -eq 0 ] && echo "All tests PASSED." || echo "Test(s) FAILED."
-exit $e
\ No newline at end of file
+exit $e
diff --git a/test/java_test/hidl_test_java_native.cpp b/test/java_test/hidl_test_java_native.cpp
index 0d3a7c3..d155e09 100644
--- a/test/java_test/hidl_test_java_native.cpp
+++ b/test/java_test/hidl_test_java_native.cpp
@@ -22,7 +22,7 @@
#include <android/hardware/tests/baz/1.0/IBaz.h>
#include <hidl/LegacySupport.h>
-
+#include <hidl/ServiceManagement.h>
#include <gtest/gtest.h>
#include <hidl/HidlTransportSupport.h>
@@ -77,9 +77,13 @@
void SetUp() override {
using namespace ::android::hardware;
+ ::android::hardware::details::waitForHwService(
+ IBaz::descriptor, "baz");
+
baz = IBaz::getService("baz");
CHECK(baz != NULL);
+ CHECK(baz->isRemote());
}
void TearDown() override {
@@ -489,6 +493,87 @@
}));
}
+TEST_F(HidlTest, BazTestArrays) {
+ IBase::LotsOfPrimitiveArrays in;
+
+ for (size_t i = 0; i < 128; ++i) {
+ in.byte1[i] = i;
+ in.boolean1[i] = (i & 4) != 0;
+ in.double1[i] = i;
+ }
+
+ size_t k = 0;
+ for (size_t i = 0; i < 8; ++i) {
+ for (size_t j = 0; j < 128; ++j, ++k) {
+ in.byte2[i][j] = k;
+ in.boolean2[i][j] = (k & 4) != 0;
+ in.double2[i][j] = k;
+ }
+ }
+
+ size_t m = 0;
+ for (size_t i = 0; i < 8; ++i) {
+ for (size_t j = 0; j < 16; ++j) {
+ for (size_t k = 0; k < 128; ++k, ++m) {
+ in.byte3[i][j][k] = m;
+ in.boolean3[i][j][k] = (m & 4) != 0;
+ in.double3[i][j][k] = m;
+ }
+ }
+ }
+
+ EXPECT_OK(
+ baz->testArrays(in,
+ [&](const auto &out) {
+ EXPECT_EQ(in, out);
+ }));
+}
+
+TEST_F(HidlTest, BazTestByteVecs) {
+ hidl_vec<IBase::ByteOneDim> in;
+ in.resize(8);
+
+ size_t k = 0;
+ for (size_t i = 0; i < in.size(); ++i) {
+ for (size_t j = 0; j < 128; ++j, ++k) {
+ in[i][j] = k;
+ }
+ }
+
+ EXPECT_OK(baz->testByteVecs(
+ in, [&](const auto &out) { EXPECT_EQ(in, out); }));
+}
+
+TEST_F(HidlTest, BazTestBooleanVecs) {
+ hidl_vec<IBase::BooleanOneDim> in;
+ in.resize(8);
+
+ size_t k = 0;
+ for (size_t i = 0; i < in.size(); ++i) {
+ for (size_t j = 0; j < 128; ++j, ++k) {
+ in[i][j] = (k & 4) != 0;
+ }
+ }
+
+ EXPECT_OK(baz->testBooleanVecs(
+ in, [&](const auto &out) { EXPECT_EQ(in, out); }));
+}
+
+TEST_F(HidlTest, BazTestDoubleVecs) {
+ hidl_vec<IBase::DoubleOneDim> in;
+ in.resize(8);
+
+ size_t k = 0;
+ for (size_t i = 0; i < in.size(); ++i) {
+ for (size_t j = 0; j < 128; ++j, ++k) {
+ in[i][j] = k;
+ }
+ }
+
+ EXPECT_OK(baz->testDoubleVecs(
+ in, [&](const auto &out) { EXPECT_EQ(in, out); }));
+}
+
int main(int argc, char **argv) {
setenv("TREBLE_TESTING_OVERRIDE", "true", true);
diff --git a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
index 16dc793..f670732 100644
--- a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
+++ b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
@@ -16,6 +16,7 @@
package com.android.commands.hidl_test_java;
+import android.hidl.manager.V1_0.IServiceManager;
import android.hardware.tests.baz.V1_0.IBase;
import android.hardware.tests.baz.V1_0.IBaz;
import android.hardware.tests.baz.V1_0.IQuux;
@@ -29,6 +30,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.NoSuchElementException;
+import java.util.Objects;
public final class HidlTestJava {
private static final String TAG = "HidlTestJava";
@@ -115,6 +117,20 @@
throw new RuntimeException();
}
+ // .equals and HidlSupport.interfacesEqual should have the same behavior.
+ private void ExpectEqual(android.hidl.base.V1_0.IBase l, android.hidl.base.V1_0.IBase r) {
+ ExpectTrue(Objects.equals(l, r));
+ ExpectTrue(Objects.equals(r, l));
+ ExpectTrue(HidlSupport.interfacesEqual(l, r));
+ ExpectTrue(HidlSupport.interfacesEqual(r, l));
+ }
+ private void ExpectNotEqual(android.hidl.base.V1_0.IBase l, android.hidl.base.V1_0.IBase r) {
+ ExpectFalse(Objects.equals(l, r));
+ ExpectFalse(Objects.equals(r, l));
+ ExpectFalse(HidlSupport.interfacesEqual(l, r));
+ ExpectFalse(HidlSupport.interfacesEqual(r, l));
+ }
+
class BazCallback extends IBazCallback.Stub {
private boolean mCalled;
@@ -135,6 +151,12 @@
public void hey() {
mCalled = true;
}
+
+ @Override public boolean equals(Object other) {
+ return other != null && other.getClass() == BazCallback.class &&
+ ((BazCallback) other).mCalled == mCalled;
+ }
+ @Override public int hashCode() { return mCalled ? 1 : 0; }
}
private String numberToEnglish(int x) {
@@ -240,6 +262,8 @@
// Test proper exceptions are thrown
try {
IBase proxy = IBase.getService("this-doesn't-exist");
+ // this should never run
+ ExpectTrue(false);
} catch (Exception e) {
ExpectTrue(e instanceof NoSuchElementException);
}
@@ -259,7 +283,18 @@
ExpectTrue(quuxProxy == null);
}
+ {
+ // Test waiting API
+ IBase baseProxyA = IBaz.getService("baz", true /* retry */);
+ ExpectTrue(baseProxyA != null);
+ IBase baseProxyB = IBaz.getService("baz", false /* retry */);
+ ExpectTrue(baseProxyB != null);
+ }
+
IBaz proxy = IBaz.getService("baz");
+
+ proxy.ping();
+
proxy.someBaseMethod();
{
@@ -630,6 +665,142 @@
}
}
+ {
+ // TestArrays
+ IBase.LotsOfPrimitiveArrays in = new IBase.LotsOfPrimitiveArrays();
+
+ for (int i = 0; i < 128; ++i) {
+ in.byte1[i] = (byte)i;
+ in.boolean1[i] = (i & 4) != 0;
+ in.double1[i] = i;
+ }
+
+ int m = 0;
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 128; ++j, ++m) {
+ in.byte2[i][j] = (byte)m;
+ in.boolean2[i][j] = (m & 4) != 0;
+ in.double2[i][j] = m;
+ }
+ }
+
+ m = 0;
+ for (int i = 0; i < 8; ++i) {
+ for (int j = 0; j < 16; ++j) {
+ for (int k = 0; k < 128; ++k, ++m) {
+ in.byte3[i][j][k] = (byte)m;
+ in.boolean3[i][j][k] = (m & 4) != 0;
+ in.double3[i][j][k] = m;
+ }
+ }
+ }
+
+ IBase.LotsOfPrimitiveArrays out = proxy.testArrays(in);
+ ExpectTrue(in.equals(out));
+ }
+
+ {
+ // testByteVecs
+
+ ArrayList<byte[]> in = new ArrayList<byte[]>();
+
+ int k = 0;
+ for (int i = 0; i < in.size(); ++i) {
+ byte[] elem = new byte[128];
+ for (int j = 0; j < 128; ++j, ++k) {
+ elem[j] = (byte)k;
+ }
+ in.add(elem);
+ }
+
+ ArrayList<byte[]> out = proxy.testByteVecs(in);
+ ExpectTrue(in.equals(out));
+ }
+
+ {
+ // testBooleanVecs
+
+ ArrayList<boolean[]> in = new ArrayList<boolean[]>();
+
+ int k = 0;
+ for (int i = 0; i < in.size(); ++i) {
+ boolean[] elem = new boolean[128];
+ for (int j = 0; j < 128; ++j, ++k) {
+ elem[j] = (k & 4) != 0;
+ }
+ in.add(elem);
+ }
+
+ ArrayList<boolean[]> out = proxy.testBooleanVecs(in);
+ ExpectTrue(in.equals(out));
+ }
+
+ {
+ // testDoubleVecs
+
+ ArrayList<double[]> in = new ArrayList<double[]>();
+
+ int k = 0;
+ for (int i = 0; i < in.size(); ++i) {
+ double[] elem = new double[128];
+ for (int j = 0; j < 128; ++j, ++k) {
+ elem[j] = k;
+ }
+ in.add(elem);
+ }
+
+ ArrayList<double[]> out = proxy.testDoubleVecs(in);
+ ExpectTrue(in.equals(out));
+ }
+
+ {
+ // testProxyEquals
+ // TODO(b/68727931): test passthrough services as well.
+
+ IBase proxy1 = IBase.getService("baz");
+ IBase proxy2 = IBase.getService("baz");
+ IBaz proxy3 = IBaz.getService("baz");
+ IBazCallback callback1 = new BazCallback();
+ IBazCallback callback2 = new BazCallback();
+ IServiceManager manager = IServiceManager.getService();
+
+ // test hwbinder proxies
+ ExpectEqual(proxy1, proxy2); // same proxy class
+ ExpectEqual(proxy1, proxy3); // different proxy class
+
+ // negative tests
+ ExpectNotEqual(proxy1, null);
+ ExpectNotEqual(proxy1, callback1); // proxy != stub
+ ExpectNotEqual(proxy1, manager);
+
+ // HidlSupport.interfacesEqual use overridden .equals for stubs
+ ExpectEqual(callback1, callback1);
+ ExpectEqual(callback1, callback2);
+ callback1.hey();
+ ExpectNotEqual(callback1, callback2);
+ callback2.hey();
+ ExpectEqual(callback1, callback2);
+
+ // test hash for proxies
+ java.util.HashSet<IBase> set = new java.util.HashSet<>();
+ set.add(proxy1);
+ ExpectTrue(set.contains(proxy1)); // hash is stable
+ ExpectTrue(set.contains(proxy2));
+ ExpectFalse(set.contains(manager));
+ }
+ {
+ IBaz baz = IBaz.getService("baz");
+ ExpectTrue(baz != null);
+ IBaz.StructWithInterface swi = new IBaz.StructWithInterface();
+ swi.dummy = baz;
+ swi.number = 12345678;
+ IBaz.StructWithInterface swi_back = baz.haveSomeStructWithInterface(swi);
+ ExpectTrue(swi_back != null);
+ ExpectTrue(swi_back.dummy != null);
+ ExpectTrue(HidlSupport.interfacesEqual(baz, swi_back.dummy));
+ ExpectTrue(swi_back.number == 12345678);
+ }
+
// --- DEATH RECIPIENT TESTING ---
// This must always be done last, since it will kill the native server process
HidlDeathRecipient recipient1 = new HidlDeathRecipient();
@@ -817,6 +988,22 @@
(byte)(second.value & bf), (byte)((bf | bf) & third));
}
+ public LotsOfPrimitiveArrays testArrays(LotsOfPrimitiveArrays in) {
+ return in;
+ }
+
+ public ArrayList<byte[]> testByteVecs(ArrayList<byte[]> in) {
+ return in;
+ }
+
+ public ArrayList<boolean[]> testBooleanVecs(ArrayList<boolean[]> in) {
+ return in;
+ }
+
+ public ArrayList<double[]> testDoubleVecs(ArrayList<double[]> in) {
+ return in;
+ }
+
public byte returnABitField() {
return 0;
}
@@ -894,6 +1081,10 @@
public void returnABunchOfStrings(returnABunchOfStringsCallback cb) {
cb.onValues("Eins", "Zwei", "Drei");
}
+
+ public StructWithInterface haveSomeStructWithInterface(StructWithInterface swi) {
+ return swi;
+ }
}
private void server() throws RemoteException {
diff --git a/test/run_all_device_tests.sh b/test/run_all_device_tests.sh
new file mode 100755
index 0000000..a32558a
--- /dev/null
+++ b/test/run_all_device_tests.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+function run() {
+ local FAILED_TESTS=()
+
+ # Tests directly relevant to HIDL infrustructure but aren't
+ # located in system/tools/hidl
+ local RELATED_RUNTIME_TESTS=(\
+ libhidl_test \
+ )
+
+ local RUN_TIME_TESTS=(\
+ libhidl-gen-utils_test \
+ )
+ RUN_TIME_TESTS+=(${RELATED_RUNTIME_TESTS[@]})
+
+ local SCRIPT_TESTS=(\
+ hidl_test\
+ hidl_test_java\
+ fmq_test\
+ )
+
+ $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode -j \
+ ${RUN_TIME_TESTS[*]} ${SCRIPT_TESTS[*]} || return
+
+ adb sync || return
+
+ local BITNESS=("nativetest" "nativetest64")
+
+ for test in ${RUN_TIME_TESTS[@]}; do
+ for bits in ${BITNESS[@]}; do
+ echo $bits $test
+ adb shell /data/$bits/$test/$test ||
+ FAILED_TESTS+=("$bits:$test")
+ done
+ done
+
+ for test in ${SCRIPT_TESTS[@]}; do
+ echo $test
+ adb shell /data/nativetest64/$test ||
+ FAILED_TESTS+=("$test")
+ done
+
+ echo
+ echo ===== ALL DEVICE TESTS SUMMARY =====
+ echo
+ if [ ${#FAILED_TESTS[@]} -gt 0 ]; then
+ for failed in ${FAILED_TESTS[@]}; do
+ echo "FAILED TEST: $failed"
+ done
+ else
+ echo "SUCCESS"
+ fi
+}
+
+run
\ No newline at end of file
diff --git a/test/run_all_host_tests.sh b/test/run_all_host_tests.sh
new file mode 100755
index 0000000..442efd7
--- /dev/null
+++ b/test/run_all_host_tests.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+function run() {
+ local FAILED_TESTS=()
+
+ local COMPILE_TIME_TESTS=(\
+ hidl_error_test \
+ hidl_export_test \
+ hidl_hash_test \
+ hidl_impl_test \
+ android.hardware.tests.foo@1.0-vts.driver \
+ android.hardware.tests.foo@1.0-vts.profiler)
+
+ local RUN_TIME_TESTS=(\
+ libhidl-gen-utils_test \
+ hidl-gen-host_test \
+ )
+
+ $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode -j \
+ ${COMPILE_TIME_TESTS[*]} ${RUN_TIME_TESTS[*]} || return
+
+ local BITNESS=("nativetest" "nativetest64")
+
+ for bits in ${BITNESS[@]}; do
+ for test in ${RUN_TIME_TESTS[@]}; do
+ echo $bits $test
+ $ANDROID_BUILD_TOP/out/host/linux-x86/$bits/$test/$test ||
+ FAILED_TESTS+=("$bits:$test")
+ done
+ done
+
+ echo
+ echo ===== ALL HOST TESTS SUMMARY =====
+ echo
+ if [ ${#FAILED_TESTS[@]} -gt 0 ]; then
+ for failed in ${FAILED_TESTS[@]}; do
+ echo "FAILED TEST: $failed"
+ done
+ else
+ echo "SUCCESS"
+ fi
+}
+
+run
\ No newline at end of file
diff --git a/test/utils_test/Android.bp b/test/utils_test/Android.bp
new file mode 100644
index 0000000..8637a17
--- /dev/null
+++ b/test/utils_test/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2017 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.
+
+cc_test {
+ name: "libhidl-gen-utils_test",
+ defaults: ["hidl-gen-defaults"],
+ host_supported: true,
+
+ shared_libs: [
+ "libhidl-gen-utils",
+ ],
+
+ srcs: ["main.cpp"],
+}
\ No newline at end of file
diff --git a/test/utils_test/main.cpp b/test/utils_test/main.cpp
new file mode 100644
index 0000000..b2c8576
--- /dev/null
+++ b/test/utils_test/main.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "libhidl-gen-utils"
+
+#include <hidl-util/FqInstance.h>
+#include <hidl-util/StringHelper.h>
+
+#include <gtest/gtest.h>
+#include <vector>
+
+using ::android::FqInstance;
+using ::android::StringHelper;
+
+class LibHidlGenUtilsTest : public ::testing::Test {};
+
+TEST_F(LibHidlGenUtilsTest, EndsWithTest) {
+ EXPECT_TRUE(StringHelper::EndsWith("", ""));
+ EXPECT_TRUE(StringHelper::EndsWith("a", ""));
+ EXPECT_TRUE(StringHelper::EndsWith("b", "b"));
+ EXPECT_TRUE(StringHelper::EndsWith("ab", "b"));
+ EXPECT_TRUE(StringHelper::EndsWith("ab", "ab"));
+ EXPECT_TRUE(StringHelper::EndsWith("abcd", "bcd"));
+ EXPECT_TRUE(StringHelper::EndsWith("abcd", "abcd"));
+ EXPECT_TRUE(StringHelper::EndsWith("abcdefghijk", "ijk"));
+ EXPECT_TRUE(StringHelper::EndsWith("abcdefghijk", "bcdefghijk"));
+
+ EXPECT_FALSE(StringHelper::EndsWith("", "a"));
+ EXPECT_FALSE(StringHelper::EndsWith("b", "a"));
+ EXPECT_FALSE(StringHelper::EndsWith("abcd", "ab"));
+}
+
+TEST_F(LibHidlGenUtilsTest, StartsWithTest) {
+ EXPECT_TRUE(StringHelper::StartsWith("", ""));
+ EXPECT_TRUE(StringHelper::StartsWith("a", ""));
+ EXPECT_TRUE(StringHelper::StartsWith("b", "b"));
+ EXPECT_TRUE(StringHelper::StartsWith("ab", "a"));
+ EXPECT_TRUE(StringHelper::StartsWith("ab", "ab"));
+ EXPECT_TRUE(StringHelper::StartsWith("abcd", "abc"));
+ EXPECT_TRUE(StringHelper::StartsWith("abcd", "abcd"));
+ EXPECT_TRUE(StringHelper::StartsWith("abcdefghijk", "abc"));
+ EXPECT_TRUE(StringHelper::StartsWith("abcdefghijk", "abcdefghij"));
+
+ EXPECT_FALSE(StringHelper::StartsWith("", "a"));
+ EXPECT_FALSE(StringHelper::StartsWith("b", "a"));
+ EXPECT_FALSE(StringHelper::StartsWith("abcd", "cd"));
+}
+
+TEST_F(LibHidlGenUtilsTest, Trim) {
+ EXPECT_EQ("", StringHelper::LTrim("", ""));
+ EXPECT_EQ("", StringHelper::LTrim("", "a"));
+ EXPECT_EQ("", StringHelper::LTrim("a", "a"));
+ EXPECT_EQ("a", StringHelper::LTrim("a", ""));
+ EXPECT_EQ("a", StringHelper::LTrim("a", "b"));
+ EXPECT_EQ("a", StringHelper::LTrim("ba", "b"));
+ EXPECT_EQ("f", StringHelper::LTrim("abcdef", "abcde"));
+ EXPECT_EQ("cdef", StringHelper::LTrim("abcdef", "ab"));
+ EXPECT_EQ("abcdef", StringHelper::LTrim("abcdef", ""));
+
+ EXPECT_EQ("", StringHelper::RTrim("", ""));
+ EXPECT_EQ("", StringHelper::RTrim("", "a"));
+ EXPECT_EQ("", StringHelper::RTrim("a", "a"));
+ EXPECT_EQ("a", StringHelper::RTrim("a", ""));
+ EXPECT_EQ("a", StringHelper::RTrim("a", "b"));
+ EXPECT_EQ("a", StringHelper::RTrim("ab", "b"));
+ EXPECT_EQ("a", StringHelper::RTrim("abcdef", "bcdef"));
+ EXPECT_EQ("abcd", StringHelper::RTrim("abcdef", "ef"));
+ EXPECT_EQ("abcdef", StringHelper::RTrim("abcdef", ""));
+}
+
+TEST_F(LibHidlGenUtilsTest, TrimAll) {
+ EXPECT_EQ("", StringHelper::LTrimAll("", ""));
+ EXPECT_EQ("", StringHelper::LTrimAll("", "a"));
+ EXPECT_EQ("", StringHelper::LTrimAll("", "ab"));
+ EXPECT_EQ("", StringHelper::LTrimAll("a", "a"));
+ EXPECT_EQ("", StringHelper::LTrimAll("aa", "a"));
+ EXPECT_EQ("b", StringHelper::LTrimAll("b", "a"));
+ EXPECT_EQ("b", StringHelper::LTrimAll("aaab", "a"));
+ EXPECT_EQ("c", StringHelper::LTrimAll("ababc", "ab"));
+ EXPECT_EQ("ac", StringHelper::LTrimAll("abac", "ab"));
+
+ EXPECT_EQ("", StringHelper::RTrimAll("", ""));
+ EXPECT_EQ("", StringHelper::RTrimAll("", "a"));
+ EXPECT_EQ("", StringHelper::RTrimAll("", "ab"));
+ EXPECT_EQ("", StringHelper::RTrimAll("a", "a"));
+ EXPECT_EQ("", StringHelper::RTrimAll("aa", "a"));
+ EXPECT_EQ("b", StringHelper::RTrimAll("b", "a"));
+ EXPECT_EQ("b", StringHelper::RTrimAll("baaa", "a"));
+ EXPECT_EQ("c", StringHelper::RTrimAll("cabab", "ab"));
+ EXPECT_EQ("ca", StringHelper::RTrimAll("caba", "ba"));
+}
+
+TEST_F(LibHidlGenUtilsTest, SplitString) {
+ std::vector<std::string> components;
+
+ StringHelper::SplitString("", '.', &components);
+ EXPECT_EQ(std::vector<std::string>({""}), components);
+ StringHelper::SplitString("a.", '.', &components);
+ EXPECT_EQ(std::vector<std::string>({"a", ""}), components);
+ StringHelper::SplitString(".a", '.', &components);
+ EXPECT_EQ(std::vector<std::string>({"", "a"}), components);
+ StringHelper::SplitString("..", '.', &components);
+ EXPECT_EQ(std::vector<std::string>({"", "", ""}), components);
+ StringHelper::SplitString("asdf.asdf", '.', &components);
+ EXPECT_EQ(std::vector<std::string>({"asdf", "asdf"}), components);
+}
+
+TEST_F(LibHidlGenUtilsTest, JoinStrings) {
+ EXPECT_EQ("", StringHelper::JoinStrings({}, ""));
+ EXPECT_EQ("", StringHelper::JoinStrings({}, "a"));
+ EXPECT_EQ("a", StringHelper::JoinStrings({"a"}, ""));
+ EXPECT_EQ("a,b", StringHelper::JoinStrings({"a", "b"}, ","));
+ EXPECT_EQ("ab,", StringHelper::JoinStrings({"ab", ""}, ","));
+ EXPECT_EQ(",ab", StringHelper::JoinStrings({"", "ab"}, ","));
+ EXPECT_EQ("a.,b", StringHelper::JoinStrings({"a", "b"}, ".,"));
+ EXPECT_EQ("a,b,c", StringHelper::JoinStrings({"a", "b", "c"}, ","));
+ EXPECT_EQ("abc.,def.,ghi", StringHelper::JoinStrings({"abc", "def", "ghi"}, ".,"));
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstance1) {
+ FqInstance e;
+ ASSERT_TRUE(e.setTo("android.hardware.foo@1.0::IFoo/instance"));
+ EXPECT_EQ("android.hardware.foo@1.0::IFoo/instance", e.string());
+ ASSERT_TRUE(e.hasPackage());
+ EXPECT_EQ("android.hardware.foo", e.getPackage());
+ ASSERT_TRUE(e.hasVersion());
+ EXPECT_EQ(1u, e.getMajorVersion());
+ EXPECT_EQ(0u, e.getMinorVersion());
+ EXPECT_EQ((std::make_pair<size_t, size_t>(1u, 0u)), e.getVersion());
+ ASSERT_TRUE(e.hasInterface());
+ EXPECT_EQ("IFoo", e.getInterface());
+ ASSERT_TRUE(e.hasInstance());
+ EXPECT_EQ("instance", e.getInstance());
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstance2) {
+ FqInstance e;
+ ASSERT_TRUE(e.setTo("@1.0::IFoo/instance"));
+ EXPECT_EQ("@1.0::IFoo/instance", e.string());
+ ASSERT_FALSE(e.hasPackage());
+ ASSERT_TRUE(e.hasVersion());
+ EXPECT_EQ((std::make_pair<size_t, size_t>(1u, 0u)), e.getVersion());
+ ASSERT_TRUE(e.hasInterface());
+ EXPECT_EQ("IFoo", e.getInterface());
+ ASSERT_TRUE(e.hasInstance());
+ EXPECT_EQ("instance", e.getInstance());
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstance3) {
+ FqInstance e;
+ ASSERT_TRUE(e.setTo("IFoo/instance"));
+ EXPECT_EQ("IFoo/instance", e.string());
+ ASSERT_FALSE(e.hasPackage());
+ ASSERT_FALSE(e.hasVersion());
+ ASSERT_TRUE(e.hasInterface());
+ EXPECT_EQ("IFoo", e.getInterface());
+ ASSERT_TRUE(e.hasInstance());
+ EXPECT_EQ("instance", e.getInstance());
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstanceFqNameOnly) {
+ FqInstance e;
+ for (auto testString :
+ {"android.hardware.foo@1.0::IFoo.Type", "@1.0::IFoo.Type", "android.hardware.foo@1.0",
+ "IFoo.Type", "Type", "android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE",
+ "@1.0::IFoo.Type:MY_ENUM_VALUE", "IFoo.Type:MY_ENUM_VALUE"}) {
+ ASSERT_TRUE(e.setTo(testString));
+ EXPECT_EQ(testString, e.string());
+ ASSERT_FALSE(e.hasInstance());
+ };
+}
+
+TEST_F(LibHidlGenUtilsTest, FqInstanceIdentifier) {
+ FqInstance e;
+ ASSERT_TRUE(e.setTo("Type"));
+ EXPECT_EQ("Type", e.string());
+ ASSERT_FALSE(e.hasInstance());
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/vendor/1.0/Android.bp b/test/vendor/1.0/Android.bp
index f640216..8519fa5 100644
--- a/test/vendor/1.0/Android.bp
+++ b/test/vendor/1.0/Android.bp
@@ -1,90 +1,23 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
+// This file is autogenerated by hidl-gen -Landroidbp.
-filegroup {
- name: "tests.vendor@1.0_hal",
+hidl_interface {
+ name: "hidl.tests.vendor@1.0",
+ owner: "some-owner-name",
+ root: "hidl.tests",
srcs: [
"types.hal",
"IVendor.hal",
],
+ interfaces: [
+ "android.hardware.tests.baz@1.0",
+ "android.hidl.base@1.0",
+ ],
+ types: [
+ "Bar",
+ "Foo",
+ "FooToo",
+ ],
+ gen_java: true,
+ gen_java_constants: true,
}
-genrule {
- name: "tests.vendor@1.0_genc++",
- tools: ["hidl-gen"],
- cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.0",
- srcs: [
- ":tests.vendor@1.0_hal",
- ],
- out: [
- "tests/vendor/1.0/types.cpp",
- "tests/vendor/1.0/VendorAll.cpp",
- ],
-}
-
-genrule {
- name: "tests.vendor@1.0_genc++_headers",
- tools: ["hidl-gen"],
- cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.0",
- srcs: [
- ":tests.vendor@1.0_hal",
- ],
- out: [
- "tests/vendor/1.0/types.h",
- "tests/vendor/1.0/hwtypes.h",
- "tests/vendor/1.0/IVendor.h",
- "tests/vendor/1.0/IHwVendor.h",
- "tests/vendor/1.0/BnHwVendor.h",
- "tests/vendor/1.0/BpHwVendor.h",
- "tests/vendor/1.0/BsVendor.h",
- ],
-}
-
-cc_library {
- name: "tests.vendor@1.0",
- defaults: ["hidl-module-defaults"],
- generated_sources: ["tests.vendor@1.0_genc++"],
- generated_headers: ["tests.vendor@1.0_genc++_headers"],
- export_generated_headers: ["tests.vendor@1.0_genc++_headers"],
- vendor_available: true,
- shared_libs: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libutils",
- "libcutils",
- "android.hardware.tests.baz@1.0",
- ],
- export_shared_lib_headers: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "libutils",
- "android.hardware.tests.baz@1.0",
- ],
-}
-
-cc_library {
- name: "tests.vendor@1.0_vendor",
- defaults: ["hidl-module-defaults"],
- generated_sources: ["tests.vendor@1.0_genc++"],
- generated_headers: ["tests.vendor@1.0_genc++_headers"],
- export_generated_headers: ["tests.vendor@1.0_genc++_headers"],
- vendor: true,
- shared_libs: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libutils",
- "libcutils",
- "android.hardware.tests.baz@1.0",
- ],
- export_shared_lib_headers: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "libutils",
- "android.hardware.tests.baz@1.0",
- ],
-}
diff --git a/test/vendor/1.0/Android.mk b/test/vendor/1.0/Android.mk
deleted file mode 100644
index 5caa041..0000000
--- a/test/vendor/1.0/Android.mk
+++ /dev/null
@@ -1,235 +0,0 @@
-# This file is autogenerated by hidl-gen. Do not edit manually.
-
-LOCAL_PATH := $(call my-dir)
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.0-java
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_JAVA_LIBRARIES := \
- android.hardware.tests.baz-V1.0-java \
- android.hidl.base-V1.0-java \
-
-
-#
-# Build types.hal (Bar)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Bar.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::types.Bar
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (Foo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Foo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::types.Foo
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (FooToo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/FooToo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::types.FooToo
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_0/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_JAVA_LIBRARY)
-
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.0-java-static
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android.hardware.tests.baz-V1.0-java-static \
- android.hidl.base-V1.0-java-static \
-
-
-#
-# Build types.hal (Bar)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Bar.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::types.Bar
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (Foo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Foo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::types.Foo
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build types.hal (FooToo)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/FooToo.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/types.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::types.FooToo
-
-$(GEN): $(LOCAL_PATH)/types.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_0/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.0-java-constants
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-#
-GEN := $(intermediates)/tests/vendor/V1_0/Constants.java
-$(GEN): $(HIDL)
-$(GEN): $(LOCAL_PATH)/types.hal
-$(GEN): $(LOCAL_PATH)/IVendor.hal
-
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava-constants \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.0
-
-$(GEN):
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-# Avoid dependency cycle of framework.jar -> this-library -> framework.jar
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test/vendor/1.0/IVendor.hal b/test/vendor/1.0/IVendor.hal
index 3beebea..c963d3f 100644
--- a/test/vendor/1.0/IVendor.hal
+++ b/test/vendor/1.0/IVendor.hal
@@ -1,37 +1,39 @@
-/*
- * 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.
- */
-
-package tests.vendor@1.0;
-
-import android.hardware.tests.baz@1.0;
-
-interface IVendor extends IBaz {
-
- struct StructTest {
- vec<uint8_t> b;
- string d;
- uint8_t[4][4] e;
- };
-
- fun2(vec<uint8_t> a) generates(vec<uint8_t> b);
- fun4(string a) generates(string b);
- fun5(uint8_t[4][4] a) generates(uint8_t[4][4] b);
-
- // Testing static functions in derived class
- // overriding virtual functions in parent class
- // due to namespace resolution.
- registerForNotifications();
-};
\ No newline at end of file
+// note this file tests carriage returns
+
+/*
+ * 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.
+ */
+
+package hidl.tests.vendor@1.0;
+
+import android.hardware.tests.baz@1.0;
+
+interface IVendor extends IBaz {
+
+ struct StructTest {
+ vec<uint8_t> b;
+ string d;
+ uint8_t[4][4] e;
+ };
+
+ fun2(vec<uint8_t> a) generates(vec<uint8_t> b);
+ fun4(string a) generates(string b);
+ fun5(uint8_t[4][4] a) generates(uint8_t[4][4] b);
+
+ // Testing static functions in derived class
+ // overriding virtual functions in parent class
+ // due to namespace resolution.
+ registerForNotifications();
+};
diff --git a/test/vendor/1.0/types.hal b/test/vendor/1.0/types.hal
index 511389e..4763f0e 100644
--- a/test/vendor/1.0/types.hal
+++ b/test/vendor/1.0/types.hal
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package tests.vendor@1.0;
+package hidl.tests.vendor@1.0;
enum Bar : uint32_t {
A
diff --git a/test/vendor/1.0/update-base.sh b/test/vendor/1.0/update-base.sh
index 7940328..933480f 100755
--- a/test/vendor/1.0/update-base.sh
+++ b/test/vendor/1.0/update-base.sh
@@ -4,7 +4,7 @@
# It should typically be created elsewhere.
options="-Lexport-header \
- -r tests:system/tools/hidl/test/\
+ -r hidl.tests:system/tools/hidl/test/\
-r android.hidl:system/libhidl/transport\
-r android.hardware:hardware/interfaces"
diff --git a/test/vendor/1.1/Android.bp b/test/vendor/1.1/Android.bp
index 6d41300..80cecb2 100644
--- a/test/vendor/1.1/Android.bp
+++ b/test/vendor/1.1/Android.bp
@@ -1,90 +1,18 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
+// This file is autogenerated by hidl-gen -Landroidbp.
-filegroup {
- name: "tests.vendor@1.1_hal",
+hidl_interface {
+ name: "hidl.tests.vendor@1.1",
+ owner: "some-owner-name",
+ root: "hidl.tests",
srcs: [
"IVendor.hal",
],
+ interfaces: [
+ "android.frameworks.displayservice@1.0",
+ "android.hardware.tests.baz@1.0",
+ "android.hidl.base@1.0",
+ "hidl.tests.vendor@1.0",
+ ],
+ gen_java: true,
}
-genrule {
- name: "tests.vendor@1.1_genc++",
- tools: ["hidl-gen"],
- cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.1",
- srcs: [
- ":tests.vendor@1.1_hal",
- ],
- out: [
- "tests/vendor/1.1/VendorAll.cpp",
- ],
-}
-
-genrule {
- name: "tests.vendor@1.1_genc++_headers",
- tools: ["hidl-gen"],
- cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport -rtests:system/tools/hidl/test/ tests.vendor@1.1",
- srcs: [
- ":tests.vendor@1.1_hal",
- ],
- out: [
- "tests/vendor/1.1/IVendor.h",
- "tests/vendor/1.1/IHwVendor.h",
- "tests/vendor/1.1/BnHwVendor.h",
- "tests/vendor/1.1/BpHwVendor.h",
- "tests/vendor/1.1/BsVendor.h",
- ],
-}
-
-cc_library {
- name: "tests.vendor@1.1",
- defaults: ["hidl-module-defaults"],
- generated_sources: ["tests.vendor@1.1_genc++"],
- generated_headers: ["tests.vendor@1.1_genc++_headers"],
- export_generated_headers: ["tests.vendor@1.1_genc++_headers"],
- vendor_available: true,
- shared_libs: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libutils",
- "libcutils",
- "android.hardware.tests.baz@1.0",
- "tests.vendor@1.0",
- ],
- export_shared_lib_headers: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "libutils",
- "android.hardware.tests.baz@1.0",
- "tests.vendor@1.0",
- ],
-}
-
-cc_library {
- name: "tests.vendor@1.1_vendor",
- defaults: ["hidl-module-defaults"],
- generated_sources: ["tests.vendor@1.1_genc++"],
- generated_headers: ["tests.vendor@1.1_genc++_headers"],
- export_generated_headers: ["tests.vendor@1.1_genc++_headers"],
- vendor: true,
- shared_libs: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libutils",
- "libcutils",
- "android.hardware.tests.baz@1.0",
- "tests.vendor@1.0_vendor",
- ],
- export_shared_lib_headers: [
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "libutils",
- "android.hardware.tests.baz@1.0",
- "tests.vendor@1.0_vendor",
- ],
-}
diff --git a/test/vendor/1.1/Android.mk b/test/vendor/1.1/Android.mk
deleted file mode 100644
index 4427516..0000000
--- a/test/vendor/1.1/Android.mk
+++ /dev/null
@@ -1,82 +0,0 @@
-# This file is autogenerated by hidl-gen. Do not edit manually.
-
-LOCAL_PATH := $(call my-dir)
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.1-java
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_JAVA_LIBRARIES := \
- android.hardware.tests.baz-V1.0-java \
- android.hidl.base-V1.0-java \
- tests.vendor-V1.0-java \
-
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_1/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.1::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_JAVA_LIBRARY)
-
-
-################################################################################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := tests.vendor-V1.1-java-static
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-intermediates := $(call local-generated-sources-dir, COMMON)
-
-HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android.hardware.tests.baz-V1.0-java-static \
- android.hidl.base-V1.0-java-static \
- tests.vendor-V1.0-java-static \
-
-
-#
-# Build IVendor.hal
-#
-GEN := $(intermediates)/tests/vendor/V1_1/IVendor.java
-$(GEN): $(HIDL)
-$(GEN): PRIVATE_HIDL := $(HIDL)
-$(GEN): PRIVATE_DEPS := $(LOCAL_PATH)/IVendor.hal
-$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
-$(GEN): PRIVATE_CUSTOM_TOOL = \
- $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
- -Ljava \
- -randroid.hardware:hardware/interfaces \
- -randroid.hidl:system/libhidl/transport \
- -rtests:system/tools/hidl/test/ \
- tests.vendor@1.1::IVendor
-
-$(GEN): $(LOCAL_PATH)/IVendor.hal
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test/vendor/1.1/IVendor.hal b/test/vendor/1.1/IVendor.hal
index 4821048..437d79f 100644
--- a/test/vendor/1.1/IVendor.hal
+++ b/test/vendor/1.1/IVendor.hal
@@ -14,10 +14,14 @@
* limitations under the License.
*/
-package tests.vendor@1.1;
+package hidl.tests.vendor@1.1;
-import tests.vendor@1.0::IVendor;
+import hidl.tests.vendor@1.0::IVendor;
-interface IVendor extends IVendor {
+// this import is unused, but it should still show up in the
+// associated Android.bp
+import android.frameworks.displayservice@1.0;
+
+interface IVendor extends @1.0::IVendor {
doAdditionalFunctionality();
};
diff --git a/test/vendor/Android.bp b/test/vendor/Android.bp
new file mode 100644
index 0000000..dfcd349
--- /dev/null
+++ b/test/vendor/Android.bp
@@ -0,0 +1,4 @@
+hidl_package_root {
+ name: "hidl.tests",
+ path: "system/tools/hidl/test",
+}
diff --git a/test/vendor/Android.mk b/test/vendor/Android.mk
deleted file mode 100644
index 3bda3b9..0000000
--- a/test/vendor/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (C) 2017 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.
-LOCAL_PATH:= $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/test/vendor/update-makefile.sh b/test/vendor/update-makefile.sh
index 87e7340..49ced20 100755
--- a/test/vendor/update-makefile.sh
+++ b/test/vendor/update-makefile.sh
@@ -1,10 +1,9 @@
#!/bin/bash
-options="-r tests:system/tools/hidl/test/ \
- -r android.hidl:system/libhidl/transport \
+options="-O some-owner-name
+ -r hidl.tests:system/tools/hidl/test/
+ -r android.hidl:system/libhidl/transport
-r android.hardware:hardware/interfaces"
-hidl-gen -Lmakefile $options tests.vendor@1.0;
-hidl-gen -Landroidbp $options -t tests.vendor@1.0;
-hidl-gen -Lmakefile $options tests.vendor@1.1;
-hidl-gen -Landroidbp $options -t tests.vendor@1.1;
+hidl-gen -Landroidbp $options hidl.tests.vendor@1.0;
+hidl-gen -Landroidbp $options hidl.tests.vendor@1.1;
diff --git a/test/version_test/Android.bp b/test/version_test/Android.bp
index 696f501..3629b8a 100644
--- a/test/version_test/Android.bp
+++ b/test/version_test/Android.bp
@@ -10,6 +10,8 @@
" test.version.version@2.3" +
" test.version.version@2.4" +
" test.version.version@2.5" +
+ " test.version.version@3.0" +
+ " test.version.version@3.1" +
"&&" +
"($(location hidl-gen) -L check " +
" -r test.version:system/tools/hidl/test/version_test/bad1" +
@@ -27,6 +29,10 @@
" -r test.version:system/tools/hidl/test/version_test/bad4" +
" test.version.version@2.3 2>&1 | grep \"doesn't pass minor version\" > /dev/null)" +
"&&" +
+ "($(location hidl-gen) -L check " +
+ " -r test.version:system/tools/hidl/test/version_test/bad5" +
+ " test.version.version@2.3 2>&1 | grep \"doesn't pass minor version\" > /dev/null)" +
+ "&&" +
"echo 'int main(){return 0;}' > $(genDir)/TODO_b_37575883.cpp",
out: ["TODO_b_37575883.cpp"],
@@ -48,6 +54,8 @@
"bad4/version/2.2/IBar.hal",
"bad4/version/2.2/IFoo.hal",
"bad4/version/2.3/IBaz.hal",
+ "bad5/version/2.2/IFoo.hal",
+ "bad5/version/2.3/IBaz.hal",
"good/version/1.0/IFoo.hal",
"good/version/2.2/IBar.hal",
"good/version/2.2/IFoo.hal",
@@ -57,10 +65,14 @@
"good/version/2.4/IFoo.hal",
"good/version/2.5/IBar.hal",
"good/version/2.5/IFoo.hal",
+ "good/version/3.0/types.hal",
+ "good/version/3.1/ICanExtendTypesOnly.hal",
+ "good/version/3.1/types.hal",
]
}
cc_test_host {
name: "hidl_version_test",
+ cflags: ["-Wall", "-Werror"],
generated_sources: ["hidl_hash_version_gen"],
-}
\ No newline at end of file
+}
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/bad5/version/2.2/IFoo.hal
similarity index 86%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/bad5/version/2.2/IFoo.hal
index d79e6ab..a672f76 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/bad5/version/2.2/IFoo.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
+
+package test.version.version@2.2;
interface IFoo {
-};
\ No newline at end of file
+
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/bad5/version/2.3/IBaz.hal
similarity index 83%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/bad5/version/2.3/IBaz.hal
index d79e6ab..8e71cd2 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/bad5/version/2.3/IBaz.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@2.3;
+
+interface IBaz {
+
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/good/version/3.0/types.hal
similarity index 90%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/good/version/3.0/types.hal
index d79e6ab..27db609 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/good/version/3.0/types.hal
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@3.0;
+
+enum A : uint32_t {
+ B
+};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/good/version/3.1/ICanExtendTypesOnly.hal
similarity index 81%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/good/version/3.1/ICanExtendTypesOnly.hal
index d79e6ab..cbbfa8e 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/good/version/3.1/ICanExtendTypesOnly.hal
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@3.1;
+
+interface ICanExtendTypesOnly {};
diff --git a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal b/test/version_test/good/version/3.1/types.hal
similarity index 88%
copy from test/error_test/no_interface_in_types/error/1.0/IFoo.hal
copy to test/version_test/good/version/3.1/types.hal
index d79e6ab..bc9964d 100644
--- a/test/error_test/no_interface_in_types/error/1.0/IFoo.hal
+++ b/test/version_test/good/version/3.1/types.hal
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package test.error.error@1.0;
-interface IFoo {
-};
\ No newline at end of file
+package test.version.version@3.1;
+
+import @3.0::A;
+
+enum A : @3.0::A {
+ C
+};
diff --git a/update-all-google-makefiles.sh b/update-all-google-makefiles.sh
index 7491ac6..178137d 100755
--- a/update-all-google-makefiles.sh
+++ b/update-all-google-makefiles.sh
@@ -7,4 +7,4 @@
$ANDROID_BUILD_TOP/frameworks/hardware/interfaces/update-makefiles.sh
$ANDROID_BUILD_TOP/system/hardware/interfaces/update-makefiles.sh
-$ANDROID_BUILD_TOP/system/tools/hidl/test/vendor/1.0/update-makefile.sh
+$ANDROID_BUILD_TOP/system/tools/hidl/test/vendor/update-makefile.sh
diff --git a/update-makefiles-helper.sh b/update-makefiles-helper.sh
index 904c4ed..30f38a6 100755
--- a/update-makefiles-helper.sh
+++ b/update-makefiles-helper.sh
@@ -52,19 +52,6 @@
}
##
-# Subdirectories of a directory which contain Android.bps
-# Note, does not return Android.bp in the current directory.
-#
-# Usage: get_bp_dirs dir
-function get_bp_dirs() {
- find $1/* \
- -mindepth 1 \
- -name "Android.bp" \
- -printf "%H\n" \
- | sort | uniq
-}
-
-##
# Returns directory path for a package
# Usage: get_package_dir package_root_dir package_prefix package
function get_package_dir() {
@@ -77,10 +64,16 @@
# Helps manage the package root of a HAL directory.
# Should be called from the android root directory.
#
-# Usage: do_makefiles_update [package:root ...]
+# Usage: do_makefiles_update [-O owner-name] [package:root ...]
# Where the first package root is the current one.
#
function do_makefiles_update() {
+ local owner=
+ if [[ "$1" = "-O" ]]; then
+ owner="$2"
+ shift 2
+ fi
+
local root_or_cwd=${ANDROID_BUILD_TOP%%/}${ANDROID_BUILD_TOP:+/}
local current_package=$(package_root_to_package $1)
@@ -95,32 +88,7 @@
for p in $packages; do
echo "Updating $p";
- local additional_options=
- if [[ -f $(get_package_dir $current_dir $current_package $p)/.hidl_for_test ]]; then additional_options="-t"; fi
- hidl-gen -Lmakefile $root_arguments $p;
+ hidl-gen -O "$owner" -Landroidbp $root_arguments $p;
rc=$?; if [[ $rc != 0 ]]; then return $rc; fi
- hidl-gen -Landroidbp $root_arguments $additional_options $p;
- rc=$?; if [[ $rc != 0 ]]; then return $rc; fi
- done
-
- local android_dirs=$(get_bp_dirs $current_dir) || return 1
-
- echo "Updating Android.bp files."
-
- for bp_dir in $android_dirs; do
- bp="$bp_dir/Android.bp"
- # locations of Android.bp files in specific subdirectory of frameworks/hardware/interfaces
- android_bps=$(find $bp_dir \
- -name "Android.bp" \
- ! -path $bp_dir/Android.bp \
- -printf "%h\n" \
- | sort)
-
- echo "// This is an autogenerated file, do not edit." > "$bp";
- echo "subdirs = [" >> "$bp";
- for a in $android_bps; do
- echo " \"${a#$bp_dir/}\"," >> "$bp";
- done
- echo "]" >> "$bp";
done
}
diff --git a/utils/Android.bp b/utils/Android.bp
index ff171d3..7d97c75 100644
--- a/utils/Android.bp
+++ b/utils/Android.bp
@@ -15,16 +15,15 @@
cc_library {
name: "libhidl-gen-utils",
host_supported: true,
- vendor_available: true,
- cflags: hidl_flags,
+ defaults: ["hidl-gen-defaults"],
srcs: [
"FQName.cpp",
"Formatter.cpp",
+ "FqInstance.cpp",
"StringHelper.cpp",
],
shared_libs: [
"libbase",
- "liblog",
],
local_include_dirs: ["include/hidl-util"],
diff --git a/utils/FQName.cpp b/utils/FQName.cpp
index 630fff4..8f56af8 100644
--- a/utils/FQName.cpp
+++ b/utils/FQName.cpp
@@ -29,27 +29,6 @@
#define RE_MAJOR "[0-9]+"
#define RE_MINOR "[0-9]+"
-// android.hardware.foo@1.0::IFoo.Type
-static const std::regex kRE1("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
-// @1.0::IFoo.Type
-static const std::regex kRE2("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
-// android.hardware.foo@1.0 (for package declaration and whole package import)
-static const std::regex kRE3("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")");
-// IFoo.Type
-static const std::regex kRE4("(" RE_COMPONENT ")([.]" RE_COMPONENT ")+");
-// Type (a plain identifier)
-static const std::regex kRE5("(" RE_COMPONENT ")");
-
-// android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE
-static const std::regex kRE6("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT ")");
-// @1.0::IFoo.Type:MY_ENUM_VALUE
-static const std::regex kRE7("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT ")");
-// IFoo.Type:MY_ENUM_VALUE
-static const std::regex kRE8("(" RE_PATH "):(" RE_COMPONENT ")");
-
-// 1.0
-static const std::regex kREVer("(" RE_MAJOR ")[.](" RE_MINOR ")");
-
namespace android {
FQName::FQName()
@@ -57,10 +36,15 @@
mIsIdentifier(false) {
}
+// TODO(b/73774955): delete
FQName::FQName(const std::string &s)
: mValid(false),
mIsIdentifier(false) {
- setTo(s);
+ (void)setTo(s);
+}
+
+bool FQName::parse(const std::string& s, FQName* into) {
+ return into->setTo(s);
}
FQName::FQName(
@@ -73,12 +57,12 @@
mPackage(package),
mName(name),
mValueName(valueName) {
- setVersion(version);
+ CHECK(setVersion(version)) << version;
// Check if this is actually a valid fqName
FQName other;
- other.setTo(this->string());
- CHECK(other.mValid && (*this) == other);
+ CHECK(parse(this->string(), &other)) << this->string();
+ CHECK((*this) == other) << this->string() << " " << other.string();
}
FQName::FQName(const FQName& other)
@@ -91,12 +75,6 @@
mValueName(other.mValueName) {
}
-FQName::FQName(const std::vector<std::string> &names)
- : mValid(false),
- mIsIdentifier(false) {
- setTo(StringHelper::JoinStrings(names, "."));
-}
-
bool FQName::isValid() const {
return mValid;
}
@@ -114,30 +92,51 @@
|| (!mName.empty() && !mValueName.empty());
}
-bool FQName::setTo(const std::string &s) {
- clearVersion();
- mPackage.clear();
- mName.clear();
+bool FQName::isInterfaceName() const {
+ return !mName.empty() && mName[0] == 'I' && mName.find('.') == std::string::npos;
+}
- mValid = true;
+bool FQName::setTo(const std::string &s) {
+ // android.hardware.foo@1.0::IFoo.Type
+ static const std::regex kRE1("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
+ // @1.0::IFoo.Type
+ static const std::regex kRE2("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")");
+ // android.hardware.foo@1.0 (for package declaration and whole package import)
+ static const std::regex kRE3("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")");
+ // IFoo.Type
+ static const std::regex kRE4("(" RE_COMPONENT ")([.]" RE_COMPONENT ")+");
+ // Type (a plain identifier)
+ static const std::regex kRE5("(" RE_COMPONENT ")");
+
+ // android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE
+ static const std::regex kRE6("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH
+ "):(" RE_COMPONENT ")");
+ // @1.0::IFoo.Type:MY_ENUM_VALUE
+ static const std::regex kRE7("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT
+ ")");
+ // IFoo.Type:MY_ENUM_VALUE
+ static const std::regex kRE8("(" RE_PATH "):(" RE_COMPONENT ")");
+
+ bool invalid = false;
+ clear();
std::smatch match;
if (std::regex_match(s, match, kRE1)) {
CHECK_EQ(match.size(), 5u);
mPackage = match.str(1);
- parseVersion(match.str(2), match.str(3));
+ invalid |= !parseVersion(match.str(2), match.str(3));
mName = match.str(4);
} else if (std::regex_match(s, match, kRE2)) {
CHECK_EQ(match.size(), 4u);
- parseVersion(match.str(1), match.str(2));
+ invalid |= !parseVersion(match.str(1), match.str(2));
mName = match.str(3);
} else if (std::regex_match(s, match, kRE3)) {
CHECK_EQ(match.size(), 4u);
mPackage = match.str(1);
- parseVersion(match.str(2), match.str(3));
+ invalid |= !parseVersion(match.str(2), match.str(3));
} else if (std::regex_match(s, match, kRE4)) {
mName = match.str(0);
} else if (std::regex_match(s, match, kRE5)) {
@@ -147,13 +146,13 @@
CHECK_EQ(match.size(), 6u);
mPackage = match.str(1);
- parseVersion(match.str(2), match.str(3));
+ invalid |= !parseVersion(match.str(2), match.str(3));
mName = match.str(4);
mValueName = match.str(5);
} else if (std::regex_match(s, match, kRE7)) {
CHECK_EQ(match.size(), 5u);
- parseVersion(match.str(1), match.str(2));
+ invalid |= !parseVersion(match.str(1), match.str(2));
mName = match.str(3);
mValueName = match.str(4);
} else if (std::regex_match(s, match, kRE8)) {
@@ -162,19 +161,21 @@
mName = match.str(1);
mValueName = match.str(2);
} else {
- mValid = false;
+ invalid = true;
}
// mValueName must go with mName.
CHECK(mValueName.empty() || !mName.empty());
// package without version is not allowed.
- CHECK(mPackage.empty() || !version().empty());
+ CHECK(invalid || mPackage.empty() || !version().empty());
- return isValid();
+ // TODO(b/73774955): remove isValid and users
+ // of old FQName constructors
+ return mValid = !invalid;
}
-std::string FQName::package() const {
+const std::string& FQName::package() const {
return mPackage;
}
@@ -197,26 +198,37 @@
return v.empty() ? "" : ("@" + v);
}
-void FQName::setVersion(const std::string &v) {
+void FQName::clear() {
+ mValid = true;
+ mIsIdentifier = false;
+ mPackage.clear();
+ clearVersion();
+ mName.clear();
+ mValueName.clear();
+}
+
+bool FQName::setVersion(const std::string& v) {
+ static const std::regex kREVer("(" RE_MAJOR ")[.](" RE_MINOR ")");
+
if (v.empty()) {
clearVersion();
- return;
+ return true;
}
- std::smatch match;
- if (std::regex_match(v, match, kREVer)) {
- CHECK_EQ(match.size(), 3u);
- parseVersion(match.str(1), match.str(2));
- } else {
- mValid = false;
+ std::smatch match;
+ if (!std::regex_match(v, match, kREVer)) {
+ return mValid = false;
}
+ CHECK_EQ(match.size(), 3u);
+
+ return parseVersion(match.str(1), match.str(2));
}
void FQName::clearVersion() {
mMajor = mMinor = 0;
}
-void FQName::parseVersion(const std::string &majorStr, const std::string &minorStr) {
+bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr) {
bool versionParseSuccess =
::android::base::ParseUint(majorStr, &mMajor) &&
::android::base::ParseUint(minorStr, &mMinor);
@@ -224,9 +236,10 @@
LOG(ERROR) << "numbers in " << majorStr << "." << minorStr << " are out of range.";
mValid = false;
}
+ return versionParseSuccess;
}
-std::string FQName::name() const {
+const std::string& FQName::name() const {
return mName;
}
@@ -240,7 +253,7 @@
return res;
}
-std::string FQName::valueName() const {
+const std::string& FQName::valueName() const {
return mValueName;
}
@@ -260,12 +273,12 @@
}
if (version().empty()) {
- setVersion(defaultVersion);
+ CHECK(setVersion(defaultVersion));
}
}
std::string FQName::string() const {
- CHECK(mValid);
+ CHECK(mValid) << mPackage << atVersion() << mName;
std::string out;
out.append(mPackage);
@@ -285,15 +298,6 @@
return out;
}
-void FQName::print() const {
- if (!mValid) {
- LOG(INFO) << "INVALID";
- return;
- }
-
- LOG(INFO) << string();
-}
-
bool FQName::operator<(const FQName &other) const {
return string() < other.string();
}
@@ -306,9 +310,8 @@
return !(*this == other);
}
-std::string FQName::getInterfaceName() const {
- CHECK(names().size() == 1) << "Must be a top level type";
- CHECK(!mName.empty() && mName[0] == 'I') << mName;
+const std::string& FQName::getInterfaceName() const {
+ CHECK(isInterfaceName()) << mName;
return mName;
}
@@ -318,6 +321,10 @@
return getInterfaceName().substr(1);
}
+std::string FQName::getInterfaceAdapterName() const {
+ return "A" + getInterfaceBaseName();
+}
+
std::string FQName::getInterfaceHwName() const {
return "IHw" + getInterfaceBaseName();
}
@@ -338,6 +345,10 @@
return FQName(package(), version(), getInterfaceProxyName());
}
+FQName FQName::getInterfaceAdapterFqName() const {
+ return FQName(package(), version(), getInterfaceAdapterName());
+}
+
FQName FQName::getInterfaceStubFqName() const {
return FQName(package(), version(), getInterfaceStubName());
}
@@ -449,6 +460,13 @@
return mMajor > 0;
}
+FQName FQName::withVersion(size_t major, size_t minor) const {
+ FQName ret(*this);
+ ret.mMajor = major;
+ ret.mMinor = minor;
+ return ret;
+}
+
size_t FQName::getPackageMajorVersion() const {
CHECK(hasVersion()) << "FQName: No version exists at getPackageMajorVersion(). "
<< "Did you check hasVersion()?";
@@ -524,5 +542,8 @@
return ret;
}
+const FQName gIBaseFqName = FQName("android.hidl.base", "1.0", "IBase");
+const FQName gIManagerFqName = FQName("android.hidl.manager", "1.0", "IServiceManager");
+
} // namespace android
diff --git a/utils/Formatter.cpp b/utils/Formatter.cpp
index a4f2047..338cb17 100644
--- a/utils/Formatter.cpp
+++ b/utils/Formatter.cpp
@@ -18,13 +18,17 @@
#include <assert.h>
+#include <android-base/logging.h>
+
namespace android {
-Formatter::Formatter(FILE *file)
+Formatter::Formatter() : mFile(NULL /* invalid */), mIndentDepth(0), mAtStartOfLine(true) {}
+
+Formatter::Formatter(FILE* file, size_t spacesPerIndent)
: mFile(file == NULL ? stdout : file),
mIndentDepth(0),
- mAtStartOfLine(true) {
-}
+ mSpacesPerIndent(spacesPerIndent),
+ mAtStartOfLine(true) {}
Formatter::~Formatter() {
if (mFile != stdout) {
@@ -42,18 +46,18 @@
mIndentDepth -= level;
}
-Formatter &Formatter::indent(size_t level, std::function<void(void)> func) {
+Formatter& Formatter::indent(size_t level, const std::function<void(void)>& func) {
this->indent(level);
func();
this->unindent(level);
return *this;
}
-Formatter &Formatter::indent(std::function<void(void)> func) {
+Formatter& Formatter::indent(const std::function<void(void)>& func) {
return this->indent(1, func);
}
-Formatter &Formatter::block(std::function<void(void)> func) {
+Formatter& Formatter::block(const std::function<void(void)>& func) {
(*this) << "{\n";
this->indent(func);
return (*this) << "}";
@@ -71,42 +75,42 @@
return (*this) << "\n";
}
-Formatter &Formatter::sIf(const std::string &cond, std::function<void(void)> block) {
+Formatter& Formatter::sIf(const std::string& cond, const std::function<void(void)>& block) {
(*this) << "if (" << cond << ") ";
return this->block(block);
}
-Formatter &Formatter::sElseIf(const std::string &cond, std::function<void(void)> block) {
+Formatter& Formatter::sElseIf(const std::string& cond, const std::function<void(void)>& block) {
(*this) << " else if (" << cond << ") ";
return this->block(block);
}
-Formatter &Formatter::sElse(std::function<void(void)> block) {
+Formatter& Formatter::sElse(const std::function<void(void)>& block) {
(*this) << " else ";
return this->block(block);
}
-Formatter &Formatter::sFor(const std::string &stmts, std::function<void(void)> block) {
+Formatter& Formatter::sFor(const std::string& stmts, const std::function<void(void)>& block) {
(*this) << "for (" << stmts << ") ";
return this->block(block);
}
-Formatter &Formatter::sTry(std::function<void(void)> block) {
+Formatter& Formatter::sTry(const std::function<void(void)>& block) {
(*this) << "try ";
return this->block(block);
}
-Formatter &Formatter::sCatch(const std::string &exception, std::function<void(void)> block) {
+Formatter& Formatter::sCatch(const std::string& exception, const std::function<void(void)>& block) {
(*this) << " catch (" << exception << ") ";
return this->block(block);
}
-Formatter &Formatter::sFinally(std::function<void(void)> block) {
+Formatter& Formatter::sFinally(const std::function<void(void)>& block) {
(*this) << " finally ";
return this->block(block);
}
-Formatter &Formatter::sWhile(const std::string &cond, std::function<void(void)> block) {
+Formatter& Formatter::sWhile(const std::string& cond, const std::function<void(void)>& block) {
(*this) << "while (" << cond << ") ";
return this->block(block);
}
@@ -115,12 +119,12 @@
const size_t len = out.length();
size_t start = 0;
while (start < len) {
- size_t pos = out.find("\n", start);
+ size_t pos = out.find('\n', start);
if (pos == std::string::npos) {
if (mAtStartOfLine) {
+ fprintf(mFile, "%*s", (int)(mSpacesPerIndent * mIndentDepth), "");
fprintf(mFile, "%s", mLinePrefix.c_str());
- fprintf(mFile, "%*s", (int)(4 * mIndentDepth), "");
mAtStartOfLine = false;
}
@@ -128,17 +132,16 @@
break;
}
+ if (mAtStartOfLine && (pos > start || !mLinePrefix.empty())) {
+ fprintf(mFile, "%*s", (int)(mSpacesPerIndent * mIndentDepth), "");
+ fprintf(mFile, "%s", mLinePrefix.c_str());
+ }
+
if (pos == start) {
fprintf(mFile, "\n");
mAtStartOfLine = true;
} else if (pos > start) {
- if (mAtStartOfLine) {
- fprintf(mFile, "%s", mLinePrefix.c_str());
- fprintf(mFile, "%*s", (int)(4 * mIndentDepth), "");
- }
-
output(out.substr(start, pos - start + 1));
-
mAtStartOfLine = true;
}
@@ -148,10 +151,11 @@
return *this;
}
-#define FORMATTER_INPUT_INTEGER(__type__) \
- Formatter &Formatter::operator<<(__type__ n) { \
- return (*this) << std::to_string(n); \
- } \
+// NOLINT to suppress missing parentheses warning about __type__.
+#define FORMATTER_INPUT_INTEGER(__type__) \
+ Formatter& Formatter::operator<<(__type__ n) { /* NOLINT */ \
+ return (*this) << std::to_string(n); \
+ }
FORMATTER_INPUT_INTEGER(short);
FORMATTER_INPUT_INTEGER(unsigned short);
@@ -167,10 +171,11 @@
#undef FORMATTER_INPUT_INTEGER
-#define FORMATTER_INPUT_CHAR(__type__) \
- Formatter &Formatter::operator<<(__type__ c) { \
- return (*this) << std::string(1, (char)c); \
- } \
+// NOLINT to suppress missing parentheses warning about __type__.
+#define FORMATTER_INPUT_CHAR(__type__) \
+ Formatter& Formatter::operator<<(__type__ c) { /* NOLINT */ \
+ return (*this) << std::string(1, (char)c); \
+ }
FORMATTER_INPUT_CHAR(char);
FORMATTER_INPUT_CHAR(signed char);
@@ -182,24 +187,12 @@
mSpace = space;
}
+bool Formatter::isValid() const {
+ return mFile != nullptr;
+}
+
void Formatter::output(const std::string &text) const {
- const size_t spaceLength = mSpace.size();
- if (spaceLength > 0) {
- // Remove all occurences of "mSpace" and output the filtered result.
- size_t matchPos = text.find(mSpace);
- if (matchPos != std::string::npos) {
- std::string newText = text.substr(0, matchPos);
- size_t startPos = matchPos + spaceLength;
- while ((matchPos = text.find(mSpace, startPos))
- != std::string::npos) {
- newText.append(text.substr(startPos, matchPos - startPos));
- startPos = matchPos + spaceLength;
- }
- newText.append(text.substr(startPos));
- fprintf(mFile, "%s", newText.c_str());
- return;
- }
- }
+ CHECK(isValid());
fprintf(mFile, "%s", text.c_str());
}
diff --git a/utils/FqInstance.cpp b/utils/FqInstance.cpp
new file mode 100644
index 0000000..0b2b8f8
--- /dev/null
+++ b/utils/FqInstance.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 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 "FqInstance.h"
+
+#include <sstream>
+
+namespace android {
+
+static const char INSTANCE_SEP = '/';
+
+const std::string& FqInstance::getPackage() const {
+ return mFqName.package();
+}
+
+bool FqInstance::hasPackage() const {
+ return !getPackage().empty();
+}
+
+size_t FqInstance::getMajorVersion() const {
+ return hasVersion() ? mFqName.getPackageMajorVersion() : 0;
+}
+
+size_t FqInstance::getMinorVersion() const {
+ return hasVersion() ? mFqName.getPackageMinorVersion() : 0;
+}
+
+std::pair<size_t, size_t> FqInstance::getVersion() const {
+ return {getMajorVersion(), getMinorVersion()};
+}
+
+bool FqInstance::hasVersion() const {
+ return mFqName.hasVersion();
+}
+
+const std::string& FqInstance::getInterface() const {
+ return mFqName.getInterfaceName();
+}
+
+bool FqInstance::hasInterface() const {
+ return mFqName.isInterfaceName();
+}
+
+const std::string& FqInstance::getInstance() const {
+ return mInstance;
+}
+
+bool FqInstance::hasInstance() const {
+ return !mInstance.empty();
+}
+
+bool FqInstance::setTo(const std::string& s) {
+ auto pos = s.find(INSTANCE_SEP);
+ if (!mFqName.setTo(s.substr(0, pos))) return false;
+ mInstance = pos == std::string::npos ? std::string{} : s.substr(pos + 1);
+
+ bool hasPkg = hasPackage();
+ bool hasVer = hasVersion();
+ bool hasIntf = hasInterface();
+ bool hasInst = hasInstance();
+
+ // android.hardware.foo@1.0::IFoo/default
+ if (hasPkg && hasVer && hasIntf && hasInst) {
+ return true;
+ }
+
+ // @1.0::IFoo/default
+ if (!hasPkg && hasVer && hasIntf && hasInst) {
+ return true;
+ }
+
+ // IFoo/default
+ if (!hasPkg && !hasVer && hasIntf && hasInst) {
+ return true;
+ }
+
+ // Other cases are covered by FQName::setTo, but instance name should be empty.
+ return !hasInst;
+}
+
+bool FqInstance::setTo(const std::string& package, size_t majorVer, size_t minorVer,
+ const std::string& interface, const std::string& instance) {
+ std::stringstream ss;
+ ss << package << "@" << majorVer << "." << minorVer << "::" << interface << INSTANCE_SEP
+ << instance;
+ return setTo(ss.str());
+}
+
+bool FqInstance::setTo(size_t majorVer, size_t minorVer, const std::string& interface,
+ const std::string& instance) {
+ return setTo("", majorVer, minorVer, interface, instance);
+}
+
+bool FqInstance::setTo(const std::string& interface, const std::string& instance) {
+ return setTo(interface + INSTANCE_SEP + instance);
+}
+
+std::string FqInstance::string() const {
+ std::string ret = mFqName.string();
+ if (hasInstance()) ret += INSTANCE_SEP + mInstance;
+ return ret;
+}
+
+bool FqInstance::operator<(const FqInstance& other) const {
+ return string() < other.string();
+}
+
+bool FqInstance::operator==(const FqInstance& other) const {
+ return string() == other.string();
+}
+
+bool FqInstance::operator!=(const FqInstance& other) const {
+ return !(*this == other);
+}
+
+} // namespace android
diff --git a/utils/StringHelper.cpp b/utils/StringHelper.cpp
index 3718d53..aae9b5a 100644
--- a/utils/StringHelper.cpp
+++ b/utils/StringHelper.cpp
@@ -184,6 +184,10 @@
}
std::string StringHelper::RTrimAll(const std::string &in, const std::string &suffix) {
+ if (suffix.empty()) {
+ return in;
+ }
+
std::string copy(in);
while (EndsWith(copy, suffix)) {
copy = copy.substr(0, copy.size() - suffix.size());
@@ -193,6 +197,10 @@
}
std::string StringHelper::LTrimAll(const std::string &in, const std::string &prefix) {
+ if (prefix.empty()) {
+ return in;
+ }
+
std::string copy(in);
while (StartsWith(copy, prefix)) {
copy = copy.substr(prefix.size());
diff --git a/utils/include/hidl-util/FQName.h b/utils/include/hidl-util/FQName.h
index 3cdc7a9..3e31431 100644
--- a/utils/include/hidl-util/FQName.h
+++ b/utils/include/hidl-util/FQName.h
@@ -18,35 +18,35 @@
#define FQNAME_H_
-#include <android-base/macros.h>
#include <string>
#include <vector>
namespace android {
struct FQName {
+ __attribute__((warn_unused_result)) static bool parse(const std::string& s, FQName* into);
+
explicit FQName();
+
+ // TODO(b/73774955): delete
explicit FQName(const std::string &s);
- FQName(const std::string &package,
- const std::string &version,
- const std::string &name,
- const std::string &valueName = "");
-
- // a synonym to FQName(names.join("."))
- FQName(const std::vector<std::string> &names);
+ FQName(const std::string& package, const std::string& version, const std::string& name = "",
+ const std::string& valueName = "");
FQName(const FQName& other);
bool isValid() const;
bool isIdentifier() const;
- bool setTo(const std::string &s);
+
+ // Returns false if string isn't a valid FQName object.
+ __attribute__((warn_unused_result)) bool setTo(const std::string& s);
void applyDefaults(
const std::string &defaultPackage,
const std::string &defaultVersion);
- std::string package() const;
+ const std::string& package() const;
// Return version in the form "@1.0" if it is present, otherwise empty string.
std::string atVersion() const;
// Return version in the form "1.0" if it is present, otherwise empty string.
@@ -56,6 +56,8 @@
// Return true only if version is present.
bool hasVersion() const;
+ FQName withVersion(size_t major, size_t minor) const;
+
// The next two methods return the name part of the FQName, that is, the
// part after the version field. For example:
//
@@ -80,13 +82,13 @@
// FQName::name() will return "IFoo.bar.baz". FQName::names() will return
// std::vector<std::string>{"IFoo","bar","baz"}
- std::string name() const;
+ const std::string& name() const;
std::vector<std::string> names() const;
// The next two methods returns two parts of the FQName, that is,
// the first part package + version + name, the second part valueName.
FQName typeName() const;
- std::string valueName() const;
+ const std::string& valueName() const;
// has package version and name
bool isFullyQualified() const;
@@ -96,7 +98,9 @@
// 2. (valueName), aka a single identifier
bool isValidValueName() const;
- void print() const;
+ // Interface names start with 'I'
+ bool isInterfaceName() const;
+
std::string string() const;
bool operator<(const FQName &other) const;
@@ -110,8 +114,13 @@
// Must be called on an interface
// android.hardware.foo@1.0::IBar
+ // -> ABar
+ std::string getInterfaceAdapterName() const;
+
+ // Must be called on an interface
+ // android.hardware.foo@1.0::IBar
// -> IBar
- std::string getInterfaceName() const;
+ const std::string& getInterfaceName() const;
// Must be called on an interface
// android.hardware.foo@1.0::IBar
@@ -120,12 +129,12 @@
// Must be called on an interface
// android.hardware.foo@1.0::IBar
- // -> BpBar
+ // -> BpHwBar
std::string getInterfaceProxyName() const;
// Must be called on an interface
// android.hardware.foo@1.0::IBar
- // -> BnBar
+ // -> BnHwBar
std::string getInterfaceStubName() const;
// Must be called on an interface
@@ -140,6 +149,11 @@
// Must be called on an interface
// android.hardware.foo@1.0::IBar
+ // -> android.hardware.foo@1.0::ABar
+ FQName getInterfaceAdapterFqName() const;
+
+ // Must be called on an interface
+ // android.hardware.foo@1.0::IBar
// -> android.hardware.foo@1.0::BnBar
FQName getInterfaceStubFqName() const;
@@ -210,8 +224,10 @@
// minor-- if result doesn't underflow, else abort.
FQName downRev() const;
-private:
+ private:
+ // TODO(b/73774955): remove
bool mValid;
+
bool mIsIdentifier;
std::string mPackage;
// mMajor == 0 means empty.
@@ -220,15 +236,16 @@
std::string mName;
std::string mValueName;
- void setVersion(const std::string &v);
+ void clear();
+
+ __attribute__((warn_unused_result)) bool setVersion(const std::string& v);
+ __attribute__((warn_unused_result)) bool parseVersion(const std::string& majorStr,
+ const std::string& minorStr);
void clearVersion();
- void parseVersion(const std::string &majorStr, const std::string &minorStr);
};
-static const FQName gIBaseFqName = FQName{"android.hidl.base@1.0::IBase"};
-static const FQName gIBasePackageFqName = FQName{"android.hidl.base"};
-static const FQName gIManagerFqName = FQName{"android.hidl.manager@1.0::IServiceManager"};
-static const FQName gIManagerPackageFqName = FQName{"android.hidl.manager"};
+extern const FQName gIBaseFqName;
+extern const FQName gIManagerFqName;
} // namespace android
diff --git a/utils/include/hidl-util/Formatter.h b/utils/include/hidl-util/Formatter.h
index bb4288b..053563f 100644
--- a/utils/include/hidl-util/Formatter.h
+++ b/utils/include/hidl-util/Formatter.h
@@ -18,7 +18,6 @@
#define FORMATTER_H_
-#include <android-base/macros.h>
#include <functional>
#include <string>
@@ -30,9 +29,11 @@
// The other is with chain calls and lambda functions
// out.sIf("good", [&] { out("blah").endl()("blah").endl(); }).endl();
struct Formatter {
+ static Formatter invalid() { return Formatter(); }
// Assumes ownership of file. Directed to stdout if file == NULL.
- Formatter(FILE *file = NULL);
+ Formatter(FILE* file, size_t spacesPerIndent = 4);
+ Formatter(Formatter&&) = default;
~Formatter();
void indent(size_t level = 1);
@@ -42,13 +43,13 @@
// out.indent(2, [&] {
// out << "Meow\n";
// });
- Formatter &indent(size_t level, std::function<void(void)> func);
+ Formatter& indent(size_t level, const std::function<void(void)>& func);
// Note that The last \n after the last line is NOT added automatically.
// out.indent([&] {
// out << "Meow\n";
// });
- Formatter &indent(std::function<void(void)> func);
+ Formatter& indent(const std::function<void(void)>& func);
// A block inside braces.
// * No space will be added before the opening brace.
@@ -62,7 +63,7 @@
// out << "{\n"
// << "one();\ntwo();\n" // func()
// << "}";
- Formatter &block(std::function<void(void)> func);
+ Formatter& block(const std::function<void(void)>& func);
// A synonym to (*this) << "\n";
Formatter &endl();
@@ -75,14 +76,14 @@
// out << "logFatal();\n";
// }).endl();
// note that there will be a space before the "else"-s.
- Formatter &sIf(const std::string &cond, std::function<void(void)> block);
- Formatter &sElseIf(const std::string &cond, std::function<void(void)> block);
- Formatter &sElse(std::function<void(void)> block);
+ Formatter& sIf(const std::string& cond, const std::function<void(void)>& block);
+ Formatter& sElseIf(const std::string& cond, const std::function<void(void)>& block);
+ Formatter& sElse(const std::function<void(void)>& block);
// out.sFor("int i = 0; i < 10; i++", [&] {
// out << "printf(\"%d\", i);\n";
// }).endl();
- Formatter &sFor(const std::string &stmts, std::function<void(void)> block);
+ Formatter& sFor(const std::string& stmts, const std::function<void(void)>& block);
// out.sTry([&] {
// out << "throw RemoteException();\n"
@@ -92,21 +93,22 @@
// // cleanup
// }).endl();
// note that there will be a space before the "catch"-s.
- Formatter &sTry(std::function<void(void)> block);
- Formatter &sCatch(const std::string &exception, std::function<void(void)> block);
- Formatter &sFinally(std::function<void(void)> block);
+ Formatter& sTry(const std::function<void(void)>& block);
+ Formatter& sCatch(const std::string& exception, const std::function<void(void)>& block);
+ Formatter& sFinally(const std::function<void(void)>& block);
// out.sWhile("z < 10", [&] {
// out << "z++;\n";
// }).endl();
- Formatter &sWhile(const std::string &cond, std::function<void(void)> block);
+ Formatter& sWhile(const std::string& cond, const std::function<void(void)>& block);
// out.join(v.begin(), v.end(), ",", [&](const auto &e) {
// out << toString(e);
// });
- template<typename I>
- Formatter &join(const I begin, const I end, const std::string &separator,
- std::function<void(const typename std::iterator_traits<I>::value_type &)> func);
+ template <typename I>
+ Formatter& join(
+ const I begin, const I end, const std::string& separator,
+ const std::function<void(const typename std::iterator_traits<I>::value_type&)>& func);
Formatter &operator<<(const std::string &out);
@@ -137,9 +139,15 @@
// Remove the line prefix.
void unsetLinePrefix();
-private:
- FILE *mFile;
+ bool isValid() const;
+
+ private:
+ // Creates an invalid formatter object.
+ Formatter();
+
+ FILE* mFile; // invalid if nullptr
size_t mIndentDepth;
+ size_t mSpacesPerIndent;
bool mAtStartOfLine;
std::string mSpace;
@@ -147,12 +155,14 @@
void output(const std::string &text) const;
- DISALLOW_COPY_AND_ASSIGN(Formatter);
+ Formatter(const Formatter&) = delete;
+ void operator=(const Formatter&) = delete;
};
-template<typename I>
-Formatter &Formatter::join(const I begin, const I end, const std::string &separator,
- std::function<void(const typename std::iterator_traits<I>::value_type &)> func) {
+template <typename I>
+Formatter& Formatter::join(
+ const I begin, const I end, const std::string& separator,
+ const std::function<void(const typename std::iterator_traits<I>::value_type&)>& func) {
for (I iter = begin; iter != end; ++iter) {
if (iter != begin) {
(*this) << separator;
diff --git a/utils/include/hidl-util/FqInstance.h b/utils/include/hidl-util/FqInstance.h
new file mode 100644
index 0000000..bdd0043
--- /dev/null
+++ b/utils/include/hidl-util/FqInstance.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_FQINSTANCE_H_
+
+#define ANDROID_FQINSTANCE_H_
+
+#include <string>
+#include <utility>
+
+#include <hidl-util/FQName.h>
+
+namespace android {
+
+// A wrapper around FQName to include instance name as well.
+// FqInstance::setTo also recognizes all FQName formats, including enum names
+// etc.
+// Typical usage:
+// FqInstance fqInstance;
+// if (!fqInstance.setTo("...")) {
+// // error handling
+// }
+// LOG(WARNING) << fqInstance.string();
+class FqInstance {
+ public:
+ const std::string& getPackage() const;
+ size_t getMajorVersion() const;
+ size_t getMinorVersion() const;
+ std::pair<size_t, size_t> getVersion() const;
+ const std::string& getInterface() const;
+ const std::string& getInstance() const;
+
+ bool hasPackage() const;
+ bool hasVersion() const;
+ bool hasInterface() const;
+ bool hasInstance() const;
+
+ // Return true if valid:
+ // android.hardware.foo@1.0::IFoo/instance
+ // @1.0::IFoo/instance
+ // IFoo/instance
+ // android.hardware.foo@1.0::IFoo.Type
+ // @1.0::IFoo.Type
+ // android.hardware.foo@1.0
+ // IFoo.Type
+ // Type
+ // android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE
+ // @1.0::IFoo.Type:MY_ENUM_VALUE
+ // IFoo.Type:MY_ENUM_VALUE
+ //
+ // If no "/instance", hasInstance() will return false afterwards.
+ // TODO(b/73774955): deprecate this and use std::optional.
+ __attribute__((warn_unused_result)) bool setTo(const std::string& s);
+
+ // Convenience method for the following formats:
+ // android.hardware.foo@1.0::IFoo/default
+ // @1.0::IFoo/default
+ // IFoo/default
+ __attribute__((warn_unused_result)) bool setTo(const std::string& package, size_t majorVer,
+ size_t minorVer, const std::string& interface,
+ const std::string& instance);
+ __attribute__((warn_unused_result)) bool setTo(size_t majorVer, size_t minorVer,
+ const std::string& interface,
+ const std::string& instance);
+ __attribute__((warn_unused_result)) bool setTo(const std::string& interface,
+ const std::string& instance);
+
+ // undefined behavior if:
+ // - Default constructor is called without setTo();
+ // - setTo is called but returned false.
+ // Should only be called after setTo() returns true.
+ std::string string() const;
+ bool operator<(const FqInstance& other) const;
+ bool operator==(const FqInstance& other) const;
+ bool operator!=(const FqInstance& other) const;
+
+ private:
+ FQName mFqName;
+ std::string mInstance;
+};
+
+} // namespace android
+
+#endif // ANDROID_FQINSTANCE_H_
diff --git a/utils/include/hidl-util/StringHelper.h b/utils/include/hidl-util/StringHelper.h
index 277367b..51a47a0 100644
--- a/utils/include/hidl-util/StringHelper.h
+++ b/utils/include/hidl-util/StringHelper.h
@@ -19,7 +19,6 @@
#define STRING_HELPER_H_
#include <string>
-#include <android-base/macros.h>
#include <vector>
namespace android {
@@ -80,12 +79,10 @@
const std::string &separator);
private:
- StringHelper() {}
+ StringHelper() = delete;
static void Tokenize(const std::string &in,
std::vector<std::string> *vec);
-
- DISALLOW_COPY_AND_ASSIGN(StringHelper);
};
} // namespace android