.clang-format0100644 0000000 0000000 00000001415 13521773237 012173 0ustar000000000 0000000 # # 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. # BasedOnStyle: Google CommentPragmas: NOLINT:.* DerivePointerAlignment: false AllowShortFunctionsOnASingleLine: Inline ColumnLimit: 100 TabWidth: 4 UseTab: Never IndentWidth: 4 AST.cpp0100644 0000000 0000000 00000062203 13521773237 010755 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "Coordinator.h" #include "EnumType.h" #include "FmqType.h" #include "HandleType.h" #include "Interface.h" #include "Location.h" #include "Scope.h" #include "TypeDef.h" #include #include #include #include #include #include #include namespace android { AST::AST(const Coordinator* coordinator, const Hash* fileHash) : mCoordinator(coordinator), mFileHash(fileHash), mRootScope("(root scope)", FQName(), Location::startOf(fileHash->getPath()), nullptr /* parent */) {} Scope* AST::getRootScope() { return &mRootScope; } // used by the parser. void AST::addSyntaxError() { mSyntaxErrors++; } size_t AST::syntaxErrors() const { return mSyntaxErrors; } const std::string& AST::getFilename() const { return mFileHash->getPath(); } const Hash* AST::getFileHash() const { return mFileHash; } bool AST::setPackage(const char *package) { if (!mPackage.setTo(package)) { return false; } if (mPackage.package().empty() || mPackage.version().empty() || !mPackage.name().empty()) { return false; } return true; } FQName AST::package() const { return mPackage; } bool AST::isInterface() const { return mRootScope.getInterface() != nullptr; } bool AST::containsInterfaces() const { 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 visited; mRootScope.recursivePass( [](Type* type) { type->setPostParseCompleted(); return OK; }, &visited); return OK; } status_t AST::constantExpressionRecursivePass( const std::function& func, bool processBeforeDependencies) { std::unordered_set visitedTypes; std::unordered_set 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 visited; return mRootScope.recursivePass( [&](Type* type) -> status_t { Scope* scope = type->isScope() ? static_cast(type) : type->parent(); for (auto* nextRef : type->getReferences()) { if (nextRef->isResolved()) { continue; } Type* nextType = lookupType(nextRef->getLookupFqName(), scope); if (nextType == nullptr) { std::cerr << "ERROR: Failed to lookup type '" << nextRef->getLookupFqName().string() << "' at " << nextRef->location() << "\n"; return UNKNOWN_ERROR; } nextRef->set(nextType); } return OK; }, &visited); } status_t AST::gatherReferencedTypes() { std::unordered_set 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(targetType)->fqName()); } } return OK; }, &visited); } status_t AST::lookupLocalIdentifiers() { std::unordered_set visitedTypes; std::unordered_set visitedCE; return mRootScope.recursivePass( [&](Type* type) -> status_t { Scope* scope = type->isScope() ? static_cast(type) : type->parent(); for (auto* ce : type->getConstantExpressions()) { status_t err = ce->recursivePass( [&](ConstantExpression* ce) { for (auto* nextRef : ce->getReferences()) { if (nextRef->isResolved()) continue; LocalIdentifier* iden = lookupLocalIdentifier(*nextRef, scope); if (iden == nullptr) return UNKNOWN_ERROR; nextRef->set(iden); } return OK; }, &visitedCE, true /* processBeforeDependencies */); if (err != OK) return err; } return OK; }, &visitedTypes); } status_t AST::validateDefinedTypesUniqueNames() const { std::unordered_set visited; return mRootScope.recursivePass( [&](const Type* type) -> status_t { // We only want to validate type definition names in this place. if (type->isScope()) { return static_cast(type)->validateUniqueNames(); } return OK; }, &visited); } status_t AST::resolveInheritance() { std::unordered_set visited; return mRootScope.recursivePass(&Type::resolveInheritance, &visited); } status_t AST::evaluate() { return constantExpressionRecursivePass( [](ConstantExpression* ce) { ce->evaluate(); return OK; }, false /* processBeforeDependencies */); } status_t AST::validate() const { std::unordered_set visited; return mRootScope.recursivePass(&Type::validate, &visited); } status_t AST::topologicalReorder() { std::unordered_map reversedOrder; std::unordered_set stack; status_t err = mRootScope.topologicalOrder(&reversedOrder, &stack).status; if (err != OK) return err; std::unordered_set visited; mRootScope.recursivePass( [&](Type* type) { if (type->isScope()) { static_cast(type)->topologicalReorder(reversedOrder); } return OK; }, &visited); return OK; } status_t AST::checkAcyclicConstantExpressions() const { std::unordered_set visitedTypes; std::unordered_set visitedCE; std::unordered_set stack; return mRootScope.recursivePass( [&](const Type* type) -> status_t { for (auto* ce : type->getConstantExpressions()) { status_t err = ce->checkAcyclic(&visitedCE, &stack).status; CHECK(err != OK || stack.empty()); if (err != OK) return err; } return OK; }, &visitedTypes); } status_t AST::checkForwardReferenceRestrictions() const { std::unordered_set visited; return mRootScope.recursivePass( [](const Type* type) -> status_t { for (const Reference* ref : type->getReferences()) { status_t err = type->checkForwardReferenceRestrictions(*ref); if (err != OK) return err; } return OK; }, &visited); } bool AST::addImport(const char *import) { FQName fqName; if (!FQName::parse(import, &fqName)) { std::cerr << "ERROR: '" << import << "' is an invalid fully-qualified name." << std::endl; return false; } fqName.applyDefaults(mPackage.package(), mPackage.version()); if (fqName.name().empty()) { // import a package std::vector packageInterfaces; status_t err = mCoordinator->appendPackageInterfacesToVector(fqName, &packageInterfaces); if (err != OK) { return false; } for (const auto &subFQName : packageInterfaces) { addToImportedNamesGranular(subFQName); // Do not enforce restrictions on imports. AST* ast = mCoordinator->parse(subFQName, &mImportedASTs, Coordinator::Enforce::NONE); if (ast == nullptr) { return false; } // all previous single type imports are ignored. mImportedTypes.erase(ast); } return true; } addToImportedNamesGranular(fqName); // cases like android.hardware.foo@1.0::IFoo.Internal // android.hardware.foo@1.0::Abc.Internal // assume it is an interface, and try to import it. const FQName interfaceName = fqName.getTopLevelType(); // Do not enforce restrictions on imports. AST* importAST; status_t err = mCoordinator->parseOptional(interfaceName, &importAST, &mImportedASTs, Coordinator::Enforce::NONE); if (err != OK) return false; // importAST nullptr == file doesn't exist if (importAST != nullptr) { // cases like android.hardware.foo@1.0::IFoo.Internal // and android.hardware.foo@1.0::IFoo if (fqName == interfaceName) { // import a single file. // all previous single type imports are ignored. // cases like android.hardware.foo@1.0::IFoo // and android.hardware.foo@1.0::types mImportedTypes.erase(importAST); return true; } // import a single type from this file // cases like android.hardware.foo@1.0::IFoo.Internal FQName matchingName; Type *match = importAST->findDefinedType(fqName, &matchingName); if (match == nullptr) { return false; } // will automatically create a set if it does not exist mImportedTypes[importAST].insert(match); return true; } // probably a type in types.hal, like android.hardware.foo@1.0::Abc.Internal FQName typesFQName = fqName.getTypesForPackage(); // Do not enforce restrictions on imports. importAST = mCoordinator->parse(typesFQName, &mImportedASTs, Coordinator::Enforce::NONE); if (importAST != nullptr) { // Attempt to find Abc.Internal in types. FQName matchingName; Type *match = importAST->findDefinedType(fqName, &matchingName); if (match == nullptr) { return false; } // will automatically create a set if not exist mImportedTypes[importAST].insert(match); return true; } // can't find an appropriate AST for fqName. return false; } void AST::addImportedAST(AST *ast) { mImportedASTs.insert(ast); } FQName AST::makeFullName(const char* localName, Scope* scope) const { std::vector pathComponents{{localName}}; for (; scope != &mRootScope; scope = scope->parent()) { pathComponents.push_back(scope->localName()); } std::reverse(pathComponents.begin(), pathComponents.end()); std::string path = StringHelper::JoinStrings(pathComponents, "."); return FQName(mPackage.package(), mPackage.version(), path); } void AST::addScopedType(NamedType* type, Scope* scope) { scope->addType(type); mDefinedTypesByFullName[type->fqName()] = type; } LocalIdentifier* AST::lookupLocalIdentifier(const Reference& ref, 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) { FQName enumTypeName = fqName.typeName(); std::string enumValueName = fqName.valueName(); CHECK(enumTypeName.isValid()); CHECK(!enumValueName.empty()); Type* type = lookupType(enumTypeName, scope); if(type == nullptr) { *errorMsg = "Cannot find type " + enumTypeName.string(); return nullptr; } type = type->resolve(); if(!type->isEnum()) { *errorMsg = "Type " + enumTypeName.string() + " is not an enum type"; return nullptr; } EnumType *enumType = static_cast(type); EnumValue *v = static_cast(enumType->lookupIdentifier(enumValueName)); if(v == nullptr) { *errorMsg = "Enum type " + enumTypeName.string() + " does not have " + enumValueName; return nullptr; } mReferencedTypeNames.insert(enumType->fqName()); return v; } Type* AST::lookupType(const FQName& fqName, Scope* scope) { CHECK(fqName.isValid()); if (fqName.name().empty()) { // Given a package and version??? return nullptr; } Type *returnedType = nullptr; if (fqName.package().empty() && fqName.version().empty()) { // resolve locally first if possible. returnedType = lookupTypeLocally(fqName, scope); if (returnedType != nullptr) { return returnedType; } } status_t status = lookupAutofilledType(fqName, &returnedType); if (status != OK) { return nullptr; } if (returnedType != nullptr) { return returnedType; } return lookupTypeFromImports(fqName); } // Rule 0: try resolve locally Type* AST::lookupTypeLocally(const FQName& fqName, Scope* scope) { CHECK(fqName.package().empty() && fqName.version().empty() && !fqName.name().empty() && fqName.valueName().empty()); for (; scope != nullptr; scope = scope->parent()) { Type* type = scope->lookupType(fqName); if (type != nullptr) { return type; } } return nullptr; } // Rule 1: auto-fill with current package status_t AST::lookupAutofilledType(const FQName &fqName, Type **returnedType) { CHECK(!fqName.name().empty() && fqName.valueName().empty()); FQName autofilled = fqName; autofilled.applyDefaults(mPackage.package(), mPackage.version()); FQName matchingName; // Given this fully-qualified name, the type may be defined in this AST, or other files // in import. Type *local = findDefinedType(autofilled, &matchingName); CHECK(local == nullptr || autofilled == matchingName); Type* fromImport = lookupTypeFromImports(autofilled); if (local != nullptr && fromImport != nullptr && local != fromImport) { // Something bad happen; two types have the same FQName. std::cerr << "ERROR: Unable to resolve type name '" << fqName.string() << "' (i.e. '" << autofilled.string() << "'), multiple definitions found.\n"; return UNKNOWN_ERROR; } if (local != nullptr) { *returnedType = local; return OK; } // If fromImport is nullptr as well, return nullptr to fall through to next rule. *returnedType = fromImport; return OK; } // Rule 2: look at imports Type *AST::lookupTypeFromImports(const FQName &fqName) { Type *resolvedType = nullptr; Type *returnedType = nullptr; FQName resolvedName; for (const auto &importedAST : mImportedASTs) { if (mImportedTypes.find(importedAST) != mImportedTypes.end()) { // ignore single type imports continue; } FQName matchingName; Type *match = importedAST->findDefinedType(fqName, &matchingName); if (match != nullptr) { if (resolvedType != nullptr) { std::cerr << "ERROR: Unable to resolve type name '" << fqName.string() << "', multiple matches found:\n"; std::cerr << " " << resolvedName.string() << "\n"; std::cerr << " " << matchingName.string() << "\n"; return nullptr; } resolvedType = match; returnedType = resolvedType; resolvedName = matchingName; // Keep going even after finding a match. } } for (const auto &pair : mImportedTypes) { AST *importedAST = pair.first; std::set importedTypes = pair.second; FQName matchingName; Type *match = importedAST->findDefinedType(fqName, &matchingName); if (match != nullptr && importedTypes.find(match) != importedTypes.end()) { if (resolvedType != nullptr) { std::cerr << "ERROR: Unable to resolve type name '" << fqName.string() << "', multiple matches found:\n"; std::cerr << " " << resolvedName.string() << "\n"; std::cerr << " " << matchingName.string() << "\n"; return nullptr; } resolvedType = match; returnedType = resolvedType; resolvedName = matchingName; // Keep going even after finding a match. } } if (resolvedType) { returnedType = resolvedType; // If the resolved type is not an interface, we need to determine // whether it is defined in types.hal, or in some other interface. In // the latter case, we need to emit a dependency for the interface in // which the type is defined. // // Consider the following: // android.hardware.tests.foo@1.0::Record // android.hardware.tests.foo@1.0::IFoo.Folder // android.hardware.tests.foo@1.0::Folder // // If Record is an interface, then we keep track of it for the purpose // of emitting dependencies in the target language (for example #include // in C++). If Record is a UDT, then we assume it is defined in // types.hal in android.hardware.tests.foo@1.0. // // In the case of IFoo.Folder, the same applies. If IFoo is an // interface, we need to track this for the purpose of emitting // dependencies. If not, then it must have been defined in types.hal. // // In the case of just specifying Folder, the resolved type is // android.hardware.tests.foo@1.0::Folder, and the same logic as // above applies. if (!resolvedType->isInterface()) { FQName ifc = resolvedName.getTopLevelType(); for (const auto &importedAST : mImportedASTs) { FQName matchingName; Type *match = importedAST->findDefinedType(ifc, &matchingName); if (match != nullptr && match->isInterface()) { resolvedType = match; } } } if (!resolvedType->isInterface()) { // Non-interface types are declared in the associated types header. FQName typesName = resolvedName.getTypesForPackage(); mImportedNames.insert(typesName); } else { // Do _not_ use fqName, i.e. the name we used to look up the type, // but instead use the name of the interface we found. // This is necessary because if fqName pointed to a typedef which // in turn referenced the found interface we'd mistakenly use the // name of the typedef instead of the proper name of the interface. const FQName &typeName = static_cast(resolvedType)->fqName(); mImportedNames.insert(typeName); } } return returnedType; } void AST::addToImportedNamesGranular(const FQName &fqName) { if (fqName.package() == package().package() && fqName.version() == package().version()) { // Our own names are _defined_ here, not imported. return; } mImportedNamesGranular.insert(fqName); } Type *AST::findDefinedType(const FQName &fqName, FQName *matchingName) const { for (const auto &pair : mDefinedTypesByFullName) { const FQName &key = pair.first; Type* type = pair.second; if (key.endsWith(fqName)) { *matchingName = key; return type; } } return nullptr; } void AST::getImportedPackages(std::set *importSet) const { for (const auto& fqName : mImportedNamesGranular) { FQName packageName = fqName.getPackageAndVersion(); if (packageName == mPackage) { // We only care about external imports, not our own package. continue; } importSet->insert(packageName); } } void AST::getImportedPackagesHierarchy(std::set *importSet) const { getImportedPackages(importSet); std::set newSet; for (const auto &ast : mImportedASTs) { if (importSet->find(ast->package()) != importSet->end()) { ast->getImportedPackagesHierarchy(&newSet); } } importSet->insert(newSet.begin(), newSet.end()); } void AST::getAllImportedNames(std::set *allImportNames) const { for (const auto& name : mImportedNames) { allImportNames->insert(name); AST* ast = mCoordinator->parse(name, nullptr /* imported */, Coordinator::Enforce::NONE); ast->getAllImportedNames(allImportNames); } } void AST::getAllImportedNamesGranular(std::set *allImportNames) const { for (const auto& fqName : mImportedNamesGranular) { if (fqName.name() == "types") { // A package will export everything _defined_ but will not // re-export anything it itself imported. AST* ast = mCoordinator->parse( fqName, nullptr /* imported */, Coordinator::Enforce::NONE); ast->addDefinedTypes(allImportNames); } else { allImportNames->insert(fqName); } } } bool AST::isJavaCompatible() const { return mRootScope.isJavaCompatible(); } void AST::appendToExportedTypesVector( std::vector *exportedTypes) const { mRootScope.appendToExportedTypesVector(exportedTypes); } bool AST::isIBase() const { Interface* iface = mRootScope.getInterface(); return iface != nullptr && iface->isIBase(); } const Interface *AST::getInterface() const { return mRootScope.getInterface(); } std::string AST::getBaseName() const { const Interface* iface = mRootScope.getInterface(); return iface ? iface->getBaseName() : "types"; } void AST::addDefinedTypes(std::set *definedTypes) const { std::for_each( mDefinedTypesByFullName.begin(), mDefinedTypesByFullName.end(), [definedTypes](const auto &elem) { if (!elem.second->isTypeDef()) { definedTypes->insert(elem.first); } }); } void AST::addReferencedTypes(std::set *referencedTypes) const { std::for_each( mReferencedTypeNames.begin(), mReferencedTypeNames.end(), [referencedTypes](const auto &fqName) { referencedTypes->insert(fqName); }); } } // namespace android; AST.h0100644 0000000 0000000 00000030300 13521773237 010413 0ustar000000000 0000000 /* * 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. */ #ifndef AST_H_ #define AST_H_ #include #include #include #include #include #include #include #include #include "Scope.h" #include "Type.h" namespace android { struct Coordinator; struct ConstantExpression; struct EnumValue; struct Formatter; struct Interface; struct Location; struct Method; struct NamedType; template struct NamedReference; struct Type; struct AST { AST(const Coordinator* coordinator, const Hash* fileHash); bool setPackage(const char *package); bool addImport(const char *import); // package and version really. FQName package() const; bool isInterface() const; bool containsInterfaces() const; // Adds package, version and scope stack to local name FQName makeFullName(const char* localName, Scope* scope) const; void addScopedType(NamedType* type, Scope* scope); 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& ref, Scope* scope); // Look up an enum value by "FQName:valueName". EnumValue* lookupEnumValue(const FQName& fqName, std::string* errorMsg, Scope* scope); // Look up a type by FQName, "pure" names, i.e. those without package // or version are first looked up in the current scope chain. // After that lookup proceeds to imports. Type* lookupType(const FQName& fqName, Scope* scope); void addImportedAST(AST *ast); // Calls all passes after parsing required before // being ready to generate output. status_t postParse(); // Recursive pass on constant expression tree status_t constantExpressionRecursivePass( const std::function& func, bool processBeforeDependencies); // 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 *importSet) const; // Run getImportedPackages on this, then run getImportedPackages on // each AST in each package referenced in importSet. void getImportedPackagesHierarchy(std::set *importSet) const; bool isJavaCompatible() const; // 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 *allImportSet) const; // Get imported types, this includes those explicitly imported as well // as all types defined in imported packages. void getAllImportedNamesGranular(std::set *allImportSet) const; void appendToExportedTypesVector( std::vector *exportedTypes) const; // used by the parser. void addSyntaxError(); size_t syntaxErrors() const; bool isIBase() const; // or nullptr if not isInterface const Interface *getInterface() const; // types or Interface base name (e.x. Foo) std::string getBaseName() const; Scope* getRootScope(); static void generateCppPackageInclude(Formatter& out, const FQName& package, const std::string& klass); void addDefinedTypes(std::set *definedTypes) const; void addReferencedTypes(std::set *referencedTypes) const; void addToImportedNamesGranular(const FQName &fqName); private: const Coordinator* mCoordinator; const Hash* mFileHash; RootScope mRootScope; FQName mPackage; // 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 mImportedNames; // This is the set of actually imported types. std::set 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 mImportedASTs; // If a single type (instead of the whole AST) is imported, the AST will be // present as a key to this map, with the value being a list of types // imported from this AST. If an AST appears in mImportedASTs but not in // mImportedTypes, then the whole AST is imported. std::map> mImportedTypes; // Types keyed by full names defined in this AST. std::map mDefinedTypesByFullName; // used by the parser. size_t mSyntaxErrors = 0; std::set mReferencedTypeNames; // Helper functions for lookupType. Type* lookupTypeLocally(const FQName& fqName, Scope* scope); status_t lookupAutofilledType(const FQName &fqName, Type **returnedType); Type *lookupTypeFromImports(const FQName &fqName); // Find a type matching fqName (which may be partial) and if found // return the associated type and fill in the full "matchingName". // Only types defined in this very AST are considered. Type *findDefinedType(const FQName &fqName, FQName *matchingName) const; void getPackageComponents(std::vector *components) const; void getPackageAndVersionComponents( std::vector *components, bool cpp_compatible) const; 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); 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; void generateTemplatizationLink(Formatter& out) const; void generateCppTag(Formatter& out, const std::string& tag) 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; void generateProxySource(Formatter& out, const FQName& fqName) const; void generateStubSource(Formatter& out, const Interface* iface) const; void generateStubSourceForMethod(Formatter& out, const Method* method, const Interface* superInterface) const; void generateStaticStubMethodSource(Formatter& out, const FQName& fqName, const Method* method) const; void generatePassthroughSource(Formatter& out) const; void generateInterfaceSource(Formatter& out) const; enum InstrumentationEvent { SERVER_API_ENTRY = 0, SERVER_API_EXIT, CLIENT_API_ENTRY, CLIENT_API_EXIT, SYNC_CALLBACK_ENTRY, SYNC_CALLBACK_EXIT, ASYNC_CALLBACK_ENTRY, ASYNC_CALLBACK_EXIT, PASSTHROUGH_ENTRY, PASSTHROUGH_EXIT, }; void generateCppAtraceCall( Formatter &out, InstrumentationEvent event, const Method *method) const; void generateCppInstrumentationCall( Formatter &out, InstrumentationEvent event, const Method *method) const; void declareCppReaderLocals(Formatter& out, const std::vector*>& arg, bool forResults) const; void emitCppReaderWriter(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer, const NamedReference* arg, bool isReader, Type::ErrorMode mode, bool addPrefixToName) const; void emitCppResolveReferences(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer, const NamedReference* arg, bool isReader, Type::ErrorMode mode, bool addPrefixToName) const; void emitJavaReaderWriter(Formatter& out, const std::string& parcelObj, const NamedReference* arg, bool isReader, bool addPrefixToName) const; void emitTypeDeclarations(Formatter& out) const; void emitJavaTypeDeclarations(Formatter& out) const; void emitVtsTypeDeclarations(Formatter& out) const; DISALLOW_COPY_AND_ASSIGN(AST); }; } // namespace android #endif // AST_H_ Android.bp0100644 0000000 0000000 00000007050 13521773237 011524 0ustar000000000 0000000 // 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. 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 { name: "hidl-module-defaults", cflags: [ "-Wall", "-Werror", "-Wextra-semi", ], product_variables: { debuggable: { cflags: ["-D__ANDROID_DEBUGGABLE__"] }, }, } // 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, }, }, } // // libhidl-gen-hash // cc_library { name: "libhidl-gen-hash", host_supported: true, defaults: ["hidl-gen-defaults"], srcs: ["Hash.cpp"], local_include_dirs: ["include_hash/hidl-hash"], export_include_dirs: ["include_hash"], shared_libs: [ "libbase", "libcrypto", "libssl", ], } // // libhidl-gen // cc_library_host_shared { name: "libhidl-gen", 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", "PointerType.cpp", "FmqType.cpp", "RefType.cpp", "ScalarType.cpp", "Scope.cpp", "StringType.cpp", "Type.cpp", "TypeDef.cpp", "VectorType.cpp", ], shared_libs: [ "libbase", "liblog", "libhidl-gen-hash", "libhidl-gen-utils", ], export_shared_lib_headers: [ "libbase", "libhidl-gen-utils", ], export_include_dirs: ["."], // for tests } // // libhidl-gen-ast // cc_library_host_shared { name: "libhidl-gen-ast", defaults: ["hidl-gen-defaults"], srcs: [ "Coordinator.cpp", "generateCpp.cpp", "generateCppAdapter.cpp", "generateCppImpl.cpp", "generateJava.cpp", "generateVts.cpp", "hidl-gen_y.yy", "hidl-gen_l.ll", "AST.cpp", ], shared_libs: [ "libbase", "liblog", "libhidl-gen", "libhidl-gen-hash", "libhidl-gen-utils", ], export_shared_lib_headers: [ "libbase", "libhidl-gen-utils", ], export_include_dirs: ["."], // for tests } // // hidl-gen // cc_binary_host { name: "hidl-gen", defaults: ["hidl-gen-defaults"], srcs: ["main.cpp"], shared_libs: [ "libbase", "liblog", "libhidl-gen", "libhidl-gen-ast", "libhidl-gen-hash", "libhidl-gen-utils", ], } Annotation.cpp0100644 0000000 0000000 00000012534 13521773237 012442 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Annotation.h" #include #include #include #include #include namespace android { AnnotationParam::AnnotationParam(const std::string& name) : mName(name) {} const std::string& AnnotationParam::getName() const { return mName; } std::vector AnnotationParam::getConstantExpressions() { const auto& constRet = static_cast(this)->getConstantExpressions(); std::vector ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ce) { return const_cast(ce); }); return ret; } std::vector AnnotationParam::getConstantExpressions() const { return {}; } std::string AnnotationParam::getSingleString() const { std::string value = getSingleValue(); CHECK(value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') << mName << " must be a string"; // unquote string value = value.substr(1, value.size() - 2); return value; } bool AnnotationParam::getSingleBool() const { std::string value = getSingleString(); if (value == "true") { return true; } else if (value == "false") { return false; } CHECK(false) << mName << " must be of boolean value (true/false)."; return false; } StringAnnotationParam::StringAnnotationParam(const std::string& name, std::vector* values) : AnnotationParam(name), mValues(values) {} std::vector 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* values) : AnnotationParam(name), mValues(values) {} std::string convertToString(const ConstantExpression* value) { if (value->descriptionIsTrivial()) { return value->value(); } return value->value() + " /* " + value->description() + " */"; } std::vector ConstantExpressionAnnotationParam::getValues() const { std::vector 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 ConstantExpressionAnnotationParam::getConstantExpressions() const { std::vector 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; } const AnnotationParamVector &Annotation::params() const { return *mParams; } const AnnotationParam *Annotation::getParam(const std::string &name) const { for (const auto* i : *mParams) { if (i->getName() == name) { return i; } } return nullptr; } std::vector Annotation::getConstantExpressions() { const auto& constRet = static_cast(this)->getConstantExpressions(); std::vector ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ce) { return const_cast(ce); }); return ret; } std::vector Annotation::getConstantExpressions() const { std::vector 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; if (mParams->size() == 0) { return; } out << "("; for (size_t i = 0; i < mParams->size(); ++i) { if (i > 0) { out << ", "; } const AnnotationParam* param = mParams->at(i); out << param->getName() << "="; const std::vector& values = param->getValues(); if (values.size() > 1) { out << "{"; } out << StringHelper::JoinStrings(values, ", "); if (values.size() > 1) { out << "}"; } } out << ")"; } } // namespace android Annotation.h0100644 0000000 0000000 00000005466 13521773237 012115 0ustar000000000 0000000 /* * 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. */ #ifndef ANNOTATION_H_ #define ANNOTATION_H_ #include #include #include #include #include "ConstantExpression.h" namespace android { struct Formatter; struct AnnotationParam { virtual ~AnnotationParam() {} const std::string& getName() const; virtual std::vector getValues() const = 0; virtual std::string getSingleValue() const = 0; /* Returns unquoted version of getSingleValue */ std::string getSingleString() const; /* Returns value interpretted as a boolean */ bool getSingleBool() const; std::vector getConstantExpressions(); virtual std::vector getConstantExpressions() const; protected: const std::string mName; AnnotationParam(const std::string& name); }; struct StringAnnotationParam : AnnotationParam { StringAnnotationParam(const std::string& name, std::vector* values); std::vector getValues() const override; std::string getSingleValue() const override; private: std::vector* const mValues; }; struct ConstantExpressionAnnotationParam : AnnotationParam { ConstantExpressionAnnotationParam(const std::string& name, std::vector* values); std::vector getValues() const override; std::string getSingleValue() const override; std::vector getConstantExpressions() const override; private: std::vector* const mValues; }; using AnnotationParamVector = std::vector; struct Annotation { Annotation(const char *name, AnnotationParamVector *params); std::string name() const; const AnnotationParamVector ¶ms() const; const AnnotationParam *getParam(const std::string &name) const; std::vector getConstantExpressions(); std::vector getConstantExpressions() const; void dump(Formatter &out) const; private: std::string mName; AnnotationParamVector *mParams; DISALLOW_COPY_AND_ASSIGN(Annotation); }; } // namespace android #endif // ANNOTATION_H_ ArrayType.cpp0100644 0000000 0000000 00000040527 13521773237 012253 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ArrayType.h" #include #include #include #include "ConstantExpression.h" namespace android { ArrayType::ArrayType(const Reference& elementType, ConstantExpression* size, Scope* parent) : Type(parent), mElementType(elementType), mSizes{size} { CHECK(!elementType.isEmptyReference()); } void ArrayType::appendDimension(ConstantExpression *size) { mSizes.push_back(size); } size_t ArrayType::countDimensions() const { return mSizes.size(); } bool ArrayType::isArray() const { return true; } bool ArrayType::deepCanCheckEquality(std::unordered_set* visited) const { return mElementType->canCheckEquality(visited); } const Type* ArrayType::getElementType() const { return mElementType.get(); } std::string ArrayType::typeName() const { if (dimension() == 1) { return "array of " + mElementType->typeName(); } return std::to_string(dimension()) + "d array of " + mElementType->typeName(); } std::vector*> ArrayType::getReferences() const { return {&mElementType}; } std::vector ArrayType::getConstantExpressions() const { std::vector 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(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); std::string space = specifyNamespaces ? "::android::hardware::" : ""; std::string arrayType = space + "hidl_array<" + base; for (size_t i = 0; i < mSizes.size(); ++i) { arrayType += ", "; arrayType += mSizes[i]->cppValue(); if (!mSizes[i]->descriptionIsTrivial()) { arrayType += " /* "; arrayType += mSizes[i]->description(); arrayType += " */"; } } arrayType += ">"; switch (mode) { case StorageMode_Stack: return arrayType; case StorageMode_Argument: return "const " + arrayType + "&"; case StorageMode_Result: return "const " + arrayType + "*"; } CHECK(!"Should not be here"); } std::string ArrayType::getInternalDataCppType() const { std::string result = mElementType->getCppStackType(); for (size_t i = 0; i < mSizes.size(); ++i) { result += "["; result += mSizes[i]->cppValue(); result += "]"; } return result; } std::string ArrayType::getJavaType(bool forInitializer) const { std::string base = mElementType->getJavaType(forInitializer); for (size_t i = 0; i < mSizes.size(); ++i) { base += "["; if (forInitializer) { base += mSizes[i]->javaValue(); } if (!forInitializer || !mSizes[i]->descriptionIsTrivial()) { if (forInitializer) base += " "; base += "/* " + mSizes[i]->description() + " */"; } base += "]"; } return base; } std::string ArrayType::getJavaWrapperType() const { return mElementType->getJavaWrapperType(); } std::string ArrayType::getVtsType() const { return "TYPE_ARRAY"; } void ArrayType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { std::string baseType = mElementType->getCppStackType(); const std::string parentName = "_hidl_" + name + "_parent"; out << "size_t " << parentName << ";\n\n"; const std::string parcelObjDeref = parcelObj + (parcelObjIsPointer ? "->" : "."); size_t numArrayElements = 1; for (auto size : mSizes) { numArrayElements *= size->castSizeT(); } if (isReader) { out << "_hidl_err = " << parcelObjDeref << "readBuffer(" << numArrayElements << " * sizeof(" << baseType << "), &" << parentName << ", " << " reinterpret_cast(" << "&" << name << "));\n\n"; handleError(out, mode); } else { out << "_hidl_err = " << parcelObjDeref << "writeBuffer(" << name << ".data(), " << numArrayElements << " * sizeof(" << baseType << "), &" << parentName << ");\n"; handleError(out, mode); } emitReaderWriterEmbedded( out, 0 /* depth */, name, name /* sanitizedName */, isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, "0 /* parentOffset */"); } void ArrayType::emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { if (!mElementType->needsEmbeddedReadWrite()) { return; } const std::string nameDeref = name + (nameIsPointer ? "->" : "."); std::string baseType = mElementType->getCppStackType(); std::string iteratorName = "_hidl_index_" + std::to_string(depth); out << "for (size_t " << iteratorName << " = 0; " << iteratorName << " < " << dimension() << "; ++" << iteratorName << ") {\n"; out.indent(); mElementType->emitReaderWriterEmbedded( out, depth + 1, nameDeref + "data()[" + iteratorName + "]", sanitizedName + "_indexed", false /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText + " + " + iteratorName + " * sizeof(" + baseType + ")"); out.unindent(); out << "}\n\n"; } void ArrayType::emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { emitResolveReferencesEmbedded( out, 0 /* depth */, name, name /* sanitizedName */, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, "_hidl_" + name + "_parent", "0 /* parentOffset */"); } void ArrayType::emitResolveReferencesEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { CHECK(needsResolveReferences() && mElementType->needsResolveReferences()); const std::string nameDeref = name + (nameIsPointer ? "->" : "."); std::string baseType = mElementType->getCppStackType(); std::string iteratorName = "_hidl_index_" + std::to_string(depth); out << "for (size_t " << iteratorName << " = 0; " << iteratorName << " < " << dimension() << "; ++" << iteratorName << ") {\n"; out.indent(); mElementType->emitResolveReferencesEmbedded( out, depth + 1, nameDeref + "data()[" + iteratorName + "]", sanitizedName + "_indexed", false /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText + " + " + iteratorName + " * sizeof(" + baseType + ")"); out.unindent(); out << "}\n\n"; } void ArrayType::emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const { out << streamName << ".append(java.util.Arrays." << (countDimensions() > 1 ? "deepToString" : "toString") << "(" << name << "));\n"; } bool ArrayType::needsEmbeddedReadWrite() const { return mElementType->needsEmbeddedReadWrite(); } bool ArrayType::deepNeedsResolveReferences(std::unordered_set* visited) const { if (mElementType->needsResolveReferences(visited)) { return true; } return Type::deepNeedsResolveReferences(visited); } bool ArrayType::resultNeedsDeref() const { return true; } void ArrayType::emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const { size_t align, size; getAlignmentAndSize(&align, &size); if (isReader) { out << "new " << getJavaType(true /* forInitializer */) << ";\n"; } out << "{\n"; out.indent(); out << "android.os.HwBlob _hidl_blob = "; if (isReader) { out << parcelObj << ".readBuffer(" << size << " /* size */);\n"; } else { out << "new android.os.HwBlob(" << size << " /* size */);\n"; } emitJavaFieldReaderWriter( out, 0 /* depth */, parcelObj, "_hidl_blob", argName, "0 /* offset */", isReader); if (!isReader) { out << parcelObj << ".writeBuffer(_hidl_blob);\n"; } out.unindent(); out << "}\n"; } void ArrayType::emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const { std::string typeName = getJavaType(false /* forInitializer */); std::string initName = getJavaType(true /* forInitializer */); out << "final " << typeName << " " << fieldName << " = new " << initName << ";\n"; } void ArrayType::emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const { out << "{\n"; out.indent(); 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 < loopDimensions; ++dim) { std::string iteratorName = "_hidl_index_" + std::to_string(depth) + "_" + std::to_string(dim); out << "for (int " << iteratorName << " = 0; " << iteratorName << " < " << mSizes[dim]->javaValue() << "; ++" << iteratorName << ") {\n"; out.indent(); indexString += "[" + iteratorName + "]"; } if (isReader && mElementType->isCompoundType()) { std::string typeName = mElementType->getJavaType(false /* forInitializer */); out << fieldName << indexString << " = new " << typeName << "();\n"; } if (!isPrimitiveArray) { mElementType->emitJavaFieldReaderWriter( out, depth + 1, parcelName, blobName, fieldName + indexString, offsetName, isReader); size_t elementAlign, elementSize; mElementType->getAlignmentAndSize(&elementAlign, &elementSize); 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"; } 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"; } out.unindent(); out << "}\n"; } 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) { mElementType->emitVtsTypeDeclarations(out); } else { // Multi-dimension array case. for (size_t index = 1; index < mSizes.size(); index++) { out << "type: " << getVtsType() << "\n"; out << "vector_size: " << mSizes[index]->value() << "\n"; out << "vector_value: {\n"; out.indent(); if (index == mSizes.size() - 1) { mElementType->emitVtsTypeDeclarations(out); } } } for (size_t index = 0; index < mSizes.size(); index++) { out.unindent(); out << "}\n"; } } bool ArrayType::deepIsJavaCompatible(std::unordered_set* visited) const { if (!mElementType->isJavaCompatible(visited)) { return false; } return Type::deepIsJavaCompatible(visited); } bool ArrayType::deepContainsPointer(std::unordered_set* visited) const { if (mElementType->containsPointer(visited)) { return true; } return Type::deepContainsPointer(visited); } void ArrayType::getAlignmentAndSize(size_t *align, size_t *size) const { mElementType->getAlignmentAndSize(align, size); for (auto sizeInDimension : mSizes) { (*size) *= sizeInDimension->castSizeT(); } } size_t ArrayType::dimension() const { size_t numArrayElements = 1; for (auto size : mSizes) { numArrayElements *= size->castSizeT(); } return numArrayElements; } } // namespace android ArrayType.h0100644 0000000 0000000 00000011145 13521773237 011712 0ustar000000000 0000000 /* * 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. */ #ifndef ARRAY_TYPE_H_ #define ARRAY_TYPE_H_ #include "Reference.h" #include "Type.h" #include namespace android { struct ConstantExpression; struct ArrayType : public Type { ArrayType(const Reference& elementType, ConstantExpression* size, Scope* parent); bool isArray() const override; bool deepCanCheckEquality(std::unordered_set* visited) const override; const Type* getElementType() const; void appendDimension(ConstantExpression *size); size_t countDimensions() const; std::string typeName() const override; std::vector*> getReferences() const override; std::vector 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; std::string getInternalDataCppType() const; std::string getJavaType(bool forInitializer) const override; std::string getJavaWrapperType() const override; std::string getVtsType() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; void emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitResolveReferencesEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; void emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const override; bool needsEmbeddedReadWrite() const override; bool deepNeedsResolveReferences(std::unordered_set* visited) const override; bool resultNeedsDeref() const override; void emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const override; void emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const override; void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const override; void emitVtsTypeDeclarations(Formatter& out) const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; bool deepContainsPointer(std::unordered_set* visited) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; private: Reference mElementType; std::vector mSizes; size_t dimension() const; DISALLOW_COPY_AND_ASSIGN(ArrayType); }; } // namespace android #endif // ARRAY_TYPE_H_ CleanSpec.mk0100644 0000000 0000000 00000004171 13521773237 012010 0ustar000000000 0000000 # 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) CompoundType.cpp0100644 0000000 0000000 00000102015 13521773237 012750 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "CompoundType.h" #include "ArrayType.h" #include "VectorType.h" #include #include #include #include namespace android { 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; } void CompoundType::setFields(std::vector*>* fields) { mFields = fields; } std::vector*> CompoundType::getReferences() const { std::vector*> ret; ret.insert(ret.begin(), mFields->begin(), mFields->end()); return ret; } status_t CompoundType::validate() const { for (const auto* field : *mFields) { const Type& type = field->type(); if ((type.isVector() && static_cast(&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()) { std::cerr << "ERROR: Union must not contain any types that need fixup at " << field->location() << "\n"; return UNKNOWN_ERROR; } } } status_t err = validateUniqueNames(); if (err != OK) return err; return Scope::validate(); } status_t CompoundType::validateUniqueNames() const { std::unordered_set 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::deepCanCheckEquality(std::unordered_set* visited) const { if (mStyle == STYLE_UNION) { return false; } for (const auto* field : *mFields) { if (!field->get()->canCheckEquality(visited)) { return false; } } return true; } std::string CompoundType::typeName() const { switch (mStyle) { case STYLE_STRUCT: { return "struct " + localName(); } case STYLE_UNION: { return "union " + localName(); } } CHECK(!"Should not be here"); } std::string CompoundType::getCppType( StorageMode mode, bool /* specifyNamespaces */) const { const std::string base = fullName(); switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: return base + (containsInterface()?"":"*"); } } std::string CompoundType::getJavaType(bool /* forInitializer */) const { return fullJavaName(); } std::string CompoundType::getVtsType() const { switch (mStyle) { case STYLE_STRUCT: { return "TYPE_STRUCT"; } case STYLE_UNION: { return "TYPE_UNION"; } } 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(&t); if (ct->containsInterface()) { return true; } } if (field->type().isInterface()) { return true; } } return false; } void CompoundType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { const std::string parcelObjDeref = parcelObj + (parcelObjIsPointer ? "->" : "."); if(containsInterface()){ for (const auto& field : *mFields) { field->type().emitReaderWriter(out, name + "." + field->name(), parcelObj, parcelObjIsPointer, isReader, mode); } } else { const std::string parentName = "_hidl_" + name + "_parent"; out << "size_t " << parentName << ";\n\n"; if (isReader) { out << "_hidl_err = " << parcelObjDeref << "readBuffer(" << "sizeof(*" << name << "), &" << parentName << ", " << " const_cast(reinterpret_cast(" << "&" << 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 */"); } } } void CompoundType::emitReaderWriterEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string & /*sanitizedName */, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { emitReaderWriterEmbeddedForTypeName( out, name, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText, fullName(), "" /* childName */, "" /* namespace */); } void CompoundType::emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const { if (isReader) { out << "new " << fullJavaName() << "();\n"; } out << argName << "." << (isReader ? "readFromParcel" : "writeToParcel") << "(" << parcelObj << ");\n"; } void CompoundType::emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const { out << "final " << fullJavaName() << " " << fieldName << " = new " << fullJavaName() << "();\n"; } void CompoundType::emitJavaFieldReaderWriter( Formatter &out, size_t /* depth */, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const { if (isReader) { out << fieldName << ".readEmbeddedFromParcel(" << parcelName << ", " << blobName << ", " << offset << ");\n"; return; } out << fieldName << ".writeEmbeddedToBlob(" << blobName << ", " << offset << ");\n"; } void CompoundType::emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { emitResolveReferencesEmbedded( out, 0 /* depth */, name, name /* sanitizedName */, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, "_hidl_" + name + "_parent", "0 /* parentOffset */"); } void CompoundType::emitResolveReferencesEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string &/* sanitizedName */, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { CHECK(needsResolveReferences()); const std::string parcelObjDeref = parcelObjIsPointer ? ("*" + parcelObj) : parcelObj; const std::string parcelObjPointer = parcelObjIsPointer ? parcelObj : ("&" + parcelObj); const std::string nameDerefed = nameIsPointer ? ("*" + name) : name; const std::string namePointer = nameIsPointer ? name : ("&" + name); out << "_hidl_err = "; if (isReader) { out << "readEmbeddedReferenceFromParcel(\n"; } else { out << "writeEmbeddedReferenceToParcel(\n"; } out.indent(2, [&]{ if (isReader) { out << "const_cast<" << fullName() << " *" << ">(" << namePointer << "),\n" << parcelObjDeref; } else { out << nameDerefed << ",\n" << parcelObjPointer; } out << ",\n" << parentName << ",\n" << offsetText << ");\n\n"; }); handleError(out, mode); } void CompoundType::emitTypeDeclarations(Formatter& out) const { out << ((mStyle == STYLE_STRUCT) ? "struct" : "union") << " " << localName() << " final {\n"; out.indent(); Scope::emitTypeDeclarations(out); if (containsPointer()) { for (const auto &field : *mFields) { field->emitDocComment(out); out << field->type().getCppStackType() << " " << field->name() << ";\n"; } out.unindent(); out << "};\n\n"; return; } for (int pass = 0; pass < 2; ++pass) { 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; } if (pass == 0) { out << field->type().getCppStackType() << " " << field->name() << " __attribute__ ((aligned(" << fieldAlign << ")));\n"; } else { out << "static_assert(offsetof(" << fullName() << ", " << field->name() << ") == " << offset << ", \"wrong offset\");\n"; } if (mStyle == STYLE_STRUCT) { offset += fieldSize; } } if (pass == 0) { out.unindent(); out << "};\n\n"; } } size_t structAlign, structSize; getAlignmentAndSize(&structAlign, &structSize); out << "static_assert(sizeof(" << fullName() << ") == " << structSize << ", \"wrong size\");\n"; out << "static_assert(__alignof(" << fullName() << ") == " << structAlign << ", \"wrong alignment\");\n\n"; } void CompoundType::emitTypeForwardDeclaration(Formatter& out) const { out << ((mStyle == STYLE_STRUCT) ? "struct" : "union") << " " << localName() << ";\n"; } void CompoundType::emitPackageTypeDeclarations(Formatter& out) const { Scope::emitPackageTypeDeclarations(out); out << "static inline 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 NamedReference* 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 << "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 << "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"; } } void CompoundType::emitPackageHwDeclarations(Formatter& out) const { if (needsEmbeddedReadWrite()) { out << "::android::status_t readEmbeddedFromParcel(\n"; out.indent(2); out << "const " << fullName() << " &obj,\n" << "const ::android::hardware::Parcel &parcel,\n" << "size_t parentHandle,\n" << "size_t parentOffset);\n\n"; out.unindent(2); out << "::android::status_t writeEmbeddedToParcel(\n"; out.indent(2); out << "const " << fullName() << " &obj,\n" << "::android::hardware::Parcel *parcel,\n" << "size_t parentHandle,\n" << "size_t parentOffset);\n\n"; out.unindent(2); } if(needsResolveReferences()) { out << "::android::status_t readEmbeddedReferenceFromParcel(\n"; out.indent(2); out << fullName() << " *obj,\n" << "const ::android::hardware::Parcel &parcel,\n" << "size_t parentHandle, size_t parentOffset);\n\n"; out.unindent(2); out << "::android::status_t writeEmbeddedReferenceToParcel(\n"; out.indent(2); out << "const " << fullName() << " &obj,\n" << "::android::hardware::Parcel *,\n" << "size_t parentHandle, size_t parentOffset);\n\n"; out.unindent(2); } } void CompoundType::emitTypeDefinitions(Formatter& out, const std::string& prefix) const { std::string space = prefix.empty() ? "" : (prefix + "::"); Scope::emitTypeDefinitions(out, space + localName()); if (needsEmbeddedReadWrite()) { emitStructReaderWriter(out, prefix, true /* isReader */); emitStructReaderWriter(out, prefix, false /* isReader */); } if (needsResolveReferences()) { emitResolveReferenceDef(out, prefix, true /* isReader */); emitResolveReferenceDef(out, prefix, false /* isReader */); } } void CompoundType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const { out << "public final "; if (!atTopLevel) { out << "static "; } out << "class " << localName() << " {\n"; out.indent(); Scope::emitJavaTypeDeclarations(out, false /* atTopLevel */); for (const auto& field : *mFields) { field->emitDocComment(out); out << "public "; field->type().emitJavaFieldInitializer(out, field->name()); } if (!mFields->empty()) { out << "\n"; } //////////////////////////////////////////////////////////////////////////// if (canCheckEquality()) { out << "@Override\npublic final boolean equals(Object otherObject) "; out.block([&] { out.sIf("this == otherObject", [&] { out << "return true;\n"; }).endl(); out.sIf("otherObject == null", [&] { out << "return false;\n"; }).endl(); // Though class is final, we use getClass instead of instanceof to be explicit. out.sIf("otherObject.getClass() != " + fullJavaName() + ".class", [&] { out << "return false;\n"; }).endl(); out << fullJavaName() << " other = (" << fullJavaName() << ")otherObject;\n"; for (const auto &field : *mFields) { std::string condition = (field->type().isScalar() || field->type().isEnum()) ? "this." + field->name() + " != other." + field->name() : ("!android.os.HidlSupport.deepEquals(this." + field->name() + ", other." + field->name() + ")"); out.sIf(condition, [&] { out << "return false;\n"; }).endl(); } out << "return true;\n"; }).endl().endl(); out << "@Override\npublic final int hashCode() "; out.block([&] { out << "return java.util.Objects.hash(\n"; out.indent(2, [&] { out.join(mFields->begin(), mFields->end(), ", \n", [&] (const auto &field) { out << "android.os.HidlSupport.deepHashCode(this." << field->name() << ")"; }); }); out << ");\n"; }).endl().endl(); } else { out << "// equals() is not generated for " << localName() << "\n"; } //////////////////////////////////////////////////////////////////////////// out << "@Override\npublic final String toString() "; out.block([&] { out << "java.lang.StringBuilder builder = new java.lang.StringBuilder();\n" << "builder.append(\"{\");\n"; for (const auto &field : *mFields) { out << "builder.append(\""; if (field != *(mFields->begin())) { out << ", "; } out << "." << field->name() << " = \");\n"; field->type().emitJavaDump(out, "builder", "this." + field->name()); } out << "builder.append(\"}\");\nreturn builder.toString();\n"; }).endl().endl(); size_t structAlign, structSize; getAlignmentAndSize(&structAlign, &structSize); //////////////////////////////////////////////////////////////////////////// out << "public final void readFromParcel(android.os.HwParcel parcel) {\n"; out.indent(); 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"; //////////////////////////////////////////////////////////////////////////// size_t vecAlign, vecSize; VectorType::getAlignmentAndSizeStatic(&vecAlign, &vecSize); 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"; 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 */);\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"; //////////////////////////////////////////////////////////////////////////// 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(); 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.unindent(); out << "}\n\n"; //////////////////////////////////////////////////////////////////////////// out << "public static final void writeVectorToParcel(\n"; out.indent(2); out << "android.os.HwParcel parcel, java.util.ArrayList<" << localName() << "> _hidl_vec) {\n"; out.unindent(); 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) */);\n"; VectorType::EmitJavaFieldReaderWriterForElementType(out, 0 /* depth */, this, "parcel", "_hidl_blob", "_hidl_vec", "0", false /* isReader */); out << "\nparcel.writeBuffer(_hidl_blob);\n"; } out.unindent(); out << "}\n\n"; //////////////////////////////////////////////////////////////////////////// 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; } out.unindent(); out << "}\n"; } out.unindent(); out << "};\n\n"; } void CompoundType::emitStructReaderWriter( Formatter &out, const std::string &prefix, bool isReader) const { std::string space = prefix.empty() ? "" : (prefix + "::"); out << "::android::status_t " << (isReader ? "readEmbeddedFromParcel" : "writeEmbeddedToParcel") << "(\n"; out.indent(2); bool useName = false; for (const auto &field : *mFields) { if (field->type().useNameInEmitReaderWriterEmbedded(isReader)) { useName = true; break; } } std::string name = useName ? "obj" : "/* obj */"; // if not useName, then obj should not be used at all, // then the #error should not be emitted. std::string error = useName ? "" : "\n#error\n"; if (isReader) { out << "const " << space << localName() << " &" << name << ",\n"; out << "const ::android::hardware::Parcel &parcel,\n"; } else { out << "const " << space << localName() << " &" << name << ",\n"; out << "::android::hardware::Parcel *parcel,\n"; } out << "size_t parentHandle,\n" << "size_t parentOffset)"; out << " {\n"; out.unindent(2); out.indent(); out << "::android::status_t _hidl_err = ::android::OK;\n\n"; for (const auto &field : *mFields) { if (!field->type().needsEmbeddedReadWrite()) { continue; } field->type().emitReaderWriterEmbedded( out, 0 /* depth */, name + "." + field->name() + error, field->name() /* sanitizedName */, false /* nameIsPointer */, "parcel", !isReader /* parcelObjIsPointer */, isReader, ErrorMode_Return, "parentHandle", "parentOffset + offsetof(" + fullName() + ", " + field->name() + ")"); } out << "return _hidl_err;\n"; out.unindent(); out << "}\n\n"; } void CompoundType::emitResolveReferenceDef(Formatter& out, const std::string& prefix, bool isReader) const { out << "::android::status_t "; const std::string space(prefix.empty() ? "" : (prefix + "::")); bool useParent = false; for (const auto &field : *mFields) { if (field->type().useParentInEmitResolveReferencesEmbedded()) { useParent = true; break; } } std::string parentHandleName = useParent ? "parentHandle" : "/* parentHandle */"; std::string parentOffsetName = useParent ? "parentOffset" : "/* parentOffset */"; if (isReader) { out << "readEmbeddedReferenceFromParcel(\n"; out.indent(2); out << space + localName() + " *obj,\n" << "const ::android::hardware::Parcel &parcel,\n" << "size_t " << parentHandleName << ", " << "size_t " << parentOffsetName << ")\n"; out.unindent(2); } else { out << "writeEmbeddedReferenceToParcel(\n"; out.indent(2); out << "const " << space + localName() + " &obj,\n" << "::android::hardware::Parcel *parcel,\n" << "size_t " << parentHandleName << ", " << "size_t " << parentOffsetName << ")\n"; out.unindent(2); } out << " {\n"; out.indent(); out << "::android::status_t _hidl_err = ::android::OK;\n\n"; const std::string nameDeref(isReader ? "obj->" : "obj."); // if not useParent, then parentName and offsetText // should not be used at all, then the #error should not be emitted. std::string error = useParent ? "" : "\n#error\n"; for (const auto &field : *mFields) { if (!field->type().needsResolveReferences()) { continue; } field->type().emitResolveReferencesEmbedded( out, 0 /* depth */, nameDeref + field->name(), field->name() /* sanitizedName */, false, // nameIsPointer "parcel", // const std::string &parcelObj, !isReader, // bool parcelObjIsPointer, isReader, // bool isReader, ErrorMode_Return, parentHandleName + error, parentOffsetName + " + offsetof(" + fullName() + ", " + field->name() + ")" + error); } out << "return _hidl_err;\n"; out.unindent(); out << "}\n\n"; } bool CompoundType::needsEmbeddedReadWrite() const { if (mStyle != STYLE_STRUCT) { return false; } for (const auto &field : *mFields) { if (field->type().needsEmbeddedReadWrite()) { return true; } } return false; } bool CompoundType::deepNeedsResolveReferences(std::unordered_set* visited) const { if (mStyle != STYLE_STRUCT) { return false; } for (const auto &field : *mFields) { if (field->type().needsResolveReferences(visited)) { return true; } } return Scope::deepNeedsResolveReferences(visited); } bool CompoundType::resultNeedsDeref() const { return !containsInterface() ; } void CompoundType::emitVtsTypeDeclarations(Formatter& out) const { out << "name: \"" << fullName() << "\"\n"; out << "type: " << getVtsType() << "\n"; // Emit declaration for each subtype. for (const auto &type : getSubTypes()) { switch (mStyle) { case STYLE_STRUCT: { out << "sub_struct: {\n"; break; } case STYLE_UNION: { out << "sub_union: {\n"; break; } } out.indent(); type->emitVtsTypeDeclarations(out); out.unindent(); out << "}\n"; } // Emit declaration for each field. for (const auto &field : *mFields) { switch (mStyle) { case STYLE_STRUCT: { out << "struct_value: {\n"; break; } case STYLE_UNION: { out << "union_value: {\n"; break; } } out.indent(); out << "name: \"" << field->name() << "\"\n"; field->type().emitVtsAttributeType(out); out.unindent(); out << "}\n"; } } void CompoundType::emitVtsAttributeType(Formatter& out) const { out << "type: " << getVtsType() << "\n"; out << "predefined_type: \"" << fullName() << "\"\n"; } bool CompoundType::deepIsJavaCompatible(std::unordered_set* visited) const { if (mStyle != STYLE_STRUCT) { return false; } for (const auto* field : *mFields) { if (!field->get()->isJavaCompatible(visited)) { return false; } } return Scope::deepIsJavaCompatible(visited); } bool CompoundType::deepContainsPointer(std::unordered_set* visited) const { for (const auto* field : *mFields) { if (field->get()->containsPointer(visited)) { return true; } } return Scope::deepContainsPointer(visited); } void CompoundType::getAlignmentAndSize(size_t *align, size_t *size) const { *align = 1; *size = 0; size_t offset = 0; for (const auto &field : *mFields) { // Each field is aligned according to its alignment requirement. // The surrounding structure's alignment is the maximum of its // fields' aligments. size_t fieldAlign, fieldSize; field->type().getAlignmentAndSize(&fieldAlign, &fieldSize); size_t pad = offset % fieldAlign; if (pad > 0) { offset += fieldAlign - pad; } if (mStyle == STYLE_STRUCT) { offset += fieldSize; } else { *size = std::max(*size, fieldSize); } if (fieldAlign > (*align)) { *align = fieldAlign; } } if (mStyle == STYLE_STRUCT) { *size = offset; } // Final padding to account for the structure's alignment. size_t pad = (*size) % (*align); if (pad > 0) { (*size) += (*align) - pad; } if (*size == 0) { // An empty struct still occupies a byte of space in C++. *size = 1; } } } // namespace android CompoundType.h0100644 0000000 0000000 00000011721 13521773237 012420 0ustar000000000 0000000 /* * 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. */ #ifndef COMPOUND_TYPE_H_ #define COMPOUND_TYPE_H_ #include "Reference.h" #include "Scope.h" #include namespace android { struct CompoundType : public Scope { enum Style { STYLE_STRUCT, STYLE_UNION, }; CompoundType(Style style, const char* localName, const FQName& fullName, const Location& location, Scope* parent); Style style() const; void setFields(std::vector*>* fields); bool isCompoundType() const override; bool deepCanCheckEquality(std::unordered_set* visited) const override; std::string typeName() const override; std::vector*> getReferences() const override; status_t validate() const override; status_t validateUniqueNames() const; std::string getCppType(StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool forInitializer) const override; std::string getVtsType() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; void emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitResolveReferencesEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; void emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const override; void emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const override; void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) 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; void emitTypeDefinitions(Formatter& out, const std::string& prefix) const override; void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override; bool needsEmbeddedReadWrite() const override; bool deepNeedsResolveReferences(std::unordered_set* visited) const override; bool resultNeedsDeref() const override; void emitVtsTypeDeclarations(Formatter& out) const override; void emitVtsAttributeType(Formatter& out) const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; bool deepContainsPointer(std::unordered_set* visited) const override; void getAlignmentAndSize(size_t *align, size_t *size) const; bool containsInterface() const; private: Style mStyle; std::vector*>* mFields; void emitStructReaderWriter( 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); }; } // namespace android #endif // COMPOUND_TYPE_H_ ConstantExpression.cpp0100644 0000000 0000000 00000064775 13521773237 014217 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ConstantExpression.h" #include #include #include #include #include #include #include #include "EnumType.h" #include "Scope.h" // LocalIdentifier // The macros are really nasty here. Consider removing // as many macros as possible. #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("!=")) #define OP_IS_BIN_SHIFT (OPEQ(">>") || OPEQ("<<")) #define OP_IS_BIN_LOGICAL (OPEQ("||") || OPEQ("&&")) #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) \ case SK(UINT8): __action__(uint8_t) \ case SK(INT8): __action__(int8_t) \ case SK(UINT16): __action__(uint16_t) \ case SK(INT16): __action__(int16_t) \ case SK(UINT32): __action__(uint32_t) \ case SK(INT32): __action__(int32_t) \ case SK(UINT64): __action__(uint64_t) \ case SK(INT64): __action__(int64_t) \ default: __def__ /* NOLINT */ \ } namespace android { static inline bool isSupported(ScalarType::Kind kind) { return SK(BOOL) == kind || ScalarType(kind, nullptr /* parent */).isValidEnumStorageType(); } /* See docs at the end for details on integral promotion. */ ScalarType::Kind integralPromotion(ScalarType::Kind in) { return SK(INT32) < in ? in : SK(INT32); // note that KIND_INT32 < KIND_UINT32 } /* See docs at the end for details on usual arithmetic conversion. */ ScalarType::Kind usualArithmeticConversion(ScalarType::Kind lft, ScalarType::Kind rgt) { CHECK(isSupported(lft) && isSupported(rgt)); // Kinds in concern: bool, (u)int[8|16|32|64] if(lft == rgt) return lft; // easy case if(lft == SK(BOOL)) return rgt; if(rgt == SK(BOOL)) return lft; bool isLftSigned = (lft == SK(INT8)) || (lft == SK(INT16)) || (lft == SK(INT32)) || (lft == SK(INT64)); bool isRgtSigned = (rgt == SK(INT8)) || (rgt == SK(INT16)) || (rgt == SK(INT32)) || (rgt == SK(INT64)); if(isLftSigned == isRgtSigned) return lft < rgt ? rgt : lft; ScalarType::Kind unsignedRank = isLftSigned ? rgt : lft; ScalarType::Kind signedRank = isLftSigned ? lft : rgt; if(unsignedRank >= signedRank) return unsignedRank; if(signedRank > unsignedRank) return signedRank; // Although there is such rule to return "the unsigned counterpart of // the signed operand", it should not reach here in our HIDL grammar. 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); case SK(INT32): return SK(UINT32); case SK(INT64): return SK(UINT64); default: return SK(UINT64); } } template T handleUnary(const std::string& op, T val) { COMPUTE_UNARY(+) COMPUTE_UNARY(-) COMPUTE_UNARY(!) COMPUTE_UNARY(~) // Should not reach here. SHOULD_NOT_REACH() << "Could not handleUnary for " << op << " " << val; return static_cast(0xdeadbeef); } template T handleBinaryCommon(T lval, const std::string& op, T rval) { COMPUTE_BINARY(+) COMPUTE_BINARY(-) COMPUTE_BINARY(*) COMPUTE_BINARY(/) COMPUTE_BINARY(%) COMPUTE_BINARY(|) COMPUTE_BINARY(^) COMPUTE_BINARY(&) // comparison operators: return 0 or 1 by nature. COMPUTE_BINARY(==) COMPUTE_BINARY(!=) COMPUTE_BINARY(<) COMPUTE_BINARY(>) COMPUTE_BINARY(<=) COMPUTE_BINARY(>=) // Should not reach here. SHOULD_NOT_REACH() << "Could not handleBinaryCommon for " << lval << " " << op << " " << rval; return static_cast(0xdeadbeef); } template 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 " << lval << " " << op << " " << rval; return static_cast(0xdeadbeef); } 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 " << lval << " " << op << " " << rval; return false; } std::unique_ptr ConstantExpression::Zero(ScalarType::Kind kind) { return ValueOf(kind, 0); } std::unique_ptr ConstantExpression::One(ScalarType::Kind kind) { return ValueOf(kind, 1); } std::unique_ptr ConstantExpression::ValueOf(ScalarType::Kind kind, uint64_t value) { return std::make_unique(kind, value); } bool ConstantExpression::isEvaluated() const { return mIsEvaluated; } LiteralConstantExpression::LiteralConstantExpression( ScalarType::Kind kind, uint64_t value, const std::string& expr) { CHECK(!expr.empty()); CHECK(isSupported(kind)); mTrivialDescription = true; mExpr = expr; mValueKind = kind; mValue = value; mIsEvaluated = true; } LiteralConstantExpression::LiteralConstantExpression(ScalarType::Kind kind, uint64_t value) : LiteralConstantExpression(kind, value, std::to_string(value)) {} LiteralConstantExpression* LiteralConstantExpression::tryParse(const std::string& value) { CHECK(!value.empty()); bool isLong = false, isUnsigned = false; 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'); } 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 kind = SK(UINT64); else // l kind = SK(INT64); } else { // no l suffix if(isUnsigned) { // u if(rawValue <= UINT32_MAX) kind = SK(UINT32); else kind = SK(UINT64); } else { // no suffix if(isHex) { 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 return nullptr; } else { if(rawValue <= INT32_MAX) // rawValue always >= 0 kind = SK(INT32); else kind = SK(INT64); } } } return new LiteralConstantExpression(kind, rawValue, value); } void LiteralConstantExpression::evaluate() { // Evaluated in constructor CHECK(isEvaluated()); } 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;) } 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(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__>(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(mLval->mValueKind); // instead of promoting rval, simply casting it to int64 should also be good. int64_t numBits = mRval->cast(); if(numBits < 0) { // shifting with negative number of bits is undefined in C. In HIDL it // is defined as shifting into the other direction. newOp = OPEQ("<<") ? std::string(">>") : std::string("<<"); numBits = -numBits; } #define CASE_SHIFT(__type__) \ mValue = handleShift(static_cast<__type__>(mLval->mValue), newOp, numBits); \ return; SWITCH_KIND(mValueKind, CASE_SHIFT, SHOULD_NOT_REACH(); return;) } // CASE 3: && || if(OP_IS_BIN_LOGICAL) { mValueKind = SK(BOOL); // easy; everything is bool. mValue = handleLogical(mLval->mValue, mOp, mRval->mValue); return; } SHOULD_NOT_REACH(); } void TernaryConstantExpression::evaluate() { if (isEvaluated()) return; CHECK(mCond->isEvaluated()); CHECK(mTrueVal->isEvaluated()); CHECK(mFalseVal->isEvaluated()); mIsEvaluated = true; mExpr = std::string("(") + mCond->description() + "?" + mTrueVal->description() + ":" + mFalseVal->description() + ")"; // 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;) } 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; } std::unique_ptr ConstantExpression::addOne(ScalarType::Kind baseKind) { auto ret = std::make_unique( this, "+", ConstantExpression::One(baseKind).release()); return ret; } 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 }; // into // enum class x : int64_t { y = (int64_t)-9223372036854775808ull }; // by adding the explicit cast. // Because 9223372036854775808 is uint64_t, and // -(uint64_t)9223372036854775808 == 9223372036854775808 could not // be narrowed to int64_t. if(castKind == SK(INT64) && (int64_t)mValue == INT64_MIN) { return "static_cast<" + ScalarType(SK(INT64), nullptr /* parent */).getCppStackType() // "int64_t" + ">(" + literal + "ull)"; } // add suffix if necessary. if(castKind == SK(UINT32) || castKind == SK(UINT64)) literal += "u"; if(castKind == SK(UINT64) || castKind == SK(INT64)) literal += "ll"; return literal; } 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"; case SK(UINT32): return rawValue(SK(INT32)); case SK(UINT16): return rawValue(SK(INT16)); case SK(UINT8) : return rawValue(SK(INT8)); case SK(BOOL) : return this->cast() ? "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__>()); SWITCH_KIND(castKind, CASE_STR, SHOULD_NOT_REACH(); return 0; ); } template T ConstantExpression::cast() const { CHECK(isEvaluated()); #define CASE_CAST_T(__type__) return static_cast(static_cast<__type__>(mValue)); SWITCH_KIND(mValueKind, CASE_CAST_T, SHOULD_NOT_REACH(); return 0; ); } size_t ConstantExpression::castSizeT() const { CHECK(isEvaluated()); return this->cast(); } bool ConstantExpression::isReferenceConstantExpression() const { return false; } std::vector ConstantExpression::getConstantExpressions() { const auto& constRet = static_cast(this)->getConstantExpressions(); std::vector ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ce) { return const_cast(ce); }); return ret; } std::vector*> ConstantExpression::getReferences() { const auto& constRet = static_cast(this)->getReferences(); std::vector*> ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ce) { return const_cast*>(ce); }); return ret; } std::vector*> ConstantExpression::getReferences() const { return {}; } status_t ConstantExpression::recursivePass(const std::function& func, std::unordered_set* 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& func, std::unordered_set* 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* visited, std::unordered_set* 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(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(this)); } } CHECK(stack->find(this) != stack->end()); stack->erase(this); return CheckAcyclicStatus(OK); } void ConstantExpression::setPostParseCompleted() { CHECK(!mIsPostParseCompleted); mIsPostParseCompleted = true; } std::vector LiteralConstantExpression::getConstantExpressions() const { return {}; } UnaryConstantExpression::UnaryConstantExpression(const std::string& op, ConstantExpression* value) : mUnary(value), mOp(op) {} std::vector UnaryConstantExpression::getConstantExpressions() const { return {mUnary}; } BinaryConstantExpression::BinaryConstantExpression(ConstantExpression* lval, const std::string& op, ConstantExpression* rval) : mLval(lval), mRval(rval), mOp(op) {} std::vector BinaryConstantExpression::getConstantExpressions() const { return {mLval, mRval}; } TernaryConstantExpression::TernaryConstantExpression(ConstantExpression* cond, ConstantExpression* trueVal, ConstantExpression* falseVal) : mCond(cond), mTrueVal(trueVal), mFalseVal(falseVal) {} std::vector TernaryConstantExpression::getConstantExpressions() const { return {mCond, mTrueVal, mFalseVal}; } ReferenceConstantExpression::ReferenceConstantExpression(const Reference& value, const std::string& expr) : mReference(value) { mExpr = expr; mTrivialDescription = mExpr.empty(); } bool ReferenceConstantExpression::isReferenceConstantExpression() const { return true; } std::vector ReferenceConstantExpression::getConstantExpressions() const { // Returns reference instead return {}; } std::vector*> ReferenceConstantExpression::getReferences() const { return {&mReference}; } /* Evaluating expressions in HIDL language The following rules are mostly like that in: http://en.cppreference.com/w/cpp/language/operator_arithmetic http://en.cppreference.com/w/cpp/language/operator_logical http://en.cppreference.com/w/cpp/language/operator_comparison http://en.cppreference.com/w/cpp/language/operator_other The type of literal is the first type which the value can fit from the list of types depending on the suffix and bases. suffix decimal bases hexadecimal bases no suffix int32_t int32_t int64_t uint32_t int64_t uint64_t u/U uint32_t (same as left) uint64_t l/L int64_t int64_t ul/UL/uL/Ul uint64_t uint64_t Note: There are no negative integer literals. -1 is the unary minus applied to 1. Unary arithmetic and bitwise operators (~ + -): don't change the type of the argument. (so -1u = -(1u) has type uint32_t) Binary arithmetic and bitwise operators (except shifts) (+ - * / % & | ^): 1. Integral promotion is first applied on both sides. 2. If both operands have the same type, no promotion is necessary. 3. Usual arithmetic conversions. Integral promotion: if an operand is of a type with less than 32 bits, (including bool), it is promoted to int32_t. Usual arithmetic conversions: 1. If operands are both signed or both unsigned, lesser conversion rank is converted to greater conversion rank. 2. Otherwise, if unsigned's rank >= signed's rank, -> unsigned's type 3. Otherwise, if signed's type can hold all values in unsigned's type, -> signed's type 4. Otherwise, both converted to the unsigned counterpart of the signed operand's type. rank: bool < int8_t < int16_t < int32_t < int64_t Shift operators (<< >>): 1. Integral promotion is applied on both sides. 2. For unsigned a, a << b discards bits that shifts out. For signed non-negative a, a << b is legal if no bits shifts out, otherwise error. For signed negative a, a << b gives error. 3. For unsigned and signed non-negative a, a >> b discards bits that shifts out. For signed negative a, a >> b discards bits that shifts out, and the signed bit gets extended. ("arithmetic right shift") 4. Shifting with negative number of bits is undefined. (Currently, the parser will shift into the other direction. This behavior may change.) 5. Shifting with number of bits exceeding the width of the type is undefined. (Currently, 1 << 32 == 1. This behavior may change.) Logical operators (!, &&, ||): 1. Convert first operand to bool. (true if non-zero, false otherwise) 2. If short-circuited, return the result as type bool, value 1 or 0. 3. Otherwise, convert second operand to bool, evaluate the result, and return the result in the same fashion. Arithmetic comparison operators (< > <= >= == !=): 1. Promote operands in the same way as binary arithmetic and bitwise operators. (Integral promotion + Usual arithmetic conversions) 2. Return type bool, value 0 or 1 the same way as logical operators. Ternary conditional operator (?:): 1. Evaluate the conditional and evaluate the operands. 2. Return type of expression is the type under usual arithmetic conversions on the second and third operand. (No integral promotions necessary.) */ } // namespace android ConstantExpression.h0100644 0000000 0000000 00000017554 13521773237 013655 0ustar000000000 0000000 /* * 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. */ #ifndef CONSTANT_EXPRESSION_H_ #define CONSTANT_EXPRESSION_H_ #include #include #include #include #include #include #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 Zero(ScalarType::Kind kind); static std::unique_ptr One(ScalarType::Kind kind); static std::unique_ptr ValueOf(ScalarType::Kind kind, uint64_t value); 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& func, std::unordered_set* visited, bool processBeforeDependencies); status_t recursivePass(const std::function& func, std::unordered_set* 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 getConstantExpressions(); virtual std::vector getConstantExpressions() const = 0; std::vector*> getReferences(); virtual std::vector*> 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; }; // Recursive tree pass that ensures that constant expressions definitions // are acyclic. CheckAcyclicStatus checkAcyclic(std::unordered_set* visited, std::unordered_set* stack) const; /* 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. */ std::string cppValue() const; /* Evaluated result in a string form. */ std::string javaValue() const; /* Evaluated result in a string form, with given contextual kind. */ std::string value(ScalarType::Kind castKind) const; /* Evaluated result in a string form, with given contextual kind. */ std::string cppValue(ScalarType::Kind castKind) const; /* Evaluated result in a string form, with given contextual kind. */ std::string javaValue(ScalarType::Kind castKind) const; /* Formatted expression with type. */ const std::string& description() const; /* See mTrivialDescription */ bool descriptionIsTrivial() const; /* Return a ConstantExpression that is 1 plus the original. */ std::unique_ptr addOne(ScalarType::Kind baseKind); size_t castSizeT() const; // 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 kind of the result value. */ ScalarType::Kind mValueKind; /* The stored result value. */ uint64_t mValue; /* 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; /* * Return the value casted to the given type. * First cast it according to mValueKind, then cast it to T. * Assumes !containsIdentifiers() */ template 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 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 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 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 getConstantExpressions() const override; private: ConstantExpression* const mCond; ConstantExpression* const mTrueVal; ConstantExpression* const mFalseVal; }; struct ReferenceConstantExpression : public ConstantExpression { ReferenceConstantExpression(const Reference& value, const std::string& expr); bool isReferenceConstantExpression() const override; void evaluate() override; std::vector getConstantExpressions() const override; std::vector*> getReferences() const override; private: Reference mReference; }; } // namespace android #endif // CONSTANT_EXPRESSION_H_ Coordinator.cpp0100644 0000000 0000000 00000073352 13521773237 012620 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Coordinator.h" #include #include #include #include #include #include #include #include #include "AST.h" #include "Interface.h" #include "hidl-gen_l.h" static bool existdir(const char *name) { DIR *dir = opendir(name); if (dir == NULL) { return false; } closedir(dir); return true; } namespace android { const std::string &Coordinator::getRootPath() const { return mRootPath; } void Coordinator::setRootPath(const std::string &rootPath) { mRootPath = rootPath; 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(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* 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* parsedASTs, Enforce enforcement) const { CHECK(fqName.isFullyQualified()); auto it = mCache.find(fqName); if (it != mCache.end()) { *ast = (*it).second; if (*ast != nullptr && parsedASTs != nullptr) { parsedASTs->insert(*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. mCache[fqName] = nullptr; AST *typesAST = nullptr; if (fqName.name() != "types") { // 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. status_t err = parseOptional(typesName, &typesAST, nullptr, Enforce::NONE); if (err != OK) return err; // fall through. } std::string packagePath; status_t err = getPackagePath(fqName, false /* relative */, false /* sanitized */, &packagePath); if (err != OK) return err; const std::string path = makeAbsolute(packagePath + fqName.name() + ".hal"); *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); } std::unique_ptr> file(fopen(path.c_str(), "rb"), fclose); 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. } 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", path.c_str()); err = UNKNOWN_ERROR; } else { 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()); err = UNKNOWN_ERROR; } else if ((*ast)->getInterface()->localName() != fqName.name()) { fprintf(stderr, "ERROR: File at '%s' does not declare interface type " "'%s'.\n", path.c_str(), fqName.name().c_str()); err = UNKNOWN_ERROR; } } else if (fqName.name() != "types") { fprintf(stderr, "ERROR: File at '%s' declares types rather than the " "expected interface type '%s'.\n", path.c_str(), fqName.name().c_str()); err = UNKNOWN_ERROR; } else if ((*ast)->containsInterfaces()) { fprintf(stderr, "ERROR: types.hal file at '%s' declares at least one " "interface type.\n", path.c_str()); err = UNKNOWN_ERROR; } } if (err != OK) { delete *ast; *ast = nullptr; return err; } if (parsedASTs != nullptr) { parsedASTs->insert(*ast); } // put it into the cache now, so that enforceRestrictionsOnPackage can // parse fqName. 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 err; } return OK; } const Coordinator::PackageRoot* Coordinator::findPackageRoot(const FQName& fqName) const { CHECK(!fqName.package().empty()); // Find the right package prefix and path for this FQName. For // example, if FQName is "android.hardware.nfc@1.0::INfc", and the // prefix:root is set to [ "android.hardware:hardware/interfaces", // "vendor.qcom.hardware:vendor/qcom"], then we will identify the // prefix "android.hardware" and the package root // "hardware/interfaces". auto ret = mPackageRoots.end(); for (auto it = mPackageRoots.begin(); it != mPackageRoots.end(); it++) { if (!fqName.inPackage(it->root.package())) { continue; } 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; } if (ret == mPackageRoots.end()) { std::cerr << "ERROR: Package root not specified for " << fqName.string() << "\n"; return nullptr; } return &(*ret); } std::string Coordinator::makeAbsolute(const std::string& path) const { if (StringHelper::StartsWith(path, "/") || mRootPath.empty()) { return path; } 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; } 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; } 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 suffixComponents; StringHelper::SplitString(suffix, '.', &suffixComponents); std::vector components; if (!relative) { components.push_back(StringHelper::RTrimAll(packageRoot->path, "/")); } components.insert(components.end(), suffixComponents.begin(), suffixComponents.end()); components.push_back(sanitized ? fqName.sanitizedVersion() : fqName.version()); *path = StringHelper::JoinStrings(components, "/") + "/"; return OK; } status_t Coordinator::getPackageInterfaceFiles( const FQName &package, std::vector *fileNames) const { fileNames->clear(); std::string packagePath; status_t err = getPackagePath(package, false /* relative */, false /* sanitized */, &packagePath); if (err != OK) return err; 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", packagePath.c_str(), package.string().c_str(), path.c_str()); return -errno; } struct dirent *ent; while ((ent = readdir(dir)) != NULL) { if (ent->d_type != DT_REG) { continue; } const auto suffix = ".hal"; const auto suffix_len = std::strlen(suffix); const auto d_namelen = strlen(ent->d_name); if (d_namelen < suffix_len || strcmp(ent->d_name + d_namelen - suffix_len, suffix)) { continue; } fileNames->push_back(std::string(ent->d_name, d_namelen - suffix_len)); } closedir(dir); dir = NULL; std::sort(fileNames->begin(), fileNames->end(), [](const std::string& lhs, const std::string& rhs) -> bool { if (lhs == "types") { return true; } if (rhs == "types") { return false; } return lhs < rhs; }); return OK; } status_t Coordinator::appendPackageInterfacesToVector( const FQName &package, std::vector *packageInterfaces) const { packageInterfaces->clear(); std::vector fileNames; status_t err = getPackageInterfaceFiles(package, &fileNames); if (err != OK) { return err; } for (const auto &fileName : fileNames) { FQName subFQName(package.package(), package.version(), fileName); packageInterfaces->push_back(subFQName); } return OK; } 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 += '.'; } std::replace(packageRoot.begin(), packageRoot.end(), '.', '/'); *path = packageRoot; // now converted to a path return OK; } status_t Coordinator::isTypesOnlyPackage(const FQName& package, bool* result) const { std::vector 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& packageInterfaces, std::set* unreferencedDefinitions, std::set* unreferencedImports) const { CHECK(unreferencedDefinitions != nullptr); CHECK(unreferencedImports != nullptr); std::set packageDefinedTypes; std::set packageReferencedTypes; std::set packageImportedTypes; std::set 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, Enforce enforcement) const { CHECK(enforcement == Enforce::FULL || enforcement == Enforce::NO_HASH || enforcement == Enforce::NONE); // need fqName to be something like android.hardware.foo@1.0. // name and valueName is ignored. if (fqName.package().empty() || fqName.version().empty()) { std::cerr << "ERROR: Cannot enforce restrictions on package " << fqName.string() << ": package or version is missing." << std::endl; return BAD_VALUE; } if (enforcement == Enforce::NONE) { return OK; } FQName package = fqName.getPackageAndVersion(); // look up cache. if (mPackagesEnforced.find(package) != mPackagesEnforced.end()) { return OK; } // enforce all rules. status_t err; err = enforceMinorVersionUprevs(package, enforcement); if (err != OK) { return err; } if (enforcement != Enforce::NO_HASH) { err = enforceHashes(package); if (err != OK) { return err; } } // cache it so that it won't need to be enforced again. mPackagesEnforced.insert(package); return OK; } status_t Coordinator::enforceMinorVersionUprevs(const FQName& currentPackage, Enforce enforcement) const { if(!currentPackage.hasVersion()) { std::cerr << "ERROR: Cannot enforce minor version uprevs for " << currentPackage.string() << ": missing version." << std::endl; return UNKNOWN_ERROR; } if (currentPackage.getPackageMinorVersion() == 0) { return OK; // ignore for @x.0 } bool hasPrevPackage = false; FQName prevPackage = currentPackage; while (prevPackage.getPackageMinorVersion() > 0) { prevPackage = prevPackage.downRev(); 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; } } if (!hasPrevPackage) { // no @x.z, where z < y, exist. return OK; } if (prevPackage != currentPackage.downRev()) { 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; } 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 packageInterfaces; err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces); if (err != OK) { return err; } bool extendedInterface = false; for (const FQName ¤tFQName : packageInterfaces) { if (currentFQName.name() == "types") { continue; // ignore types.hal } const Interface *iface = nullptr; AST* currentAST = parse(currentFQName, nullptr /* parsedASTs */, enforcement); if (currentAST != nullptr) { iface = currentAST->getInterface(); } if (iface == nullptr) { if (currentAST == nullptr) { 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 { std::cerr << "WARNING: Skipping " << currentFQName.string() << " because the file might contain more than one interface." << std::endl; } continue; } if (iface->superType() == nullptr) { CHECK(iface->isIBase()); continue; } // Assume that currentFQName == android.hardware.foo@2.2::IFoo. FQName lastFQName(prevPackage.package(), prevPackage.version(), currentFQName.name()); AST *lastAST = parse(lastFQName); for (; lastFQName.getPackageMinorVersion() > 0 && (lastAST == nullptr || lastAST->getInterface() == nullptr) ; lastFQName = lastFQName.downRev(), lastAST = parse(lastFQName)) { // nothing } // Then lastFQName == android.hardware.foo@2.1::IFoo or // lastFQName == android.hardware.foo@2.0::IFoo if 2.1 doesn't exist. bool lastFQNameExists = lastAST != nullptr && lastAST->getInterface() != nullptr; 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; } 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). 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; } 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 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* result) const { CHECK(result != nullptr); AST* ast = parse(fqName); if (ast == nullptr) return UNKNOWN_ERROR; std::set 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 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 packageInterfaces; status_t err = appendPackageInterfacesToVector(currentPackage, &packageInterfaces); if (err != OK) { return err; } for (const FQName& currentFQName : packageInterfaces) { HashStatus status = checkHash(currentFQName); 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 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; } } // UNFROZEN, ignore } return err; } bool Coordinator::MakeParentHierarchy(const std::string &path) { static const mode_t kMode = 0755; size_t start = 1; // Ignore leading '/' size_t slashPos; while ((slashPos = path.find('/', start)) != std::string::npos) { std::string partial = path.substr(0, slashPos); struct stat st; if (stat(partial.c_str(), &st) < 0) { if (errno != ENOENT) { return false; } int res = mkdir(partial.c_str(), kMode); if (res < 0) { return false; } } else if (!S_ISDIR(st.st_mode)) { return false; } start = slashPos + 1; } return true; } } // namespace android Coordinator.h0100644 0000000 0000000 00000016533 13521773237 012263 0ustar000000000 0000000 /* * 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. */ #ifndef COORDINATOR_H_ #define COORDINATOR_H_ #include #include #include #include #include #include #include #include namespace android { struct AST; struct Type; struct 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 NONE, // only for use during enforcement }; // Attempts to parse the interface/types referred to by fqName. // Parsing an interface also parses the associated package's types.hal // file if it exists. // If "parsedASTs" is non-NULL, successfully parsed ASTs are inserted // into the set. // If !enforce, enforceRestrictionsOnPackage won't be run. AST* parse(const FQName& fqName, std::set* 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* parsedASTs = nullptr, Enforce enforcement = Enforce::FULL) const; // Given package-root paths of ["hardware/interfaces", // "vendor//interfaces"], package roots of // ["android.hardware", "vendor..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). status_t getPackagePath(const FQName& fqName, bool relative, bool sanitized, std::string* path) const; // Given package roots of ["android.hardware", // "vendor..hardware"] and a FQName of // "android.hardware.nfc@1.0::INfc, then getPackageRoot() will // return "android.hardware". status_t getPackageRoot(const FQName& fqName, std::string* root) const; status_t getPackageInterfaceFiles( const FQName &package, std::vector *fileNames) const; status_t appendPackageInterfacesToVector( const FQName &package, std::vector *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& packageInterfaces, std::set* unreferencedDefinitions, std::set* 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". // - hashing restrictions status_t enforceRestrictionsOnPackage(const FQName& fqName, Enforce enforcement = Enforce::FULL) const; private: static bool MakeParentHierarchy(const std::string &path); enum class HashStatus { ERROR, UNFROZEN, FROZEN, CHANGED, // frozen but changed }; HashStatus checkHash(const FQName& fqName) const; status_t getUnfrozenDependencies(const FQName& fqName, std::set* result) const; // 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//interfaces"], package roots of // ["android.hardware", "vendor..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 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 mCache; // cache to enforceRestrictionsOnPackage(). mutable std::set mPackagesEnforced; mutable std::set mReadFiles; // 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, Enforce enforcement) const; status_t enforceHashes(const FQName &fqName) const; DISALLOW_COPY_AND_ASSIGN(Coordinator); }; } // namespace android #endif // COORDINATOR_H_ DeathRecipientType.cpp0100644 0000000 0000000 00000004772 13521773237 014067 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "DeathRecipientType.h" #include #include namespace android { DeathRecipientType::DeathRecipientType(Scope* parent) : Type(parent) {} std::string DeathRecipientType::typeName() const { return "death recipient"; } std::string DeathRecipientType::getCppType(StorageMode mode, bool specifyNamespaces) const { const std::string base = std::string(specifyNamespaces ? "::android::" : "") + "sp<" + (specifyNamespaces ? "::android::hardware::" : "") + "hidl_death_recipient>"; switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: return "const " + base + "*"; } } std::string DeathRecipientType::getJavaType(bool /* forInitializer */) const { // TODO(b/33440494) decouple from hwbinder return "android.os.IHwBinder.DeathRecipient"; } std::string DeathRecipientType::getVtsType() const { return "TYPE_DEATH_RECIPIENT"; } void DeathRecipientType::emitReaderWriter( Formatter& out, const std::string& /* name */, const std::string& /* parcelObj */, bool /* parcelObjIsPointer */, bool /* isReader */, ErrorMode /* mode */) const { out << "LOG_ALWAYS_FATAL(\"DeathRecipient is only supported in passthrough mode\");\n"; } bool DeathRecipientType::needsEmbeddedReadWrite() const { return false; } bool DeathRecipientType::resultNeedsDeref() 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 } void DeathRecipientType::emitVtsTypeDeclarations(Formatter& out) const { out << "type: " << getVtsType() << "\n"; } } // namespace android DeathRecipientType.h0100644 0000000 0000000 00000003122 13521773237 013520 0ustar000000000 0000000 /* * 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. */ #ifndef DEATH_RECIPIENT_TYPE_H_ #define DEATH_RECIPIENT_TYPE_H_ #include "Type.h" namespace android { struct DeathRecipientType : public Type { DeathRecipientType(Scope* parent); std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool forInitializer) const override; std::string getVtsType() const override; std::string typeName() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; bool needsEmbeddedReadWrite() const override; bool resultNeedsDeref() const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; void emitVtsTypeDeclarations(Formatter& out) const override; }; } // namespace android #endif // DEATH_RECIPIENT_TYPE_H_ DocComment.cpp0100644 0000000 0000000 00000003524 13521773237 012357 0ustar000000000 0000000 /* * 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 #include #include namespace android { DocComment::DocComment(const std::string& comment) { std::vector 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 DocComment.h0100644 0000000 0000000 00000002412 13521773237 012017 0ustar000000000 0000000 /* * 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 #include 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_ EnumType.cpp0100644 0000000 0000000 00000071352 13521773237 012101 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "EnumType.h" #include #include #include #include #include "Annotation.h" #include "Location.h" #include "ScalarType.h" namespace android { EnumType::EnumType(const char* localName, const FQName& fullName, const Location& location, const Reference& storageType, Scope* parent) : Scope(localName, fullName, location, parent), mValues(), mStorageType(storageType) {} const Type *EnumType::storageType() const { return mStorageType.get(); } const std::vector &EnumType::values() const { return mValues; } void EnumType::forEachValueFromRoot(const std::function f) const { std::vector chain = typeChain(); for (auto it = chain.rbegin(); it != chain.rend(); ++it) { const auto& type = *it; for (EnumValue* v : type->values()) { f(v); } } } 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; } } for (auto* value : mValues) { value->autofill(prevType, prevValue, mStorageType->resolveToScalarType()); prevType = this; prevValue = value; } return Scope::resolveInheritance(); } std::vector*> EnumType::getReferences() const { return {&mStorageType}; } std::vector EnumType::getConstantExpressions() const { std::vector 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 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 { return mStorageType->isElidableType(); } const ScalarType *EnumType::resolveToScalarType() const { return mStorageType->resolveToScalarType(); } std::string EnumType::typeName() const { return "enum " + localName(); } bool EnumType::isEnum() const { return true; } bool EnumType::deepCanCheckEquality(std::unordered_set* /* visited */) const { return true; } std::string EnumType::getCppType(StorageMode, bool /* specifyNamespaces */) const { return fullName(); } std::string EnumType::getJavaType(bool forInitializer) const { return mStorageType->resolveToScalarType()->getJavaType(forInitializer); } std::string EnumType::getJavaSuffix() const { return mStorageType->resolveToScalarType()->getJavaSuffix(); } std::string EnumType::getJavaWrapperType() const { return mStorageType->resolveToScalarType()->getJavaWrapperType(); } std::string EnumType::getVtsType() const { return "TYPE_ENUM"; } 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 chain = typeChain(); for (auto it = chain.begin(); it != chain.end(); ++it) { const auto &type = *it; for(EnumValue *v : type->values()) { if(v->name() == name) { return v; } } } return nullptr; } void EnumType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { const ScalarType *scalarType = mStorageType->resolveToScalarType(); CHECK(scalarType != NULL); scalarType->emitReaderWriterWithCast( out, name, parcelObj, parcelObjIsPointer, isReader, mode, true /* needsCast */); } void EnumType::emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const { return mStorageType->emitJavaFieldReaderWriter( out, depth, parcelName, blobName, fieldName, offset, isReader); } void EnumType::emitTypeDeclarations(Formatter& out) const { const ScalarType *scalarType = mStorageType->resolveToScalarType(); CHECK(scalarType != nullptr); const std::string storageType = scalarType->getCppStackType(); out << "enum class " << localName() << " : " << storageType << " {\n"; out.indent(); std::vector 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()); CHECK(!value.empty()); // use autofilled values for c++. out << " = " << value; out << ","; std::string comment = entry->comment(); if (!comment.empty()) { out << " // " << comment; } out << "\n"; } } out.unindent(); out << "};\n\n"; } 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( Formatter &out, bool lhsIsEnum, bool rhsIsEnum, const std::string &op) const { const ScalarType *scalarType = mStorageType->resolveToScalarType(); CHECK(scalarType != nullptr); const std::string storageType = scalarType->getCppStackType(); out << "constexpr " << storageType << " operator" << op << "(const " << (lhsIsEnum ? fullName() : storageType) << " lhs, const " << (rhsIsEnum ? fullName() : storageType) << " rhs) {\n"; out.indent([&] { out << "return static_cast<" << storageType << ">("; if (lhsIsEnum) { out << "static_cast<" << storageType << ">(lhs)"; } else { out << "lhs"; } out << " " << op << " "; if (rhsIsEnum) { out << "static_cast<" << storageType << ">(rhs)"; } else { out << "rhs"; } out << ");\n"; }); out << "}\n\n"; } void EnumType::emitBitFieldBitwiseAssignmentOperator( Formatter &out, const std::string &op) const { const ScalarType *scalarType = mStorageType->resolveToScalarType(); CHECK(scalarType != nullptr); const std::string storageType = scalarType->getCppStackType(); out << "constexpr " << storageType << " &operator" << op << "=(" << storageType << "& v, const " << fullName() << " e) {\n"; out.indent([&] { out << "v " << op << "= static_cast<" << storageType << ">(e);\n"; out << "return v;\n"; }); out << "}\n\n"; } 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 */, "|"); emitEnumBitwiseOperator(out, true /* lhsIsEnum */, true /* rhsIsEnum */, "&"); emitEnumBitwiseOperator(out, false /* lhsIsEnum */, true /* rhsIsEnum */, "&"); emitEnumBitwiseOperator(out, true /* lhsIsEnum */, false /* rhsIsEnum */, "&"); emitBitFieldBitwiseAssignmentOperator(out, "|"); emitBitFieldBitwiseAssignmentOperator(out, "&"); const ScalarType *scalarType = mStorageType->resolveToScalarType(); CHECK(scalarType != NULL); out << "template\n" << "static inline std::string toString(" << resolveToScalarType()->getCppArgumentType() << " o);\n"; out << "template<>\n" << "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" << getBitfieldCppType(StorageMode_Stack) << " flipped = 0;\n" << "bool first = true;\n"; forEachValueFromRoot([&](EnumValue* value) { std::string valueName = fullName() + "::" + value->name(); out.sIf("(o & " + valueName + ")" + " == static_cast<" + scalarType->getCppStackType() + ">(" + valueName + ")", [&] { out << "os += (first ? \"\" : \" | \");\n" << "os += \"" << value->name() << "\";\n" << "first = false;\n" << "flipped |= " << valueName << ";\n"; }).endl(); }); // put remaining bits out.sIf("o != flipped", [&] { out << "os += (first ? \"\" : \" | \");\n"; scalarType->emitHexDump(out, "os", "o & (~flipped)"); }); out << "os += \" (\";\n"; scalarType->emitHexDump(out, "os", "o"); out << "os += \")\";\n"; out << "return os;\n"; }).endl().endl(); out << "static inline std::string toString(" << getCppArgumentType() << " o) "; out.block([&] { out << "using ::android::hardware::details::toHexString;\n"; 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(); } void EnumType::emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const { const ScalarType *scalarType = mStorageType->resolveToScalarType(); CHECK(scalarType != NULL); out << "public " << (atTopLevel ? "" : "static ") << "final class " << localName() << " {\n"; out.indent(); const std::string typeName = scalarType->getJavaType(false /* forInitializer */); std::vector 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 << " " << entry->name() << " = "; // javaValue will make the number signed. std::string value = entry->javaValue(scalarType->getKind()); CHECK(!value.empty()); // use autofilled values for java. out << value; out << ";"; std::string comment = entry->comment(); if (!comment.empty()) { out << " // " << comment; } out << "\n"; } } out << "public static final String toString(" << typeName << " o) "; out.block([&] { 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 = getBitfieldJavaType(false /* forInitializer */); auto bitfieldWrapperType = getBitfieldJavaWrapperType(); out << "\n" << "public static final String dumpBitfield(" << bitfieldType << " o) "; out.block([&] { out << "java.util.ArrayList list = new java.util.ArrayList<>();\n"; out << bitfieldType << " flipped = 0;\n"; forEachValueFromRoot([&](EnumValue* value) { if (value->constExpr()->castSizeT() == 0) { out << "list.add(\"" << value->name() << "\"); // " << value->name() << " == 0\n"; 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\" + "; scalarType->emitConvertToJavaHexString(out, "o & (~flipped)"); out << ");\n"; }).endl(); out << "return String.join(\" | \", list);\n"; }).endl().endl(); out.unindent(); out << "};\n\n"; } void EnumType::emitVtsTypeDeclarations(Formatter& out) const { const ScalarType *scalarType = mStorageType->resolveToScalarType(); out << "name: \"" << fullName() << "\"\n"; out << "type: " << getVtsType() << "\n"; out << "enum_value: {\n"; out.indent(); out << "scalar_type: \"" << scalarType->getVtsScalarType() << "\"\n\n"; std::vector chain = typeChain(); for (auto it = chain.rbegin(); it != chain.rend(); ++it) { const auto &type = *it; for (const auto &entry : type->values()) { out << "enumerator: \"" << entry->name() << "\"\n"; out << "scalar_value: {\n"; out.indent(); // use autofilled values for vts. std::string value = entry->value(scalarType->getKind()); CHECK(!value.empty()); out << mStorageType->resolveToScalarType()->getVtsScalarType() << ": " << value << "\n"; out.unindent(); out << "}\n"; } } out.unindent(); out << "}\n"; } void EnumType::emitVtsAttributeType(Formatter& out) const { out << "type: " << getVtsType() << "\n"; out << "predefined_type: \"" << fullName() << "\"\n"; } void EnumType::emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const { out << streamName << ".append(" << fqName().javaName() << ".toString(" << name << "));\n"; } std::vector EnumType::typeChain() const { std::vector types; for (const EnumType* type = this; type != nullptr;) { types.push_back(type); const Type* superType = type->storageType(); if (superType != nullptr && superType->isEnum()) { type = static_cast(superType); } else { type = nullptr; } } return types; } std::vector EnumType::superTypeChain() const { const Type* superType = storageType(); if (superType == nullptr || !superType->isEnum()) { return {}; } return static_cast(superType)->typeChain(); } void EnumType::getAlignmentAndSize(size_t *align, size_t *size) const { mStorageType->getAlignmentAndSize(align, size); } const Annotation *EnumType::findExportAnnotation() const { for (const auto &annotation : annotations()) { if (annotation->name() == "export") { return annotation; } } return nullptr; } void EnumType::appendToExportedTypesVector( std::vector *exportedTypes) const { if (findExportAnnotation() != nullptr) { exportedTypes->push_back(this); } } void EnumType::emitExportedHeader(Formatter& out, bool forJava) const { const Annotation *annotation = findExportAnnotation(); CHECK(annotation != nullptr); std::string name = localName(); const AnnotationParam *nameParam = annotation->getParam("name"); if (nameParam != nullptr) { name = nameParam->getSingleString(); } bool exportParent = true; const AnnotationParam *exportParentParam = annotation->getParam("export_parent"); if (exportParentParam != nullptr) { exportParent = exportParentParam->getSingleBool(); } std::string valuePrefix; const AnnotationParam *prefixParam = annotation->getParam("value_prefix"); if (prefixParam != nullptr) { valuePrefix = prefixParam->getSingleString(); } std::string valueSuffix; const AnnotationParam *suffixParam = annotation->getParam("value_suffix"); if (suffixParam != nullptr) { valueSuffix = suffixParam->getSingleString(); } const ScalarType *scalarType = mStorageType->resolveToScalarType(); CHECK(scalarType != nullptr); std::vector chain; if (exportParent) { chain = typeChain(); } else { chain = { this }; } if (forJava) { if (!name.empty()) { out << "public final class " << name << " {\n"; out.indent(); } else { out << "// Values declared in " << localName() << " follow.\n"; } const std::string typeName = scalarType->getJavaType(false /* forInitializer */); for (auto it = chain.rbegin(); it != chain.rend(); ++it) { const auto &type = *it; for (const auto &entry : type->values()) { out << "public static final " << typeName << " " << valuePrefix << entry->name() << valueSuffix << " = "; // javaValue will make the number signed. std::string value = entry->javaValue(scalarType->getKind()); CHECK(!value.empty()); // use autofilled values for java. out << value; out << ";"; std::string comment = entry->comment(); if (!comment.empty()) { out << " // " << comment; } out << "\n"; } } if (!name.empty()) { out.unindent(); out << "};\n"; } out << "\n"; return; } if (!name.empty()) { out << "typedef "; } out << "enum {\n"; out.indent(); for (auto it = chain.rbegin(); it != chain.rend(); ++it) { const auto &type = *it; for (const auto &entry : type->values()) { out << valuePrefix << entry->name() << valueSuffix; std::string value = entry->cppValue(scalarType->getKind()); CHECK(!value.empty()); // use autofilled values for c++. out << " = " << value; out << ","; std::string comment = entry->comment(); if (!comment.empty()) { out << " // " << comment; } out << "\n"; } } out.unindent(); out << "}"; if (!name.empty()) { out << " " << name; } out << ";\n\n"; } //////////////////////////////////////////////////////////////////////////////// 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; } std::string EnumValue::value(ScalarType::Kind castKind) const { CHECK(mValue != nullptr); return mValue->value(castKind); } std::string EnumValue::cppValue(ScalarType::Kind castKind) const { CHECK(mValue != nullptr); return mValue->cppValue(castKind); } std::string EnumValue::javaValue(ScalarType::Kind castKind) const { CHECK(mValue != nullptr); return mValue->javaValue(castKind); } std::string EnumValue::comment() const { CHECK(mValue != nullptr); if (mValue->descriptionIsTrivial()) return ""; return mValue->description(); } ConstantExpression *EnumValue::constExpr() const { CHECK(mValue != nullptr); return mValue; } 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; if (prevValue == nullptr) { mValue = ConstantExpression::Zero(type->getKind()).release(); } else { std::string description = prevType->fullName() + "." + prevValue->name() + " implicitly"; auto* prevReference = new ReferenceConstantExpression( Reference(prevValue, mLocation), description); mValue = prevReference->addOne(type->getKind()).release(); } } bool EnumValue::isAutoFill() const { return mIsAutoFill; } bool EnumValue::isEnumValue() const { return true; } const Location& EnumValue::location() const { return mLocation; } //////////////////////////////////////////////////////////////////////////////// BitFieldType::BitFieldType(Scope* parent) : TemplatedType(parent) {} bool BitFieldType::isBitField() const { return true; } const EnumType* BitFieldType::getElementEnumType() const { CHECK(mElementType.get() != nullptr && mElementType->isEnum()); return static_cast(mElementType.get()); } std::string BitFieldType::templatedTypeName() const { return "mask"; } bool BitFieldType::isCompatibleElementType(const Type* elementType) const { return elementType->isEnum(); } const ScalarType *BitFieldType::resolveToScalarType() const { return mElementType->resolveToScalarType(); } std::string BitFieldType::getCppType(StorageMode mode, bool specifyNamespaces) const { return getElementEnumType()->getBitfieldCppType(mode, specifyNamespaces); } std::string BitFieldType::getJavaType(bool forInitializer) const { return getElementEnumType()->getBitfieldJavaType(forInitializer); } std::string BitFieldType::getJavaSuffix() const { return resolveToScalarType()->getJavaSuffix(); } std::string BitFieldType::getJavaWrapperType() const { return getElementEnumType()->getBitfieldJavaWrapperType(); } std::string BitFieldType::getVtsType() const { return "TYPE_MASK"; } bool BitFieldType::isElidableType() const { return resolveToScalarType()->isElidableType(); } bool BitFieldType::deepCanCheckEquality(std::unordered_set* visited) const { return resolveToScalarType()->canCheckEquality(visited); } void BitFieldType::emitVtsAttributeType(Formatter& out) const { out << "type: " << getVtsType() << "\n"; out << "scalar_type: \"" << mElementType->resolveToScalarType()->getVtsScalarType() << "\"\n"; out << "predefined_type: \"" << static_cast(mElementType.get())->fullName() << "\"\n"; } void BitFieldType::getAlignmentAndSize(size_t *align, size_t *size) const { resolveToScalarType()->getAlignmentAndSize(align, size); } void BitFieldType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { resolveToScalarType()->emitReaderWriterWithCast( out, name, parcelObj, parcelObjIsPointer, isReader, mode, true /* needsCast */); } const EnumType* BitFieldType::getEnumType() const { CHECK(mElementType->isEnum()); return static_cast(mElementType.get()); } // a bitfield maps to the underlying scalar type in C++, so operator<< is // already defined. We can still emit useful information if the bitfield is // in a struct / union by overriding emitDump as below. void BitFieldType::emitDump( Formatter &out, const std::string &streamName, const std::string &name) const { out << streamName << " += "<< getEnumType()->fqName().cppNamespace() << "::toString<" << getEnumType()->getCppStackType() << ">(" << name << ");\n"; } void BitFieldType::emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const { out << streamName << ".append(" << getEnumType()->fqName().javaName() << ".dumpBitfield(" << name << "));\n"; } void BitFieldType::emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const { return resolveToScalarType()->emitJavaFieldReaderWriter( out, depth, parcelName, blobName, fieldName, offset, isReader); } } // namespace android EnumType.h0100644 0000000 0000000 00000016111 13521773237 011536 0ustar000000000 0000000 /* * 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. */ #ifndef ENUM_TYPE_H_ #define ENUM_TYPE_H_ #include "ConstantExpression.h" #include "Reference.h" #include "Scope.h" #include namespace android { struct EnumValue; struct BitFieldType; struct EnumType : public Scope { EnumType(const char* localName, const FQName& fullName, const Location& location, const Reference& storageType, Scope* parent); const Type *storageType() const; const std::vector &values() const; void addValue(EnumValue *value); void forEachValueFromRoot(const std::function f) const; LocalIdentifier *lookupIdentifier(const std::string &name) const override; bool isElidableType() const override; const ScalarType *resolveToScalarType() const override; std::string typeName() const override; bool isEnum() const override; bool deepCanCheckEquality(std::unordered_set* visited) const override; std::string getCppType(StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool forInitializer) const override; std::string getJavaSuffix() const override; std::string getJavaWrapperType() const override; 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. const BitFieldType* getBitfieldType() const; std::vector*> getReferences() const override; std::vector getConstantExpressions() const override; status_t resolveInheritance() override; status_t validate() const override; status_t validateUniqueNames() const; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) 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; void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const override; void emitVtsTypeDeclarations(Formatter& out) const override; void emitVtsAttributeType(Formatter& out) const override; void emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; void appendToExportedTypesVector( std::vector *exportedTypes) const override; void emitExportedHeader(Formatter& out, bool forJava) const override; private: std::vector typeChain() const; std::vector superTypeChain() const; const Annotation *findExportAnnotation() const; void emitIteratorDeclaration(Formatter& out) const; void emitIteratorDefinitions(Formatter& out) const; void emitEnumBitwiseOperator( Formatter &out, bool lhsIsEnum, bool rhsIsEnum, const std::string &op) const; void emitBitFieldBitwiseAssignmentOperator( Formatter &out, const std::string &op) const; std::vector mValues; Reference mStorageType; DISALLOW_COPY_AND_ASSIGN(EnumType); }; 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 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; const Location mLocation; bool mIsAutoFill; DISALLOW_COPY_AND_ASSIGN(EnumValue); }; struct BitFieldType : public TemplatedType { BitFieldType(Scope* parent); std::string templatedTypeName() const override; const EnumType* getElementEnumType() const; bool isBitField() const override; bool isCompatibleElementType(const Type* elementType) const override; bool isElidableType() const override; bool deepCanCheckEquality(std::unordered_set* visited) const override; const ScalarType *resolveToScalarType() const override; std::string getCppType(StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool forInitializer) const override; std::string getJavaSuffix() const override; std::string getJavaWrapperType() const override; std::string getVtsType() const override; const EnumType* getEnumType() const; void emitVtsAttributeType(Formatter& out) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitDump( Formatter &out, const std::string &streamName, const std::string &name) const override; void emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const override; void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const override; }; } // namespace android #endif // ENUM_TYPE_H_ FmqType.cpp0100644 0000000 0000000 00000011152 13521773237 011710 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FmqType.h" #include "HidlTypeAssertion.h" #include #include namespace android { FmqType::FmqType(const char* nsp, const char* name, Scope* parent) : TemplatedType(parent), mNamespace(nsp), mName(name) {} std::string FmqType::templatedTypeName() const { return mName; } std::string FmqType::fullName() const { return mNamespace + (mNamespace.empty() ? "" : "::") + mName + "<" + mElementType->getCppStackType(true) + ">"; } std::string FmqType::getCppType( StorageMode mode, bool) const { const std::string base = fullName(); switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: return "const " + base + "*"; } } void FmqType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, 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(" << "&" << name << "));\n\n"; handleError(out, mode); } else { out << "_hidl_err = " << parcelObjDeref << "writeBuffer(&" << name << ", sizeof(" << name << "), &" << parentName << ");\n"; handleError(out, mode); } emitReaderWriterEmbedded( out, 0 /* depth */, name, name /* sanitizedName */, isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, "0 /* parentOffset */"); } void FmqType::emitReaderWriterEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string & /* sanitizedName */, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { emitReaderWriterEmbeddedForTypeName( out, name, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText, fullName(), "" /* childName */, mNamespace); } bool FmqType::deepIsJavaCompatible(std::unordered_set* /* visited */) const { return false; } // All MQDescriptor have the same size. static HidlTypeAssertion assertion( "MQDescriptor", 32); void FmqType::getAlignmentAndSize( size_t *align, size_t *size) const { *align = 8; // MQDescriptor<> *size = assertion.size(); } bool FmqType::needsEmbeddedReadWrite() const { return true; } bool FmqType::resultNeedsDeref() const { return true; } bool FmqType::isCompatibleElementType(const Type* elementType) const { return (!elementType->isInterface() && !elementType->needsEmbeddedReadWrite()); } std::string FmqType::getVtsType() const { if (mName == "MQDescriptorSync") { return "TYPE_FMQ_SYNC"; } else if (mName == "MQDescriptorUnsync") { return "TYPE_FMQ_UNSYNC"; } CHECK(false) << "Invalid FmqType."; return ""; } std::string FmqType::getVtsValueName() const { return "fmq_value"; } } // namespace android FmqType.h0100644 0000000 0000000 00000004326 13521773237 011362 0ustar000000000 0000000 /* * 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. */ #ifndef FMQ_TYPE_H_ #define FMQ_TYPE_H_ #include "Type.h" namespace android { struct FmqType : public TemplatedType { FmqType(const char* nsp, const char* name, Scope* parent); std::string fullName() const; std::string templatedTypeName() const; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; bool needsEmbeddedReadWrite() const override; bool resultNeedsDeref() const override; bool isCompatibleElementType(const Type* elementType) const override; std::string getVtsType() const override; std::string getVtsValueName() const override; private: std::string mNamespace; std::string mName; DISALLOW_COPY_AND_ASSIGN(FmqType); }; } // namespace android #endif // FMQ_TYPE_H_ HandleType.cpp0100644 0000000 0000000 00000010600 13521773237 012355 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "HandleType.h" #include "HidlTypeAssertion.h" #include #include namespace android { HandleType::HandleType(Scope* parent) : Type(parent) {} bool HandleType::isHandle() const { return true; } std::string HandleType::typeName() const { return "handle"; } std::string HandleType::getCppType(StorageMode mode, bool specifyNamespaces) const { const std::string base = std::string(specifyNamespaces ? "::android::hardware::" : "") + "hidl_handle"; switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: return base; } } std::string HandleType::getVtsType() const { return "TYPE_HANDLE"; } void HandleType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { const std::string parcelObjDeref = parcelObj + (parcelObjIsPointer ? "->" : "."); if (isReader) { out << "const native_handle_t *" << name << "_ptr;\n\n"; out << "_hidl_err = " << parcelObjDeref << "readNullableNativeHandleNoDup(" << "&" << name << "_ptr" << ");\n\n"; handleError(out, mode); out << name << " = " << name << "_ptr;\n"; } else { out << "_hidl_err = "; out << parcelObjDeref << "writeNativeHandleNoDup(" << name << ");\n"; handleError(out, mode); } } bool HandleType::useNameInEmitReaderWriterEmbedded(bool isReader) const { return !isReader; } void HandleType::emitReaderWriterEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { if (isReader) { const std::string ptrName = "_hidl_" + sanitizedName + "_ptr"; out << "const native_handle_t *" << ptrName << ";\n" << "_hidl_err = " << parcelObj << (parcelObjIsPointer ? "->" : ".") << "readNullableEmbeddedNativeHandle(\n"; out.indent(); out.indent(); out << parentName << ",\n" << offsetText << ",\n" << "&" << ptrName << "\n" << ");\n\n"; out.unindent(); out.unindent(); handleError(out, mode); } else { out << "_hidl_err = " << parcelObj << (parcelObjIsPointer ? "->" : ".") << "writeEmbeddedNativeHandle(\n"; out.indent(); out.indent(); out << (nameIsPointer ? ("*" + name) : name) << ",\n" << parentName << ",\n" << offsetText << ");\n\n"; out.unindent(); out.unindent(); handleError(out, mode); } } bool HandleType::needsEmbeddedReadWrite() const { return true; } bool HandleType::deepIsJavaCompatible(std::unordered_set* /* visited */) const { return false; } static HidlTypeAssertion assertion("hidl_handle", 16 /* size */); void HandleType::getAlignmentAndSize(size_t *align, size_t *size) const { *align = 8; // hidl_handle *size = assertion.size(); } void HandleType::emitVtsTypeDeclarations(Formatter& out) const { out << "type: " << getVtsType() << "\n"; } } // namespace android HandleType.h0100644 0000000 0000000 00000004074 13521773237 012032 0ustar000000000 0000000 /* * 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. */ #ifndef HANDLE_TYPE_H_ #define HANDLE_TYPE_H_ #include "Type.h" namespace android { struct HandleType : public Type { HandleType(Scope* parent); bool isHandle() const override; std::string typeName() const override; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getVtsType() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; bool needsEmbeddedReadWrite() const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; bool useNameInEmitReaderWriterEmbedded(bool isReader) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; void emitVtsTypeDeclarations(Formatter& out) const override; }; } // namespace android #endif // HANDLE_TYPE_H_ Hash.cpp0100644 0000000 0000000 00000011367 13521773237 011216 0ustar000000000 0000000 /* * 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 "Hash.h" #include #include #include #include #include #include #include #include namespace android { const std::vector Hash::kEmptyHash = std::vector(SHA256_DIGEST_LENGTH, 0); Hash& Hash::getMutableHash(const std::string& path) { static std::map hashes; auto it = hashes.find(path); if (hashes.find(path) == hashes.end()) { it = hashes.insert(it, {path, Hash(path)}); } 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 sha256File(const std::string &path) { std::ifstream stream(path); std::stringstream fileStream; fileStream << stream.rdbuf(); std::string fileContent = fileStream.str(); std::vector ret = std::vector(SHA256_DIGEST_LENGTH); SHA256(reinterpret_cast(fileContent.c_str()), fileContent.size(), ret.data()); return ret; } Hash::Hash(const std::string &path) : mPath(path), mHash(sha256File(path)) {} std::string Hash::hexString(const std::vector &hash) { std::ostringstream s; s << std::hex << std::setfill('0'); for (uint8_t i : hash) { s << std::setw(2) << static_cast(i); } return s.str(); } std::string Hash::hexString() const { return hexString(mHash); } const std::vector &Hash::raw() const { return mHash; } const std::string &Hash::getPath() const { return mPath; } #define HASH "([0-9a-f]+)" #define FQNAME "([^\\s]+)" #define SPACES " +" #define MAYBE_SPACES " *" #define OPTIONAL_COMMENT "(?:#.*)?" static const std::regex kHashLine( "(?:" MAYBE_SPACES HASH SPACES FQNAME MAYBE_SPACES ")?" OPTIONAL_COMMENT); struct HashFile { static const HashFile *parse(const std::string &path, std::string *err) { static std::map hashfiles; auto it = hashfiles.find(path); if (it == hashfiles.end()) { it = hashfiles.insert(it, {path, readHashFile(path, err)}); } return it->second; } std::vector lookup(const std::string &fqName) const { auto it = hashes.find(fqName); if (it == hashes.end()) { return {}; } return it->second; } private: static HashFile *readHashFile(const std::string &path, std::string *err) { std::ifstream stream(path); if (!stream) { return nullptr; } HashFile *file = new HashFile(); file->path = path; std::string line; while(std::getline(stream, line)) { std::smatch match; bool valid = std::regex_match(line, match, kHashLine); if (!valid) { *err = "Error reading line from " + path + ": " + line; delete file; return nullptr; } CHECK_EQ(match.size(), 3u); std::string hash = match.str(1); std::string fqName = match.str(2); if (hash.size() == 0 && fqName.size() == 0) { continue; } if (hash.size() == 0 || fqName.size() == 0) { *err = "Hash or fqName empty on " + path + ": " + line; delete file; return nullptr; } file->hashes[fqName].push_back(hash); } return file; } std::string path; std::map> hashes; }; std::vector 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); } } // android HidlTypeAssertion.cpp0100644 0000000 0000000 00000003054 13521773237 013737 0ustar000000000 0000000 /* * 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 "HidlTypeAssertion.h" #include #include #include namespace android { typedef std::vector> Registry; static Registry ®istry() { static Registry sRegistry; return sRegistry; } HidlTypeAssertion::HidlTypeAssertion(const char *name, size_t size) : mSize(size) { registry().push_back(std::make_pair(name, size)); } size_t HidlTypeAssertion::size() const { return mSize; } void HidlTypeAssertion::EmitAll(Formatter &out) { std::sort( registry().begin(), registry().end(), [](const auto &a, const auto &b) { return a.first < b.first; }); for (auto entry : registry()) { out << "static_assert(sizeof(::android::hardware::" << entry.first << ") == " << entry.second << ", \"wrong size\");\n"; } } } // namespace android HidlTypeAssertion.h0100644 0000000 0000000 00000002463 13521773237 013407 0ustar000000000 0000000 /* * 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 HIDL_TYPE_ASSERTIONS_H_ #define HIDL_TYPE_ASSERTIONS_H_ #include namespace android { struct Formatter; // Declare a HidlTypeAssertion at static scope to enforce a size requirement // on a type (assumed to be declared in namespace ::android::hardware). // This will cause the C++ backend of hidl-gen to emit the appropriate // static_assert(...) statements at the end of FooAll.cpp. struct HidlTypeAssertion { HidlTypeAssertion(const char *name, size_t size); size_t size() const; static void EmitAll(Formatter &out); private: size_t mSize; DISALLOW_COPY_AND_ASSIGN(HidlTypeAssertion); }; } // namespace android #endif // HIDL_TYPE_ASSERTIONS_H_ Interface.cpp0100644 0000000 0000000 00000100176 13521773237 012230 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Interface.h" #include "Annotation.h" #include "ArrayType.h" #include "ConstantExpression.h" #include "DeathRecipientType.h" #include "Method.h" #include "ScalarType.h" #include "StringType.h" #include "VectorType.h" #include #include #include #include #include #include #include #include namespace android { #define B_PACK_CHARS(c1, c2, c3, c4) \ ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) /* It is very important that these values NEVER change. These values * must remain unchanged over the lifetime of android. This is * because the framework on a device will be updated independently of * the hals on a device. If the hals are compiled with one set of * transaction values, and the framework with another, then the * interface between them will be destroyed, and the device will not * work. */ enum { /////////////////// User defined transactions FIRST_CALL_TRANSACTION = 0x00000001, LAST_CALL_TRANSACTION = 0x0effffff, /////////////////// HIDL reserved FIRST_HIDL_TRANSACTION = 0x0f000000, HIDL_PING_TRANSACTION = B_PACK_CHARS(0x0f, 'P', 'N', 'G'), HIDL_DESCRIPTOR_CHAIN_TRANSACTION = B_PACK_CHARS(0x0f, 'C', 'H', 'N'), HIDL_GET_DESCRIPTOR_TRANSACTION = B_PACK_CHARS(0x0f, 'D', 'S', 'C'), HIDL_SYSPROPS_CHANGED_TRANSACTION = B_PACK_CHARS(0x0f, 'S', 'Y', 'S'), HIDL_LINK_TO_DEATH_TRANSACTION = B_PACK_CHARS(0x0f, 'L', 'T', 'D'), HIDL_UNLINK_TO_DEATH_TRANSACTION = B_PACK_CHARS(0x0f, 'U', 'T', 'D'), HIDL_SET_HAL_INSTRUMENTATION_TRANSACTION = B_PACK_CHARS(0x0f, 'I', 'N', 'T'), HIDL_GET_REF_INFO_TRANSACTION = B_PACK_CHARS(0x0f, 'R', 'E', 'F'), HIDL_DEBUG_TRANSACTION = B_PACK_CHARS(0x0f, 'D', 'B', 'G'), HIDL_HASH_CHAIN_TRANSACTION = B_PACK_CHARS(0x0f, 'H', 'S', 'H'), LAST_HIDL_TRANSACTION = 0x0fffffff, }; Interface::Interface(const char* localName, const FQName& fullName, const Location& location, Scope* parent, const Reference& 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; } method->fillImplementation( HIDL_PING_TRANSACTION, { {IMPL_INTERFACE, [](auto &out) { out << "return ::android::hardware::Void();\n"; } }, {IMPL_STUB_IMPL, [](auto &out) { out << "return ::android::hardware::Void();\n"; } } }, /*cppImpl*/ { {IMPL_INTERFACE, [](auto &out) { out << "return;\n"; } }, } /*javaImpl*/ ); return true; } bool Interface::fillLinkToDeathMethod(Method *method) const { if (method->name() != "linkToDeath") { return false; } method->fillImplementation( HIDL_LINK_TO_DEATH_TRANSACTION, { {IMPL_INTERFACE, [](auto &out) { out << "(void)cookie;\n" << "return (recipient != nullptr);\n"; } }, {IMPL_PROXY, [](auto &out) { out << "::android::hardware::ProcessState::self()->startThreadPool();\n"; out << "::android::hardware::hidl_binder_death_recipient *binder_recipient" << " = new ::android::hardware::hidl_binder_death_recipient(recipient, cookie, this);\n" << "std::unique_lock lock(_hidl_mMutex);\n" << "_hidl_mDeathRecipients.push_back(binder_recipient);\n" << "return (remote()->linkToDeath(binder_recipient)" << " == ::android::OK);\n"; } }, {IMPL_STUB, nullptr} }, /*cppImpl*/ { {IMPL_INTERFACE, [](auto &out) { out << "return true;"; } }, {IMPL_PROXY, [](auto &out) { out << "return mRemote.linkToDeath(recipient, cookie);\n"; } }, {IMPL_STUB, nullptr} } /*javaImpl*/ ); return true; } bool Interface::fillUnlinkToDeathMethod(Method *method) const { if (method->name() != "unlinkToDeath") { return false; } method->fillImplementation( HIDL_UNLINK_TO_DEATH_TRANSACTION, { {IMPL_INTERFACE, [](auto &out) { out << "return (recipient != nullptr);\n"; } }, {IMPL_PROXY, [](auto &out) { out << "std::unique_lock lock(_hidl_mMutex);\n" << "for (auto it = _hidl_mDeathRecipients.begin();" << "it != _hidl_mDeathRecipients.end();" << "++it) {\n"; out.indent([&] { out.sIf("(*it)->getRecipient() == recipient", [&] { out << "::android::status_t status = remote()->unlinkToDeath(*it);\n" << "_hidl_mDeathRecipients.erase(it);\n" << "return status == ::android::OK;\n"; }); }); out << "}\n"; out << "return false;\n"; } }, {IMPL_STUB, nullptr /* don't generate code */} }, /*cppImpl*/ { {IMPL_INTERFACE, [](auto &out) { out << "return true;\n"; } }, {IMPL_PROXY, [](auto &out) { out << "return mRemote.unlinkToDeath(recipient);\n"; } }, {IMPL_STUB, nullptr /* don't generate code */} } /*javaImpl*/ ); return true; } bool Interface::fillSyspropsChangedMethod(Method *method) const { if (method->name() != "notifySyspropsChanged") { return false; } method->fillImplementation( HIDL_SYSPROPS_CHANGED_TRANSACTION, { { IMPL_INTERFACE, [](auto &out) { out << "::android::report_sysprop_change();\n"; out << "return ::android::hardware::Void();"; } } }, /*cppImpl */ { { IMPL_INTERFACE, [](auto &out) { /* javaImpl */ out << "android.os.HwBinder.enableInstrumentation();"; } } } /*javaImpl */ ); return true; } bool Interface::fillSetHALInstrumentationMethod(Method *method) const { if (method->name() != "setHALInstrumentation") { return false; } method->fillImplementation( HIDL_SET_HAL_INSTRUMENTATION_TRANSACTION, { {IMPL_INTERFACE, [](auto &out) { // do nothing for base class. out << "return ::android::hardware::Void();\n"; } }, {IMPL_STUB, [](auto &out) { out << "configureInstrumentation();\n"; } }, {IMPL_PASSTHROUGH, [](auto &out) { out << "configureInstrumentation();\n"; out << "return ::android::hardware::Void();\n"; } }, }, /*cppImpl */ { { IMPL_INTERFACE, [](auto & /*out*/) { /* javaImpl */ // Not support for Java Impl for now. } } } /*javaImpl */ ); return true; } bool Interface::fillDescriptorChainMethod(Method *method) const { if (method->name() != "interfaceChain") { return false; } method->fillImplementation( HIDL_DESCRIPTOR_CHAIN_TRANSACTION, { { IMPL_INTERFACE, [this](auto &out) { std::vector chain = typeChain(); out << "_hidl_cb("; out.block([&] { for (const Interface *iface : chain) { out << iface->fullName() << "::descriptor,\n"; } }); out << ");\n"; out << "return ::android::hardware::Void();"; } } }, /* cppImpl */ { { IMPL_INTERFACE, [this](auto &out) { std::vector chain = typeChain(); out << "return new java.util.ArrayList(java.util.Arrays.asList(\n"; out.indent(); out.indent(); for (size_t i = 0; i < chain.size(); ++i) { if (i != 0) out << ",\n"; out << chain[i]->fullJavaName() << ".kInterfaceName"; } out << "));"; out.unindent(); out.unindent(); } } } /* javaImpl */ ); return true; } void Interface::emitDigestChain( Formatter& out, const std::string& prefix, const std::vector& chain, std::function)> byteToString) const { out.join(chain.begin(), chain.end(), ",\n", [&](const auto& iface) { out << prefix; out << "{"; 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 << iface->getFileHash()->hexString(); out << " */"; }); } bool Interface::fillHashChainMethod(Method *method) const { if (method->name() != "getHashChain") { return false; } const VectorType *chainType = static_cast(&method->results()[0]->type()); const ArrayType *digestType = static_cast(chainType->getElementType()); method->fillImplementation( HIDL_HASH_CHAIN_TRANSACTION, { { IMPL_INTERFACE, [this, digestType](auto &out) { std::vector chain = typeChain(); out << "_hidl_cb("; out.block([&] { emitDigestChain(out, "(" + digestType->getInternalDataCppType() + ")", chain, [](const auto& e) { return e->cppValue(); }); }); out << ");\n"; out << "return ::android::hardware::Void();\n"; } } }, /* cppImpl */ { { IMPL_INTERFACE, [this, digestType, chainType](auto &out) { std::vector chain = typeChain(); out << "return new " << chainType->getJavaType(false /* forInitializer */) << "(java.util.Arrays.asList(\n"; 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(); }); }); out << "));\n"; } } } /* javaImpl */ ); return true; } bool Interface::fillGetDescriptorMethod(Method *method) const { if (method->name() != "interfaceDescriptor") { return false; } method->fillImplementation( HIDL_GET_DESCRIPTOR_TRANSACTION, { { IMPL_INTERFACE, [this](auto &out) { out << "_hidl_cb(" << fullName() << "::descriptor);\n" << "return ::android::hardware::Void();"; } } }, /* cppImpl */ { { IMPL_INTERFACE, [this](auto &out) { out << "return " << fullJavaName() << ".kInterfaceName;\n"; } } } /* javaImpl */ ); return true; } bool Interface::fillGetDebugInfoMethod(Method *method) const { if (method->name() != "getDebugInfo") { return false; } static const std::string sArch = "#if defined(__LP64__)\n" "::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT\n" "#else\n" "::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT\n" "#endif\n"; method->fillImplementation( HIDL_GET_REF_INFO_TRANSACTION, { {IMPL_INTERFACE, [](auto &out) { // getDebugInfo returns N/A for local objects. out << "::android::hidl::base::V1_0::DebugInfo info = {};\n"; out << "info.pid = -1;\n"; out << "info.ptr = 0;\n"; out << "info.arch = \n" << sArch << ";\n"; out << "_hidl_cb(info);\n"; out << "return ::android::hardware::Void();\n"; } }, {IMPL_STUB_IMPL, [](auto &out) { out << "::android::hidl::base::V1_0::DebugInfo info = {};\n"; out << "info.pid = ::android::hardware::details::getPidIfSharable();\n"; out << "info.ptr = ::android::hardware::details::debuggable()" << "? reinterpret_cast(this) : 0;\n"; out << "info.arch = \n" << sArch << ";\n"; out << "_hidl_cb(info);\n"; out << "return ::android::hardware::Void();\n"; } } }, /* cppImpl */ { { IMPL_INTERFACE, [method](auto &out) { const Type &refInfo = method->results().front()->type(); out << refInfo.getJavaType(false /* forInitializer */) << " info = new " << refInfo.getJavaType(true /* forInitializer */) << "();\n" << "info.pid = android.os.HidlSupport.getPidIfSharable();\n" << "info.ptr = 0;\n" << "info.arch = android.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;\n" << "return info;"; } } } /* javaImpl */ ); return true; } bool Interface::fillDebugMethod(Method *method) const { if (method->name() != "debug") { return false; } method->fillImplementation( HIDL_DEBUG_TRANSACTION, { {IMPL_INTERFACE, [](auto &out) { out << "(void)fd;\n" << "(void)options;\n" << "return ::android::hardware::Void();"; } }, }, /* cppImpl */ { /* unused, as the debug method is hidden from Java */ } /* javaImpl */ ); return true; } static std::map gAllReservedMethods; bool Interface::addMethod(Method *method) { if (isIBase()) { if (!gAllReservedMethods.emplace(method->name(), method).second) { std::cerr << "ERROR: hidl-gen encountered duplicated reserved method " << method->name() << std::endl; return false; } // will add it in addAllReservedMethods return true; } CHECK(!method->isHidlReserved()); mUserMethods.push_back(method); return true; } std::vector*> Interface::getReferences() const { std::vector*> 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 Interface::getConstantExpressions() const { std::vector ret; for (const auto* method : methods()) { const auto& retMethod = method->getConstantExpressions(); ret.insert(ret.end(), retMethod.begin(), retMethod.end()); } return ret; } std::vector*> 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*> 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 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 reservedMethodsById; for (const auto &pair : gAllReservedMethods) { Method *method = pair.second->copySignature(); bool fillSuccess = fillPingMethod(method) || fillDescriptorChainMethod(method) || fillGetDescriptorMethod(method) || fillHashChainMethod(method) || fillSyspropsChangedMethod(method) || fillLinkToDeathMethod(method) || fillUnlinkToDeathMethod(method) || fillSetHALInstrumentationMethod(method) || fillGetDebugInfoMethod(method) || fillDebugMethod(method); if (!fillSuccess) { std::cerr << "ERROR: hidl-gen does not recognize a reserved method " << method->name() << std::endl; return false; } if (!reservedMethodsById.emplace(method->getSerialId(), method).second) { std::cerr << "ERROR: hidl-gen uses duplicated serial id for " << method->name() << " and " << reservedMethodsById[method->getSerialId()]->name() << ", serialId = " << method->getSerialId() << std::endl; return false; } } for (const auto &pair : reservedMethodsById) { this->mReservedMethods.push_back(pair.second); } return true; } 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(mSuperType.get()); } std::vector Interface::typeChain() const { std::vector v; const Interface *iface = this; while (iface != nullptr) { v.push_back(iface); iface = iface->superType(); } return v; } std::vector Interface::superTypeChain() const { return isIBase() ? std::vector() : superType()->typeChain(); } bool Interface::isElidableType() const { return true; } bool Interface::isInterface() const { return true; } bool Interface::isBinder() const { return true; } const std::vector &Interface::userDefinedMethods() const { return mUserMethods; } const std::vector &Interface::hidlReservedMethods() const { return mReservedMethods; } std::vector Interface::methods() const { std::vector v(mUserMethods); v.insert(v.end(), mReservedMethods.begin(), mReservedMethods.end()); return v; } std::vector Interface::allMethodsFromRoot() const { std::vector v; std::vector chain = typeChain(); for (auto it = chain.rbegin(); it != chain.rend(); ++it) { const Interface *iface = *it; for (Method *userMethod : iface->userDefinedMethods()) { v.push_back(InterfaceAndMethod(iface, userMethod)); } } for (Method *reservedMethod : hidlReservedMethods()) { v.push_back(InterfaceAndMethod( *chain.rbegin(), // IBase reservedMethod)); } return v; } std::vector Interface::allSuperMethodsFromRoot() const { return isIBase() ? std::vector() : 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(); } std::string Interface::getStubName() const { return fqName().getInterfaceStubName(); } std::string Interface::getHwName() const { return fqName().getInterfaceHwName(); } std::string Interface::getPassthroughName() const { return fqName().getInterfacePassthroughName(); } FQName Interface::getProxyFqName() const { return fqName().getInterfaceProxyFqName(); } FQName Interface::getStubFqName() const { return fqName().getInterfaceStubFqName(); } FQName Interface::getPassthroughFqName() const { return fqName().getInterfacePassthroughFqName(); } std::string Interface::getCppType(StorageMode mode, bool specifyNamespaces) const { const std::string base = std::string(specifyNamespaces ? "::android::" : "") + "sp<" + fullName() + ">"; switch (mode) { case StorageMode_Stack: case StorageMode_Result: return base; case StorageMode_Argument: return "const " + base + "&"; } } std::string Interface::getJavaType(bool /* forInitializer */) const { return fullJavaName(); } std::string Interface::getVtsType() const { if (StringHelper::EndsWith(localName(), "Callback")) { return "TYPE_HIDL_CALLBACK"; } else { return "TYPE_HIDL_INTERFACE"; } } void Interface::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { const std::string parcelObjDeref = parcelObj + (parcelObjIsPointer ? "->" : "."); if (isReader) { out << "{\n"; out.indent(); const std::string binderName = "_hidl_binder"; out << "::android::sp<::android::hardware::IBinder> " << binderName << ";\n"; out << "_hidl_err = "; out << parcelObjDeref << "readNullableStrongBinder(&" << binderName << ");\n"; handleError(out, mode); out << name << " = " << "::android::hardware::fromBinder<" << fqName().cppName() << "," << getProxyFqName().cppName() << "," << getStubFqName().cppName() << ">(" << binderName << ");\n"; out.unindent(); out << "}\n\n"; } else { out << "if (" << name << " == nullptr) {\n"; out.indent(); out << "_hidl_err = "; out << parcelObjDeref << "writeStrongBinder(nullptr);\n"; out.unindent(); out << "} else {\n"; out.indent(); out << "::android::sp<::android::hardware::IBinder> _hidl_binder = " << "::android::hardware::toBinder<\n"; out.indent(2, [&] { out << fqName().cppName() << ">(" << name << ");\n"; }); out << "if (_hidl_binder.get() != nullptr) {\n"; out.indent([&] { out << "_hidl_err = " << parcelObjDeref << "writeStrongBinder(_hidl_binder);\n"; }); out << "} else {\n"; out.indent([&] { out << "_hidl_err = ::android::UNKNOWN_ERROR;\n"; }); out << "}\n"; out.unindent(); out << "}\n"; handleError(out, mode); } } void Interface::emitPackageTypeDeclarations(Formatter& out) const { Scope::emitPackageTypeDeclarations(out); out << "static inline std::string toString(" << getCppArgumentType() << " o) "; out.block([&] { out << "std::string os = \"[class or subclass of \";\n" << "os += " << fullName() << "::descriptor;\n" << "os += \"]\";\n" << "os += o->isRemote() ? \"@remote\" : \"@local\";\n" << "return os;\n"; }).endl().endl(); } void Interface::emitTypeDefinitions(Formatter& out, const std::string& prefix) const { std::string space = prefix.empty() ? "" : (prefix + "::"); Scope::emitTypeDefinitions(out, space + localName()); } void Interface::emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const { if (isReader) { out << fullJavaName() << ".asInterface(" << parcelObj << ".readStrongBinder());\n"; } else { out << parcelObj << ".writeStrongBinder(" << argName << " == null ? null : " << argName << ".asBinder());\n"; } } 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()) { continue; } out << "attribute: {\n"; out.indent(); type->emitVtsTypeDeclarations(out); out.unindent(); out << "}\n\n"; } } void Interface::emitVtsMethodDeclaration(Formatter& out) const { for (const auto &method : methods()) { if (method->isHidlReserved()) { continue; } out << "api: {\n"; out.indent(); out << "name: \"" << method->name() << "\"\n"; // Generate declaration for each return value. for (const auto &result : method->results()) { out << "return_type_hidl: {\n"; out.indent(); result->type().emitVtsAttributeType(out); out.unindent(); out << "}\n"; } // Generate declaration for each input argument for (const auto &arg : method->args()) { out << "arg: {\n"; out.indent(); arg->type().emitVtsAttributeType(out); out.unindent(); out << "}\n"; } // Generate declaration for each annotation. for (const auto &annotation : method->annotations()) { out << "callflow: {\n"; out.indent(); const std::string name = annotation->name(); if (name == "entry") { out << "entry: true\n"; } else if (name == "exit") { out << "exit: true\n"; } else if (name == "callflow") { const AnnotationParam *param = annotation->getParam("next"); if (param != nullptr) { for (const auto& value : param->getValues()) { out << "next: " << value << "\n"; } } } else { CHECK(false); } out.unindent(); out << "}\n"; } out.unindent(); out << "}\n\n"; } } void Interface::emitVtsAttributeType(Formatter& out) const { out << "type: " << getVtsType() << "\n" << "predefined_type: \"" << fullName() << "\"\n"; } bool Interface::hasOnewayMethods() const { for (auto const &method : methods()) { if (method->isOneway()) { return true; } } const Interface* superClass = superType(); if (superClass != nullptr) { return superClass->hasOnewayMethods(); } return false; } bool Interface::deepIsJavaCompatible(std::unordered_set* visited) const { if (superType() != nullptr && !superType()->isJavaCompatible(visited)) { return false; } for (const auto* method : methods()) { if (!method->deepIsJavaCompatible(visited)) { return false; } } return Scope::isJavaCompatible(visited); } bool Interface::isNeverStrongReference() const { return true; } } // namespace android Interface.h0100644 0000000 0000000 00000013707 13521773237 011700 0ustar000000000 0000000 /* * 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. */ #ifndef INTERFACE_H_ #define INTERFACE_H_ #include #include #include "Reference.h" #include "Scope.h" namespace android { struct Method; struct InterfaceAndMethod; struct Interface : public Scope { enum { /////////////////// Flag(s) - DO NOT CHANGE FLAG_ONEWAY = 0x00000001, }; Interface(const char* localName, const FQName& fullName, const Location& location, Scope* parent, const Reference& superType, const Hash* fileHash); const Hash* getFileHash() const; bool addMethod(Method *method); bool addAllReservedMethods(); bool isElidableType() const override; bool isInterface() const override; bool isBinder() const override; bool isIBase() const { return fqName() == gIBaseFqName; } std::string typeName() const override; const Interface* superType() const; // Super type chain to root type. // First element is superType(). std::vector superTypeChain() const; // Super type chain to root type, including myself. // First element is this. std::vector typeChain() const; // user defined methods (explicit definition in HAL files) const std::vector &userDefinedMethods() const; // HIDL reserved methods (every interface has these implicitly defined) const std::vector &hidlReservedMethods() const; // the sum of userDefinedMethods() and hidlReservedMethods(). std::vector methods() const; // userDefinedMethods() for all super type + methods() // The order will be as follows (in the transaction code order): // great-great-...-great-grand parent->userDefinedMethods() // ... // parent->userDefinedMethods() // this->userDefinedMethods() // this->hidlReservedMethods() std::vector allMethodsFromRoot() const; // allMethodsFromRoot for parent std::vector 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; std::string getHwName() const; FQName getProxyFqName() const; FQName getStubFqName() const; FQName getPassthroughFqName() const; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool forInitializer) const override; std::string getVtsType() const override; std::vector*> getReferences() const override; std::vector*> getStrongReferences() const override; std::vector 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, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) 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; void emitVtsAttributeType(Formatter& out) const override; void emitVtsAttributeDeclaration(Formatter& out) const; void emitVtsMethodDeclaration(Formatter& out) const; bool hasOnewayMethods() const; bool deepIsJavaCompatible(std::unordered_set* visited) const override; bool isNeverStrongReference() const override; private: Reference mSuperType; std::vector mUserMethods; std::vector 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& chain, std::function)> byteToString) const; DISALLOW_COPY_AND_ASSIGN(Interface); }; // An interface / method tuple. struct InterfaceAndMethod { InterfaceAndMethod(const Interface *iface, Method *method) : mInterface(iface), mMethod(method) {} Method *method() const { return mMethod; } const Interface *interface() const { return mInterface; } private: // do not own these objects. const Interface *mInterface; Method *mMethod; }; } // namespace android #endif // INTERFACE_H_ Location.cpp0100644 0000000 0000000 00000006275 13521773237 012105 0ustar000000000 0000000 /* * 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 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(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 Location.h0100644 0000000 0000000 00000004264 13521773237 011546 0ustar000000000 0000000 /* * 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. */ #ifndef LOCATION_H_ #define LOCATION_H_ #include #include #include // Mimics for yy::location and yy::position namespace android { struct Position { Position() = default; Position(std::string filename, size_t line, size_t column); 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. std::string mFilename; // Current line number. size_t mLine; // Current column number. size_t mColumn; }; std::ostream& operator<<(std::ostream& ostr, const Position& pos); struct Location { Location() = default; Location(const Position& begin, const Position& end); void setLocation(const Position& begin, const Position& end); 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. Position mBegin; // End of the located region. Position mEnd; }; std::ostream& operator<<(std::ostream& ostr, const Location& loc); } // namespace android #endif // LOCATION_H_ MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13521773237 012742 0ustar000000000 0000000 MemoryType.cpp0100644 0000000 0000000 00000010263 13521773237 012437 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MemoryType.h" #include "HidlTypeAssertion.h" #include #include namespace android { MemoryType::MemoryType(Scope* parent) : Type(parent) {} std::string MemoryType::getCppType(StorageMode mode, bool specifyNamespaces) const { const std::string base = std::string(specifyNamespaces ? "::android::hardware::" : "") + "hidl_memory"; switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: return "const " + base + "*"; } } std::string MemoryType::typeName() const { return "memory"; } std::string MemoryType::getVtsType() const { return "TYPE_HIDL_MEMORY"; } void MemoryType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, 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(" << "&" << name << "));\n\n"; handleError(out, mode); } else { out << "_hidl_err = " << parcelObjDeref << "writeBuffer(&" << name << ", sizeof(" << name << "), &" << parentName << ");\n"; handleError(out, mode); } emitReaderWriterEmbedded( out, 0 /* depth */, name, name /* sanitizedName */, isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, "0 /* parentOffset */"); } void MemoryType::emitReaderWriterEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string & /*sanitizedName*/, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { emitReaderWriterEmbeddedForTypeName( out, name, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText, "::android::hardware::hidl_memory", "" /* childName */, "::android::hardware"); } bool MemoryType::needsEmbeddedReadWrite() const { return true; } bool MemoryType::resultNeedsDeref() const { return true; } bool MemoryType::isMemory() const { return true; } bool MemoryType::deepIsJavaCompatible(std::unordered_set* /* visited */) const { return false; } static HidlTypeAssertion assertion("hidl_memory", 40 /* size */); void MemoryType::getAlignmentAndSize(size_t *align, size_t *size) const { *align = 8; // hidl_memory *size = assertion.size(); } void MemoryType::emitVtsTypeDeclarations(Formatter& out) const { out << "type: " << getVtsType() << "\n"; } } // namespace android MemoryType.h0100644 0000000 0000000 00000004035 13521773237 012104 0ustar000000000 0000000 /* * 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. */ #ifndef MEMORY_TYPE_H_ #define MEMORY_TYPE_H_ #include "Type.h" namespace android { struct MemoryType : public Type { MemoryType(Scope* parent); std::string typeName() const override; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getVtsType() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; bool needsEmbeddedReadWrite() const override; bool resultNeedsDeref() const override; bool isMemory() const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; void emitVtsTypeDeclarations(Formatter& out) const override; }; } // namespace android #endif // MEMORY_TYPE_H_ Method.cpp0100644 0000000 0000000 00000022143 13521773237 011545 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Method.h" #include "Annotation.h" #include "ConstantExpression.h" #include "ScalarType.h" #include "Type.h" #include #include #include namespace android { Method::Method(const char* name, std::vector*>* args, std::vector*>* results, bool oneway, std::vector* annotations, const Location& location) : mName(name), mArgs(args), mResults(results), mOneway(oneway), mAnnotations(annotations), mLocation(location) {} void Method::fillImplementation( size_t serial, MethodImpl cppImpl, MethodImpl javaImpl) { mIsHidlReserved = true; mSerial = serial; mCppImpl = cppImpl; mJavaImpl = javaImpl; CHECK(mJavaImpl.find(IMPL_STUB_IMPL) == mJavaImpl.end()) << "FATAL: mJavaImpl should not use IMPL_STUB_IMPL; use IMPL_INTERFACE instead."; CHECK(mCppImpl.find(IMPL_STUB_IMPL) == mCppImpl.end() || mCppImpl.find(IMPL_STUB) == mCppImpl.end()) << "FATAL: mCppImpl IMPL_STUB will override IMPL_STUB_IMPL."; } std::string Method::name() const { return mName; } const std::vector*>& Method::args() const { return *mArgs; } const std::vector*>& Method::results() const { return *mResults; } const std::vector &Method::annotations() const { return *mAnnotations; } std::vector*> Method::getReferences() { const auto& constRet = static_cast(this)->getReferences(); std::vector*> ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ref) { return const_cast*>(ref); }); return ret; } std::vector*> Method::getReferences() const { std::vector*> ret; ret.insert(ret.end(), mArgs->begin(), mArgs->end()); ret.insert(ret.end(), mResults->begin(), mResults->end()); return ret; } std::vector*> Method::getStrongReferences() { const auto& constRet = static_cast(this)->getStrongReferences(); std::vector*> ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ref) { return const_cast*>(ref); }); return ret; } std::vector*> Method::getStrongReferences() const { std::vector*> ret; for (const auto* ref : getReferences()) { if (!ref->shallowGet()->isNeverStrongReference()) { ret.push_back(ref); } } return ret; } std::vector Method::getConstantExpressions() { const auto& constRet = static_cast(this)->getConstantExpressions(); std::vector ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ce) { return const_cast(ce); }); return ret; } std::vector Method::getConstantExpressions() const { std::vector 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); if (it != mCppImpl.end()) { if (it->second != nullptr) { it->second(out); } } } void Method::javaImpl(MethodImplType type, Formatter &out) const { CHECK(mIsHidlReserved); auto it = mJavaImpl.find(type); if (it != mJavaImpl.end()) { if (it->second != nullptr) { it->second(out); } } } bool Method::isHiddenFromJava() const { return isHidlReserved() && name() == "debug"; } bool Method::overridesCppImpl(MethodImplType type) const { CHECK(mIsHidlReserved); return mCppImpl.find(type) != mCppImpl.end(); } bool Method::overridesJavaImpl(MethodImplType type) const { CHECK(mIsHidlReserved); return mJavaImpl.find(type) != mJavaImpl.end(); } Method *Method::copySignature() const { return new Method(mName.c_str(), mArgs, mResults, mOneway, mAnnotations, Location()); } void Method::setSerialId(size_t serial) { CHECK(!mIsHidlReserved); mSerial = serial; } size_t Method::getSerialId() const { return mSerial; } bool Method::hasEmptyCppArgSignature() const { return args().empty() && (results().empty() || canElideCallback() != nullptr); } void Method::generateCppReturnType(Formatter &out, bool specifyNamespaces) const { const NamedReference* elidedReturn = canElideCallback(); const std::string space = (specifyNamespaces ? "::android::hardware::" : ""); if (elidedReturn == nullptr) { out << space << "Return "; } else { out << space << "Return<" << elidedReturn->type().getCppResultType( specifyNamespaces) << "> "; } } void Method::generateCppSignature(Formatter &out, const std::string &className, bool specifyNamespaces) const { generateCppReturnType(out, specifyNamespaces); if (!className.empty()) { out << className << "::"; } out << name() << "("; emitCppArgSignature(out, specifyNamespaces); out << ")"; } static void emitCppArgResultSignature(Formatter& out, const std::vector*>& args, bool specifyNamespaces) { out.join(args.begin(), args.end(), ", ", [&](auto arg) { out << arg->type().getCppArgumentType(specifyNamespaces); out << " "; out << arg->name(); }); } static void emitJavaArgResultSignature(Formatter& out, const std::vector*>& args) { out.join(args.begin(), args.end(), ", ", [&](auto arg) { out << arg->type().getJavaType(); out << " "; out << arg->name(); }); } void Method::emitCppArgSignature(Formatter &out, bool specifyNamespaces) const { emitCppArgResultSignature(out, args(), specifyNamespaces); const bool returnsValue = !results().empty(); const NamedReference* elidedReturn = canElideCallback(); if (returnsValue && elidedReturn == nullptr) { if (!args().empty()) { out << ", "; } out << name() << "_cb _hidl_cb"; } } void Method::emitCppResultSignature(Formatter &out, bool specifyNamespaces) const { emitCppArgResultSignature(out, results(), specifyNamespaces); } void Method::emitJavaArgSignature(Formatter &out) const { emitJavaArgResultSignature(out, args()); } void Method::emitJavaResultSignature(Formatter &out) const { emitJavaArgResultSignature(out, results()); } void Method::dumpAnnotations(Formatter &out) const { if (mAnnotations->size() == 0) { return; } out << "// "; for (size_t i = 0; i < mAnnotations->size(); ++i) { if (i > 0) { out << " "; } mAnnotations->at(i)->dump(out); } out << "\n"; } bool Method::deepIsJavaCompatible(std::unordered_set* visited) const { if (isHiddenFromJava()) { return true; } if (!std::all_of(mArgs->begin(), mArgs->end(), [&](const auto* arg) { return (*arg)->isJavaCompatible(visited); })) { return false; } if (!std::all_of(mResults->begin(), mResults->end(), [&](const auto* arg) { return (*arg)->isJavaCompatible(visited); })) { return false; } return true; } const NamedReference* Method::canElideCallback() const { // Can't elide callback for void or tuple-returning methods if (mResults->size() != 1) { return nullptr; } const NamedReference* typedVar = mResults->at(0); if (typedVar->type().isElidableType()) { return typedVar; } return nullptr; } const Location& Method::location() const { return mLocation; } //////////////////////////////////////////////////////////////////////////////// bool TypedVarVector::add(NamedReference* v) { if (mNames.emplace(v->name()).second) { push_back(v); return true; } return false; } } // namespace android Method.h0100644 0000000 0000000 00000011016 13521773237 011207 0ustar000000000 0000000 /* * 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. */ #ifndef METHOD_H_ #define METHOD_H_ #include #include #include #include #include #include #include #include #include #include "DocComment.h" #include "Location.h" #include "Reference.h" namespace android { struct Annotation; struct ConstantExpression; struct Formatter; struct ScalarType; struct Type; struct TypedVarVector; enum MethodImplType { IMPL_INTERFACE, IMPL_PROXY, IMPL_STUB, // overrides the code in onTransact; IMPL_STUB_IMPL will be ignored IMPL_STUB_IMPL, // use this->method() instead of mImpl->method() IMPL_PASSTHROUGH, }; using MethodImpl = std::map>; struct Method : DocCommentable { Method(const char* name, std::vector*>* args, std::vector*>* results, bool oneway, std::vector* annotations, const Location& location); std::string name() const; const std::vector*>& args() const; const std::vector*>& results() const; bool isOneway() const { return mOneway; } bool overridesCppImpl(MethodImplType type) const; bool overridesJavaImpl(MethodImplType type) const; void cppImpl(MethodImplType type, Formatter &out) const; void javaImpl(MethodImplType type, Formatter &out) const; bool isHidlReserved() const { return mIsHidlReserved; } bool isHiddenFromJava() const; const std::vector &annotations() const; std::vector*> getReferences(); std::vector*> getReferences() const; std::vector*> getStrongReferences(); std::vector*> getStrongReferences() const; std::vector getConstantExpressions(); std::vector getConstantExpressions() const; // Make a copy with the same name, args, results, oneway, annotations. // Implementations, serial are not copied. Method *copySignature() const; void setSerialId(size_t serial); size_t getSerialId() const; // Fill implementation for HIDL reserved methods. mIsHidlReserved will be // set to true. void fillImplementation( size_t serial, MethodImpl cppImpl, MethodImpl javaImpl); void generateCppReturnType(Formatter &out, bool specifyNamespaces = true) const; void generateCppSignature(Formatter &out, const std::string &className = "", bool specifyNamespaces = true) const; bool hasEmptyCppArgSignature() const; void emitCppArgSignature(Formatter &out, bool specifyNamespaces = true) const; void emitCppResultSignature(Formatter &out, bool specifyNamespaces = true) const; void emitJavaArgSignature(Formatter &out) const; void emitJavaResultSignature(Formatter &out) const; const NamedReference* canElideCallback() const; void dumpAnnotations(Formatter &out) const; bool deepIsJavaCompatible(std::unordered_set* visited) const; const Location& location() const; private: std::string mName; size_t mSerial = 0; std::vector*>* mArgs; std::vector*>* mResults; bool mOneway; std::vector *mAnnotations; bool mIsHidlReserved = false; // The following fields have no meaning if mIsHidlReserved is false. // hard-coded implementation for HIDL reserved methods. MethodImpl mCppImpl; MethodImpl mJavaImpl; const Location mLocation; DISALLOW_COPY_AND_ASSIGN(Method); }; struct TypedVarVector : public std::vector*> { TypedVarVector() = default; bool add(NamedReference* v); private: std::set mNames; }; } // namespace android #endif // METHOD_H_ NOTICE0100644 0000000 0000000 00000026136 13521773237 010533 0ustar000000000 0000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. NamedType.cpp0100644 0000000 0000000 00000003007 13521773237 012211 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "NamedType.h" namespace android { 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; } const FQName &NamedType::fqName() const { return mFullName; } std::string NamedType::localName() const { return mLocalName; } std::string NamedType::fullName() const { return mFullName.cppName(); } std::string NamedType::fullJavaName() const { return mFullName.javaName(); } const Location &NamedType::location() const { return mLocation; } void NamedType::emitDump( Formatter &out, const std::string &streamName, const std::string &name) const { emitDumpWithMethod(out, streamName, fqName().cppNamespace() + "::toString", name); } } // namespace android NamedType.h0100644 0000000 0000000 00000003032 13521773237 011654 0ustar000000000 0000000 /* * 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. */ #ifndef NAMED_TYPE_H_ #define NAMED_TYPE_H_ #include "Location.h" #include "Type.h" #include #include namespace android { struct NamedType : public Type { NamedType(const char* localName, const FQName& fullName, const Location& loc, Scope* parent); bool isNamedType() const override; const FQName &fqName() const; std::string localName() const; /* short for fqName().cppName() */ std::string fullName() const; /* short for fqName().fullJavaName() */ std::string fullJavaName() const; const Location& location() const; void emitDump( Formatter &out, const std::string &streamName, const std::string &name) const override; private: const std::string mLocalName; const FQName mFullName; const Location mLocation; DISALLOW_COPY_AND_ASSIGN(NamedType); }; } // namespace android #endif // NAMED_TYPE_H_ OWNERS0100644 0000000 0000000 00000000131 13521773237 010552 0ustar000000000 0000000 smoreland@google.com maco@google.com elsk@google.com malchev@google.com andih@google.com PREUPLOAD.cfg0100644 0000000 0000000 00000000114 13521773237 011627 0ustar000000000 0000000 [Options] ignore_merged_commits = true [Builtin Hooks] clang_format = true PointerType.cpp0100644 0000000 0000000 00000004063 13521773237 012610 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PointerType.h" #include #include namespace android { PointerType::PointerType(Scope* parent) : Type(parent) {} bool PointerType::isPointer() const { return true; } bool PointerType::isElidableType() const { return true; } std::string PointerType::getCppType(StorageMode /* mode */, bool /* specifyNamespaces */) const { return "void*"; } std::string PointerType::typeName() const { return "local pointer"; } std::string PointerType::getVtsType() const { return "TYPE_POINTER"; } void PointerType::emitReaderWriter( Formatter& out, const std::string& name, const std::string& /* parcelObj */, bool /* parcelObjIsPointer */, bool /* isReader */, ErrorMode /* mode */) const { out << "(void)" << name << ";\n"; out << "LOG_ALWAYS_FATAL(\"Pointer is only supported in passthrough mode\");\n"; } bool PointerType::needsEmbeddedReadWrite() const { return false; } bool PointerType::resultNeedsDeref() const { return false; } bool PointerType::deepIsJavaCompatible(std::unordered_set* /* visited */) const { return false; } bool PointerType::deepContainsPointer(std::unordered_set* /* visited */) const { return true; } void PointerType::emitVtsTypeDeclarations(Formatter& out) const { out << "type: " << getVtsType() << "\n"; } } // namespace android PointerType.h0100644 0000000 0000000 00000003241 13521773237 012252 0ustar000000000 0000000 /* * 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. */ #ifndef POINTER_TYPE_H_ #define POINTER_TYPE_H_ #include "Type.h" namespace android { struct PointerType : public Type { PointerType(Scope* parent); bool isPointer() const override; bool isElidableType() const override; std::string typeName() const override; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getVtsType() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; bool needsEmbeddedReadWrite() const override; bool resultNeedsDeref() const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; bool deepContainsPointer(std::unordered_set* visited) const override; void emitVtsTypeDeclarations(Formatter& out) const override; }; } // namespace android #endif // POINTER_TYPE_H_ README.md0100644 0000000 0000000 00000002145 13521773237 011100 0ustar000000000 0000000 # hidl-gen user guide ## 1. Build ``` croot make hidl-gen ``` ## 2. Run ``` hidl-gen -o output-path -L language (-r interface-root) fqname output-path: directory to store the output files. language: output file for given language. e.g.c++, vts.. fqname: fully qualified name of the input files. For singe file input, follow the format: package@version::fileName For directory input, follow the format: package@version interface-root(optional): prefix and root path for fqname. If not set, use the default prefix: android.hardware and default root path defined in $TOP. examples: croot hidl-gen -o output -L c++ -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.nfc@1.0::INfc.hal hidl-gen -o output -L vts -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.nfc@1.0 hidl-gen -o test -L c++ -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.nfc@1.0 hidl-gen -L hash -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.nfc@1.0 ``` RefType.cpp0100644 0000000 0000000 00000016020 13521773237 011700 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "RefType.h" #include "ArrayType.h" #include "CompoundType.h" #include #include namespace android { RefType::RefType(Scope* parent) : TemplatedType(parent) {} std::string RefType::templatedTypeName() const { return "ref"; } std::vector*> RefType::getStrongReferences() const { return {}; } std::string RefType::getVtsType() const { return "TYPE_REF"; } std::string RefType::getVtsValueName() const { return "ref_value"; } bool RefType::isCompatibleElementType(const Type* elementType) const { if (elementType->isScalar()) { return true; } if (elementType->isString()) { return true; } if (elementType->isEnum()) { return true; } if (elementType->isBitField()) { return true; } if (elementType->isCompoundType() && static_cast(elementType)->style() == CompoundType::STYLE_STRUCT) { return true; } if (elementType->isTemplatedType()) { return this->isCompatibleElementType( static_cast(elementType)->getElementType()); } if (elementType->isArray()) { return this->isCompatibleElementType( static_cast(elementType)->getElementType()); } return false; } /* return something like "T const *". * The reason we don't return "const T *" is to handle cases like * ref>> t_3ptr; * in this case the const's will get stacked on the left (const const const T *** t_3ptr) * but in this implementation it would be clearer (T const* const* const* t_3ptr) */ std::string RefType::getCppType(StorageMode /*mode*/, bool specifyNamespaces) const { return mElementType->getCppStackType(specifyNamespaces) + " const*"; } void RefType::emitReaderWriter( Formatter &, const std::string &, const std::string &, bool, bool, ErrorMode) const { // RefType doesn't get read / written at this stage. return; } void RefType::emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { emitResolveReferencesEmbedded( out, 0 /* depth */, name, name /* sanitizedName */, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, "", // parentName ""); // offsetText } void RefType::emitResolveReferencesEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string &sanitizedName, bool /*nameIsPointer*/, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { std::string elementType = mElementType->getCppStackType(); std::string baseType = getCppStackType(); const std::string parcelObjDeref = parcelObjIsPointer ? ("*" + parcelObj) : parcelObj; const std::string parcelObjPointer = parcelObjIsPointer ? parcelObj : ("&" + parcelObj); // as if nameIsPointer is false. Pointers are always pass by values, // so name is always the pointer value itself. Hence nameIsPointer = false. const std::string namePointer = "&" + name; const std::string handleName = "_hidl_" + sanitizedName + "__ref_handle"; const std::string resolveBufName = "_hidl_" + sanitizedName + "__ref_resolve_buf"; bool isEmbedded = (!parentName.empty() && !offsetText.empty()); out << "size_t " << handleName << ";\n" << "bool " << resolveBufName << ";\n\n"; out << "_hidl_err = "; if (isReader) { out << "::android::hardware::read" << (isEmbedded ? "Embedded" : "") << "ReferenceFromParcel<" << elementType << ">(const_cast<" << baseType << " *>(" << namePointer << "),"; } else { out << "::android::hardware::write" << (isEmbedded ? "Embedded" : "") << "ReferenceToParcel<" << elementType << ">(" << name << ","; } out.indent(); out.indent(); out << (isReader ? parcelObjDeref : parcelObjPointer); if(isEmbedded) out << ",\n" << parentName << ",\n" << offsetText; out << ",\n&" + handleName; out << ",\n&" + resolveBufName; out << ");\n\n"; out.unindent(); out.unindent(); handleError(out, mode); if(!mElementType->needsResolveReferences() && !mElementType->needsEmbeddedReadWrite()) return; // no need to deal with element type recursively. out << "if(" << resolveBufName << ") {\n"; out.indent(); if(mElementType->needsEmbeddedReadWrite()) { mElementType->emitReaderWriterEmbedded( out, 0 /* depth */, name, sanitizedName, true /* nameIsPointer */, // for element type, name is a pointer. parcelObj, parcelObjIsPointer, isReader, mode, handleName, "0 /* parentOffset */"); } if(mElementType->needsResolveReferences()) { mElementType->emitResolveReferencesEmbedded( out, 0 /* depth */, "(*" + name + ")", sanitizedName + "_deref", false /* nameIsPointer */, // must deref it and say false here, otherwise pointer to pointers don't work parcelObj, parcelObjIsPointer, isReader, mode, handleName, "0 /* parentOffset */"); } out.unindent(); out << "}\n\n"; } bool RefType::deepNeedsResolveReferences(std::unordered_set* /* visited */) const { return true; } bool RefType::needsEmbeddedReadWrite() const { return false; } bool RefType::resultNeedsDeref() const { return false; } bool RefType::deepIsJavaCompatible(std::unordered_set* /* visited */) const { return false; } bool RefType::deepContainsPointer(std::unordered_set* /* visited */) const { return true; } } // namespace android RefType.h0100644 0000000 0000000 00000005154 13521773237 011353 0ustar000000000 0000000 /* * 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. */ #ifndef REF_TYPE_H_ #define REF_TYPE_H_ #include #include "Reference.h" #include "Type.h" namespace android { struct RefType : public TemplatedType { RefType(Scope* parent); std::string templatedTypeName() const override; bool isCompatibleElementType(const Type* elementType) const override; std::vector*> getStrongReferences() const override; std::string getCppType(StorageMode mode, bool specifyNamespaces) const override; std::string getVtsType() const override; std::string getVtsValueName() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitResolveReferencesEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; bool needsEmbeddedReadWrite() const override; bool deepNeedsResolveReferences(std::unordered_set* visited) const override; bool resultNeedsDeref() const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; bool deepContainsPointer(std::unordered_set* visited) const override; private: DISALLOW_COPY_AND_ASSIGN(RefType); }; } // namespace android #endif // REF_TYPE_H_ Reference.h0100644 0000000 0000000 00000007664 13521773237 011703 0ustar000000000 0000000 /* * 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 #include #include "DocComment.h" #include "Location.h" namespace android { /** * Reference placeholder */ template 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 Reference(const Reference& ref) : mResolved(ref.mResolved), mFqName(ref.mFqName), mLocation(ref.mLocation) {} template Reference(const Reference& 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 friend struct Reference; }; template struct NamedReference : public Reference, DocCommentable { NamedReference(const std::string& name, const Reference& reference, const Location& location) : Reference(reference, location), mName(name) {} const std::string& name() const { return mName; } // TODO(b/64715470) Legacy const T& type() const { return *Reference::get(); } private: const std::string mName; }; } // namespace android #endif // REFERENCE_H_ ScalarType.cpp0100644 0000000 0000000 00000016033 13521773237 012375 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ScalarType.h" #include namespace android { ScalarType::ScalarType(Kind kind, Scope* parent) : Type(parent), mKind(kind) {} const ScalarType *ScalarType::resolveToScalarType() const { return this; } bool ScalarType::isValidEnumStorageType() const { // Only integer types. return mKind >= KIND_INT8 && mKind <= KIND_UINT64; } bool ScalarType::isScalar() const { return true; } bool ScalarType::isElidableType() const { return true; } bool ScalarType::deepCanCheckEquality(std::unordered_set* /* visited */) const { return true; } std::string ScalarType::typeName() const { return getCppStackType(); } std::string ScalarType::getCppType(StorageMode, bool) const { static const char *const kName[] = { "bool", "int8_t", "uint8_t", "int16_t", "uint16_t", "int32_t", "uint32_t", "int64_t", "uint64_t", "float", "double" }; return kName[mKind]; } std::string ScalarType::getJavaType(bool /* forInitializer */) const { static const char *const kName[] = { "boolean", "byte", "byte", "short", "short", "int", "int", "long", "long", "float", "double" }; return kName[mKind]; } std::string ScalarType::getJavaWrapperType() const { static const char *const kName[] = { "Boolean", "Byte", "Byte", "Short", "Short", "Integer", "Integer", "Long", "Long", "Float", "Double" }; return kName[mKind]; } std::string ScalarType::getJavaSuffix() const { static const char *const kSuffix[] = { "Bool", "Int8", "Int8", "Int16", "Int16", "Int32", "Int32", "Int64", "Int64", "Float", "Double" }; return kSuffix[mKind]; } std::string ScalarType::getVtsType() const { return "TYPE_SCALAR"; } std::string ScalarType::getVtsScalarType() const { static const char * const kName[] = { "bool_t", "int8_t", "uint8_t", "int16_t", "uint16_t", "int32_t", "uint32_t", "int64_t", "uint64_t", "float_t", "double_t" }; return kName[mKind]; } void ScalarType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { emitReaderWriterWithCast( out, name, parcelObj, parcelObjIsPointer, isReader, mode, false /* needsCast */); } void ScalarType::emitReaderWriterWithCast( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, bool needsCast) const { static const char *const kSuffix[] = { "Bool", "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" }; const std::string parcelObjDeref = parcelObj + (parcelObjIsPointer ? "->" : "."); out << "_hidl_err = " << parcelObjDeref << (isReader ? "read" : "write") << kSuffix[mKind] << "("; if (needsCast) { out << "(" << getCppStackType() << (isReader ? " *)" : ")"); } if (isReader) { out << "&"; } out << name << ");\n"; handleError(out, mode); } void ScalarType::emitHexDump( Formatter &out, const std::string &streamName, const std::string &name) const { out << streamName << " += toHexString(" << name << ");\n"; } void ScalarType::emitConvertToJavaHexString( Formatter &out, const std::string &name) const { switch(mKind) { case KIND_BOOL: { out << "((" << name << ") ? \"0x1\" : \"0x0\")"; break; } case KIND_INT8: // fallthrough case KIND_UINT8: // fallthrough case KIND_INT16: // fallthrough case KIND_UINT16: { // Because Byte and Short doesn't have toHexString, we have to use Integer.toHexString. out << "Integer.toHexString(" << getJavaWrapperType() << ".toUnsignedInt((" << getJavaType(false /* forInitializer */) << ")(" << name << ")))"; break; } case KIND_INT32: // fallthrough case KIND_UINT32: // fallthrough case KIND_INT64: // fallthrough case KIND_UINT64: { out << getJavaWrapperType() << ".toHexString(" << name << ")"; break; } case KIND_FLOAT: // fallthrough case KIND_DOUBLE: // fallthrough default: { // no hex for floating point numbers. out << name; break; } } } void ScalarType::emitJavaFieldReaderWriter( Formatter &out, size_t /* depth */, const std::string & /* parcelName */, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const { if (isReader) { out << fieldName << " = " << blobName << ".get" << getJavaSuffix() << "(" << offset << ");\n"; return; } out << blobName << ".put" << getJavaSuffix() << "(" << offset << ", " << fieldName << ");\n"; } void ScalarType::emitVtsTypeDeclarations(Formatter& out) const { out << "type: " << getVtsType() << "\n"; out << "scalar_type: \"" << getVtsScalarType() << "\"\n"; } void ScalarType::getAlignmentAndSize(size_t *align, size_t *size) const { static const size_t kAlign[] = { 1, // bool, this is NOT standardized! 1, // int8_t 1, // uint8_t 2, // int16_t 2, // uint16_t 4, // int32_t 4, // uint32_t 8, // int64_t 8, // uint64_t 4, // float 8 // double }; *align = *size = kAlign[mKind]; } ScalarType::Kind ScalarType::getKind() const { return mKind; } } // namespace android ScalarType.h0100644 0000000 0000000 00000005770 13521773237 012050 0ustar000000000 0000000 /* * 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. */ #ifndef SCALAR_TYPE_H_ #define SCALAR_TYPE_H_ #include "Type.h" namespace android { struct ScalarType : public Type { enum Kind { KIND_BOOL, KIND_INT8, KIND_UINT8, KIND_INT16, KIND_UINT16, KIND_INT32, KIND_UINT32, KIND_INT64, KIND_UINT64, KIND_FLOAT, KIND_DOUBLE, }; ScalarType(Kind kind, Scope* parent); bool isScalar() const override; bool isElidableType() const override; const ScalarType *resolveToScalarType() const override; bool deepCanCheckEquality(std::unordered_set* visited) const override; std::string typeName() const override; bool isValidEnumStorageType() const; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool forInitializer) const override; std::string getJavaWrapperType() const override; std::string getJavaSuffix() const override; std::string getVtsType() const override; std::string getVtsScalarType() const; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterWithCast( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, bool needsCast) const; void emitHexDump( Formatter &out, const std::string &streamName, const std::string &name) const; void emitConvertToJavaHexString( Formatter &out, const std::string &name) const; void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const override; void emitVtsTypeDeclarations(Formatter& out) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; Kind getKind() const; private: Kind mKind; DISALLOW_COPY_AND_ASSIGN(ScalarType); }; } // namespace android #endif // SCALAR_TYPE_H_ Scope.cpp0100644 0000000 0000000 00000016466 13521773237 011411 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Scope.h" #include "Annotation.h" #include "ConstantExpression.h" #include "Interface.h" #include #include #include #include #include #include namespace android { Scope::Scope(const char* localName, const FQName& fullName, const Location& location, Scope* parent) : NamedType(localName, fullName, location, parent) {} Scope::~Scope(){} void Scope::addType(NamedType* type) { size_t index = mTypes.size(); mTypes.push_back(type); mTypeIndexByName[type->localName()] = index; } 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()) { std::cerr << "ERROR: " << fqName.string() << " does not refer to a type." << std::endl; return nullptr; } std::vector names = fqName.names(); CHECK_GT(names.size(), 0u); auto it = mTypeIndexByName.find(names[0]); if (it == mTypeIndexByName.end()) { return nullptr; } NamedType *outerType = mTypes[it->second]; if (names.size() == 1) { return outerType; } if (!outerType->isScope()) { // more than one names, but the first name is not a scope return nullptr; } Scope *outerScope = static_cast(outerType); // *slowly* pop first element names.erase(names.begin()); FQName innerName; CHECK(FQName::parse(StringHelper::JoinStrings(names, "."), &innerName)); return outerScope->lookupType(innerName); } LocalIdentifier *Scope::lookupIdentifier(const std::string & /*name*/) const { return NULL; } bool Scope::isScope() const { return true; } Interface *Scope::getInterface() const { if (mTypes.size() == 1 && mTypes[0]->isInterface()) { return static_cast(mTypes[0]); } return NULL; } bool Scope::containsInterfaces() const { for (const NamedType *type : mTypes) { if (type->isInterface()) { return true; } } return false; } const std::vector& Scope::annotations() const { return mAnnotations; } void Scope::setAnnotations(std::vector* annotations) { CHECK(mAnnotations.empty()); CHECK(annotations != nullptr); mAnnotations = *annotations; } std::vector Scope::getDefinedTypes() const { std::vector ret; ret.insert(ret.end(), mTypes.begin(), mTypes.end()); return ret; } std::vector Scope::getConstantExpressions() const { std::vector 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& 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"; } for (const Type* type : mTypes) { type->emitDocComment(out); type->emitTypeDeclarations(out); } } void Scope::emitGlobalTypeDeclarations(Formatter& out) const { for (const Type* type : mTypes) { type->emitGlobalTypeDeclarations(out); } } void Scope::emitPackageTypeDeclarations(Formatter& out) const { for (const Type* type : mTypes) { type->emitPackageTypeDeclarations(out); } } void Scope::emitPackageHwDeclarations(Formatter& out) const { for (const Type* type : mTypes) { type->emitPackageHwDeclarations(out); } } 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); } } void Scope::emitTypeDefinitions(Formatter& out, const std::string& prefix) const { for (const Type* type : mTypes) { type->emitTypeDefinitions(out, prefix); } } const std::vector &Scope::getSubTypes() const { return mTypes; } void Scope::emitVtsTypeDeclarations(Formatter& out) const { for (const Type* type : mTypes) { type->emitVtsTypeDeclarations(out); } } bool Scope::deepIsJavaCompatible(std::unordered_set* visited) const { for (const Type* type : mTypes) { if (!type->isJavaCompatible(visited)) { return false; } } return Type::deepIsJavaCompatible(visited); } void Scope::appendToExportedTypesVector( std::vector *exportedTypes) const { for (const Type* type : mTypes) { type->appendToExportedTypesVector(exportedTypes); } } //////////////////////////////////////// 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(){} bool LocalIdentifier::isEnumValue() const { return false; } const LocalIdentifier* LocalIdentifier::resolve() const { return this; } LocalIdentifier* LocalIdentifier::resolve() { return this; } ConstantExpression* LocalIdentifier::constExpr() const { return nullptr; } } // namespace android Scope.h0100644 0000000 0000000 00000006550 13521773237 011047 0ustar000000000 0000000 /* * 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. */ #ifndef SCOPE_H_ #define SCOPE_H_ #include "NamedType.h" #include #include #include namespace android { struct Annotation; struct ConstantExpression; struct Formatter; struct Interface; struct LocalIdentifier; struct Scope : public NamedType { Scope(const char* localName, const FQName& fullName, const Location& location, Scope* parent); virtual ~Scope(); void addType(NamedType* type); status_t validateUniqueNames() const; // lookup a type given an FQName. // Assume fqName.package(), fqName.version(), fqName.valueName() is empty. NamedType *lookupType(const FQName &fqName) const; virtual LocalIdentifier *lookupIdentifier(const std::string &name) const; bool isScope() const override; // Returns the single interface or NULL. Interface *getInterface() const; bool containsInterfaces() const; const std::vector& annotations() const; void setAnnotations(std::vector* annotations); std::vector getDefinedTypes() const override; std::vector getConstantExpressions() const override; void topologicalReorder(const std::unordered_map& 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 &getSubTypes() const; void emitVtsTypeDeclarations(Formatter& out) const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; void appendToExportedTypesVector( std::vector *exportedTypes) const override; private: std::vector mTypes; std::map mTypeIndexByName; std::vector mAnnotations; bool mTypeOrderChanged = false; DISALLOW_COPY_AND_ASSIGN(Scope); }; struct RootScope : public Scope { 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; }; struct LocalIdentifier { LocalIdentifier(); virtual ~LocalIdentifier(); virtual bool isEnumValue() const; const LocalIdentifier* resolve() const; LocalIdentifier* resolve(); virtual ConstantExpression* constExpr() const; }; } // namespace android #endif // SCOPE_H_ StringType.cpp0100644 0000000 0000000 00000013134 13521773237 012435 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "StringType.h" #include "HidlTypeAssertion.h" #include namespace android { StringType::StringType(Scope* parent) : Type(parent) {} bool StringType::isString() const { return true; } bool StringType::deepCanCheckEquality(std::unordered_set* /* visited */) const { return true; } std::string StringType::typeName() const { return "string"; } std::string StringType::getCppType(StorageMode mode, bool specifyNamespaces) const { const std::string base = std::string(specifyNamespaces ? "::android::hardware::" : "") + "hidl_string"; switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: return "const " + base + "*"; } } std::string StringType::getJavaType(bool /* forInitializer */) const { return "String"; } std::string StringType::getJavaSuffix() const { return "String"; } std::string StringType::getVtsType() const { return "TYPE_STRING"; } void StringType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, 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(" << "&" << name << "));\n\n"; handleError(out, mode); } else { out << "_hidl_err = " << parcelObjDeref << "writeBuffer(&" << name << ", sizeof(" << name << "), &" << parentName << ");\n"; handleError(out, mode); } emitReaderWriterEmbedded( out, 0 /* depth */, name, name /* sanitizedName */, isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, "0 /* parentOffset */"); } void StringType::emitReaderWriterEmbedded( Formatter &out, size_t /* depth */, const std::string &name, const std::string & /*sanitizedName*/, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { emitReaderWriterEmbeddedForTypeName( out, name, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText, "::android::hardware::hidl_string", "" /* childName */, "::android::hardware"); } void StringType::emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const { out << "String " << fieldName << " = new String();\n"; } void StringType::emitJavaFieldReaderWriter( Formatter &out, size_t /* depth */, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const { if (isReader) { out << fieldName << " = " << blobName << ".getString(" << offset << ");\n"; out << "\n" << parcelName << ".readEmbeddedBuffer(\n"; out.indent(); out.indent(); // hidl_string's embedded buffer is never null(able), because it defaults to a // buffer containing an empty string. out << fieldName << ".getBytes().length + 1,\n" << blobName << ".handle(),\n" << offset << " + 0 /* offsetof(hidl_string, mBuffer) */," << "false /* nullable */);\n\n"; out.unindent(); out.unindent(); return; } out << blobName << ".putString(" << offset << ", " << fieldName << ");\n"; } bool StringType::needsEmbeddedReadWrite() const { return true; } bool StringType::resultNeedsDeref() const { return true; } void StringType::emitVtsTypeDeclarations(Formatter& out) const { out << "type: " << getVtsType() << "\n"; } static HidlTypeAssertion assertion("hidl_string", 16 /* size */); void StringType::getAlignmentAndSize(size_t *align, size_t *size) const { *align = 8; // hidl_string *size = assertion.size(); } } // namespace android StringType.h0100644 0000000 0000000 00000005064 13521773237 012105 0ustar000000000 0000000 /* * 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. */ #ifndef STRING_TYPE_H_ #define STRING_TYPE_H_ #include "Type.h" namespace android { struct StringType : public Type { StringType(Scope* parent); bool isString() const override; bool deepCanCheckEquality(std::unordered_set* visited) const override; std::string typeName() const override; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool /* forInitializer */) const override; std::string getJavaSuffix() const override; std::string getVtsType() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string & /*sanitizedName*/, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; void emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const override; void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const override; bool needsEmbeddedReadWrite() const override; bool resultNeedsDeref() const override; void emitVtsTypeDeclarations(Formatter& out) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; }; } // namespace android #endif // STRING_TYPE_H_ Type.cpp0100644 0000000 0000000 00000051762 13521773237 011257 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Type.h" #include "ConstantExpression.h" #include "NamedType.h" #include "ScalarType.h" #include "Scope.h" #include #include #include #include namespace android { Type::Type(Scope* parent) : mParent(parent) {} Type::~Type() {} bool Type::isScope() const { return false; } bool Type::isInterface() const { return false; } bool Type::isScalar() const { return false; } bool Type::isString() const { return false; } bool Type::isEnum() const { return false; } bool Type::isBitField() const { return false; } bool Type::isHandle() const { return false; } bool Type::isTypeDef() const { return false; } bool Type::isBinder() const { return false; } bool Type::isNamedType() const { return false; } bool Type::isMemory() const { return false; } bool Type::isCompoundType() const { return false; } bool Type::isArray() const { return false; } bool Type::isVector() const { return false; } bool Type::isTemplatedType() const { return false; } bool Type::isPointer() const { return false; } Type* Type::resolve() { return const_cast(static_cast(this)->resolve()); } const Type* Type::resolve() const { return this; } std::vector Type::getDefinedTypes() { const auto& constRet = static_cast(this)->getDefinedTypes(); std::vector ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* type) { return const_cast(type); }); return ret; } std::vector Type::getDefinedTypes() const { return {}; } std::vector*> Type::getReferences() { const auto& constRet = static_cast(this)->getReferences(); std::vector*> ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ref) { return const_cast*>(ref); }); return ret; } std::vector*> Type::getReferences() const { return {}; } std::vector Type::getConstantExpressions() { const auto& constRet = static_cast(this)->getConstantExpressions(); std::vector ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ce) { return const_cast(ce); }); return ret; } std::vector Type::getConstantExpressions() const { return {}; } std::vector*> Type::getStrongReferences() { const auto& constRet = static_cast(this)->getStrongReferences(); std::vector*> ret(constRet.size()); std::transform(constRet.begin(), constRet.end(), ret.begin(), [](const auto* ref) { return const_cast*>(ref); }); return ret; } std::vector*> Type::getStrongReferences() const { std::vector*> ret; for (const auto* ref : getReferences()) { if (!ref->shallowGet()->isNeverStrongReference()) { ret.push_back(ref); } } return ret; } status_t Type::recursivePass(const std::function& func, std::unordered_set* 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& func, std::unordered_set* 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* reversedOrder, std::unordered_set* 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(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& 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* innerRef : refType->getReferences()) { status_t err = checkForwardReferenceRestrictions(*innerRef); if (err != OK) return err; } return OK; } const Location& typeLoc = static_cast(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; } bool Type::isValidEnumStorageType() const { const ScalarType *scalarType = resolveToScalarType(); if (scalarType == NULL) { return false; } return scalarType->isValidEnumStorageType(); } bool Type::isElidableType() const { return false; } bool Type::canCheckEquality() const { std::unordered_set visited; return canCheckEquality(&visited); } bool Type::canCheckEquality(std::unordered_set* 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* /* 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(); } std::string Type::decorateCppName( const std::string &name, StorageMode mode, bool specifyNamespaces) const { return getCppType(mode, specifyNamespaces) + " " + name; } std::string Type::getJavaType(bool /* forInitializer */) const { CHECK(!"Should not be here"); return std::string(); } std::string Type::getJavaWrapperType() const { return getJavaType(); } std::string Type::getJavaSuffix() const { CHECK(!"Should not be here"); return std::string(); } std::string Type::getVtsType() const { CHECK(!"Should not be here"); return std::string(); } std::string Type::getVtsValueName() const { CHECK(!"Should not be here"); return std::string(); } void Type::emitReaderWriter( Formatter &, const std::string &, const std::string &, bool, bool, ErrorMode) const { CHECK(!"Should not be here"); } void Type::emitResolveReferences( Formatter &, const std::string &, bool, const std::string &, bool, bool, ErrorMode) const { CHECK(!"Should not be here"); } void Type::emitResolveReferencesEmbedded( Formatter &, size_t, const std::string &, const std::string &, bool, const std::string &, bool, bool, ErrorMode, const std::string &, const std::string &) const { CHECK(!"Should not be here"); } void Type::emitDump( Formatter &out, const std::string &streamName, const std::string &name) const { emitDumpWithMethod(out, streamName, "::android::hardware::toString", name); } void Type::emitDumpWithMethod( Formatter &out, const std::string &streamName, const std::string &methodName, const std::string &name) const { out << streamName << " += " << methodName << "(" << name << ");\n"; } void Type::emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const { out << streamName << ".append(" << name << ");\n"; } bool Type::useParentInEmitResolveReferencesEmbedded() const { return needsResolveReferences(); } bool Type::useNameInEmitReaderWriterEmbedded(bool) const { return needsEmbeddedReadWrite(); } void Type::emitReaderWriterEmbedded( Formatter &, size_t, const std::string &, const std::string &, bool, const std::string &, bool, bool, ErrorMode, const std::string &, const std::string &) const { CHECK(!"Should not be here"); } void Type::emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const { emitJavaReaderWriterWithSuffix( out, parcelObj, argName, isReader, getJavaSuffix(), "" /* extra */); } void Type::emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const { out << getJavaType() << " " << fieldName << ";\n"; } void Type::emitJavaFieldReaderWriter( Formatter &, size_t, const std::string &, const std::string &, const std::string &, const std::string &, bool) const { CHECK(!"Should not be here"); } void Type::handleError(Formatter &out, ErrorMode mode) const { switch (mode) { case ErrorMode_Ignore: { out << "/* _hidl_err ignored! */\n\n"; break; } case ErrorMode_Goto: { out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n"; break; } case ErrorMode_Break: { out << "if (_hidl_err != ::android::OK) { break; }\n\n"; break; } case ErrorMode_Return: { out << "if (_hidl_err != ::android::OK) { return _hidl_err; }\n\n"; break; } } } void Type::emitReaderWriterEmbeddedForTypeName( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText, const std::string &typeName, const std::string &childName, const std::string &funcNamespace) const { const std::string parcelObjDeref = parcelObjIsPointer ? ("*" + parcelObj) : parcelObj; const std::string parcelObjPointer = parcelObjIsPointer ? parcelObj : ("&" + parcelObj); const std::string nameDerefed = nameIsPointer ? ("*" + name) : name; const std::string namePointer = nameIsPointer ? name : ("&" + name); out << "_hidl_err = "; if (!funcNamespace.empty()) { out << funcNamespace << "::"; } out << (isReader ? "readEmbeddedFromParcel(\n" : "writeEmbeddedToParcel(\n"); out.indent(); out.indent(); if (isReader) { out << "const_cast<" << typeName << " &>(" << nameDerefed << "),\n"; } else { out << nameDerefed << ",\n"; } out << (isReader ? parcelObjDeref : parcelObjPointer) << ",\n" << parentName << ",\n" << offsetText; if (!childName.empty()) { out << ", &" << childName; } out << ");\n\n"; out.unindent(); out.unindent(); handleError(out, mode); } void Type::emitTypeDeclarations(Formatter&) const {} void Type::emitTypeForwardDeclaration(Formatter&) const {} void Type::emitGlobalTypeDeclarations(Formatter&) const {} void Type::emitPackageTypeDeclarations(Formatter&) const {} 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::resultNeedsDeref() const { return false; } bool Type::needsResolveReferences() const { std::unordered_set visited; return needsResolveReferences(&visited); } bool Type::needsResolveReferences(std::unordered_set* 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* /* visited */) const { return false; } std::string Type::getCppStackType(bool specifyNamespaces) const { return getCppType(StorageMode_Stack, specifyNamespaces); } std::string Type::getCppResultType(bool specifyNamespaces) const { return getCppType(StorageMode_Result, specifyNamespaces); } std::string Type::getCppArgumentType(bool specifyNamespaces) const { return getCppType(StorageMode_Argument, specifyNamespaces); } void Type::emitJavaReaderWriterWithSuffix( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader, const std::string &suffix, const std::string &extra) const { out << parcelObj << "." << (isReader ? "read" : "write") << suffix << "("; if (isReader) { out << extra; } else { out << (extra.empty() ? "" : (extra + ", ")); out << argName; } out << ");\n"; } void Type::emitVtsTypeDeclarations(Formatter&) const {} void Type::emitVtsAttributeType(Formatter& out) const { emitVtsTypeDeclarations(out); } bool Type::isJavaCompatible() const { std::unordered_set visited; return isJavaCompatible(&visited); } bool Type::containsPointer() const { std::unordered_set visited; return containsPointer(&visited); } bool Type::isJavaCompatible(std::unordered_set* 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* 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* /* visited */) const { return true; } bool Type::deepContainsPointer(std::unordered_set* /* visited */) const { return false; } void Type::getAlignmentAndSize( size_t * /* align */, size_t * /* size */) const { CHECK(!"Should not be here."); } void Type::appendToExportedTypesVector( std::vector * /* exportedTypes */) const { } void Type::emitExportedHeader(Formatter& /* out */, bool /* forJava */) const {} bool Type::isNeverStrongReference() const { return false; } //////////////////////////////////////// TemplatedType::TemplatedType(Scope* parent) : Type(parent) {} std::string TemplatedType::typeName() const { return templatedTypeName() + " of " + mElementType->typeName(); } void TemplatedType::setElementType(const Reference& elementType) { // can only be set once. CHECK(mElementType.isEmptyReference()); CHECK(!elementType.isEmptyReference()); mElementType = elementType; } const Type* TemplatedType::getElementType() const { return mElementType.get(); } bool TemplatedType::isTemplatedType() const { return true; } std::vector*> TemplatedType::getReferences() const { return {&mElementType}; } 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(); mElementType->emitVtsTypeDeclarations(out); out.unindent(); out << "}\n"; } void TemplatedType::emitVtsAttributeType(Formatter& out) const { out << "type: " << getVtsType() << "\n"; out << getVtsValueName() << ": {\n"; out.indent(); mElementType->emitVtsAttributeType(out); out.unindent(); out << "}\n"; } } // namespace android Type.h0100644 0000000 0000000 00000031736 13521773237 010723 0ustar000000000 0000000 /* * 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. */ #ifndef TYPE_H_ #define TYPE_H_ #include #include #include #include #include #include #include #include "DocComment.h" #include "Reference.h" namespace android { struct ConstantExpression; struct Formatter; struct FQName; struct ScalarType; struct Scope; struct Type : DocCommentable { Type(Scope* parent); virtual ~Type(); virtual bool isArray() const; virtual bool isBinder() const; virtual bool isBitField() const; virtual bool isCompoundType() const; virtual bool isEnum() const; virtual bool isHandle() const; virtual bool isInterface() const; virtual bool isNamedType() const; virtual bool isMemory() const; virtual bool isPointer() const; virtual bool isScope() const; virtual bool isScalar() const; virtual bool isString() const; virtual bool isTemplatedType() const; 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 getDefinedTypes(); virtual std::vector getDefinedTypes() const; // All types referenced in this type. std::vector*> getReferences(); virtual std::vector*> getReferences() const; // All constant expressions referenced in this type. std::vector getConstantExpressions(); virtual std::vector getConstantExpressions() const; // All types referenced in this type that must have completed // definiton before being referenced. std::vector*> getStrongReferences(); virtual std::vector*> getStrongReferences() const; // Proceeds recursive pass // Makes sure to visit each node only once. status_t recursivePass(const std::function& func, std::unordered_set* visited); status_t recursivePass(const std::function& func, std::unordered_set* 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* reversedOrder, std::unordered_set* stack) const; // Checks following C++ restriction on forward declaration: // inner struct could be forward declared only inside its parent. status_t checkForwardReferenceRestrictions(const Reference& 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* visited) const; virtual bool deepCanCheckEquality(std::unordered_set* 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, StorageMode_Argument, StorageMode_Result, }; // specifyNamespaces: whether to specify namespaces for built-in types virtual std::string getCppType( StorageMode mode, bool specifyNamespaces) const; std::string decorateCppName( const std::string &name, StorageMode mode, bool specifyNamespaces) const; std::string getCppStackType(bool specifyNamespaces = true) const; std::string getCppResultType(bool specifyNamespaces = true) const; std::string getCppArgumentType(bool specifyNamespaces = true) const; // For an array type, dimensionality information will be accumulated at the // end of the returned string. // if forInitializer == true, actual dimensions are included, i.e. [3][5], // otherwise (and by default), they are omitted, i.e. [][]. virtual std::string getJavaType(bool forInitializer = false) const; virtual std::string getJavaWrapperType() const; virtual std::string getJavaSuffix() const; virtual std::string getVtsType() const; virtual std::string getVtsValueName() const; enum ErrorMode { ErrorMode_Ignore, ErrorMode_Goto, ErrorMode_Break, ErrorMode_Return, }; virtual void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const; virtual void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const; virtual void emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const; virtual void emitResolveReferencesEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const; virtual void emitDump( Formatter &out, const std::string &streamName, const std::string &name) const; virtual void emitJavaDump( Formatter &out, const std::string &streamName, const std::string &name) const; virtual bool useParentInEmitResolveReferencesEmbedded() const; virtual bool useNameInEmitReaderWriterEmbedded(bool isReader) const; virtual void emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const; virtual void emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const; virtual void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) 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. // 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 // For android.hardware.foo@1.0::*, this will be in namespace // android::hardware::foo::V1_0 virtual void emitPackageHwDeclarations(Formatter& out) const; virtual void emitTypeDefinitions(Formatter& out, const std::string& prefix) const; virtual void emitJavaTypeDeclarations(Formatter& out, bool atTopLevel) const; virtual bool needsEmbeddedReadWrite() const; virtual bool resultNeedsDeref() const; bool needsResolveReferences() const; bool needsResolveReferences(std::unordered_set* visited) const; virtual bool deepNeedsResolveReferences(std::unordered_set* visited) const; // Generates type declaration for vts proto file. // TODO (b/30844146): make it a pure virtual method. 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 void emitVtsAttributeType(Formatter& out) const; // Returns true iff this type is supported through the Java backend. bool isJavaCompatible() const; bool isJavaCompatible(std::unordered_set* visited) const; virtual bool deepIsJavaCompatible(std::unordered_set* visited) const; // Returns true iff type contains pointer // (excluding methods and inner types). bool containsPointer() const; bool containsPointer(std::unordered_set* visited) const; virtual bool deepContainsPointer(std::unordered_set* visited) const; virtual void getAlignmentAndSize(size_t *align, size_t *size) const; virtual void appendToExportedTypesVector( std::vector *exportedTypes) const; virtual void emitExportedHeader(Formatter& out, bool forJava) const; virtual bool isNeverStrongReference() const; protected: void handleError(Formatter &out, ErrorMode mode) const; void emitReaderWriterEmbeddedForTypeName( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText, const std::string &typeName, const std::string &childName, const std::string &funcNamespace) const; void emitJavaReaderWriterWithSuffix( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader, const std::string &suffix, const std::string &extra) const; void emitDumpWithMethod( Formatter &out, const std::string &streamName, const std::string &methodName, const std::string &name) const; private: bool mIsPostParseCompleted = false; Scope* const mParent; DISALLOW_COPY_AND_ASSIGN(Type); }; /* Base type for VectorType and RefType. */ struct TemplatedType : public Type { void setElementType(const Reference& elementType); const Type* getElementType() const; virtual std::string templatedTypeName() const = 0; std::string typeName() const override; bool isTemplatedType() const override; virtual bool isCompatibleElementType(const Type* elementType) const = 0; std::vector*> 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 mElementType; private: DISALLOW_COPY_AND_ASSIGN(TemplatedType); }; } // namespace android #endif // TYPE_H_ TypeDef.cpp0100644 0000000 0000000 00000004061 13521773237 011664 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TypeDef.h" #include #include namespace android { TypeDef::TypeDef(const char* localName, const FQName& fullName, const Location& location, Scope* parent, const Reference& type) : NamedType(localName, fullName, location, parent), mReferencedType(type) {} const ScalarType *TypeDef::resolveToScalarType() const { CHECK(!"Should not be here"); return NULL; } Type* TypeDef::referencedType() { return mReferencedType.get(); } const Type* TypeDef::referencedType() const { return mReferencedType.get(); } bool TypeDef::isInterface() const { return false; } bool TypeDef::isEnum() const { CHECK(!"Should not be here"); return false; } std::string TypeDef::typeName() const { return "typedef " + localName(); } bool TypeDef::isTypeDef() const { return true; } const Type* TypeDef::resolve() const { return mReferencedType.get(); } std::vector*> TypeDef::getReferences() const { return {&mReferencedType}; } bool TypeDef::needsEmbeddedReadWrite() const { CHECK(!"Should not be here"); return false; } bool TypeDef::resultNeedsDeref() const { CHECK(!"Should not be here"); return false; } void TypeDef::emitTypeDeclarations(Formatter& out) const { out << "typedef " << mReferencedType->getCppStackType() << " " << localName() << ";\n\n"; } } // namespace android TypeDef.h0100644 0000000 0000000 00000003063 13521773237 011332 0ustar000000000 0000000 /* * 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. */ #ifndef TYPE_DEF_H_ #define TYPE_DEF_H_ #include "NamedType.h" namespace android { struct TypeDef : public NamedType { TypeDef(const char* localName, const FQName& fullName, const Location& location, Scope* parent, const Reference& type); const ScalarType *resolveToScalarType() const override; std::string typeName() const override; Type* referencedType(); const Type* referencedType() const; bool isInterface() const override; bool isEnum() const override; bool isTypeDef() const override; bool needsEmbeddedReadWrite() const override; bool resultNeedsDeref() const override; const Type* resolve() const override; std::vector*> getReferences() const override; void emitTypeDeclarations(Formatter& out) const override; private: Reference mReferencedType; DISALLOW_COPY_AND_ASSIGN(TypeDef); }; } // namespace android #endif // TYPE_DEF_H_ VectorType.cpp0100644 0000000 0000000 00000047262 13521773237 012442 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "VectorType.h" #include "ArrayType.h" #include "CompoundType.h" #include "HidlTypeAssertion.h" #include #include namespace android { VectorType::VectorType(Scope* parent) : TemplatedType(parent) {} std::string VectorType::templatedTypeName() const { return "vector"; } bool VectorType::isCompatibleElementType(const Type* elementType) const { if (elementType->isScalar()) { return true; } if (elementType->isString()) { return true; } if (elementType->isEnum()) { return true; } if (elementType->isBitField()) { return true; } if (elementType->isCompoundType()) { return true; } if (elementType->isInterface()) { return true; } if (elementType->isHandle()) { return true; } if (elementType->isMemory()) { return true; } if (elementType->isTemplatedType()) { const Type* inner = static_cast(elementType)->getElementType(); return this->isCompatibleElementType(inner) && !inner->isInterface(); } if (elementType->isArray()) { const Type* inner = static_cast(elementType)->getElementType(); return this->isCompatibleElementType(inner) && !inner->isInterface(); } return false; } bool VectorType::isVector() const { return true; } bool VectorType::isVectorOfBinders() const { return mElementType->isBinder(); } bool VectorType::deepCanCheckEquality(std::unordered_set* visited) const { return mElementType->canCheckEquality(visited); } std::vector*> VectorType::getStrongReferences() const { return {}; } std::string VectorType::getCppType(StorageMode mode, bool specifyNamespaces) const { const std::string base = std::string(specifyNamespaces ? "::android::hardware::" : "") + "hidl_vec<" + mElementType->getCppStackType( specifyNamespaces) + ">"; switch (mode) { case StorageMode_Stack: return base; case StorageMode_Argument: return "const " + base + "&"; case StorageMode_Result: { if (isVectorOfBinders()) { return base; } return "const " + base + "*"; } } } std::string VectorType::getJavaType(bool /* forInitializer */) const { std::string elementJavaType; if (mElementType->isArray()) { elementJavaType = mElementType->getJavaType(); } else { elementJavaType = mElementType->getJavaWrapperType(); } return "java.util.ArrayList<" + elementJavaType + ">"; } std::string VectorType::getVtsType() const { return "TYPE_VECTOR"; } std::string VectorType::getVtsValueName() const { return "vector_value"; } void VectorType::emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { if (isVectorOfBinders()) { emitReaderWriterForVectorOfBinders( out, name, parcelObj, parcelObjIsPointer, isReader, mode); return; } std::string baseType = mElementType->getCppStackType(); 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(" << "&" << name << "));\n\n"; handleError(out, mode); } else { out << "_hidl_err = " << parcelObjDeref << "writeBuffer(&" << name << ", sizeof(" << name << "), &" << parentName << ");\n"; handleError(out, mode); } emitReaderWriterEmbedded( out, 0 /* depth */, name, name /* sanitizedName */ , isReader /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, parentName, "0 /* parentOffset */"); } void VectorType::emitReaderWriterForVectorOfBinders( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { const std::string parcelObjDeref = parcelObj + (parcelObjIsPointer ? "->" : "."); if (isReader) { out << "{\n"; out.indent(); const std::string sizeName = "_hidl_" + name + "_size"; out << "uint64_t " << sizeName << ";\n"; out << "_hidl_err = " << parcelObjDeref << "readUint64(&" << sizeName << ");\n"; handleError(out, mode); out << name << ".resize(" << sizeName << ");\n\n" << "for (size_t _hidl_index = 0; _hidl_index < " << sizeName << "; ++_hidl_index) {\n"; out.indent(); out << mElementType->getCppStackType(true /* specifyNamespaces */) << " _hidl_base;\n"; mElementType->emitReaderWriter( out, "_hidl_base", parcelObj, parcelObjIsPointer, isReader, mode); out << name << "[_hidl_index] = _hidl_base;\n"; out.unindent(); out << "}\n"; out.unindent(); out << "}\n"; } else { out << "_hidl_err = " << parcelObjDeref << "writeUint64(" << name << ".size());\n"; handleError(out, mode); out << "for (size_t _hidl_index = 0; _hidl_index < " << name << ".size(); ++_hidl_index) {\n"; out.indent(); mElementType->emitReaderWriter( out, name + "[_hidl_index]", parcelObj, parcelObjIsPointer, isReader, mode); out.unindent(); out << "}\n"; } } void VectorType::emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const { std::string baseType = getCppStackType(); const std::string childName = "_hidl_" + sanitizedName + "_child"; out << "size_t " << childName << ";\n\n"; emitReaderWriterEmbeddedForTypeName( out, name, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, parentName, offsetText, baseType, childName, "::android::hardware"); if (!mElementType->needsEmbeddedReadWrite()) { return; } const std::string nameDeref = name + (nameIsPointer ? "->" : "."); baseType = mElementType->getCppStackType(); std::string iteratorName = "_hidl_index_" + std::to_string(depth); out << "for (size_t " << iteratorName << " = 0; " << iteratorName << " < " << nameDeref << "size(); ++" << iteratorName << ") {\n"; out.indent(); mElementType->emitReaderWriterEmbedded( out, depth + 1, (nameIsPointer ? "(*" + name + ")" : name) + "[" + iteratorName + "]", sanitizedName + (nameIsPointer ? "_deref" : "") + "_indexed", false /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, childName, iteratorName + " * sizeof(" + baseType + ")"); out.unindent(); out << "}\n\n"; } void VectorType::emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const { emitResolveReferencesEmbeddedHelper( out, 0, /* depth */ name, name /* sanitizedName */, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, "_hidl_" + name + "_child", "0 /* parentOffset */"); } void VectorType::emitResolveReferencesEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string & /* parentName */, const std::string & /* offsetText */) const { emitResolveReferencesEmbeddedHelper( out, depth, name, sanitizedName, nameIsPointer, parcelObj, parcelObjIsPointer, isReader, mode, "", ""); } bool VectorType::useParentInEmitResolveReferencesEmbedded() const { // parentName and offsetText is not used in emitResolveReferencesEmbedded return false; } void VectorType::emitResolveReferencesEmbeddedHelper( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &childName, const std::string &childOffsetText) const { CHECK(needsResolveReferences() && mElementType->needsResolveReferences()); const std::string nameDeref = name + (nameIsPointer ? "->" : "."); const std::string nameDerefed = (nameIsPointer ? "*" : "") + name; std::string elementType = mElementType->getCppStackType(); std::string myChildName = childName, myChildOffset = childOffsetText; if(myChildName.empty() && myChildOffset.empty()) { myChildName = "_hidl_" + sanitizedName + "_child"; myChildOffset = "0"; out << "size_t " << myChildName << ";\n"; out << "_hidl_err = ::android::hardware::findInParcel(" << nameDerefed << ", " << (parcelObjIsPointer ? "*" : "") << parcelObj << ", " << "&" << myChildName << ");\n"; handleError(out, mode); } std::string iteratorName = "_hidl_index_" + std::to_string(depth); out << "for (size_t " << iteratorName << " = 0; " << iteratorName << " < " << nameDeref << "size(); ++" << iteratorName << ") {\n"; out.indent(); mElementType->emitResolveReferencesEmbedded( out, depth + 1, (nameIsPointer ? "(*" + name + ")" : name) + "[" + iteratorName + "]", sanitizedName + (nameIsPointer ? "_deref" : "") + "_indexed", false /* nameIsPointer */, parcelObj, parcelObjIsPointer, isReader, mode, myChildName, myChildOffset + " + " + iteratorName + " * sizeof(" + elementType + ")"); out.unindent(); out << "}\n\n"; } void VectorType::emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const { if (mElementType->isCompoundType()) { if (isReader) { out << mElementType->getJavaType() << ".readVectorFromParcel(" << parcelObj << ");\n"; } else { out << mElementType->getJavaType() << ".writeVectorToParcel(" << parcelObj << ", " << argName << ");\n"; } return; } if (mElementType->isArray()) { size_t align, size; getAlignmentAndSize(&align, &size); if (isReader) { out << " new " << getJavaType(false /* forInitializer */) << "();\n"; } out << "{\n"; out.indent(); out << "android.os.HwBlob _hidl_blob = "; if (isReader) { out << parcelObj << ".readBuffer(" << size << " /* size */);\n"; } else { out << "new android.os.HwBlob(" << size << " /* size */);\n"; } emitJavaFieldReaderWriter( out, 0 /* depth */, parcelObj, "_hidl_blob", argName, "0 /* offset */", isReader); if (!isReader) { out << parcelObj << ".writeBuffer(_hidl_blob);\n"; }; out.unindent(); out << "}\n"; return; } emitJavaReaderWriterWithSuffix( out, parcelObj, argName, isReader, mElementType->getJavaSuffix() + "Vector", "" /* extra */); } void VectorType::emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const { std::string javaType = getJavaType(false /* forInitializer */); out << "final " << javaType << " " << fieldName << " = new " << javaType << "();\n"; } void VectorType::emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const { VectorType::EmitJavaFieldReaderWriterForElementType( out, depth, mElementType.get(), parcelName, blobName, fieldName, offset, isReader); } void VectorType::EmitJavaFieldReaderWriterForElementType( Formatter &out, size_t depth, const Type *elementType, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) { size_t elementAlign, elementSize; elementType->getAlignmentAndSize(&elementAlign, &elementSize); if (isReader) { out << "{\n"; out.indent(); out << "int _hidl_vec_size = " << blobName << ".getInt32(" << offset << " + 8 /* offsetof(hidl_vec, mSize) */);\n"; out << "android.os.HwBlob childBlob = " << parcelName << ".readEmbeddedBuffer(\n"; out.indent(); out.indent(); out << "_hidl_vec_size * " << elementSize << "," << blobName << ".handle(),\n" << offset << " + 0 /* offsetof(hidl_vec, mBuffer) */," << "true /* nullable */);\n\n"; out.unindent(); out.unindent(); out << fieldName << ".clear();\n"; std::string iteratorName = "_hidl_index_" + std::to_string(depth); out << "for (int " << iteratorName << " = 0; " << iteratorName << " < _hidl_vec_size; " << "++" << iteratorName << ") {\n"; out.indent(); elementType->emitJavaFieldInitializer(out, "_hidl_vec_element"); elementType->emitJavaFieldReaderWriter( out, depth + 1, parcelName, "childBlob", "_hidl_vec_element", iteratorName + " * " + std::to_string(elementSize), true /* isReader */); out << fieldName << ".add(_hidl_vec_element);\n"; out.unindent(); out << "}\n"; out.unindent(); out << "}\n"; return; } out << "{\n"; out.indent(); out << "int _hidl_vec_size = " << fieldName << ".size();\n"; out << blobName << ".putInt32(" << offset << " + 8 /* offsetof(hidl_vec, mSize) */, _hidl_vec_size);\n"; out << blobName << ".putBool(" << offset << " + 12 /* offsetof(hidl_vec, mOwnsBuffer) */, false);\n"; // XXX make HwBlob constructor take a long instead of an int? out << "android.os.HwBlob childBlob = new android.os.HwBlob((int)(_hidl_vec_size * " << elementSize << "));\n"; std::string iteratorName = "_hidl_index_" + std::to_string(depth); out << "for (int " << iteratorName << " = 0; " << iteratorName << " < _hidl_vec_size; " << "++" << iteratorName << ") {\n"; out.indent(); elementType->emitJavaFieldReaderWriter( out, depth + 1, parcelName, "childBlob", fieldName + ".get(" + iteratorName + ")", iteratorName + " * " + std::to_string(elementSize), false /* isReader */); out.unindent(); out << "}\n"; out << blobName << ".putBlob(" << offset << " + 0 /* offsetof(hidl_vec, mBuffer) */, childBlob);\n"; out.unindent(); out << "}\n"; } bool VectorType::needsEmbeddedReadWrite() const { return true; } bool VectorType::deepNeedsResolveReferences(std::unordered_set* visited) const { if (mElementType->needsResolveReferences(visited)) { return true; } return TemplatedType::deepNeedsResolveReferences(visited); } bool VectorType::resultNeedsDeref() const { return !isVectorOfBinders(); } bool VectorType::deepIsJavaCompatible(std::unordered_set* visited) const { if (!mElementType->isJavaCompatible(visited)) { return false; } if (mElementType->isArray()) { return static_cast(mElementType.get())->countDimensions() == 1; } if (mElementType->isVector()) { return false; } if (isVectorOfBinders()) { return false; } return TemplatedType::deepIsJavaCompatible(visited); } bool VectorType::deepContainsPointer(std::unordered_set* visited) const { if (mElementType->containsPointer(visited)) { return true; } return TemplatedType::deepContainsPointer(visited); } // All hidl_vec have the same size. static HidlTypeAssertion assertion("hidl_vec", 16 /* size */); void VectorType::getAlignmentAndSizeStatic(size_t *align, size_t *size) { *align = 8; // hidl_vec *size = assertion.size(); } void VectorType::getAlignmentAndSize(size_t *align, size_t *size) const { VectorType::getAlignmentAndSizeStatic(align, size); } } // namespace android VectorType.h0100644 0000000 0000000 00000013056 13521773237 012101 0ustar000000000 0000000 /* * 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. */ #ifndef VECTOR_TYPE_H_ #define VECTOR_TYPE_H_ #include #include "Reference.h" #include "Type.h" namespace android { struct VectorType : public TemplatedType { VectorType(Scope* parent); bool isVector() const override; bool isVectorOfBinders() const; std::string templatedTypeName() const override; bool isCompatibleElementType(const Type* elementType) const override; std::vector*> getStrongReferences() const override; bool deepCanCheckEquality(std::unordered_set* visited) const override; std::string getCppType( StorageMode mode, bool specifyNamespaces) const override; std::string getJavaType(bool forInitializer) const override; std::string getVtsType() const override; std::string getVtsValueName() const override; void emitReaderWriter( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitReaderWriterEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; void emitResolveReferences( Formatter &out, const std::string &name, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const override; void emitResolveReferencesEmbedded( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &parentName, const std::string &offsetText) const override; bool useParentInEmitResolveReferencesEmbedded() const override; void emitJavaReaderWriter( Formatter &out, const std::string &parcelObj, const std::string &argName, bool isReader) const override; void emitJavaFieldInitializer( Formatter &out, const std::string &fieldName) const override; void emitJavaFieldReaderWriter( Formatter &out, size_t depth, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader) const override; static void EmitJavaFieldReaderWriterForElementType( Formatter &out, size_t depth, const Type *elementType, const std::string &parcelName, const std::string &blobName, const std::string &fieldName, const std::string &offset, bool isReader); bool needsEmbeddedReadWrite() const override; bool deepNeedsResolveReferences(std::unordered_set* visited) const override; bool resultNeedsDeref() const override; bool deepIsJavaCompatible(std::unordered_set* visited) const override; bool deepContainsPointer(std::unordered_set* visited) const override; void getAlignmentAndSize(size_t *align, size_t *size) const override; static void getAlignmentAndSizeStatic(size_t *align, size_t *size); private: // Helper method for emitResolveReferences[Embedded]. // Pass empty childName and childOffsetText if the original // childHandle is unknown. // For example, given a vec> (T is a simple struct that // contains primitive values only), then the following methods are // invoked: // 1. VectorType::emitResolveReferences // ... which calls the helper with empty childName and childOffsetText // 2. RefType::emitResolveReferencesEmbedded void emitResolveReferencesEmbeddedHelper( Formatter &out, size_t depth, const std::string &name, const std::string &sanitizedName, bool nameIsPointer, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode, const std::string &childName, const std::string &childOffsetText) const; void emitReaderWriterForVectorOfBinders( Formatter &out, const std::string &name, const std::string &parcelObj, bool parcelObjIsPointer, bool isReader, ErrorMode mode) const; DISALLOW_COPY_AND_ASSIGN(VectorType); }; } // namespace android #endif // VECTOR_TYPE_H_ build/0040755 0000000 0000000 00000000000 13521773237 010721 5ustar000000000 0000000 build/Android.bp0100644 0000000 0000000 00000002006 13521773237 012617 0ustar000000000 0000000 // 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"], } build/fqName.go0100644 0000000 0000000 00000006542 13521773237 012463 0ustar000000000 0000000 // 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" } build/hidl_interface.go0100644 0000000 0000000 00000032543 13521773237 014214 0ustar000000000 0000000 // 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 /Android.bp: hidl_package_root { name: "%s", path: "", } This corresponds to the "-r%s:" 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 } build/hidl_package_root.go0100644 0000000 0000000 00000002764 13521773237 014714 0ustar000000000 0000000 // 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 } build/properties.go0100644 0000000 0000000 00000003116 13521773237 013442 0ustar000000000 0000000 // 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 } build/utils.go0100644 0000000 0000000 00000002224 13521773237 012405 0ustar000000000 0000000 // 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 } c2hal/0040755 0000000 0000000 00000000000 13521773237 010613 5ustar000000000 0000000 c2hal/AST.cpp0100644 0000000 0000000 00000020122 13521773237 011740 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "FunctionDeclaration.h" #include "EnumVarDeclaration.h" #include "Scope.h" #include "Declaration.h" #include "CompositeDeclaration.h" #include "VarDeclaration.h" #include "Define.h" #include "Include.h" #include "Note.h" #include #include #include #include namespace android { AST::AST(const std::string &path, const std::string &outputDir, const std::string &package, bool isOpenGl) : mScanner(NULL), mPath(path), mOutputDir(outputDir), mPackage(package), mIsOpenGl(isOpenGl) {} AST::~AST() { delete mExpression; if(mDeclarations != NULL) { for(auto* decl : *mDeclarations) { delete decl; } } delete mDeclarations; if(mInterfaces != NULL) { for(auto* inter : *mInterfaces) { delete inter; } } delete mInterfaces; if(mIncludes != NULL) { for(auto* incl : *mIncludes) { delete incl; } } delete mIncludes; } void *AST::scanner() { return mScanner; } void AST::setScanner(void *scanner) { mScanner = scanner; } bool AST::isOpenGl() const { return mIsOpenGl; } const std::string& AST::getFilename() const { return mPath; } void AST::setDeclarations(std::vector *declarations) { // on the top level, no var declarations are allowed. for(size_t i = 0; i < declarations->size(); i++) { if(declarations->at(i)->decType() == VarDeclaration::type()) { declarations->at(i) = new Note(declarations->at(i)); } } mDeclarations = declarations; } void AST::setIncludes(std::vector *includes) { mIncludes = includes; } Expression *AST::getExpression() const { return mExpression; } void AST::setExpression(Expression *expression) { mExpression = expression; } const Scope &AST::getDefinesScope() const { return mDefinesScope; } Scope &AST::getDefinesScope() { return mDefinesScope; } void AST::processContents() { CHECK(mDeclarations != NULL); for (auto &declaration : *mDeclarations) { CHECK(declaration != NULL); declaration->processContents(*this); } isolateInterfaces(); isolateGlobalInterface(); isolateIncludes(); isolateConstants(Expression::Type::U64); isolateConstants(Expression::Type::S64); isolateConstants(Expression::Type::U32); isolateConstants(Expression::Type::S32); } /* take interface-like structs out of the type file */ void AST::isolateInterfaces() { mInterfaces = new std::vector; auto it = mDeclarations->begin(); while (it != mDeclarations->end()) { if ((*it)->decType() == CompositeDeclaration::type() && ((CompositeDeclaration *) (*it))->isInterface()) { mInterfaces->push_back((CompositeDeclaration *) *it); it = mDeclarations->erase(it); } else { it++; } } } /* take global function declarations out of the type file and into a new * interface */ void AST::isolateGlobalInterface() { auto globalFuns = new std::vector; auto it = mDeclarations->begin(); while (it != mDeclarations->end()) { if ((*it)->decType() == FunctionDeclaration::type()) { globalFuns->push_back(*it); it = mDeclarations->erase(it); } else { it++; } } if (!globalFuns->empty()) { std::string path = mPackage.substr(0, mPackage.find_first_of('@')); std::string name = path.substr(path.find_last_of('.') + 1); auto interface = new CompositeDeclaration( Type::Qualifier::STRUCT, name + "_global_t", globalFuns); mInterfaces->push_back(interface); } } void AST::isolateIncludes() { mIncludes = new std::vector; auto it = mDeclarations->begin(); while (it != mDeclarations->end()) { if ((*it)->decType() == Include::type()) { mIncludes->push_back((Include *) *it); it = mDeclarations->erase(it); } else { it++; } } } void AST::isolateConstants(Expression::Type ofType) { auto constants = new std::vector; auto it = mDeclarations->begin(); while (it != mDeclarations->end()) { if ((*it)->decType() == Define::type() && ((Define *)*it)->getExpressionType() == ofType) { Define* define = (Define *)*it; auto var = new EnumVarDeclaration(define->getName(), define->getExpression()); define->setExpression(NULL); constants->push_back(var); it = mDeclarations->erase(it); delete define; } else { it++; } } if (!constants->empty()) { auto constEnum = new CompositeDeclaration( Type::Qualifier::ENUM, "Const" + Expression::getTypeDescription(ofType), constants); constEnum->setEnumTypeName(Expression::getTypeName(ofType)); mDeclarations->insert(mDeclarations->begin(), constEnum); } } status_t AST::generateCode() const { CHECK(mDeclarations != NULL); status_t err; for (auto &interface : *mInterfaces) { err = generateFile(interface); if (err != OK) { return err; } } err = generateTypesFile(); if (err != OK) { return err; } return OK; } status_t AST::generateFile(CompositeDeclaration* declaration) const { std::string fileName = declaration->getInterfaceName() + ".hal"; FILE *file = fopen((getFileDir() + fileName).c_str(), "w"); if(file == NULL) { return -errno; } Formatter out(file); // formatter closes out generatePackageLine(out); generateIncludes(out); declaration->generateInterface(out); return OK; } status_t AST::generateTypesFile() const { if (mDeclarations->empty()) { return OK; } FILE *file = fopen((getFileDir() + "types.hal").c_str(), "w"); if(file == NULL) { return -errno; } Formatter out(file); // formatter closes out generatePackageLine(out); generateIncludes(out); for (auto &declaration : *mDeclarations) { declaration->generateCommentText(out); declaration->generateSource(out); out << "\n"; } return OK; } void AST::generateIncludes(Formatter &out) const { for (auto &include : *mIncludes) { include->generateSource(out); out << "\n"; } } void AST::generatePackageLine(Formatter &out) const { out << "package " << mPackage << ";\n\n"; } bool MakeParentHierarchy(const std::string &path) { static const mode_t kMode = 0755; size_t start = 1; // Ignore leading '/' size_t slashPos; while ((slashPos = path.find('/', start)) != std::string::npos) { std::string partial = path.substr(0, slashPos); struct stat st; if (stat(partial.c_str(), &st) < 0) { if (errno != ENOENT) { return false; } int res = mkdir(partial.c_str(), kMode); if (res < 0) { return false; } } else if (!S_ISDIR(st.st_mode)) { return false; } start = slashPos + 1; } return true; } const std::string AST::getFileDir() const { CHECK(MakeParentHierarchy(mOutputDir)); return mOutputDir; } } // namespace android c2hal/AST.h0100644 0000000 0000000 00000004651 13521773237 011416 0ustar000000000 0000000 /* * 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. */ #ifndef AST_H_ #define AST_H_ #include "Scope.h" #include "Expression.h" #include #include #include #include #include #include namespace android { struct Include; struct Define; struct CompositeDeclaration; struct Declaration; struct AST { AST(const std::string &path, const std::string &outputDir, const std::string &package, bool isOpenGl); ~AST(); void *scanner(); void setScanner(void *scanner); bool isOpenGl() const; const std::string &getFilename() const; void setDeclarations(std::vector *declarations); void setIncludes(std::vector *includes); Expression *getExpression() const; void setExpression(Expression *expression); status_t generateCode() const; void processContents(); const Scope &getDefinesScope() const; Scope &getDefinesScope(); private: void * mScanner = NULL; std::string mPath; std::string mOutputDir; std::string mPackage; bool mIsOpenGl; Expression* mExpression = NULL; std::vector *mDeclarations = NULL; std::vector *mInterfaces = NULL; std::vector *mIncludes = NULL; Scope mDefinesScope; const std::string getFileDir() const; status_t generateFile(CompositeDeclaration* declaration) const; status_t generateTypesFile() const; void generateIncludes(Formatter &out) const; void generatePackageLine(Formatter &out) const; void isolateInterfaces(); void isolateGlobalInterface(); void isolateIncludes(); void isolateConstants(Expression::Type ofType); DISALLOW_COPY_AND_ASSIGN(AST); }; } // namespace android #endif // AST_H_ c2hal/Android.bp0100644 0000000 0000000 00000002273 13521773237 012517 0ustar000000000 0000000 // 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. cc_binary_host { name: "c2hal", defaults: ["hidl-gen-defaults"], srcs: [ "AST.cpp", "c2hal_l.ll", "c2hal_y.yy", "CompositeDeclaration.cpp", "Declaration.cpp", "Define.cpp", "EnumVarDeclaration.cpp", "Expression.cpp", "FunctionDeclaration.cpp", "Include.cpp", "main.cpp", "Note.cpp", "Type.cpp", "TypeDef.cpp", "VarDeclaration.cpp", ], shared_libs: [ "libbase", "liblog", "libhidl-gen-utils", ], static_libs: ["libutils"] } c2hal/CompositeDeclaration.cpp0100644 0000000 0000000 00000011344 13521773237 015427 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "CompositeDeclaration.h" #include "FunctionDeclaration.h" #include "VarDeclaration.h" #include "Declaration.h" #include #include #include namespace android { CompositeDeclaration::CompositeDeclaration( const Type::Qualifier::Qualification qualifier, const std::string &name, std::vector *fieldDeclarations) : Declaration(""), mQualifier(qualifier), mFieldDeclarations(fieldDeclarations) { setName(name); } CompositeDeclaration::~CompositeDeclaration() { if(mFieldDeclarations != NULL) { for(auto* decl : *mFieldDeclarations) { delete decl; } } delete mFieldDeclarations; } void CompositeDeclaration::setName(const std::string &name) { Declaration::setName(name); forcePascalCase(); } const Type::Qualifier::Qualification &CompositeDeclaration::getQualifier() const { return mQualifier; } const std::vector* CompositeDeclaration::getFieldDeclarations() const { return mFieldDeclarations; } void CompositeDeclaration::generateInterface(Formatter &out) const { generateCommentText(out); out << "interface " << getInterfaceName() << " {\n\n"; generateBody(out); out << "};\n"; } void CompositeDeclaration::generateSource(Formatter &out) const { CHECK(mQualifier == Type::Qualifier::STRUCT || mQualifier == Type::Qualifier::UNION || mQualifier == Type::Qualifier::ENUM); out << Type::qualifierText(mQualifier) << " " << getName(); if (mQualifier == Type::Qualifier::ENUM) { out << " : "; if (mEnumTypeName.empty()) { out << "int32_t /* NOTE: type is guessed */"; } else { out << mEnumTypeName; } } out << " {\n"; generateBody(out); out << "};\n"; } void CompositeDeclaration::generateBody(Formatter &out) const { out.indent(); for (auto *declaration : *mFieldDeclarations) { declaration->generateCommentText(out); declaration->generateSource(out); out << "\n"; } out.unindent(); } void CompositeDeclaration::processContents(AST &ast) { for (auto &declaration : *mFieldDeclarations) { declaration->processContents(ast); } if (isInterface()) { // move non function fields into a containing struct auto nonFpDecs = new std::vector; auto it = mFieldDeclarations->begin(); while (it != mFieldDeclarations->end()) { if((*it)->decType() != FunctionDeclaration::type()) { bool keep = true; if((*it)->decType() == VarDeclaration::type()) { VarDeclaration* var = (VarDeclaration *)(*it); // Conventional HALs were all required to have // a member of this type. // This member is no longer needed for HIDL if(var->getType()->isHwDevice()) { keep = false; } } if (keep) { nonFpDecs->push_back(*it); } it = mFieldDeclarations->erase(it); } else { it++; } } if (!nonFpDecs->empty()) { auto subStruct = new CompositeDeclaration(Type::Qualifier::STRUCT, getName(), nonFpDecs); mFieldDeclarations->insert(mFieldDeclarations->begin(), subStruct); } } } std::string CompositeDeclaration::getInterfaceName() const { return "I" + getName(); } bool CompositeDeclaration::isInterface() const { if (mQualifier != Type::Qualifier::STRUCT) { return false; } for (auto &declaration : *mFieldDeclarations) { if (declaration->decType() == FunctionDeclaration::type()) { return true; } } return false; } void CompositeDeclaration::setEnumTypeName(const std::string &name) { CHECK(mQualifier == Type::Qualifier::ENUM); mEnumTypeName = name; } } //namespace android c2hal/CompositeDeclaration.h0100644 0000000 0000000 00000003576 13521773237 015104 0ustar000000000 0000000 /* * 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. */ #ifndef COMPOSITE_DECLARATION_H_ #define COMPOSITE_DECLARATION_H_ #include "Declaration.h" #include "Type.h" namespace android { struct CompositeDeclaration : Declaration { CompositeDeclaration( const Type::Qualifier::Qualification qualifier, const std::string &name, std::vector *fieldDeclarations); ~CompositeDeclaration(); void setName(const std::string &name) override; const Type::Qualifier::Qualification &getQualifier() const; const std::vector* getFieldDeclarations() const; static std::string type() { return "composite"; } const std::string decType() const override { return type(); } void generateSource(Formatter &out) const override; void processContents(AST &ast) override; void generateInterface(Formatter &out) const; std::string getInterfaceName() const; bool isInterface() const; void setEnumTypeName(const std::string &name); private: const Type::Qualifier::Qualification mQualifier; std::vector *mFieldDeclarations; std::string mEnumTypeName; void generateBody(Formatter &out) const; DISALLOW_COPY_AND_ASSIGN(CompositeDeclaration); }; } // namespace android #endif // COMPOSITE_DECLARATION_H_c2hal/Declaration.cpp0100644 0000000 0000000 00000003632 13521773237 013545 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Declaration.h" #include #include namespace android { static const std::regex RE_LEADING_SPACES("\n +"); Declaration::Declaration(const std::string &name) : mName(name) {} Declaration::~Declaration() {} const std::string& Declaration::getName() const { return mName; } void Declaration::setName(const std::string &name) { mName = name; } void Declaration::forceCamelCase() { mName = StringHelper::RTrim(mName, "_t"); mName = StringHelper::ToCamelCase(mName); } void Declaration::forcePascalCase() { mName = StringHelper::RTrim(mName, "_t"); mName = StringHelper::ToPascalCase(mName); } void Declaration::forceUpperSnakeCase() { mName = StringHelper::RTrim(mName, "_t"); mName = StringHelper::ToUpperSnakeCase(mName); } const std::string& Declaration::getComment() const { return mComment; } void Declaration::setComment(const std::string &comment) { // remove excess leading whitespace mComment = regex_replace(comment, RE_LEADING_SPACES, "\n "); } void Declaration::generateCommentText(Formatter &out) const { if (!mComment.empty()) { out << mComment << "\n"; } } void Declaration::generateParameterSource(Formatter &out) const { out << "/* UNKNOWN PARAMTER */" << "\n"; } } //namespace android c2hal/Declaration.h0100644 0000000 0000000 00000003363 13521773237 013213 0ustar000000000 0000000 /* * 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. */ #ifndef DECLARATION_H_ #define DECLARATION_H_ #include #include #include #include #include namespace android { struct AST; struct Declaration { Declaration(const std::string &name); virtual ~Declaration(); const std::string &getName() const; virtual void setName(const std::string &name); void forceCamelCase(); void forcePascalCase(); void forceUpperSnakeCase(); const std::string& getComment() const; void setComment(const std::string &comment); std::string getInterfaceName() const; void generateCommentText(Formatter &out) const; virtual const std::string decType() const = 0; /* for example "int test;" */ virtual void generateSource(Formatter &out) const = 0; /* for example "int test" in fun(int test, float jeff) */ virtual void generateParameterSource(Formatter &out) const; virtual void processContents(AST &ast) = 0; private: std::string mName; std::string mComment; DISALLOW_COPY_AND_ASSIGN(Declaration); }; } // namespace android #endif // DECLARATION_H_ c2hal/Define.cpp0100644 0000000 0000000 00000003177 13521773237 012516 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Define.h" #include "AST.h" extern android::status_t parseExpression(android::AST *ast, std::string str); namespace android { Define::Define(const std::string &name, const std::string &slurp) : Declaration(name), mSlurp(slurp) {} Define::~Define() { delete mExpression; } Expression::Type Define::getExpressionType() const { return mExpressionType; } Expression *Define::getExpression() const { return mExpression; } void Define::setExpression(Expression* expression) { mExpression = expression; } void Define::generateSource(Formatter &out) const { out << "/* #define " << getName() << " " << mSlurp << " */\n"; } void Define::processContents(AST &ast) { status_t res = parseExpression(&ast, mSlurp); if (res != 0) { mExpressionType = Expression::Type::UNKNOWN; return; } mExpression = ast.getExpression(); ast.setExpression(NULL); mExpressionType = mExpression->getType(ast); ast.getDefinesScope().enter(getName(), this); } } //namespace android c2hal/Define.h0100644 0000000 0000000 00000002733 13521773237 012160 0ustar000000000 0000000 /* * 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. */ #ifndef DEFINE_H_ #define DEFINE_H_ #include #include #include #include #include "Declaration.h" #include "Expression.h" namespace android { struct Define : Declaration { Define(const std::string &name, const std::string &slurp); ~Define(); static std::string type() { return "define"; } const std::string decType() const override { return type(); } void generateSource(Formatter &out) const override; void processContents(AST &ast) override; Expression::Type getExpressionType() const; Expression *getExpression() const; void setExpression(Expression* expression); private: const std::string mSlurp; Expression::Type mExpressionType; Expression* mExpression = NULL; DISALLOW_COPY_AND_ASSIGN(Define); }; } // namespace android #endif // DEFINE_H_c2hal/EnumVarDeclaration.cpp0100644 0000000 0000000 00000002722 13521773237 015042 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "EnumVarDeclaration.h" #include "Expression.h" namespace android { EnumVarDeclaration::EnumVarDeclaration(const std::string &name, Expression *expression) : Declaration(""), mExpression(expression) { setName(name); } EnumVarDeclaration::~EnumVarDeclaration() { delete mExpression; } void EnumVarDeclaration::setName(const std::string &name) { Declaration::setName(name); forceUpperSnakeCase(); } Expression *EnumVarDeclaration::getExpression() const { return mExpression; } void EnumVarDeclaration::generateSource(Formatter &out) const { out << getName(); if(mExpression != NULL) { out << " = " << mExpression->toString(StringHelper::kUpperSnakeCase); } out << ",\n"; } void EnumVarDeclaration::processContents(AST &) { // nothing to do } } //namespace android c2hal/EnumVarDeclaration.h0100644 0000000 0000000 00000002675 13521773237 014516 0ustar000000000 0000000 /* * 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. */ #ifndef ENUM_VAR_DECLARATION_H_ #define ENUM_VAR_DECLARATION_H_ #include #include #include #include #include "Declaration.h" namespace android { struct Expression; struct EnumVarDeclaration : Declaration { EnumVarDeclaration(const std::string &name, Expression *expression); ~EnumVarDeclaration(); void setName(const std::string &name) override; static std::string type() { return "enum"; } const std::string decType() const override { return type(); } Expression *getExpression() const; void generateSource(Formatter &out) const override; void processContents(AST &ast) override; private: Expression *mExpression = NULL; DISALLOW_COPY_AND_ASSIGN(EnumVarDeclaration); }; } // namespace android #endif // ENUM_VAR_DECLARATION_H_ c2hal/Expression.cpp0100644 0000000 0000000 00000016247 13521773237 013465 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Expression.h" #include "Define.h" #include "AST.h" #include "Scope.h" #include #include namespace android { static const std::regex RE_S32("[^ul]$"); static const std::regex RE_U32("[^ul]u$"); static const std::regex RE_S64("[^ul](l|ll)$"); static const std::regex RE_U64("[^ul](ul|ull)$"); Expression::Type Expression::integralType(const std::string& integer) { if (std::regex_search(integer, RE_S32)) { return Type::S32; } if (std::regex_search(integer, RE_U32)) { return Type::U32; } if (std::regex_search(integer, RE_S64)) { return Type::S64; } if (std::regex_search(integer, RE_U64)) { return Type::U64; } LOG(WARNING) << "UNKNOWN INTEGER LITERAL: " << integer; return Type::UNKNOWN; } Expression::Type Expression::coalesceTypes(Type lhs, Type rhs) { // 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 if (lhs == rhs) { return lhs; } // lhs != rhs if (SIGNED(lhs) == SIGNED(rhs)) { return (Type)MAX_RANK(lhs); } // lhs != rhs && SIGNED(lhs) != SIGNED(rhs) if (lhs == U32 || rhs == U32) { return S64; } return Type::UNKNOWN; #undef SIGNED #undef MAX_RANK } struct ParenthesizedExpression : Expression { ParenthesizedExpression(Expression* inner) : mInner(inner) {} ~ParenthesizedExpression() { delete mInner; } virtual Type getType(const AST &ast) { return mInner->getType(ast); } virtual std::string toString(StringHelper::Case atomCase) { return "(" + mInner->toString(atomCase) + ")"; } private: Expression* mInner; DISALLOW_COPY_AND_ASSIGN(ParenthesizedExpression); }; struct AtomExpression : Expression { AtomExpression(Type type, const std::string &value, bool isId) : mType(type), mValue(value), mIsId(isId) {} virtual Type getType(const AST &ast) { if (mType != Type::UNKNOWN) { return mType; } Define *define = ast.getDefinesScope().lookup(mValue); if (define == NULL) { return Type::UNKNOWN; } return define->getExpressionType(); } virtual std::string toString(StringHelper::Case atomCase) { // do not enforce case if it is not an identifier. return mIsId ? StringHelper::ToCase(atomCase, mValue) : mValue; } private: Type mType; std::string mValue; bool mIsId; DISALLOW_COPY_AND_ASSIGN(AtomExpression); }; struct UnaryExpression : Expression { UnaryExpression(std::string op, Expression* rhs) : mOp(op), mRhs(rhs) {} ~UnaryExpression() { delete mRhs; } virtual Type getType(const AST &ast) { return mRhs->getType(ast); } virtual std::string toString(StringHelper::Case atomCase) { return mOp + mRhs->toString(atomCase); } private: std::string mOp; Expression* mRhs; DISALLOW_COPY_AND_ASSIGN(UnaryExpression); }; struct BinaryExpression : Expression { BinaryExpression(Expression *lhs, std::string op, Expression* rhs) : mLhs(lhs), mOp(op), mRhs(rhs) {} ~BinaryExpression() { delete mLhs; delete mRhs; } virtual Type getType(const AST &ast) { return coalesceTypes(mLhs->getType(ast), mRhs->getType(ast)); } virtual std::string toString(StringHelper::Case atomCase) { return mLhs->toString(atomCase) + " " + mOp + " " + mRhs->toString(atomCase); } private: Expression* mLhs; std::string mOp; Expression* mRhs; DISALLOW_COPY_AND_ASSIGN(BinaryExpression); }; struct TernaryExpression : Expression { TernaryExpression(Expression *lhs, Expression *mhs, Expression* rhs) : mLhs(lhs), mMhs(mhs), mRhs(rhs) {} ~TernaryExpression() { delete mLhs; delete mMhs; delete mRhs; } virtual Type getType(const AST &ast) { return coalesceTypes(mMhs->getType(ast), mRhs->getType(ast)); } virtual std::string toString(StringHelper::Case atomCase) { return mLhs->toString(atomCase) + " ? " + mMhs->toString(atomCase) + " : " + mRhs->toString(atomCase); } private: Expression* mLhs; Expression* mMhs; Expression* mRhs; DISALLOW_COPY_AND_ASSIGN(TernaryExpression); }; struct ArraySubscript : Expression { ArraySubscript(std::string id, Expression* subscript) : mId(id), mSubscript(subscript) {} ~ArraySubscript() { delete mSubscript; } virtual Type getType(const AST &) { return Type::UNKNOWN; } virtual std::string toString(StringHelper::Case atomCase) { return mId + "[" + mSubscript->toString(atomCase) + "]"; } private: std::string mId; Expression* mSubscript; DISALLOW_COPY_AND_ASSIGN(ArraySubscript); }; struct FunctionCall : Expression { FunctionCall(std::string id, std::vector *args) : mId(id), mArgs(args) {} ~FunctionCall() { if(mArgs != NULL) { for(auto* args : *mArgs) { delete args; } } delete mArgs; } virtual Type getType(const AST &) { return Type::UNKNOWN; } virtual std::string toString(StringHelper::Case atomCase) { std::string out = mId + "("; for (auto it = mArgs->begin(); it != mArgs->end(); ++it) { if (it != mArgs->begin()) { out += ", "; } out += (*it)->toString(atomCase); } out += ")"; return out; } private: std::string mId; std::vector *mArgs; DISALLOW_COPY_AND_ASSIGN(FunctionCall); }; Expression *Expression::parenthesize(Expression *inner) { return new ParenthesizedExpression(inner); } Expression *Expression::atom(Type type, const std::string &value, bool isId) { return new AtomExpression(type, value, isId); } Expression *Expression::unary(std::string op, Expression *rhs) { return new UnaryExpression(op, rhs); } Expression *Expression::binary(Expression *lhs, std::string op, Expression *rhs) { return new BinaryExpression(lhs, op, rhs); } Expression *Expression::ternary(Expression *lhs, Expression *mhs, Expression *rhs) { return new TernaryExpression(lhs, mhs, rhs); } Expression *Expression::arraySubscript(std::string id, Expression *subscript) { return new ArraySubscript(id, subscript); } Expression *Expression::functionCall(std::string id, std::vector *args) { return new FunctionCall(id, args); } } //namespace android c2hal/Expression.h0100644 0000000 0000000 00000005331 13521773237 013122 0ustar000000000 0000000 /* * 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. */ #ifndef EXPRESSION_H_ #define EXPRESSION_H_ #include #include #include #include namespace android { struct AST; struct Define; struct Expression { Expression() {} virtual ~Expression() {} enum Type { S32 = 0, // 0b00 S64 = 1, // 0b01 U32 = 2, // 0b10 U64 = 3, // 0b11 UNKNOWN = -1 }; static std::string getTypeDescription(Type type) { switch (type) { case S32: return "S32"; case S64: return "S64"; case U32: return "U32"; case U64: return "U64"; case UNKNOWN: default: return "UNKNOWN"; } } static std::string getTypeName(Type type) { switch (type) { case S32: return "int32_t"; case S64: return "int64_t"; case U32: return "uint32_t"; case U64: return "uint64_t"; case UNKNOWN: default: return "/* UNKNOWN */"; } } static Type integralType(const std::string& integer); static Type coalesceTypes(Type lhs, Type rhs); static Expression *parenthesize(Expression *inner); static Expression *atom(Type type, const std::string &value, bool isId = false); static Expression *unary(std::string op, Expression *rhs); static Expression *binary(Expression *lhs, std::string op, Expression *rhs); static Expression *ternary(Expression *lhs, Expression *mhs, Expression *rhs); static Expression *arraySubscript(std::string id, Expression *subscript); static Expression *functionCall(std::string id, std::vector *args); virtual Type getType(const AST &scope) = 0; // convert this expression to a string. // atomCase: when it comes to atoms, force identifiers into a certain case. // numerical values are not affected. virtual std::string toString(StringHelper::Case atomCase = StringHelper::kNoCase) = 0; private: DISALLOW_COPY_AND_ASSIGN(Expression); }; } // namespace android #endif // EXPRESSION_H_ c2hal/FunctionDeclaration.cpp0100644 0000000 0000000 00000004623 13521773237 015254 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FunctionDeclaration.h" #include "VarDeclaration.h" #include "Type.h" #include namespace android { FunctionDeclaration::FunctionDeclaration(Type* type, const std::string &name, std::vector *params) : Declaration(""), mType(type), mParams(params) { setName(name); } FunctionDeclaration::~FunctionDeclaration() { delete mType; if(mParams != NULL) { for(auto* param : *mParams) { delete param; } } delete mParams; } void FunctionDeclaration::setName(const std::string &name) { Declaration::setName(name); forceCamelCase(); } const Type* FunctionDeclaration::getType() const { return mType; } void FunctionDeclaration::generateSource(Formatter &out) const { out << getName(); generateParams(out); if (!getType()->isVoid()) { out << " generates (" << getType()->decorateName(getName() + "Ret") << ")"; } out << ";\n"; } void FunctionDeclaration::generateParameterSource(Formatter &out) const { out << getType()->decorateName("(*" + getName() + ")"); generateParams(out); } void FunctionDeclaration::processContents(AST &) { if (mParams->size() == 1 && (*mParams)[0]->decType() == VarDeclaration::type()) { VarDeclaration* var = (VarDeclaration *)(*mParams)[0]; if (var->getType()->isVoid()) { mParams->clear(); } } } void FunctionDeclaration::generateParams(Formatter &out) const { out << "("; for (auto it = mParams->begin(); it != mParams->end(); ++it) { if (it != mParams->begin()) { out << ", "; } (*it)->generateParameterSource(out); } out << ")"; } } //namespace androidc2hal/FunctionDeclaration.h0100644 0000000 0000000 00000003163 13521773237 014717 0ustar000000000 0000000 /* * 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. */ #ifndef FUNCTION_DECLARATION_H_ #define FUNCTION_DECLARATION_H_ #include "Declaration.h" #include #include namespace android { struct Declaration; struct Type; struct FunctionDeclaration : Declaration { FunctionDeclaration(Type* type, const std::string &name, std::vector *params); ~FunctionDeclaration(); void setName(const std::string &name) override; const Type * getType() const; static std::string type() { return "function"; } const std::string decType() const override { return type(); } void generateSource(Formatter &out) const override; void generateParameterSource(Formatter &out) const override; void processContents(AST &ast) override; private: const Type *mType; std::vector *mParams; void generateParams(Formatter &out) const; DISALLOW_COPY_AND_ASSIGN(FunctionDeclaration); }; } // namespace android #endif // FUNCTION_DECLARATION_H_c2hal/Include.cpp0100644 0000000 0000000 00000002234 13521773237 012700 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Include.h" namespace android { Include::Include(const std::string &name, bool isLibrary) : Declaration(name), mIsLibrary(isLibrary) {} Include::~Include() {} bool Include::isLibrary() const { return mIsLibrary; } void Include::generateSource(Formatter &out) const { out << "// import " << getName(); if (isLibrary()) { out << "/* library file */"; } else { out << "/* local file */"; } out << "\n"; } void Include::processContents(AST &) { // nothing to do } } //namespace androidc2hal/Include.h0100644 0000000 0000000 00000002430 13521773237 012343 0ustar000000000 0000000 /* * 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. */ #ifndef INCLUDE_H_ #define INCLUDE_H_ #include "Declaration.h" #include #include #include #include namespace android { struct Include : Declaration { Include(const std::string &name, bool isLibrary); ~Include(); static std::string type() { return "include"; } const std::string decType() const override { return type(); } void generateSource(Formatter &out) const override; void processContents(AST &ast) override; bool isLibrary() const; private: const bool mIsLibrary; DISALLOW_COPY_AND_ASSIGN(Include); }; } // namespace android #endif // INCLUDE_H_c2hal/Note.cpp0100644 0000000 0000000 00000002400 13521773237 012215 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Note.h" namespace android { Note::Note(const std::string &name) : Declaration(name) {} Note::Note(Declaration *decl) : Declaration(""), mDecl(decl) {} Note::~Note() { if(mDecl) { delete mDecl; } } void Note::generateSource(Formatter &out) const { out.setLinePrefix("//"); out << "NOTE:\n"; out.indent(); if(mDecl) { mDecl->generateSource(out); } else { out << getName(); } out.unindent(); out.unsetLinePrefix(); out << "\n"; } void Note::processContents(AST &ast) { if (mDecl) { mDecl->processContents(ast); } } } //namespace android c2hal/Note.h0100644 0000000 0000000 00000002633 13521773237 011672 0ustar000000000 0000000 /* * 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. */ #ifndef NOTE_DECLARATION_H_ #define NOTE_DECLARATION_H_ #include #include #include #include #include "Declaration.h" namespace android { /* This class is used to represent declarations or notes * which are otherwise not included in a HIDL HAL */ struct Note : Declaration { Note(const std::string &name); // assume ownership on decl Note(Declaration *decl); ~Note(); static std::string type() { return "note"; } const std::string decType() const override { return type(); } void generateSource(Formatter &out) const override; void processContents(AST &ast) override; private: Declaration *mDecl = nullptr; DISALLOW_COPY_AND_ASSIGN(Note); }; } // namespace android #endif // NOTE_DECLARATION_H_ c2hal/README.md0100644 0000000 0000000 00000003500 13521773237 012065 0ustar000000000 0000000 # c2hal user-guide ## 1. Build ``` croot make c2hal -j64 ``` ## 2. Run ``` c2hal [-g] [-o dir] -p package (-r interface-root)+ (header-filepath)+ ``` -o output path: If missing, the second half of a relevant interface-root will be used. -p package: For example android.hardware.baz@1.0. This will be used as the package in .hal files and will also be used to construct the correct directory structure. -g: Enabling this flag changes the behavior of c2hal to parse opengl files. -r package:path root: For example 'android.hardware:hardware/interfaces'. Examples: ``` # Build the test.h header: c2hal -r android.hardware:hardware/interfaces -p android.hardware.baz@1.0 system/tools/hidl/c2hal/test/test.h # Build the simple.h header: c2hal -r android.hardware:hardware/interfaces -p android.hardware.simple@1.0 system/tools/hidl/c2hal/test/simple.h # Build a particular libhardware header: c2hal -r android.hardware:hardware/interfaces -p android.hardware.nfc@1.0 hardware/libhardware/include/hardware/nfc.h # Build all headers from libhardware: python3 system/tools/hidl/c2hal/test/build_all.py ~/android/master/hardware/libhardware/include/hardware/ # Build various OpenGl versions: python3 system/tools/hidl/c2hal/test/build_all.py -g ~/android/master/frameworks/native/opengl/include/EGL/ python3 system/tools/hidl/c2hal/test/build_all.py -g ~/android/master/frameworks/native/opengl/include/ETC1/ python3 system/tools/hidl/c2hal/test/build_all.py -g ~/android/master/frameworks/native/opengl/include/GLES/ python3 system/tools/hidl/c2hal/test/build_all.py -g ~/android/master/frameworks/native/opengl/include/GLES2/ python3 system/tools/hidl/c2hal/test/build_all.py -g ~/android/master/frameworks/native/opengl/include/GLES3/ python3 system/tools/hidl/c2hal/test/build_all.py -g ~/android/master/frameworks/native/opengl/include/KHR/ ``` c2hal/Scope.h0100644 0000000 0000000 00000003714 13521773237 012037 0ustar000000000 0000000 /* * 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. */ #ifndef SCOPE_H_ #define SCOPE_H_ #include #include #include #include #include namespace android { /* This class is used to represent declarations or notes * which are otherwise not included in a HIDL HAL */ template struct Scope { Scope() {} ~Scope() {} void enter(std::string name, T item); void leave(std::string name); T lookup(std::string name) const; private: std::map mScopeContents; DISALLOW_COPY_AND_ASSIGN(Scope); }; template void Scope::enter(std::string name, T item) { auto it = mScopeContents.find(name); if (it != mScopeContents.end()) { LOG(WARNING) << "Redeclaring variable in scope: " << name; return; } mScopeContents[name] = item; } template void Scope::leave(std::string name) { auto it = mScopeContents.find(name); if (it == mScopeContents.end()) { LOG(WARNING) << "Tried to undefined already undefined value in scope: " << name; return; } mScopeContents.erase(it); } template T Scope::lookup(std::string name) const { auto it = mScopeContents.find(name); if (it == mScopeContents.end()) { return NULL; } return (*it).second; } } // namespace android #endif // SCOPE_H_c2hal/Type.cpp0100644 0000000 0000000 00000015206 13521773237 012241 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Type.h" #include #include namespace android { Type::Type(std::vector *qualifiers) : mQualifiers(qualifiers) {} Type::~Type() { if(mArrays != NULL) { for(auto* array : *mArrays) { delete array; } } if(mQualifiers != NULL) { for(auto* qual : *mQualifiers) { delete qual; } } delete mQualifiers;} void Type::setArrays(std::vector *arrays) { mArrays = arrays; } const std::string Type::decorateName(const std::string &name) const { std::stringstream ss; std::string special = getSpecialTypeName(); if(special.empty()) { ss << getHidlType(); } else { ss << special; } ss << " " << name; return ss.str(); } std::map Type::kSignedToUnsignedMap = { { "char", "uint8_t" }, { "short", "uint16_t" }, { "int", "uint32_t" }, { "long", "uint64_t" }, { "int8_t", "uint8_t" }, { "int16_t", "uint16_t" }, { "int32_t", "uint32_t" }, { "int64_t", "uint64_t" }, }; const std::string Type::signedToUnsigned(const std::string &signedType) { auto it = kSignedToUnsignedMap.find(signedType); if (it == kCToHidlMap.end()) { return ""; } return (*it).second; } std::map Type::kCToHidlMap = { { "char", "int8_t /* NOTE: char */" }, { "short", "int16_t" }, { "int", "int32_t" }, { "long", "int64_t"}, { "native_handle_t", "handle" }, { "size_t", "uint64_t" }, { "int8_t", "int8_t" }, { "uint8_t", "uint8_t" }, { "int16_t", "int16_t" }, { "uint16_t", "uint16_t" }, { "int32_t", "int32_t" }, { "uint32_t", "uint32_t" }, { "int64_t", "int64_t" }, { "uint64_t", "uint64_t" }, { "float", "float" }, { "double", "double" }, { "bool", "bool" }, { "wchar_t", "int32_t /* NOTE: wchar_t */"}, // { "hidl_string", "string" }, // { "hidl_vec", "vec"}, }; const std::string Type::cToHidlType(const std::string &cType) { auto it = kCToHidlMap.find(cType); if (it == kCToHidlMap.end()) { return ""; } return (*it).second; } const std::string Type::getHidlType() const { if (mQualifiers == NULL) { return ""; } std::stringstream ss; for (auto it = mQualifiers->begin(); it != mQualifiers->end(); ++it) { if (it != mQualifiers->begin()) { ss << " "; } switch((*it)->qualification) { case Type::Qualifier::STRUCT: case Type::Qualifier::UNION: case Type::Qualifier::ENUM: case Type::Qualifier::POINTER: case Type::Qualifier::CONST: { ss << "/* " << Type::qualifierText((*it)->qualification) << " */"; break; } case Type::Qualifier::ID: { std::string id = (*it)->id; std::string conversion = cToHidlType(id); if (!conversion.empty()) { ss << conversion; } else { std::string baseName = StringHelper::RTrim(id, "_t"); ss << StringHelper::ToPascalCase(baseName); } break; } case Type::Qualifier::GENERICS: { ss << "<" << (*it)->generics->decorateName("") << ">"; break; } case Type::Qualifier::UNSIGNED: { auto next = it + 1; if (next == mQualifiers->end()) { ss << "uint32_t"; // 'unsigned a' -> 'uint32_t a' break; } std::string unsignedType = signedToUnsigned((*next)->id); if(unsignedType.empty()) { ss << Type::qualifierText((*it)->qualification); } else { ss << unsignedType; ++it; } break; } default: { ss << Type::qualifierText((*it)->qualification); } } } if (mArrays != NULL) { for (const auto &array : *mArrays) { ss << "[" << array->toString() << "]"; } } return ss.str(); } const std::string Type::getRawQualifierList() const { if (mQualifiers == NULL) { return ""; } std::stringstream ss; for(auto* qualifier : *mQualifiers) { ss << Type::qualifierText(qualifier->qualification) << " "; } return ss.str(); } const std::string Type::getSpecialTypeName() const { // this makes for a relatively expensive comparison, but it is // readable until the converstion get nailed down. std::string qualifiers = getRawQualifierList(); if (qualifiers == "const ID * " || qualifiers == "ID * ") { std::string id = mQualifiers->at(mQualifiers->size() - 2)->id; if (id == "char") { return "string"; } else { // can't tell if it's a hidl_vec or a pointer // return "vec<" + id + ">"; return ""; } } return ""; } bool Type::isVoid() const { if (mQualifiers->size() == 0) { return true; } return mQualifiers->size() == 1 && (*mQualifiers)[0]->qualification == Type::Qualifier::VOID; } bool Type::isHwDevice() const { if (mQualifiers->size() < 2) { return false; } return (*mQualifiers)[0]->qualification == Type::Qualifier::STRUCT && (*mQualifiers)[1]->qualification == Type::Qualifier::ID && (*mQualifiers)[1]->id == "hw_device_t"; } std::string Type::removeLastId() { if(mQualifiers == NULL || mQualifiers->size() == 0) { return ""; } Qualifier *last = (*mQualifiers)[mQualifiers->size() - 1]; if(last == NULL || last->qualification != Qualifier::ID) { return ""; } std::string ret{last->id}; mQualifiers->erase(mQualifiers->end() - 1); return ret; } } //namespace android c2hal/Type.h0100644 0000000 0000000 00000006331 13521773237 011705 0ustar000000000 0000000 /* * 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. */ #ifndef TYPE_H_ #define TYPE_H_ #include "Expression.h" #include #include #include #include #include namespace android { struct Type { struct Qualifier { enum Qualification { NONE = 0, STRUCT, UNION, SIGNED, UNSIGNED, VOID, POINTER, CONST, GENERICS, ID, ENUM } qualification; union { std::string id; Type *generics; }; Qualifier(Qualification qualification) : qualification(qualification) {} Qualifier(Qualification qualification, std::string id) : qualification(qualification), id(id) {} Qualifier(Qualification qualification, Type* generics) : qualification(qualification), generics(generics) {} ~Qualifier() { if (qualification == GENERICS) { delete generics; } } }; Type(std::vector *qualifiers); ~Type(); static std::string qualifierText(Qualifier::Qualification qual) { switch(qual) { case Qualifier::STRUCT: return "struct"; case Qualifier::UNION: return "union"; case Qualifier::ENUM: return "enum"; case Qualifier::SIGNED: return "signed"; case Qualifier::UNSIGNED: return "unsigned"; case Qualifier::VOID: return "void"; case Qualifier::POINTER: return "*"; case Qualifier::CONST: return "const"; case Qualifier::ID: return "ID"; case Qualifier::NONE: return ""; default: return "/* UNKNOWN TYPE QUALIFIER */"; } } void setArrays(std::vector *arrays); const std::string decorateName(const std::string &name) const; bool isVoid() const; bool isHwDevice() const; std::string removeLastId(); private: static std::map kSignedToUnsignedMap; static const std::string signedToUnsigned(const std::string &signedType); static std::map kCToHidlMap; static const std::string cToHidlType(const std::string &cType); const std::string getHidlType() const; const std::string getRawQualifierList() const; const std::string getSpecialTypeName() const; std::vector *mQualifiers = NULL; /* [ expression ] [ expression ] ... [ expression ] */ std::vector *mArrays = NULL; DISALLOW_COPY_AND_ASSIGN(Type); }; } // namespace android #endif // TYPE_H_ c2hal/TypeDef.cpp0100644 0000000 0000000 00000002132 13521773237 012652 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TypeDef.h" namespace android { TypeDef::TypeDef(const std::string &name, Declaration* declaration) : Declaration(name), mDeclaration(declaration) { mDeclaration->forcePascalCase(); } TypeDef::~TypeDef() {} void TypeDef::generateSource(Formatter &out) const { out << "typedef "; mDeclaration->generateParameterSource(out); out << ";\n"; } void TypeDef::processContents(AST &ast) { mDeclaration->processContents(ast); } } //namespace androidc2hal/TypeDef.h0100644 0000000 0000000 00000002374 13521773237 012327 0ustar000000000 0000000 /* * 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. */ #ifndef TYPE_DEF_H_ #define TYPE_DEF_H_ #include #include #include #include #include "Declaration.h" namespace android { struct TypeDef : Declaration { TypeDef(const std::string &name, Declaration* declaration); ~TypeDef(); static std::string type() { return "typedef"; } const std::string decType() const override { return type(); } void generateSource(Formatter &out) const override; void processContents(AST &ast) override; private: Declaration* mDeclaration; DISALLOW_COPY_AND_ASSIGN(TypeDef); }; } // namespace android #endif // TYPE_DEF_H_c2hal/VarDeclaration.cpp0100644 0000000 0000000 00000002664 13521773237 014222 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "VarDeclaration.h" namespace android { VarDeclaration::VarDeclaration(Type *type, const std::string &name) : Declaration(""), mType(type) { setName(name); } VarDeclaration::~VarDeclaration() { delete mType; } void VarDeclaration::setName(const std::string &name) { Declaration::setName(name); // special case for varargs if(getName() == "...") return; forceCamelCase(); } Type* VarDeclaration::getType() const { return mType; } void VarDeclaration::generateSource(Formatter &out) const { out << getType()->decorateName(getName()) << ";\n"; } void VarDeclaration::generateParameterSource(Formatter &out) const { out << getType()->decorateName(getName()); } void VarDeclaration::processContents(AST &) { // nothing to do } } // namespace androidc2hal/VarDeclaration.h0100644 0000000 0000000 00000002506 13521773237 013662 0ustar000000000 0000000 /* * 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. */ #ifndef VARDECLARATION_H_ #define VARDECLARATION_H_ #include "Declaration.h" #include "Type.h" namespace android { struct VarDeclaration : Declaration{ VarDeclaration(Type *type, const std::string &name); ~VarDeclaration(); void setName(const std::string &name) override; Type* getType() const; static std::string type() { return "var"; } const std::string decType() const override { return type(); } void generateSource(Formatter &out) const override; void generateParameterSource(Formatter &out) const override; void processContents(AST &ast) override; private: Type *mType; DISALLOW_COPY_AND_ASSIGN(VarDeclaration); }; } // namespace android #endif // VARDECLARATION_H_c2hal/c2hal_l.ll0100644 0000000 0000000 00000025647 13521773237 012463 0ustar000000000 0000000 /* * 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. */ D [0-9] L [a-zA-Z_] AN [a-zA-Z_0-9] H [a-fA-F_0-9] E [Ee][+-]?{D}+ FS (f|F|l|L) IS (u|U|l|L)* S [ \t] DOT [.] PATH ({DOT}|{AN}|\/|-)+ ID {L}{AN}* %{ #include "AST.h" #include "Declaration.h" #include "Type.h" #include "VarDeclaration.h" #include "FunctionDeclaration.h" #include "CompositeDeclaration.h" #include "Define.h" #include "Include.h" #include "EnumVarDeclaration.h" #include "Note.h" #include "TypeDef.h" #include "Expression.h" #include #include #include "c2hal_y.h" using namespace android; int check_type(yyscan_t yyscanner, struct yyguts_t *yyg); extern int start_token; extern std::string last_comment; // :( extern int numB; extern std::string functionText; extern std::string defineText; extern std::string otherText; extern bool isOpenGl; #define YY_USER_ACTION yylloc->first_line = yylineno; #define ID_UNLESS_OPEN_GL(OPEN_GL_CODE) \ do { \ if (isOpenGl) { \ OPEN_GL_CODE \ } else { \ yylval->str = strdup(yytext); \ return ID; \ } \ } while(0) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wdeprecated-register" #pragma clang diagnostic ignored "-Wregister" %} %option yylineno %option nounput %option noinput %option reentrant %option bison-bridge %option bison-locations %option extra-type="android::AST *" %x COMMENT_STATE %x INCLUDE_STATE %x COPY_DECL_STATE %x FUNCTION_STATE %x DEFINE_STATE %x DEFINE_SLURP_STATE %% %{ if (start_token) { int token = start_token; start_token = 0; return token; } %} "\n" { /* needed for yylineno to update */ } \/\*([^*]|\*+[^*\/])*\*+\/ { last_comment = strdup(yytext); } "//"[^\r\n]* { /* skip C++ style comment */ } "__BEGIN_DECLS" { /* macro'd 'extern "C" {' if CPP or nothing */ } "__END_DECLS" { /* '}' */ } "__attribute__((__packed__))" { /* ignore */ } "__attribute__((packed))" { /* ignore */ } "__attribute__((__deprecated__))" { /* ignore */ } "EGLAPIENTRYP" { ID_UNLESS_OPEN_GL(return '*';); } "EGLAPIENTRY" { ID_UNLESS_OPEN_GL(/* actually is nothing on android */); } "GL_APIENTRYP" { ID_UNLESS_OPEN_GL(return '*';); } "GL_APIENTRY" { ID_UNLESS_OPEN_GL(/* actually is nothing on android */); } "GL_APICALL" { ID_UNLESS_OPEN_GL(/* __attribute__((visibility("default"))) */); } "#include" { BEGIN(INCLUDE_STATE); return INCLUDE; } "<" { return '<'; } ">" { return '>'; } "\"" { return '"'; } "\n" { BEGIN(INITIAL); } {PATH} { yylval->str = strdup(yytext); return INCLUDE_FILE; } . { /* ignore other characters */ } "static"|"inline" { BEGIN(FUNCTION_STATE); functionText = strdup(yytext); numB = 0; } [^{}]+ { functionText += yytext; } "{" { functionText += yytext; numB += 1;} "}" { functionText += yytext; numB -= 1; // Will fail if unbalanced brackets in // strings or comments in the function. if (numB <= 0) { BEGIN(INITIAL); yylval->str = strdup(functionText.c_str()); return FUNCTION; } } "#"{S}*"define" { BEGIN(DEFINE_STATE); return DEFINE; } {ID} { BEGIN(DEFINE_SLURP_STATE); defineText = ""; yylval->str = strdup(yytext); return ID; } . { /* ignore other characters */ } \/\*([^*]|\*+[^*\/])*\*+\/ { defineText += yytext; } [^\\\n] { defineText += yytext; } "\\\n" { defineText += yytext; } "\n" { BEGIN(INITIAL); yylval->str = strdup(defineText.c_str()); return DEFINE_SLURP; } "using" { BEGIN(COPY_DECL_STATE); otherText = strdup(yytext); } "#"{S}*{L}+ { BEGIN(COPY_DECL_STATE); otherText = strdup(yytext); } \/\*([^*]|\*+[^*\/])*\*+\/ { otherText += yytext; } [^\\\n] { otherText += yytext; } "\\\n" { otherText += yytext; } "\n" { BEGIN(INITIAL); yylval->str = strdup(otherText.c_str()); // decls/macros we want to preserve // in the output, but there is nothing // special to do about them yet return OTHER_STATEMENT; } "struct" { return STRUCT; } "union" { return UNION; } "enum" { return ENUM; } "class" { return CLASS; } "const" { return CONST; } "typedef" { return TYPEDEF; } "void" { return VOID; } "unsigned" { return UNSIGNED; } "signed" { return SIGNED; } "namespace" { return NAMESPACE; } "extern" { return EXTERN; } "\"C\"" { return C_STRING; } {ID} { yylval->str = strdup(yytext); return ID; } 0[xX]{H}+{IS}? { yylval->str = strdup(yytext); return INTEGRAL_VALUE; } 0{D}+{IS}? { yylval->str = strdup(yytext); return INTEGRAL_VALUE; } {D}+{IS}? { yylval->str = strdup(yytext); return INTEGRAL_VALUE; } {D}+{E}{FS}? { yylval->str = strdup(yytext); return VALUE; } {D}+\.{E}?{FS}? { yylval->str = strdup(yytext); return VALUE; } {D}*\.{D}+{E}?{FS}? { yylval->str = strdup(yytext); return VALUE; } L?\"(\\.|[^\\"])*\" { yylval->str = strdup(yytext); return VALUE; } "(" { return '('; } ")" { return ')'; } "<" { return '<'; } ">" { return '>'; } "{" { return '{'; } "}" { return '}'; } "[" { return '['; } "]" { return ']'; } "?" { return '?'; } ":" { return ':'; } "*" { return '*'; } ";" { return ';'; } "," { return ','; } "=" { return '='; } "+" { return '+'; } "-" { return '-'; } "/" { return '/'; } "%" { return '%'; } "&" { return '&'; } "|" { return '|'; } "^" { return '^'; } "~" { return '~'; } "<<" { return LSHIFT; } ">>" { return RSHIFT; } "..." { return VARARGS; } . { /* ignore other characters */ } %% #pragma clang diagnostic pop // allows us to specify what start symbol will be used in the grammar int start_token; bool should_report_errors; std::string last_comment; // this is so frowned upon on so many levels, but here vars are so that we can // slurp up function text as a string and don't have to implement // the *entire* grammar of C (and C++ in some files) just to parse headers int numB; std::string functionText; std::string defineText; std::string otherText; bool isOpenGl; int yywrap(yyscan_t) { return 1; } status_t parseFile(AST *ast) { FILE *file = fopen(ast->getFilename().c_str(), "rb"); if (file == NULL) { return -errno; } start_token = START_HEADER; isOpenGl = ast->isOpenGl(); should_report_errors = true; yyscan_t scanner; yylex_init_extra(ast, &scanner); ast->setScanner(scanner); yyset_in(file, scanner); int res = yyparse(ast); yylex_destroy(scanner); ast->setScanner(NULL); fclose(file); file = NULL; return res; } status_t parseExpression(AST *ast, std::string str) { start_token = START_EXPR; isOpenGl = ast->isOpenGl(); should_report_errors = false; yyscan_t scanner; yylex_init_extra(ast, &scanner); ast->setScanner(scanner); YY_BUFFER_STATE buf = yy_scan_string(str.c_str(), scanner); int res = yyparse(ast); yy_delete_buffer(buf, scanner); yylex_destroy(scanner); ast->setScanner(NULL); return res; } c2hal/c2hal_y.yy0100644 0000000 0000000 00000031433 13521773237 012520 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ %{ #include "AST.h" #include "Declaration.h" #include "Type.h" #include "VarDeclaration.h" #include "FunctionDeclaration.h" #include "CompositeDeclaration.h" #include "Define.h" #include "Include.h" #include "EnumVarDeclaration.h" #include "Note.h" #include "TypeDef.h" #include "Expression.h" #include "c2hal_y.h" #include #include using namespace android; extern int yylex(YYSTYPE *yylval_param, YYLTYPE *llocp, void *); int yyerror(YYLTYPE *llocp, AST *, const char *s) { extern bool should_report_errors; if (!should_report_errors) { return 0; } fflush(stdout); LOG(ERROR) << " " << s << " near line " << llocp->first_line; return 0; } #define scanner ast->scanner() std::string get_last_comment() { extern std::string last_comment; std::string ret{last_comment}; // clear the last comment now that it's been taken last_comment = ""; return ret; } %} %parse-param { android::AST *ast } %lex-param { void *scanner } %locations %pure-parser %glr-parser /* These have to do with the fact that * struct_or_union_declaration and enum_declaration * both start with STRUCT/UNION/ENUM opt_id * and type_qualifiers contain these. */ %expect 3 %token START_HEADER %token START_EXPR %token STRUCT %token UNION %token ENUM %token CLASS %token CONST %token VOID %token INCLUDE %token DEFINE %token TYPEDEF %token UNSIGNED %token SIGNED %token LSHIFT %token RSHIFT %token VARARGS %token NAMESPACE %token EXTERN %token C_STRING %left ',' %right '?' ':' %left '|' %left '^' %left '&' %left RSHIFT LSHIFT %left '+' '-' %left '*' '/' '%' %right '~' '!' UMINUS UPLUS %left ARRAY_SUBSCRIPT FUNCTION_CALL %right STRUCT ENUM %token ID %token COMMENT %token VALUE %token INTEGRAL_VALUE %token INCLUDE_FILE %token FUNCTION %token DEFINE_SLURP %token OTHER_STATEMENT %type array %type arrays %type expr %type args %type type %type opt_enum_base_type %type type_qualifier %type type_qualifiers %type declaration %type declarations %type struct_or_union_declaration %type enum_declaration %type param %type params %type struct_or_union %type opt_id %type include %type enum_var %type enum_vars enum_vars_all_but_last %type enum_var_line enum_var_last_line %start parse_selector %union { const char *str; int count; android::Declaration *declaration; android::CompositeDeclaration *composite; std::vector *declarations; android::EnumVarDeclaration *enum_var; android::Declaration *param; std::vector *params; android::Type *type; android::Type::Qualifier *qualifier; android::Type::Qualifier::Qualification qualification; std::vector *qualifiers; android::Include *include; std::vector *includes; android::Expression *expression; std::vector *expressions; } %% parse_selector : START_HEADER header | START_EXPR expr_parser ; expr_parser : expr { ast->setExpression($1); } ; header : declarations /* well, we are a header file */ { ast->setDeclarations($1); } ; declarations : /* EMPTY */ { $$ = new std::vector; } | declarations declaration { $$ = $1; $$->push_back($2); } | declarations EXTERN C_STRING '{' declarations '}' { $1->push_back(new Note("extern \"C\" { ")); $1->insert($1->end(), $5->begin(), $5->end()); $1->push_back(new Note("} // end of extern C")); delete $5; $$ = $1; } ; declaration : param ';' { $$ = $1; $$->setComment(get_last_comment()); } | struct_or_union_declaration ';' { $$ = $1; } | enum_declaration ';' { $$ = $1; } | TYPEDEF struct_or_union_declaration ';' { // ignore that it is a typedef, for our purposes it doesn't matter $$ = $2; } | TYPEDEF enum_declaration ';' { // ignore that it is a typedef, for our purposes it doesn't matter $$ = $2; } | TYPEDEF param ';' /* looks like 'typedef const int8_t store;' */ { $$ = new TypeDef($2->getName(), $2); $$->setComment(get_last_comment()); } | DEFINE ID DEFINE_SLURP { $$ = new Define($2, $3); $$->setComment(get_last_comment()); } | OTHER_STATEMENT { $$ = new Note($1); $$->setComment(get_last_comment()); } | FUNCTION { $$ = new Note($1); $$->setComment(get_last_comment()); } | type ID '=' expr ';' { $$ = new Note($1->decorateName($2) + " = " + $4->toString()); } | include { $$ = $1; $$->setComment(get_last_comment()); } | NAMESPACE ID '{' declarations '}' { $$ = new CompositeDeclaration(Type::Qualifier::STRUCT, $2, $4); get_last_comment(); // clear it $$->setComment("/* from namespace declaration */"); } ; include : INCLUDE '<' INCLUDE_FILE '>' { $$ = new Include($3, true /* isLibrary */); } | INCLUDE '"' INCLUDE_FILE '"' { $$ = new Include($3, false /* isLibrary */); } ; struct_or_union_declaration : struct_or_union opt_id { $$ = strdup(get_last_comment().c_str()); } '{' declarations '}' opt_id { $$ = new CompositeDeclaration($1, $2, $5); $$->setComment($3); if(!std::string($7).empty()) { $$->setName($7); } } ; opt_comma : /* EMPTY */ | ',' ; enum_key : ENUM | ENUM CLASS /* c++11 */ | ENUM STRUCT /* c++11 */ ; opt_enum_base_type : /* EMPTY */ { $$ = NULL; } | ':' type { $$ = $2; } ; enum_declaration : enum_key opt_id { $$ = strdup(get_last_comment().c_str()); } opt_enum_base_type '{' enum_vars '}' opt_id { $$ = new CompositeDeclaration(Type::Qualifier::ENUM, $2, $6); $$->setComment($3); if($4) { $$->setEnumTypeName($4->decorateName("")); delete $4; } if(!std::string($8).empty()) { $$->setName($8); } } ; enum_vars : /* EMPTY */ { $$ = new std::vector; } | enum_vars_all_but_last enum_var_last_line { $$ = $1; $$->push_back($2); } enum_vars_all_but_last : /* EMPTY */ { $$ = new std::vector; } | enum_vars_all_but_last enum_var_line { $$ = $1; $$->push_back($2); } ; enum_var_last_line : enum_var opt_comma { $$ = $1; } | OTHER_STATEMENT { $$ = new Note($1); $$->setComment(get_last_comment()); } ; enum_var_line : enum_var ',' { $$ = $1; } | OTHER_STATEMENT { $$ = new Note($1); $$->setComment(get_last_comment()); } ; enum_var : ID { $$ = new EnumVarDeclaration($1, NULL); $$->setComment(get_last_comment()); } | ID '=' expr { $$ = new EnumVarDeclaration($1, $3); $$->setComment(get_last_comment()); } ; params : /* EMPTY */ { $$ = new std::vector; } | param { $$ = new std::vector; $$->push_back($1); } | params ',' param { $$ = $1; $$->push_back($3); } ; param : type arrays { $1->setArrays($2); // allow for either "const int myvar" or "const int" // as a parameter declaration std::string lastId = $1->removeLastId(); $$ = new VarDeclaration($1, lastId); } | type '(' '*' ID arrays ')' '(' params ')' { $1->setArrays($5); $$ = new FunctionDeclaration($1, $4, $8); } | type ID '(' params ')' { $$ = new FunctionDeclaration($1, $2, $4); } | type '(' ID ')' '(' params ')' { $$ = new FunctionDeclaration($1, $3, $6); } | VARARGS { $$ = new VarDeclaration(new Type(NULL), "..."); } ; type : type_qualifiers { $$ = new Type($1); } ; type_qualifiers : type_qualifier { $$ = new std::vector; $$->push_back($1); } | type_qualifiers type_qualifier { $$ = $1; $$->push_back($2); } ; opt_id : /* EMPTY */ { $$ = ""; } | ID { $$ = $1; } ; expr : ID { $$ = Expression::atom(Expression::Type::UNKNOWN, $1, true /* isId*/ ); } | VALUE { $$ = Expression::atom(Expression::Type::UNKNOWN, $1); } | INTEGRAL_VALUE { $$ = Expression::atom(Expression::integralType($1), $1); } | '(' expr ')' { $$ = Expression::parenthesize($2); } | ID '[' expr ']' %prec ARRAY_SUBSCRIPT { $$ = Expression::arraySubscript($1, $3); } | ID '(' args ')' %prec FUNCTION_CALL { $$ = Expression::functionCall($1, $3); } | expr '?' expr ':' expr { $$ = Expression::ternary($1, $3, $5); } | expr '+' expr { $$ = Expression::binary($1, "+", $3); } | expr '-' expr { $$ = Expression::binary($1, "-", $3); } | expr '/' expr { $$ = Expression::binary($1, "/", $3); } | expr '*' expr { $$ = Expression::binary($1, "*", $3); } | expr '%' expr { $$ = Expression::binary($1, "%%", $3); } | expr '&' expr { $$ = Expression::binary($1, "&", $3); } | expr '|' expr { $$ = Expression::binary($1, "|", $3); } | expr '^' expr { $$ = Expression::binary($1, "^", $3); } | expr LSHIFT expr { $$ = Expression::binary($1, "<<", $3); } | expr RSHIFT expr { $$ = Expression::binary($1, ">>", $3); } | '~' expr { $$ = Expression::unary("~", $2); } | '-' expr %prec UMINUS { $$ = Expression::unary("-", $2); } | '+' expr %prec UPLUS { $$ = Expression::unary("+", $2); } ; args : /* empty */ { $$ = new std::vector; } | expr { $$ = new std::vector; $$->push_back($1); } | args ',' expr { $$ = $1; $$->push_back($3); } ; type_qualifier : UNSIGNED { $$ = new Type::Qualifier(Type::Qualifier::UNSIGNED); } | SIGNED { $$ = new Type::Qualifier(Type::Qualifier::SIGNED); } | VOID { $$ = new Type::Qualifier(Type::Qualifier::VOID); } | '*' { $$ = new Type::Qualifier(Type::Qualifier::POINTER); } | CONST { $$ = new Type::Qualifier(Type::Qualifier::CONST); } | ID { $$ = new Type::Qualifier(Type::Qualifier::ID, $1); } | '<' type '>' { $$ = new Type::Qualifier(Type::Qualifier::GENERICS, $2); } | enum_key { $$ = new Type::Qualifier(Type::Qualifier::ENUM); } | struct_or_union { $$ = new Type::Qualifier($1); } ; struct_or_union : STRUCT { $$ = android::Type::Qualifier::STRUCT; } | UNION { $$ = android::Type::Qualifier::UNION; } ; arrays : /* empty */ { $$ = new std::vector; } | arrays array { $$ = $1; $$->push_back($2); } ; array : '[' ']' { $$ = Expression::atom(Expression::Type::UNKNOWN, " "); } | '[' expr ']' { $$ = $2; } ; %% c2hal/main.cpp0100644 0000000 0000000 00000012161 13521773237 012241 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include #include #include #include #include #include #include #include using namespace android; extern status_t parseFile(android::AST *ast); static void usage(const char *me) { fprintf(stderr, "usage: %s [-g] [-o dir] -p package (-r interface-root)+ (header-filepath)+\n", me); fprintf(stderr, " -h print this message\n"); fprintf(stderr, " -o output path\n"); fprintf(stderr, " (example: ~/android/master)\n"); fprintf(stderr, " -p package\n"); fprintf(stderr, " (example: android.hardware.baz@1.0)\n"); fprintf(stderr, " -g (enable open-gl mode) \n"); fprintf(stderr, " -r package:path root " "(e.g., android.hardware:hardware/interfaces)\n"); } static void addPackageRootToMap(const std::string &val, std::map &packageRootPaths) { auto index = val.find_first_of(':'); CHECK(index != std::string::npos); auto package = val.substr(0, index); auto path = val.substr(index + 1); packageRootPaths[package] = path; } static bool isPathPrefix(const std::string &prefix, const std::string &base) { if (prefix.size() >= base.size()) { LOG(DEBUG) << "Not long enough"; return false; } if (base[prefix.size()] != '.') { LOG(DEBUG) << "not full"; return false; } return prefix == base.substr(0, prefix.size()); } static void applyPackageRootPath( const std::map &packageRootPaths, const std::string &package, std::string &outputPath) { auto index = package.find_first_of('@'); CHECK(index != std::string::npos); auto packagePath = package.substr(0, index); auto packageVersion = package.substr(index + 1); for (auto const& pair : packageRootPaths) { const std::string& rootPackage = pair.first; const std::string& rootPath = pair.second; if (isPathPrefix(rootPackage, packagePath)) { packagePath = packagePath.substr(rootPackage.size() + 1); std::replace(packagePath.begin(), packagePath.end(), '.', '/'); packagePath += '/' + packageVersion; if (outputPath.empty()) { outputPath = rootPath; } outputPath += '/' + packagePath + '/'; return; } } CHECK(!outputPath.empty()) << "No package root path provided for: " << package; outputPath += '/'; } int main(int argc, char **argv) { const char *me = argv[0]; std::string outputDir; std::string package; std::map packageRootPaths; bool isOpenGl = false; bool verbose = false; int res; while ((res = getopt(argc, argv, "ghvo:p:r:")) >= 0) { switch (res) { case 'o': { outputDir = optarg; break; } case 'p': { package = optarg; break; } case 'g': { isOpenGl = true; break; } case 'v': { verbose = true; break; } case 'r': { addPackageRootToMap(optarg, packageRootPaths); break; } case 'h': default: { usage(me); exit(1); break; } } } // if no arguments are provided, show usage instead of specific errors if (optind == 1) { usage(me); exit(0); } if (verbose) { SetMinimumLogSeverity(android::base::VERBOSE); } applyPackageRootPath(packageRootPaths, package, outputDir); if (package.empty()) { LOG(WARNING) << "You must provide a package."; usage(me); exit(0); } if (optind == argc) { LOG(WARNING) << "You must provide a header-filepath."; usage(me); exit(0); } for(int i = optind; i < argc; i++) { std::string path = argv[i]; LOG(DEBUG) << "Processing " << path; AST ast(path, outputDir, package, isOpenGl); int res = parseFile(&ast); if (res != 0) { LOG(ERROR) << "Could not parse: " << res; exit(1); } ast.processContents(); ast.generateCode(); } return 0; } c2hal/test/0040755 0000000 0000000 00000000000 13521773237 011572 5ustar000000000 0000000 c2hal/test/Android.bp0100644 0000000 0000000 00000005413 13521773237 013475 0ustar000000000 0000000 // 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. // genrule { name: "c2hal_test_genc++_headers", tools: [ "c2hal", "hidl-gen", ], cmd: "$(location c2hal) -o $(genDir)/c2hal_test/1.0 " + " -p android.hardware.c2hal_test@1.0 $(in) && " + "$(location hidl-gen) -o $(genDir) -Lc++ " + " -r android.hardware:$(genDir) " + " -r android.hidl:system/libhidl/transport " + " android.hardware.c2hal_test@1.0", srcs: [ "simple.h", ], out: [ "android/hardware/c2hal_test/1.0/BnHwSimple.h", "android/hardware/c2hal_test/1.0/BpHwSimple.h", "android/hardware/c2hal_test/1.0/BsSimple.h", "android/hardware/c2hal_test/1.0/IHwSimple.h", "android/hardware/c2hal_test/1.0/ISimple.h", "android/hardware/c2hal_test/1.0/BnHwSimpleLocation.h", "android/hardware/c2hal_test/1.0/BpHwSimpleLocation.h", "android/hardware/c2hal_test/1.0/BsSimpleLocation.h", "android/hardware/c2hal_test/1.0/IHwSimpleLocation.h", "android/hardware/c2hal_test/1.0/ISimpleLocation.h", "android/hardware/c2hal_test/1.0/types.h", "android/hardware/c2hal_test/1.0/hwtypes.h", ], } genrule { name: "c2hal_test_genc++", tools: [ "c2hal", "hidl-gen", ], cmd: "$(location c2hal) -o $(genDir)/c2hal_test/1.0 " + " -p android.hardware.c2hal_test@1.0 $(in) && " + "$(location hidl-gen) -o $(genDir) -Lc++ " + " -r android.hardware:$(genDir) " + " -r android.hidl:system/libhidl/transport " + " android.hardware.c2hal_test@1.0", srcs: [ "simple.h", ], out: [ "android/hardware/c2hal_test/1.0/SimpleAll.cpp", "android/hardware/c2hal_test/1.0/types.cpp", ], } cc_test_library { name: "c2hal_test", defaults: ["hidl-module-defaults"], generated_headers: ["c2hal_test_genc++_headers"], generated_sources: ["c2hal_test_genc++"], export_generated_headers: ["c2hal_test_genc++_headers"], shared_libs: [ "libhidlbase", "libhidltransport", "libhwbinder", "liblog", "libutils", "libcutils", ], gtest: false, } c2hal/test/build_all.py0100644 0000000 0000000 00000004276 13521773237 014101 0ustar000000000 0000000 #!/usr/bin/env python3 # # 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. from os import listdir from os.path import isfile, join as path_join from subprocess import call import argparse def main(): """this python program tries to build all hardware interfaces from a directory""" args = parseArgs() path = args.path is_open_gl = args.g success, failure = genFiles(path, is_open_gl) print("Success: ", ", ".join(success)) print("Failure: ", ", ".join(failure)) ratio = len(success) / (len(success) + len(failure)) print("%% success = %.2f" % (100 * ratio)) def parseArgs(): parser = argparse.ArgumentParser() parser.add_argument("path", help="location of headers to parse", type=str) parser.add_argument("-g", help="enable opengl specific parsing", action="store_true") return parser.parse_args() def genFiles(path, is_open_gl): success = [] failure = [] for header in sorted(headers(path)): fname = header[:-2] command = ["c2hal", "-r", "android.hardware:hardware/interfaces", "-p", "android.hardware." + fname + "@1.0"] if is_open_gl: command += ["-g"] command += [path_join(path, header)] res = call(command) if res == 0: success += [header] else: failure += [header] return success, failure def headers(path): """all .h files in a directory""" for item in listdir(path): if not isfile(path_join(path, item)): continue if not item.endswith(".h"): continue yield item if __name__ == "__main__": main()c2hal/test/simple.h0100644 0000000 0000000 00000004043 13521773237 013232 0ustar000000000 0000000 /* * 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. */ #ifndef SIMPLE_H #define SIMPLE_H #include #include #include #include #include __BEGIN_DECLS #define FORGROUND_COLOR "#133742" #define ACTOR_COLOR "#424242" /* Simple example */ typedef struct simple_t { /* * Common methods of the simple device. */ struct hw_device_t common; /* resolution of the framebuffer's display panel in pixel per inch*/ const float xdpi; const float ydpi; /* framebuffer's display panel refresh rate in frames per second */ const float fps; int (*setSwapInterval)(struct simple_t* window, int interval); /* * This hook is OPTIONAL. */ int (*setUpdateRect)(struct simple_t* window, int left, int top, int width, int height); } simple_t; /* Holds pixel coordinates */ typedef struct { int px; int py; /* * If non NULL it will be caused by SurfaceFlinger on dumpsys */ void (*doDump)(int foo, char *buff, int buff_len); } simple_location_t; /* convenience API for coloring */ static inline int showColor(const struct hw_module_t* module, struct simple_t** device) { return module->methods->open(module, FORGROUND_COLOR, (struct simple_t**)device); } static inline int hideColor(struct simple_t* device) { return device->common.close(&device->common); } __END_DECLS #endif // SIMPLE_H c2hal/test/test.h0100644 0000000 0000000 00000025253 13521773237 012726 0ustar000000000 0000000 /* * 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. */ #ifndef ANDROID_FB_INTERFACE_H #define ANDROID_FB_INTERFACE_H #include #include #include "mylib.h" __BEGIN_DECLS // comments #define MY_DEFINE 1 \ + 1 #define META(thing1, thing2) thing1 + thing2 #define VERSION HARDWARE_MODULE_API_VERSION(0, 1) #define ONE 1 /* got to get rid of magic numbers */ /* test */ /* test **/ /* test / ** ** / test */ /* test //// ***** test /****/ #define a 1l #define b 1l + 2ll #define c 1ul + 1l #define d 2 + 1l #define e 3 + 1ll #define f 4 + 3ul #define g 1l + 3 #define h 32u #define i 64ull #define j 2 + a #define k 1u #define l k + 1l /*****************************************************************************/ typedef enum { A = 47, /* B is a very important value */ B, #ifdef UNHAPPY C = 1 + test(19) + test2[21], #endif D = 1 ? 1 : 2 } onehere; inline std::string to_string(T value) { return to_string(static_cast(value)); } const res_t RESULT_ACCESS_DENIED = ~2 | -1; const res_t RESULT_INVALID_PARAMETER = 54; #ifdef __cplusplus extern "C" { #endif static void fun1() { } typedef int my_int_type; typedef my_int_type my_type_two; namespace MyNamespace { static void fun1() { } static void fun2() { } } #ifdef __cplusplus } #endif static void fun3() { test; } static void fun4() { test; } #undef ONE typedef void (*no_arg_fun)(void); typedef int (*other_fun)(int j); typedef void (*alarm_cb)(void *data); typedef void (*special_types)(const native_handle_t* a, int b); typedef foo_t bar_t; struct baz_t; typedef pthread_t (* fun_with_funs)(void (*my_fun)(void *), void* arg); int (*global_fun_1)(struct framebuffer_device_t* dev, int enable); 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; typedef enum another_here { A = 3 | 4, B, C = 4 } another_here; /* anon struct */ struct { float b; }; struct not_type_defd { double latitude[]; double halfLongitude; }; char here; /* flags describing some attributes of the framebuffer */ const uint32_t flags; /* dimensions of the framebuffer in pixels */ const uint32_t width; const uint32_t height; /* frambuffer stride in pixels */ const int stride; /* framebuffer pixel format */ const int format_type; /* resolution of the framebuffer's display panel in pixel per inch*/ const float xdpi; const float ydpi; /* framebuffer's display panel refresh rate in frames per second */ const float fps; /* min swap interval supported by this framebuffer */ const int minSwapInterval; /* max swap interval supported by this framebuffer */ const int maxSwapInterval; /* Number of framebuffers supported*/ const int numFramebuffers; int reserved[7]; /* * requests a specific swap-interval (same definition than EGL) * * Returns 0 on success or -errno on error. */ int (*setSwapInterval)(struct framebuffer_device_t* window, int interval); /* * This hook is OPTIONAL. * * It is non NULL If the framebuffer driver supports "update-on-demand" * and the given rectangle is the area of the screen that gets * updated during (*post)(). * * This is useful on devices that are able to DMA only a portion of * the screen to the display panel, upon demand -- as opposed to * constantly refreshing the panel 60 times per second, for instance. * * Only the area defined by this rectangle is guaranteed to be valid, that * is, the driver is not allowed to post anything outside of this * rectangle. * * The rectangle evaluated during (*post)() and specifies which area * of the buffer passed in (*post)() shall to be posted. * * return -EINVAL if width or height <=0, or if left or top < 0 */ int (*setUpdateRect)(struct framebuffer_device_t* window, int left, int top, int width, int height); /* * Post to the display (display it on the screen) * The buffer must have been allocated with the * GRALLOC_USAGE_HW_FB usage flag. * buffer must be the same width and height as the display and must NOT * be locked. * * The buffer is shown during the next VSYNC. * * If the same buffer is posted again (possibly after some other buffer), * post() will block until the the first post is completed. * * Internally, post() is expected to lock the buffer so that a * subsequent call to gralloc_module_t::(*lock)() with USAGE_RENDER or * USAGE_*_WRITE will block until it is safe; that is typically once this * buffer is shown and another buffer has been posted. * * Returns 0 on success or -errno on error. */ int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer); /* * The (*compositionComplete)() method must be called after the * compositor has finished issuing GL commands for client buffers. */ int (*compositionComplete)(struct framebuffer_device_t* dev); /* * This hook is OPTIONAL. * * If non NULL it will be caused by SurfaceFlinger on dumpsys */ void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len); /* * (*enableScreen)() is used to either blank (enable=0) or * unblank (enable=1) the screen this framebuffer is attached to. * * Returns 0 on success or -errno on error. */ int (*enableScreen)(struct framebuffer_device_t* dev, int enable); void* reserved_proc[6]; } framebuffer_device_t; typedef int context_hub_callback(uint32_t hub_id, const struct hub_message_t *rxed_msg, void *cookie); typedef struct my_other_t { int a; int b[]; int c[3]; int d[][]; int e[3][]; int f[3][5]; int g[4+4][6 * 6][]; int h[1][2][][3][4][5][6][7][8]; unsigned int i; unsigned int8_t j; unsigned int16_t k; unsigned int32_t l; unsigned int64_t m; unsigned int32_t * n; const unsigned int32_t *** o; unsigned p; short q; long r; unsigned short s; unsigned long t; unsigned char u; char v; int (*store_meta_data_in_buffers)(struct camera_device *, int enable); typedef void (*scan_result_callback)(FooFooBarFoo* bda, int rssi, vector adv_data); pthread_t (* gps_create_thread)(const char* name, void (*start)(void *), void* arg); int (*p1)(struct framebuffer_device_t* dev); void (*p2)(struct framebuffer_device_t* dev, char *buff, int buff_len); int (*p3)(struct framebuffer_device_t* dev, int enable[3][4][5]); int (*get_supported_activities_list)(struct activity_recognition_module* module, char const* const* *activity_list); int (*read_energy_info)(); void (*reserved_procs[16 - 4])(void); } my_other_t; #define another 4 typedef struct { /** set to sizeof(GpsCallbacks_v1) */ size_t size; myWierdSize mySize; wchar_t MyWideChar; gps_location_callback location_cb; gps_status_callback status_cb; gps_sv_status_callback sv_status_cb; gps_nmea_callback nmea_cb; gps_set_capabilities set_capabilities_cb; gps_acquire_wakelock acquire_wakelock_cb; gps_release_wakelock release_wakelock_cb; gps_create_thread create_thread_cb; gps_request_utc_time request_utc_time_cb; } __attribute__((packed)) GpsCallbacks_v1; typedef struct one_name { float a; } another_name; typedef struct this_t { int hello; } this_t; typedef union that_t { float a; 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) { size_t chan_samp_sz; audio_format_t format = s->common.get_format(&s->common); if (audio_has_proportional_frames(format)) { chan_samp_sz = audio_bytes_per_sample(format); return audio_channel_count_from_out_mask(s->common.get_channels(&s->common)) * chan_samp_sz; } return sizeof(int8_t); } /* effective and commanding */ enum effect_command_e { EFFECT_CMD_INIT, // initialize effect engine EFFECT_CMD_SET_CONFIG, // configure effect engine (see effect_config_t) EFFECT_CMD_RESET, // reset effect engine EFFECT_CMD_ENABLE, // enable effect process EFFECT_CMD_DISABLE, // disable effect process EFFECT_CMD_SET_PARAM, // set parameter immediately (see effect_param_t) EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred EFFECT_CMD_GET_PARAM, // get parameter EFFECT_CMD_SET_DEVICE, // set audio device (see audio.h, audio_devices_t) EFFECT_CMD_SET_VOLUME, // set volume EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...) EFFECT_CMD_SET_CONFIG_REVERSE, // configure effect engine reverse stream(see effect_config_t) EFFECT_CMD_SET_INPUT_DEVICE, // set capture device (see audio.h, audio_devices_t) EFFECT_CMD_GET_CONFIG, // read effect engine configuration EFFECT_CMD_GET_CONFIG_REVERSE, // read configure effect engine reverse stream configuration EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature. EFFECT_CMD_GET_FEATURE_CONFIG, // get current feature configuration EFFECT_CMD_SET_FEATURE_CONFIG, // set current feature configuration EFFECT_CMD_SET_AUDIO_SOURCE, // set the audio source (see audio.h, audio_source_t) EFFECT_CMD_OFFLOAD, // set if effect thread is an offload one, // send the ioHandle of the effect thread EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code }; namespace myspace { enum class enum_class : int32_t { great, }; } // namespace myspace enum struct enum_struct { great, }; __END_DECLS #endif docs/0040755 0000000 0000000 00000000000 13521773237 010552 5ustar000000000 0000000 docs/Android.bp0100644 0000000 0000000 00000005102 13521773237 012450 0ustar000000000 0000000 // 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", ], } docs/README.md0100644 0000000 0000000 00000001600 13521773237 012023 0ustar000000000 0000000 # 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. docs/etc/0040755 0000000 0000000 00000000000 13521773237 011325 5ustar000000000 0000000 docs/etc/manifest.txt0100644 0000000 0000000 00000000022 13521773237 013663 0ustar000000000 0000000 Main-Class: MainKtdocs/resources/0040755 0000000 0000000 00000000000 13521773237 012564 5ustar000000000 0000000 docs/resources/assets/0040755 0000000 0000000 00000000000 13521773237 014066 5ustar000000000 0000000 docs/resources/assets/style.css0100644 0000000 0000000 00000000324 13521773237 015734 0ustar000000000 0000000 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; }docs/resources/template/0040755 0000000 0000000 00000000000 13521773237 014377 5ustar000000000 0000000 docs/resources/template/index.html0100644 0000000 0000000 00000002162 13521773237 016372 0ustar000000000 0000000 $title | HIDL reference

HIDL Reference

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 HIDL guides.

Interfaces and types

$entries
docs/resources/template/interface.html0100644 0000000 0000000 00000001105 13521773237 017217 0ustar000000000 0000000 $name | HIDL reference $header $propertyDefs $methodDefs docs/resources/template/types.html0100644 0000000 0000000 00000001071 13521773237 016425 0ustar000000000 0000000 $name | HIDL reference $header $propertyDefs docs/src/0040755 0000000 0000000 00000000000 13521773237 011341 5ustar000000000 0000000 docs/src/lexer/0040755 0000000 0000000 00000000000 13521773237 012460 5ustar000000000 0000000 docs/src/lexer/DocLexer.kt0100644 0000000 0000000 00000005527 13521773237 014533 0ustar000000000 0000000 /* * 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 { val tokens = mutableListOf() 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() } }docs/src/lexer/HidlLexer.kt0100644 0000000 0000000 00000011643 13521773237 014702 0ustar000000000 0000000 /* * 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 { val tokens = mutableListOf() //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() } }docs/src/lexer/ILexer.kt0100644 0000000 0000000 00000006713 13521773237 014214 0ustar000000000 0000000 /* * 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 fun tokenize(file: File): List { 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 } } }docs/src/lexer/Token.kt0100644 0000000 0000000 00000015700 13521773237 014100 0ustar000000000 0000000 /* * 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 { return TokenGrammar.values().filter { it.category == TokenCategory.Annotation } } fun docAnnotations(): List { return TokenGrammar.values().filter { it.category == TokenCategory.DocAnnotation } } } }docs/src/main.kt0100644 0000000 0000000 00000005224 13521773237 012625 0ustar000000000 0000000 /* * 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) { 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): Pair { 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) }docs/src/parser/0040755 0000000 0000000 00000000000 13521773237 012635 5ustar000000000 0000000 docs/src/parser/config.kt0100644 0000000 0000000 00000011725 13521773237 014445 0ustar000000000 0000000 /* * 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() 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) { if (args.isEmpty()) { printUsage() exitProcess(1) } val dirPathArgs = mutableListOf() val filePathArgs = mutableListOf() val excludedPathArgs = mutableListOf() 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() //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, ext: String, excludedPaths: List) { 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"))) } }docs/src/parser/elements/0040755 0000000 0000000 00000000000 13521773237 014451 5ustar000000000 0000000 docs/src/parser/elements/AbstractParser.kt0100644 0000000 0000000 00000010042 13521773237 017723 0ustar000000000 0000000 /* * 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) { 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) /** * Determine end of token sequence, collect tokens from here to there. */ abstract fun scanTokens(iter: ListIterator): List protected fun scanDocTokens(iter: ListIterator): List { val tokens = mutableListOf() 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): List { val tokens = mutableListOf() 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) { while (iter.hasPrevious() && iter.previousIndex() >= indexStart) { iter.previous() } assert(iter.nextIndex() == this.indexStart) } }docs/src/parser/elements/AnnotationParser.kt0100644 0000000 0000000 00000004332 13521773237 020277 0ustar000000000 0000000 /* * 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, 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): List { val tokens = mutableListOf() //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) { 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() ?: "" } }docs/src/parser/elements/DocAnnotationParser.kt0100644 0000000 0000000 00000007770 13521773237 020736 0ustar000000000 0000000 /* * 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, 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): 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): List { val tokens = mutableListOf() 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) { 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() 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) } } } }docs/src/parser/elements/DocParser.kt0100644 0000000 0000000 00000005061 13521773237 016672 0ustar000000000 0000000 /* * 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, var shouldResetIterator: Boolean = false) : AbstractParser(iter) { val description: String by lazy { formatDescription(this.descTokens) } var docAnnotationParsers = mutableListOf() private var descTokens = mutableListOf() init { parseTokens(scanTokens(iter)) if (shouldResetIterator) resetIterator(iter) } private fun formatDescription(tokens: List): 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): List { //keep doc_start and doc_end tokens /** ... */ return scanDocTokens(iter) } override fun parseTokens(tokens: List) { 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) } } } }docs/src/parser/elements/EntryCollectionParser.kt0100644 0000000 0000000 00000004527 13521773237 021310 0ustar000000000 0000000 /* * 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, var shouldResetIterator: Boolean = false) : AbstractParser(iter) { var entryParsers = mutableListOf() constructor(tokens: List) : this(tokens.listIterator()) init { parseTokens(scanTokens(iter)) if (shouldResetIterator) this.resetIterator(iter) } //use all the tokens override fun scanTokens(iter: ListIterator): List { val tokens = mutableListOf() while (iter.hasNext()) { tokens.add(iter.next()) } return tokens } override fun parseTokens(tokens: List) { 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) } } } } } }docs/src/parser/elements/EntryParser.kt0100644 0000000 0000000 00000014366 13521773237 017276 0ustar000000000 0000000 /* * 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, var shouldResetIterator: Boolean = false) : AbstractParser(iter) { //doc description, summary, and doc annottions lateinit var docParser: DocParser //annotation val annotationParsers = mutableListOf() //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): List { //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() //collect doc block /** ... */ tokens += scanDocTokens(iter) //collect annotations and declaration, nested to semicolon tokens += scanDeclarationTokens(iter) return tokens } override fun parseTokens(tokens: List) { 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) } } }docs/src/parser/elements/declarations/0040755 0000000 0000000 00000000000 13521773237 017121 5ustar000000000 0000000 docs/src/parser/elements/declarations/AbstractDeclarationParser.kt0100644 0000000 0000000 00000011044 13521773237 024544 0ustar000000000 0000000 /* * 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) : 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): List { 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. * Ignore nested lists using same open/close delimiter. * For method param lists, enum members, struct members, etc. */ protected fun scanDelimitedList(iter: ListIterator, delimiter: TokenGrammar = TokenGrammar.COMMA, openDelimiter: TokenGrammar = TokenGrammar.PAREN_OPEN, closeDelimiter: TokenGrammar = TokenGrammar.PAREN_CLOSE): List> { val allFields = mutableListOf>() //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() 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 } }docs/src/parser/elements/declarations/CompoundDeclarationParser.kt0100644 0000000 0000000 00000013021 13521773237 024562 0ustar000000000 0000000 /* * 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, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) { lateinit var type: TokenGrammar lateinit override var name: String var members = mutableListOf() //defined below init { parseTokens(scanTokens(iter)) if (shouldResetIterator) resetIterator(iter) } override fun parseTokens(tokens: List) { 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 //TODO: doesn't seem needed } class MemberDeclaration(override val type: String, override val name: String, override val docParser: DocParser?, override val tokens: List) : IMemberDeclaration class CompoundMemberDeclaration(override val type: String, override val name: String, override val docParser: DocParser?, override val tokens: List, val typeDef: TokenGrammar) : IMemberDeclaration docs/src/parser/elements/declarations/EnumDeclarationParser.kt0100644 0000000 0000000 00000010006 13521773237 023702 0ustar000000000 0000000 /* * 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, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) { lateinit override var name: String lateinit var type: String var members = mutableListOf() init { parseTokens(scanTokens(iter)) if (shouldResetIterator) resetIterator(iter) } override fun parseTokens(tokens: List) { 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) { 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(" ") } } }docs/src/parser/elements/declarations/InterfaceDeclarationParser.kt0100644 0000000 0000000 00000003731 13521773237 024705 0ustar000000000 0000000 /* * 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, 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) { 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() } } }docs/src/parser/elements/declarations/MethodDeclarationParser.kt0100644 0000000 0000000 00000006263 13521773237 024230 0ustar000000000 0000000 /* * 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, var shouldResetIterator: Boolean = false) : AbstractDeclarationParser(iter) { lateinit override var name: String var prefix: String? = null var params = mutableListOf() var returns = mutableListOf() init { parseTokens(scanTokens(iter)) if (shouldResetIterator) resetIterator(iter) } // [prefix] ( [typedef] [name], ...) [generates] ( [typedef], [name], ...); override fun parseTokens(tokens: List) { 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, list: MutableList) { val argLists = this.scanDelimitedList(iter) //return list of list for (argList in argLists) { list.add(formatArgEntry(argList)) //use tokens to create ArgEntry } } private fun formatArgEntry(paramTokens: List): ArgEntry { val type = paramTokens.takeWhile { it != paramTokens.last() } //but-last .map { it.value } .joinToString("") val name = paramTokens.last().value return ArgEntry(type, name) } }docs/src/parser/elements/declarations/TypedefDeclarationParser.kt0100644 0000000 0000000 00000002634 13521773237 024406 0ustar000000000 0000000 /* * 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, 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) { 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("") } }docs/src/parser/files/0040755 0000000 0000000 00000000000 13521773237 013737 5ustar000000000 0000000 docs/src/parser/files/AbstractFileParser.kt0100644 0000000 0000000 00000012025 13521773237 020014 0ustar000000000 0000000 /* * 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) { 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 by lazy { assert(tokens.isNotEmpty()) //add empty docblocks EntryCollectionParser(insertDocsForRequiredTypes(tokens)).entryParsers } val enums: List by lazy { getEntriesByDeclarationParser() } val typedefs: List by lazy { getEntriesByDeclarationParser() } val structs: List by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.STRUCT) } val unions: List by lazy { getEntriesByCompoundDeclarationParser(TokenGrammar.UNION) } protected inline fun getEntriesByDeclarationParser(): List { return entries.filter { it.declarationParser is T } } private fun getEntriesByCompoundDeclarationParser(identifier: TokenGrammar): List { return getEntriesByDeclarationParser() .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): List { val tokensCopy = mutableListOf() 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: if (peekToken(iter)?.identifier == TokenGrammar.CHEVRON_CLOSE) { continue } val idx = indexInsertionPointforDocTokens(tokensCopy) if (idx != -1) { val removedTokens = mutableListOf() 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): 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) } }docs/src/parser/files/InterfaceFileParser.kt0100644 0000000 0000000 00000006041 13521773237 020152 0ustar000000000 0000000 /* * 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) : 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 get() = interfaceEntry.annotationParsers //name, value val methods: List by lazy { getEntriesByDeclarationParser() } companion object { /** * Searches tokens for an interface identifier. * Maybe not the most accurate measurement, but good enough. */ fun isInterface(tokens: List): 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 } } }docs/src/parser/files/TypesFileParser.kt0100644 0000000 0000000 00000001402 13521773237 017352 0ustar000000000 0000000 /* * 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 class TypesFileParser(tokens: List) : AbstractFileParser(tokens) { override val name = "types" }docs/src/parser/files/package.kt0100644 0000000 0000000 00000004261 13521773237 015672 0ustar000000000 0000000 /* * 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): PackageInfo { val iter: ListIterator = tokens.listIterator() var token: Token while (iter.hasNext()) { token = iter.next() if (token.identifier == TokenGrammar.PACKAGE) { //collect namespace val pkgNameToks = mutableListOf() 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) }docs/src/parser/utils.kt0100644 0000000 0000000 00000002170 13521773237 014332 0ustar000000000 0000000 /* * 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? { if (iter.hasNext()) { val token = iter.next() iter.previous() return token } else { return null } } fun peekPreviousToken(iter: ListIterator): Token? { if (iter.hasPrevious()) { val token = iter.previous() iter.next() return token } else { return null } }docs/src/writer/0040755 0000000 0000000 00000000000 13521773237 012655 5ustar000000000 0000000 docs/src/writer/elements/0040755 0000000 0000000 00000000000 13521773237 014471 5ustar000000000 0000000 docs/src/writer/elements/AbstractElement.kt0100644 0000000 0000000 00000004644 13521773237 020113 0ustar000000000 0000000 /* * 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 """

${declarationParser.name}

${declaration()}
${formatTextasHTML(docParser.description)}
${detailsRows()} ${annotationRows()}
Details
""".trim() } private fun annotationRows(): String { val sb = StringBuilder() if (annotationParsers.isNotEmpty()) { sb.append(""" Annotations """) //AnnotationParser => name:TokenGrammar, value:String annotationParsers.forEach { arg -> sb.append(""" """) } sb.append("""
${arg.name.value}
${htmlEscape(arg.value)}
""") } return sb.toString().trim() } }docs/src/writer/elements/CompoundElement.kt0100644 0000000 0000000 00000005023 13521773237 020124 0ustar000000000 0000000 /* * 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(""" Members """) //type, name, tokens, typedef declarationParser.members.forEach { arg -> val fieldDesc = arg.docParser?.description?.let { formatTextasHTML(it, useParagraphs = false) } ?: "" sb.append(""" """) } sb.append("""
${arg.name}
${fieldDesc}
""") } return sb.toString() } }docs/src/writer/elements/EnumElement.kt0100644 0000000 0000000 00000004303 13521773237 017244 0ustar000000000 0000000 /* * 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(""" Members """) //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(""" """) } sb.append("""
${htmlEscape(arg.name)}${fieldVal}
$fieldDesc
""") } return sb.toString().trim() } }docs/src/writer/elements/MethodElement.kt0100644 0000000 0000000 00000011770 13521773237 017566 0ustar000000000 0000000 /* * 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(""" RPC mode
${declarationParser.prefix}
""") } return sb.toString() } private fun buildParamRows(): String { //docParser.docAnnotationParsers //=> tag [TokenGrammer], arg?, description val sb = StringBuilder() if (declarationParser.params.isNotEmpty()) { sb.append(""" Parameters """) declarationParser.params.forEach { arg -> sb.append(""" """) } sb.append("""
${htmlEscape(arg.name)}
${getDocAnnotationDesc(TokenGrammar.PARAM, arg)}
""") } return sb.toString() } private fun buildReturnRows(): String { val sb = StringBuilder() if (declarationParser.returns.isNotEmpty()) { sb.append(""" Generates """) declarationParser.returns.forEach { arg -> sb.append(""" """) } sb.append("""
${htmlEscape(arg.name)}
${getDocAnnotationDesc(TokenGrammar.RETURN, arg)}
""") } return sb.toString() } }docs/src/writer/elements/TypedefElement.kt0100644 0000000 0000000 00000002560 13521773237 017743 0ustar000000000 0000000 /* * 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 """

${declarationParser.name}

${declaration()}
${formatTextasHTML(docParser.description)}
""".trim() } }docs/src/writer/files/0040755 0000000 0000000 00000000000 13521773237 013757 5ustar000000000 0000000 docs/src/writer/files/AbstractFileWriter.kt0100644 0000000 0000000 00000004105 13521773237 020054 0ustar000000000 0000000 /* * 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}") } }docs/src/writer/files/AbstractParserFileWriter.kt0100644 0000000 0000000 00000005152 13521773237 021234 0ustar000000000 0000000 /* * 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("
\n") sb.append("

Properties

\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
\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}") } }docs/src/writer/files/IndexFileWriter.kt0100644 0000000 0000000 00000013216 13521773237 017363 0ustar000000000 0000000 /* * 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() 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 """ ${entries.map { buildClassEntry(it) }.joinToString("\n")}
Entry Version Summary
""".trim() } private fun buildClassEntry(entry: EntryData): String { return """ ${entry.fullName} ${entry.packageVersion} ${entry.summary} """.trim() } private class EntryNameComparator : Comparator { 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 { 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> { val pkgEntries = mutableMapOf>() 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() } }docs/src/writer/files/InterfaceFileWriter.kt0100644 0000000 0000000 00000004640 13521773237 020215 0ustar000000000 0000000 /* * 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("
\n") sb.append("

Methods

\n") sb.append(parser.methods.map { MethodElement(it).toHTML() }.joinToString("\n")) sb.append("\n
\n") } sb.toString() } } private fun buildHeader(): String { return """
Package: ${parser.packageName}@${parser.packageVersion}

${parser.name}

${buildDeclaration()} ${formatTextasHTML(parser.description)}
""".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}") } }docs/src/writer/files/TypesFileWriter.kt0100644 0000000 0000000 00000002247 13521773237 017422 0ustar000000000 0000000 /* * 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 """
Package: ${parser.packageName}@${parser.packageVersion}

${parser.name}

""".trim() } }docs/src/writer/files/resources.kt0100644 0000000 0000000 00000003523 13521773237 016331 0ustar000000000 0000000 /* * 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() //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 } } }docs/src/writer/formatutils.kt0100644 0000000 0000000 00000005422 13521773237 015566 0ustar000000000 0000000 /* * 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): 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("

") //match and replace empty lines val replaceText = if (useParagraphs) "

\n

" else "
\n" sb.append(htmlEscape(string.trim()).replace(Regex("\\s*\n\n\\s*"), replaceText)) if (useParagraphs) sb.append("

") 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") }generateCpp.cpp0100644 0000000 0000000 00000173236 13521773237 012574 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "Coordinator.h" #include "EnumType.h" #include "HidlTypeAssertion.h" #include "Interface.h" #include "Method.h" #include "Reference.h" #include "ScalarType.h" #include "Scope.h" #include #include #include #include #include #include namespace android { void AST::getPackageComponents( std::vector *components) const { mPackage.getPackageComponents(components); } void AST::getPackageAndVersionComponents( std::vector *components, bool cpp_compatible) const { mPackage.getPackageAndVersionComponents(components, cpp_compatible); } std::string AST::makeHeaderGuard(const std::string &baseName, bool indicateGenerated) const { std::string guard; if (indicateGenerated) { guard += "HIDL_GENERATED_"; } guard += StringHelper::Uppercase(mPackage.tokenName()); guard += "_"; guard += StringHelper::Uppercase(baseName); guard += "_H"; return guard; } void AST::generateCppPackageInclude( Formatter &out, const FQName &package, const std::string &klass) { out << "#include <"; std::vector components; package.getPackageAndVersionComponents(&components, false /* cpp_compatible */); for (const auto &component : components) { out << component << "/"; } out << klass << ".h>\n"; } void AST::enterLeaveNamespace(Formatter &out, bool enter) const { std::vector packageComponents; getPackageAndVersionComponents( &packageComponents, true /* cpp_compatible */); if (enter) { for (const auto &component : packageComponents) { out << "namespace " << component << " {\n"; } out.setNamespace(mPackage.cppNamespace() + "::"); } else { out.setNamespace(std::string()); for (auto it = packageComponents.rbegin(); it != packageComponents.rend(); ++it) { out << "} // namespace " << *it << "\n"; } } } static void declareGetService(Formatter &out, const std::string &interfaceName, bool isTry) { const std::string functionName = isTry ? "tryGetService" : "getService"; out << "static ::android::sp<" << interfaceName << "> " << functionName << "(" << "const std::string &serviceName=\"default\", bool getStub=false);\n"; out << "static ::android::sp<" << interfaceName << "> " << functionName << "(" << "const char serviceName[], bool getStub=false)" << " { std::string str(serviceName ? serviceName : \"\");" << " return " << functionName << "(str, getStub); }\n"; out << "static ::android::sp<" << interfaceName << "> " << functionName << "(" << "const ::android::hardware::hidl_string& serviceName, bool getStub=false)" // without c_str the std::string constructor is ambiguous << " { std::string str(serviceName.c_str());" << " return " << functionName << "(str, getStub); }\n"; out << "static ::android::sp<" << interfaceName << "> " << functionName << "(" << "bool getStub) { return " << functionName << "(\"default\", getStub); }\n"; } static void declareServiceManagerInteractions(Formatter &out, const std::string &interfaceName) { declareGetService(out, interfaceName, true /* isTry */); declareGetService(out, interfaceName, false /* isTry */); out << "__attribute__ ((warn_unused_result))" << "::android::status_t registerAsService(const std::string &serviceName=\"default\");\n"; out << "static bool registerForNotifications(\n"; out.indent(2, [&] { out << "const std::string &serviceName,\n" << "const ::android::sp<::android::hidl::manager::V1_0::IServiceNotification> " << "¬ification);\n"; }); } static void implementGetService(Formatter &out, const FQName &fqName, bool isTry) { const std::string interfaceName = fqName.getInterfaceName(); const std::string functionName = isTry ? "tryGetService" : "getService"; out << "// static\n" << "::android::sp<" << interfaceName << "> " << interfaceName << "::" << functionName << "(" << "const std::string &serviceName, const bool getStub) "; out.block([&] { out << "return ::android::hardware::details::getServiceInternal<" << fqName.getInterfaceProxyName() << ">(serviceName, " << (!isTry ? "true" : "false") // retry << ", getStub);\n"; }).endl().endl(); } static void implementServiceManagerInteractions(Formatter &out, const FQName &fqName, const std::string &package) { const std::string interfaceName = fqName.getInterfaceName(); implementGetService(out, fqName, true /* isTry */); implementGetService(out, fqName, false /* isTry */); out << "::android::status_t " << interfaceName << "::registerAsService(" << "const std::string &serviceName) "; out.block([&] { out << "::android::hardware::details::onRegistration(\"" << fqName.getPackageAndVersion().string() << "\", \"" << interfaceName << "\", serviceName);\n\n"; out << "const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm\n"; out.indent(2, [&] { out << "= ::android::hardware::defaultServiceManager();\n"; }); out.sIf("sm == nullptr", [&] { out << "return ::android::INVALID_OPERATION;\n"; }).endl(); out << "::android::hardware::Return ret = " << "sm->add(serviceName.c_str(), this);\n" << "return ret.isOk() && ret ? ::android::OK : ::android::UNKNOWN_ERROR;\n"; }).endl().endl(); out << "bool " << interfaceName << "::registerForNotifications(\n"; out.indent(2, [&] { out << "const std::string &serviceName,\n" << "const ::android::sp<::android::hidl::manager::V1_0::IServiceNotification> " << "¬ification) "; }); out.block([&] { out << "const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm\n"; out.indent(2, [&] { out << "= ::android::hardware::defaultServiceManager();\n"; }); out.sIf("sm == nullptr", [&] { out << "return false;\n"; }).endl(); out << "::android::hardware::Return success =\n"; out.indent(2, [&] { out << "sm->registerForNotifications(\"" << package << "::" << interfaceName << "\",\n"; out.indent(2, [&] { out << "serviceName, notification);\n"; }); }); out << "return success.isOk() && success;\n"; }).endl().endl(); } void AST::generateInterfaceHeader(Formatter& out) const { const Interface *iface = getInterface(); std::string ifaceName = iface ? iface->localName() : "types"; const std::string guard = makeHeaderGuard(ifaceName); out << "#ifndef " << guard << "\n"; out << "#define " << guard << "\n\n"; for (const auto &item : mImportedNames) { generateCppPackageInclude(out, item, item.name()); } if (!mImportedNames.empty()) { out << "\n"; } if (iface) { if (isIBase()) { out << "// skipped #include IServiceNotification.h\n\n"; } else { out << "#include \n\n"; } } out << "#include \n"; out << "#include \n"; if (iface) { out << "#include \n"; } out << "#include \n"; out << "#include \n\n"; /* for report_sysprop_change() */ enterLeaveNamespace(out, true /* enter */); out << "\n"; if (iface) { out << "struct " << ifaceName; const Interface *superType = iface->superType(); if (superType == NULL) { out << " : virtual public ::android::RefBase"; } else { out << " : public " << superType->fullName(); } out << " {\n"; out.indent(); generateCppTag(out, "android::hardware::details::i_tag"); } emitTypeDeclarations(out); if (iface) { out << "virtual bool isRemote() const "; if (!isIBase()) { out << "override "; } out << "{ return false; }\n\n"; for (const auto& tuple : iface->allMethodsFromRoot()) { const Method* method = tuple.method(); out << "\n"; const bool returnsValue = !method->results().empty(); const NamedReference* elidedReturn = method->canElideCallback(); if (elidedReturn == nullptr && returnsValue) { out << "using " << method->name() << "_cb = std::functionemitCppResultSignature(out, true /* specify namespaces */); out << ")>;\n"; } method->dumpAnnotations(out); method->emitDocComment(out); if (elidedReturn) { out << "virtual ::android::hardware::Return<"; out << elidedReturn->type().getCppResultType() << "> "; } else { out << "virtual ::android::hardware::Return "; } out << method->name() << "("; method->emitCppArgSignature(out, true /* specify namespaces */); out << ")"; if (method->isHidlReserved()) { if (!isIBase()) { out << " override"; } } else { out << " = 0"; } out << ";\n"; } out << "// cast static functions\n"; std::string childTypeResult = iface->getCppResultType(); for (const Interface *superType : iface->typeChain()) { out << "static ::android::hardware::Return<" << childTypeResult << "> castFrom(" << superType->getCppArgumentType() << " parent" << ", bool emitError = false);\n"; } out << "\nstatic const char* descriptor;\n\n"; if (isIBase()) { out << "// skipped getService, registerAsService, registerForNotifications\n\n"; } else { declareServiceManagerInteractions(out, iface->localName()); } } if (iface) { out.unindent(); out << "};\n\n"; } mRootScope.emitPackageTypeDeclarations(out); out << "\n"; enterLeaveNamespace(out, false /* enter */); mRootScope.emitGlobalTypeDeclarations(out); out << "\n#endif // " << guard << "\n"; } void AST::generateHwBinderHeader(Formatter& out) const { const Interface *iface = getInterface(); std::string klassName = iface ? iface->getHwName() : "hwtypes"; const std::string guard = makeHeaderGuard(klassName); out << "#ifndef " << guard << "\n"; out << "#define " << guard << "\n\n"; generateCppPackageInclude(out, mPackage, iface ? iface->localName() : "types"); out << "\n"; for (const auto &item : mImportedNames) { if (item.name() == "types") { generateCppPackageInclude(out, item, "hwtypes"); } else { generateCppPackageInclude(out, item, item.getInterfaceStubName()); generateCppPackageInclude(out, item, item.getInterfaceProxyName()); } } out << "\n"; out << "#include \n"; out << "#include \n"; out << "#include \n"; out << "\n"; enterLeaveNamespace(out, true /* enter */); mRootScope.emitPackageHwDeclarations(out); enterLeaveNamespace(out, false /* enter */); out << "\n#endif // " << guard << "\n"; } void AST::emitTypeDeclarations(Formatter& out) const { return mRootScope.emitTypeDeclarations(out); } static void wrapPassthroughArg(Formatter& out, const NamedReference* arg, bool addPrefixToName, std::function handleError) { if (!arg->type().isInterface()) { return; } std::string name = (addPrefixToName ? "_hidl_out_" : "") + arg->name(); std::string wrappedName = (addPrefixToName ? "_hidl_out_wrapped_" : "_hidl_wrapped_") + arg->name(); const Interface &iface = static_cast(arg->type()); out << iface.getCppStackType() << " " << wrappedName << ";\n"; // TODO(elsk): b/33754152 Should not wrap this if object is Bs* out.sIf(name + " != nullptr && !" + name + "->isRemote()", [&] { out << wrappedName << " = " << "::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. handleError(); }).endl(); }).sElse([&] { out << wrappedName << " = " << name << ";\n"; }).endl().endl(); } void AST::generatePassthroughMethod(Formatter& out, const Method* method) const { method->generateCppSignature(out); out << " {\n"; out.indent(); if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PASSTHROUGH)) { method->cppImpl(IMPL_PASSTHROUGH, out); out.unindent(); out << "}\n\n"; return; } const bool returnsValue = !method->results().empty(); const NamedReference* elidedReturn = method->canElideCallback(); if (returnsValue && elidedReturn == nullptr) { generateCheckNonNull(out, "_hidl_cb"); } generateCppInstrumentationCall( out, InstrumentationEvent::PASSTHROUGH_ENTRY, method); for (const auto &arg : method->args()) { wrapPassthroughArg(out, arg, false /* addPrefixToName */, [&] { out << "return ::android::hardware::Status::fromExceptionCode(\n"; out.indent(2, [&] { out << "::android::hardware::Status::EX_TRANSACTION_FAILED,\n" << "\"Cannot wrap passthrough interface.\");\n"; }); }); } out << "auto _hidl_error = ::android::hardware::Void();\n"; out << "auto _hidl_return = "; if (method->isOneway()) { out << "addOnewayTask([mImpl = this->mImpl\n" << "#ifdef __ANDROID_DEBUGGABLE__\n" ", mEnableInstrumentation = this->mEnableInstrumentation, " "mInstrumentationCallbacks = this->mInstrumentationCallbacks\n" << "#endif // __ANDROID_DEBUGGABLE__\n"; for (const auto &arg : method->args()) { out << ", " << (arg->type().isInterface() ? "_hidl_wrapped_" : "") << arg->name(); } out << "] {\n"; out.indent(); } out << "mImpl->" << method->name() << "("; out.join(method->args().begin(), method->args().end(), ", ", [&](const auto &arg) { out << (arg->type().isInterface() ? "_hidl_wrapped_" : "") << arg->name(); }); if (returnsValue && elidedReturn == nullptr) { // never true if oneway since oneway methods don't return values if (!method->args().empty()) { out << ", "; } out << "[&]("; out.join(method->results().begin(), method->results().end(), ", ", [&](const auto &arg) { out << "const auto &_hidl_out_" << arg->name(); }); out << ") {\n"; out.indent(); generateCppInstrumentationCall( out, InstrumentationEvent::PASSTHROUGH_EXIT, method); for (const auto &arg : method->results()) { wrapPassthroughArg(out, arg, true /* addPrefixToName */, [&] { out << "_hidl_error = ::android::hardware::Status::fromExceptionCode(\n"; out.indent(2, [&] { out << "::android::hardware::Status::EX_TRANSACTION_FAILED,\n" << "\"Cannot wrap passthrough interface.\");\n"; }); out << "return;\n"; }); } out << "_hidl_cb("; out.join(method->results().begin(), method->results().end(), ", ", [&](const auto &arg) { out << (arg->type().isInterface() ? "_hidl_out_wrapped_" : "_hidl_out_") << arg->name(); }); out << ");\n"; out.unindent(); out << "});\n\n"; } else { out << ");\n\n"; // used by generateCppInstrumentationCall if (elidedReturn != nullptr) { out << "#ifdef __ANDROID_DEBUGGABLE__\n" << elidedReturn->type().getCppResultType() << " _hidl_out_" << elidedReturn->name() << " = _hidl_return;\n" << "#endif // __ANDROID_DEBUGGABLE__\n"; } generateCppInstrumentationCall( out, InstrumentationEvent::PASSTHROUGH_EXIT, method); } if (method->isOneway()) { out.unindent(); out << "});\n"; } out << "return _hidl_return;\n"; out.unindent(); out << "}\n"; } void AST::generateMethods(Formatter& out, const MethodGenerator& gen, bool includeParent) const { const Interface* iface = mRootScope.getInterface(); const Interface *prevIterface = nullptr; for (const auto &tuple : iface->allMethodsFromRoot()) { const Method *method = tuple.method(); const Interface *superInterface = tuple.interface(); if (!includeParent && superInterface != iface) { continue; } if(prevIterface != superInterface) { if (prevIterface != nullptr) { out << "\n"; } out << "// Methods from " << superInterface->fullName() << " follow.\n"; prevIterface = superInterface; } gen(method, superInterface); } out << "\n"; } void AST::generateTemplatizationLink(Formatter& out) const { out << "typedef " << mRootScope.getInterface()->localName() << " Pure;\n\n"; } 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(); 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 */); out << "\n"; out << "struct " << klassName; if (iface->isIBase()) { out << " : public ::android::hardware::BHwBinder"; out << ", public ::android::hardware::details::HidlInstrumentor {\n"; } else { out << " : public " << gIBaseFqName.getInterfaceStubFqName().cppName() << " {\n"; } out.indent(); out << "explicit " << klassName << "(const ::android::sp<" << iface->localName() << "> &_hidl_impl);" << "\n"; out << "explicit " << klassName << "(const ::android::sp<" << iface->localName() << "> &_hidl_impl," << " const std::string& HidlInstrumentor_package," << " const std::string& HidlInstrumentor_interface);" << "\n\n"; out << "virtual ~" << klassName << "();\n\n"; out << "::android::status_t onTransact(\n"; out.indent(); out.indent(); out << "uint32_t _hidl_code,\n"; out << "const ::android::hardware::Parcel &_hidl_data,\n"; out << "::android::hardware::Parcel *_hidl_reply,\n"; out << "uint32_t _hidl_flags = 0,\n"; out << "TransactCallback _hidl_cb = nullptr) override;\n\n"; out.unindent(); out.unindent(); out.endl(); generateTemplatizationLink(out); generateCppTag(out, "android::hardware::details::bnhw_tag"); out << "::android::sp<" << iface->localName() << "> getImpl() { return _hidl_mImpl; }\n"; 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.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(); generateMethods(out, [&](const Method* method, const Interface* iface) { if (!method->isHidlReserved() || !method->overridesCppImpl(IMPL_STUB_IMPL)) { return; } const bool returnsValue = !method->results().empty(); const NamedReference* elidedReturn = method->canElideCallback(); if (elidedReturn == nullptr && returnsValue) { out << "using " << method->name() << "_cb = " << iface->fqName().cppName() << "::" << method->name() << "_cb;\n"; } method->generateCppSignature(out); out << ";\n"; }); out << "::android::sp<" << iface->localName() << "> _hidl_mImpl;\n"; out.unindent(); out << "};\n\n"; enterLeaveNamespace(out, false /* enter */); out << "\n#endif // " << guard << "\n"; } void AST::generateProxyHeader(Formatter& out) const { if (!AST::isInterface()) { // types.hal does not get a proxy header. return; } const Interface* iface = mRootScope.getInterface(); const std::string proxyName = iface->getProxyName(); const std::string guard = makeHeaderGuard(proxyName); out << "#ifndef " << guard << "\n"; out << "#define " << guard << "\n\n"; out << "#include \n\n"; std::vector packageComponents; getPackageAndVersionComponents( &packageComponents, false /* cpp_compatible */); generateCppPackageInclude(out, mPackage, iface->getHwName()); out << "\n"; enterLeaveNamespace(out, true /* enter */); out << "\n"; out << "struct " << proxyName << " : public ::android::hardware::BpInterface<" << iface->localName() << ">, public ::android::hardware::details::HidlInstrumentor {\n"; out.indent(); out << "explicit " << proxyName << "(const ::android::sp<::android::hardware::IBinder> &_hidl_impl);" << "\n\n"; generateTemplatizationLink(out); generateCppTag(out, "android::hardware::details::bphw_tag"); out << "virtual bool isRemote() const override { return true; }\n\n"; 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"; if (!method->hasEmptyCppArgSignature()) { out << ", "; } method->emitCppArgSignature(out); out << ");\n"; }, false /* include parents */); generateMethods(out, [&](const Method* method, const Interface*) { method->generateCppSignature(out); out << " override;\n"; }); out.unindent(); out << "private:\n"; out.indent(); out << "std::mutex _hidl_mMutex;\n" << "std::vector<::android::sp<::android::hardware::hidl_binder_death_recipient>>" << " _hidl_mDeathRecipients;\n"; out.unindent(); out << "};\n\n"; enterLeaveNamespace(out, false /* enter */); out << "\n#endif // " << guard << "\n"; } void AST::generateCppSource(Formatter& out) const { std::string baseName = getBaseName(); const Interface *iface = getInterface(); const std::string klassName = baseName + (baseName == "types" ? "" : "All"); out << "#define LOG_TAG \"" << mPackage.string() << "::" << baseName << "\"\n\n"; out << "#include \n"; out << "#include \n"; out << "#include \n\n"; if (iface) { // This is a no-op for IServiceManager itself. out << "#include \n"; generateCppPackageInclude(out, mPackage, iface->getProxyName()); generateCppPackageInclude(out, mPackage, iface->getStubName()); generateCppPackageInclude(out, mPackage, iface->getPassthroughName()); for (const Interface *superType : iface->superTypeChain()) { generateCppPackageInclude(out, superType->fqName(), superType->fqName().getInterfaceProxyName()); } out << "#include \n"; } else { generateCppPackageInclude(out, mPackage, "types"); generateCppPackageInclude(out, mPackage, "hwtypes"); } out << "\n"; enterLeaveNamespace(out, true /* enter */); out << "\n"; generateTypeSource(out, iface ? iface->localName() : ""); if (iface) { const Interface* iface = mRootScope.getInterface(); // need to be put here, generateStubSource is using this. out << "const char* " << iface->localName() << "::descriptor(\"" << iface->fqName().string() << "\");\n\n"; out << "__attribute__((constructor)) "; out << "static void static_constructor() {\n"; out.indent([&] { out << "::android::hardware::details::getBnConstructorMap().set(" << iface->localName() << "::descriptor,\n"; out.indent(2, [&] { out << "[](void *iIntf) -> ::android::sp<::android::hardware::IBinder> {\n"; out.indent([&] { out << "return new " << iface->getStubName() << "(static_cast<" << iface->localName() << " *>(iIntf));\n"; }); out << "});\n"; }); out << "::android::hardware::details::getBsConstructorMap().set(" << iface->localName() << "::descriptor,\n"; out.indent(2, [&] { out << "[](void *iIntf) -> ::android::sp<" << gIBaseFqName.cppName() << "> {\n"; out.indent([&] { out << "return new " << iface->getPassthroughName() << "(static_cast<" << iface->localName() << " *>(iIntf));\n"; }); out << "});\n"; }); }); out << "};\n\n"; out << "__attribute__((destructor))"; out << "static void static_destructor() {\n"; out.indent([&] { out << "::android::hardware::details::getBnConstructorMap().erase(" << iface->localName() << "::descriptor);\n"; out << "::android::hardware::details::getBsConstructorMap().erase(" << iface->localName() << "::descriptor);\n"; }); out << "};\n\n"; generateInterfaceSource(out); generateProxySource(out, iface->fqName()); generateStubSource(out, iface); generatePassthroughSource(out); if (isIBase()) { out << "// skipped getService, registerAsService, registerForNotifications\n"; } else { std::string package = iface->fqName().package() + iface->fqName().atVersion(); implementServiceManagerInteractions(out, iface->fqName(), package); } } HidlTypeAssertion::EmitAll(out); out << "\n"; enterLeaveNamespace(out, false /* enter */); } void AST::generateCheckNonNull(Formatter &out, const std::string &nonNull) { out.sIf(nonNull + " == nullptr", [&] { out << "return ::android::hardware::Status::fromExceptionCode(\n"; out.indent(2, [&] { out << "::android::hardware::Status::EX_ILLEGAL_ARGUMENT,\n" << "\"Null synchronous callback passed.\");\n"; }); }).endl().endl(); } void AST::generateTypeSource(Formatter& out, const std::string& ifaceName) const { mRootScope.emitTypeDefinitions(out, ifaceName); } void AST::declareCppReaderLocals(Formatter& out, const std::vector*>& args, bool forResults) const { if (args.empty()) { return; } for (const auto &arg : args) { const Type &type = arg->type(); out << type.getCppResultType() << " " << (forResults ? "_hidl_out_" : "") + arg->name() << ";\n"; } out << "\n"; } void AST::emitCppReaderWriter(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer, const NamedReference* arg, bool isReader, Type::ErrorMode mode, bool addPrefixToName) const { const Type &type = arg->type(); type.emitReaderWriter( out, addPrefixToName ? ("_hidl_out_" + arg->name()) : arg->name(), parcelObj, parcelObjIsPointer, isReader, mode); } void AST::emitCppResolveReferences(Formatter& out, const std::string& parcelObj, bool parcelObjIsPointer, const NamedReference* arg, bool isReader, Type::ErrorMode mode, bool addPrefixToName) const { const Type &type = arg->type(); if(type.needsResolveReferences()) { type.emitResolveReferences( out, addPrefixToName ? ("_hidl_out_" + arg->name()) : arg->name(), isReader, // nameIsPointer parcelObj, parcelObjIsPointer, isReader, mode); } } void AST::generateProxyMethodSource(Formatter& out, const std::string& klassName, const Method* method, const Interface* superInterface) const { method->generateCppSignature(out, klassName, true /* specify namespaces */); if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) { out.block([&] { method->cppImpl(IMPL_PROXY, out); }).endl().endl(); return; } out.block([&] { const bool returnsValue = !method->results().empty(); const NamedReference* elidedReturn = method->canElideCallback(); method->generateCppReturnType(out); out << " _hidl_out = " << superInterface->fqName().cppNamespace() << "::" << superInterface->getProxyName() << "::_hidl_" << method->name() << "(this, this"; if (!method->hasEmptyCppArgSignature()) { out << ", "; } out.join(method->args().begin(), method->args().end(), ", ", [&](const auto &arg) { out << arg->name(); }); if (returnsValue && elidedReturn == nullptr) { if (!method->args().empty()) { out << ", "; } out << "_hidl_cb"; } out << ");\n\n"; out << "return _hidl_out;\n"; }).endl().endl(); } void AST::generateStaticProxyMethodSource(Formatter& out, const std::string& klassName, const Method* method) const { if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) { return; } method->generateCppReturnType(out); out << klassName << "::_hidl_" << method->name() << "(" << "::android::hardware::IInterface *_hidl_this, " << "::android::hardware::details::HidlInstrumentor *_hidl_this_instrumentor"; if (!method->hasEmptyCppArgSignature()) { out << ", "; } method->emitCppArgSignature(out); out << ") {\n"; out.indent(); out << "#ifdef __ANDROID_DEBUGGABLE__\n"; out << "bool mEnableInstrumentation = _hidl_this_instrumentor->isInstrumentationEnabled();\n"; out << "const auto &mInstrumentationCallbacks = _hidl_this_instrumentor->getInstrumentationCallbacks();\n"; out << "#else\n"; out << "(void) _hidl_this_instrumentor;\n"; out << "#endif // __ANDROID_DEBUGGABLE__\n"; const bool returnsValue = !method->results().empty(); const NamedReference* elidedReturn = method->canElideCallback(); if (returnsValue && elidedReturn == nullptr) { generateCheckNonNull(out, "_hidl_cb"); } generateCppInstrumentationCall( out, InstrumentationEvent::CLIENT_API_ENTRY, method); out << "::android::hardware::Parcel _hidl_data;\n"; out << "::android::hardware::Parcel _hidl_reply;\n"; out << "::android::status_t _hidl_err;\n"; out << "::android::hardware::Status _hidl_status;\n\n"; declareCppReaderLocals( out, method->results(), true /* forResults */); out << "_hidl_err = _hidl_data.writeInterfaceToken("; out << klassName; out << "::descriptor);\n"; out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n"; bool hasInterfaceArgument = false; // First DFS: write all buffers and resolve pointers for parent for (const auto &arg : method->args()) { if (arg->type().isInterface()) { hasInterfaceArgument = true; } emitCppReaderWriter( out, "_hidl_data", false /* parcelObjIsPointer */, arg, false /* reader */, Type::ErrorMode_Goto, false /* addPrefixToName */); } // Second DFS: resolve references. for (const auto &arg : method->args()) { emitCppResolveReferences( out, "_hidl_data", false /* parcelObjIsPointer */, arg, false /* reader */, Type::ErrorMode_Goto, false /* addPrefixToName */); } if (hasInterfaceArgument) { // Start binder threadpool to handle incoming transactions out << "::android::hardware::ProcessState::self()->startThreadPool();\n"; } out << "_hidl_err = ::android::hardware::IInterface::asBinder(_hidl_this)->transact(" << method->getSerialId() << " /* " << method->name() << " */, _hidl_data, &_hidl_reply"; if (method->isOneway()) { out << ", " << Interface::FLAG_ONEWAY << " /* oneway */"; } out << ");\n"; out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n"; if (!method->isOneway()) { out << "_hidl_err = ::android::hardware::readFromParcel(&_hidl_status, _hidl_reply);\n"; out << "if (_hidl_err != ::android::OK) { goto _hidl_error; }\n\n"; out << "if (!_hidl_status.isOk()) { return _hidl_status; }\n\n"; // First DFS: write all buffers and resolve pointers for parent for (const auto &arg : method->results()) { emitCppReaderWriter( out, "_hidl_reply", false /* parcelObjIsPointer */, arg, true /* reader */, Type::ErrorMode_Goto, true /* addPrefixToName */); } // Second DFS: resolve references. for (const auto &arg : method->results()) { emitCppResolveReferences( out, "_hidl_reply", false /* parcelObjIsPointer */, arg, true /* reader */, Type::ErrorMode_Goto, true /* addPrefixToName */); } if (returnsValue && elidedReturn == nullptr) { out << "_hidl_cb("; out.join(method->results().begin(), method->results().end(), ", ", [&] (const auto &arg) { if (arg->type().resultNeedsDeref()) { out << "*"; } out << "_hidl_out_" << arg->name(); }); out << ");\n\n"; } } generateCppInstrumentationCall( out, InstrumentationEvent::CLIENT_API_EXIT, method); if (elidedReturn != nullptr) { out << "_hidl_status.setFromStatusT(_hidl_err);\n"; out << "return ::android::hardware::Return<"; out << elidedReturn->type().getCppResultType() << ">(_hidl_out_" << elidedReturn->name() << ");\n\n"; } else { out << "_hidl_status.setFromStatusT(_hidl_err);\n"; out << "return ::android::hardware::Return();\n\n"; } out.unindent(); out << "_hidl_error:\n"; out.indent(); out << "_hidl_status.setFromStatusT(_hidl_err);\n"; out << "return ::android::hardware::Return<"; if (elidedReturn != nullptr) { out << method->results().at(0)->type().getCppResultType(); } else { out << "void"; } out << ">(_hidl_status);\n"; out.unindent(); out << "}\n\n"; } void AST::generateProxySource(Formatter& out, const FQName& fqName) const { const std::string klassName = fqName.getInterfaceProxyName(); out << klassName << "::" << klassName << "(const ::android::sp<::android::hardware::IBinder> &_hidl_impl)\n"; out.indent(); out.indent(); out << ": BpInterface" << "<" << fqName.getInterfaceName() << ">(_hidl_impl),\n" << " ::android::hardware::details::HidlInstrumentor(\"" << mPackage.string() << "\", \"" << fqName.getInterfaceName() << "\") {\n"; out.unindent(); out.unindent(); out << "}\n\n"; generateMethods(out, [&](const Method* method, const Interface*) { generateStaticProxyMethodSource(out, klassName, method); }, false /* include parents */); generateMethods(out, [&](const Method* method, const Interface* superInterface) { generateProxyMethodSource(out, klassName, method, superInterface); }); } void AST::generateStubSource(Formatter& out, const Interface* iface) const { const std::string interfaceName = iface->localName(); const std::string klassName = iface->getStubName(); out << klassName << "::" << klassName << "(const ::android::sp<" << interfaceName <<"> &_hidl_impl)\n"; out.indent(); out.indent(); if (iface->isIBase()) { out << ": ::android::hardware::details::HidlInstrumentor(\""; } else { out << ": " << gIBaseFqName.getInterfaceStubFqName().cppName() << "(_hidl_impl, \""; } out << mPackage.string() << "\", \"" << interfaceName << "\") { \n"; out.indent(); out << "_hidl_mImpl = _hidl_impl;\n"; out << "auto prio = ::android::hardware::details::gServicePrioMap.get(" << "_hidl_impl, {SCHED_NORMAL, 0});\n"; out << "mSchedPolicy = prio.sched_policy;\n"; out << "mSchedPriority = prio.prio;\n"; out << "setRequestingSid(::android::hardware::details::gServiceSidMap.get(_hidl_impl, false));\n"; out.unindent(); out.unindent(); out.unindent(); out << "}\n\n"; if (iface->isIBase()) { // BnHwBase has a constructor to initialize the HidlInstrumentor // class properly. out << klassName << "::" << klassName << "(const ::android::sp<" << interfaceName << "> &_hidl_impl," << " const std::string &HidlInstrumentor_package," << " const std::string &HidlInstrumentor_interface)\n"; out.indent(); out.indent(); out << ": ::android::hardware::details::HidlInstrumentor(" << "HidlInstrumentor_package, HidlInstrumentor_interface) {\n"; out.indent(); out << "_hidl_mImpl = _hidl_impl;\n"; out.unindent(); out.unindent(); out.unindent(); out << "}\n\n"; } out << klassName << "::~" << klassName << "() "; out.block([&]() { out << "::android::hardware::details::gBnMap.eraseIfEqual(_hidl_mImpl.get(), this);\n"; }).endl().endl(); generateMethods(out, [&](const Method* method, const Interface*) { return generateStaticStubMethodSource(out, iface->fqName(), method); }, false /* include parents */); generateMethods(out, [&](const Method* method, const Interface*) { if (!method->isHidlReserved() || !method->overridesCppImpl(IMPL_STUB_IMPL)) { return; } method->generateCppSignature(out, iface->getStubName()); out << " "; out.block([&] { method->cppImpl(IMPL_STUB_IMPL, out); }).endl(); }); out << "::android::status_t " << klassName << "::onTransact(\n"; out.indent(); out.indent(); out << "uint32_t _hidl_code,\n" << "const ::android::hardware::Parcel &_hidl_data,\n" << "::android::hardware::Parcel *_hidl_reply,\n" << "uint32_t _hidl_flags,\n" << "TransactCallback _hidl_cb) {\n"; out.unindent(); out << "::android::status_t _hidl_err = ::android::OK;\n\n"; out << "switch (_hidl_code) {\n"; out.indent(); for (const auto &tuple : iface->allMethodsFromRoot()) { const Method *method = tuple.method(); const Interface *superInterface = tuple.interface(); if (!isIBase() && method->isHidlReserved()) { continue; } out << "case " << method->getSerialId() << " /* " << method->name() << " */:\n{\n"; out.indent(); 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(); generateStubSourceForMethod(out, method, superInterface); out.unindent(); out << "}\n\n"; } out << "default:\n{\n"; out.indent(); if (iface->isIBase()) { out << "(void)_hidl_flags;\n"; out << "return ::android::UNKNOWN_TRANSACTION;\n"; } else { out << "return "; out << gIBaseFqName.getInterfaceStubFqName().cppName(); out << "::onTransact(\n"; out.indent(); out.indent(); out << "_hidl_code, _hidl_data, _hidl_reply, " << "_hidl_flags, _hidl_cb);\n"; out.unindent(); out.unindent(); } out.unindent(); out << "}\n"; out.unindent(); out << "}\n\n"; out.sIf("_hidl_err == ::android::UNEXPECTED_NULL", [&] { out << "_hidl_err = ::android::hardware::writeToParcel(\n"; out.indent(2, [&] { out << "::android::hardware::Status::fromExceptionCode(::android::hardware::Status::EX_NULL_POINTER),\n"; out << "_hidl_reply);\n"; }); }); out << "return _hidl_err;\n"; out.unindent(); out << "}\n\n"; } 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; } out << "_hidl_err = " << superInterface->fqName().cppNamespace() << "::" << superInterface->getStubName() << "::_hidl_" << method->name() << "(this, _hidl_data, _hidl_reply, _hidl_cb);\n"; out << "break;\n"; } void AST::generateStaticStubMethodSource(Formatter& out, const FQName& fqName, const Method* method) const { if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB)) { return; } const std::string& klassName = fqName.getInterfaceStubName(); out << "::android::status_t " << klassName << "::_hidl_" << method->name() << "(\n"; out.indent(); out.indent(); 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"; out.unindent(); out << "#ifdef __ANDROID_DEBUGGABLE__\n"; out << "bool mEnableInstrumentation = _hidl_this->isInstrumentationEnabled();\n"; out << "const auto &mInstrumentationCallbacks = _hidl_this->getInstrumentationCallbacks();\n"; out << "#endif // __ANDROID_DEBUGGABLE__\n\n"; out << "::android::status_t _hidl_err = ::android::OK;\n"; out << "if (!_hidl_data.enforceInterface(" << klassName << "::Pure::descriptor)) {\n"; out.indent(); out << "_hidl_err = ::android::BAD_TYPE;\n"; out << "return _hidl_err;\n"; out.unindent(); out << "}\n\n"; declareCppReaderLocals(out, method->args(), false /* forResults */); // First DFS: write buffers for (const auto &arg : method->args()) { emitCppReaderWriter( out, "_hidl_data", false /* parcelObjIsPointer */, arg, true /* reader */, Type::ErrorMode_Return, false /* addPrefixToName */); } // Second DFS: resolve references for (const auto &arg : method->args()) { emitCppResolveReferences( out, "_hidl_data", false /* parcelObjIsPointer */, arg, true /* reader */, Type::ErrorMode_Return, false /* addPrefixToName */); } generateCppInstrumentationCall( out, InstrumentationEvent::SERVER_API_ENTRY, method); const bool returnsValue = !method->results().empty(); const NamedReference* elidedReturn = method->canElideCallback(); std::string callee; if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB_IMPL)) { callee = "_hidl_this"; } else { callee = "static_cast<" + fqName.getInterfaceName() + "*>(_hidl_this->getImpl().get())"; } if (elidedReturn != nullptr) { out << elidedReturn->type().getCppResultType() << " _hidl_out_" << elidedReturn->name() << " = " << callee << "->" << method->name() << "("; out.join(method->args().begin(), method->args().end(), ", ", [&] (const auto &arg) { if (arg->type().resultNeedsDeref()) { out << "*"; } out << arg->name(); }); out << ");\n\n"; out << "::android::hardware::writeToParcel(::android::hardware::Status::ok(), " << "_hidl_reply);\n\n"; elidedReturn->type().emitReaderWriter( out, "_hidl_out_" + elidedReturn->name(), "_hidl_reply", true, /* parcelObjIsPointer */ false, /* isReader */ Type::ErrorMode_Ignore); emitCppResolveReferences( out, "_hidl_reply", true /* parcelObjIsPointer */, elidedReturn, false /* reader */, Type::ErrorMode_Ignore, true /* addPrefixToName */); generateCppInstrumentationCall( out, InstrumentationEvent::SERVER_API_EXIT, method); out << "_hidl_cb(*_hidl_reply);\n"; } else { if (returnsValue) { out << "bool _hidl_callbackCalled = false;\n\n"; } out << callee << "->" << method->name() << "("; out.join(method->args().begin(), method->args().end(), ", ", [&] (const auto &arg) { if (arg->type().resultNeedsDeref()) { out << "*"; } out << arg->name(); }); if (returnsValue) { if (!method->args().empty()) { out << ", "; } out << "[&]("; out.join(method->results().begin(), method->results().end(), ", ", [&](const auto &arg) { out << "const auto &_hidl_out_" << arg->name(); }); out << ") {\n"; out.indent(); out << "if (_hidl_callbackCalled) {\n"; out.indent(); out << "LOG_ALWAYS_FATAL(\"" << method->name() << ": _hidl_cb called a second time, but must be called once.\");\n"; out.unindent(); out << "}\n"; out << "_hidl_callbackCalled = true;\n\n"; out << "::android::hardware::writeToParcel(::android::hardware::Status::ok(), " << "_hidl_reply);\n\n"; // First DFS: buffers for (const auto &arg : method->results()) { emitCppReaderWriter( out, "_hidl_reply", true /* parcelObjIsPointer */, arg, false /* reader */, Type::ErrorMode_Ignore, true /* addPrefixToName */); } // Second DFS: resolve references for (const auto &arg : method->results()) { emitCppResolveReferences( out, "_hidl_reply", true /* parcelObjIsPointer */, arg, false /* reader */, Type::ErrorMode_Ignore, true /* addPrefixToName */); } generateCppInstrumentationCall( out, InstrumentationEvent::SERVER_API_EXIT, method); out << "_hidl_cb(*_hidl_reply);\n"; out.unindent(); out << "});\n\n"; } else { out << ");\n\n"; out << "(void) _hidl_cb;\n\n"; generateCppInstrumentationCall( out, InstrumentationEvent::SERVER_API_EXIT, method); } if (returnsValue) { out << "if (!_hidl_callbackCalled) {\n"; out.indent(); out << "LOG_ALWAYS_FATAL(\"" << method->name() << ": _hidl_cb not called, but must be called once.\");\n"; out.unindent(); out << "}\n\n"; } else { out << "::android::hardware::writeToParcel(" << "::android::hardware::Status::ok(), " << "_hidl_reply);\n\n"; } } out << "return _hidl_err;\n"; out.unindent(); out << "}\n\n"; } void AST::generatePassthroughHeader(Formatter& out) const { if (!AST::isInterface()) { // types.hal does not get a stub header. return; } const Interface* iface = mRootScope.getInterface(); CHECK(iface != nullptr); const std::string klassName = iface->getPassthroughName(); bool supportOneway = iface->hasOnewayMethods(); const std::string guard = makeHeaderGuard(klassName); out << "#ifndef " << guard << "\n"; out << "#define " << guard << "\n\n"; std::vector packageComponents; getPackageAndVersionComponents( &packageComponents, false /* cpp_compatible */); out << "#include \n"; out << "#include \n"; out << "#include \n"; generateCppPackageInclude(out, mPackage, iface->localName()); out << "\n"; out << "#include \n"; if (supportOneway) { out << "#include \n"; } enterLeaveNamespace(out, true /* enter */); out << "\n"; out << "struct " << klassName << " : " << iface->localName() << ", ::android::hardware::details::HidlInstrumentor {\n"; out.indent(); out << "explicit " << klassName << "(const ::android::sp<" << iface->localName() << "> impl);\n"; out.endl(); generateTemplatizationLink(out); generateCppTag(out, "android::hardware::details::bs_tag"); generateMethods(out, [&](const Method* method, const Interface*) { generatePassthroughMethod(out, method); }); out.unindent(); out << "private:\n"; out.indent(); out << "const ::android::sp<" << iface->localName() << "> mImpl;\n"; if (supportOneway) { out << "::android::hardware::details::TaskRunner mOnewayQueue;\n"; out << "\n"; out << "::android::hardware::Return addOnewayTask(" "std::function);\n\n"; } out.unindent(); out << "};\n\n"; enterLeaveNamespace(out, false /* enter */); out << "\n#endif // " << guard << "\n"; } void AST::generateInterfaceSource(Formatter& out) const { const Interface* iface = mRootScope.getInterface(); // generate castFrom functions std::string childTypeResult = iface->getCppResultType(); generateMethods(out, [&](const Method* method, const Interface*) { bool reserved = method->isHidlReserved(); if (!reserved) { out << "// no default implementation for: "; } method->generateCppSignature(out, iface->localName()); if (reserved) { out.block([&]() { method->cppImpl(IMPL_INTERFACE, out); }).endl(); } out << "\n"; return; }); for (const Interface *superType : iface->typeChain()) { out << "// static \n::android::hardware::Return<" << childTypeResult << "> " << iface->localName() << "::castFrom(" << superType->getCppArgumentType() << " parent, bool " << (iface == superType ? "/* emitError */" : "emitError") << ") {\n"; out.indent(); if (iface == superType) { out << "return parent;\n"; } else { out << "return ::android::hardware::details::castInterface<"; out << iface->localName() << ", " << superType->fqName().cppName() << ", " << iface->getProxyName() << ">(\n"; out.indent(); out.indent(); out << "parent, \"" << iface->fqName().string() << "\", emitError);\n"; out.unindent(); out.unindent(); } out.unindent(); out << "}\n\n"; } } void AST::generatePassthroughSource(Formatter& out) const { const Interface* iface = mRootScope.getInterface(); const std::string klassName = iface->getPassthroughName(); out << klassName << "::" << klassName << "(const ::android::sp<" << iface->fullName() << "> impl) : ::android::hardware::details::HidlInstrumentor(\"" << mPackage.string() << "\", \"" << iface->localName() << "\"), mImpl(impl) {"; if (iface->hasOnewayMethods()) { out << "\n"; out.indent([&] { out << "mOnewayQueue.start(3000 /* similar limit to binderized */);\n"; }); } out << "}\n\n"; if (iface->hasOnewayMethods()) { out << "::android::hardware::Return " << klassName << "::addOnewayTask(std::function fun) {\n"; out.indent(); out << "if (!mOnewayQueue.push(fun)) {\n"; out.indent(); out << "return ::android::hardware::Status::fromExceptionCode(\n"; out.indent(); out.indent(); out << "::android::hardware::Status::EX_TRANSACTION_FAILED,\n" << "\"Passthrough oneway function queue exceeds maximum size.\");\n"; out.unindent(); out.unindent(); out.unindent(); out << "}\n"; out << "return ::android::hardware::Status();\n"; out.unindent(); out << "}\n\n"; } } void AST::generateCppAtraceCall(Formatter &out, InstrumentationEvent event, const Method *method) const { const Interface* iface = mRootScope.getInterface(); std::string baseString = "HIDL::" + iface->localName() + "::" + method->name(); switch (event) { case SERVER_API_ENTRY: { out << "atrace_begin(ATRACE_TAG_HAL, \"" << baseString + "::server\");\n"; break; } case CLIENT_API_ENTRY: { out << "atrace_begin(ATRACE_TAG_HAL, \"" << baseString + "::client\");\n"; break; } case PASSTHROUGH_ENTRY: { out << "atrace_begin(ATRACE_TAG_HAL, \"" << baseString + "::passthrough\");\n"; break; } case SERVER_API_EXIT: case CLIENT_API_EXIT: case PASSTHROUGH_EXIT: { out << "atrace_end(ATRACE_TAG_HAL);\n"; break; } default: { CHECK(false) << "Unsupported instrumentation event: " << event; } } } void AST::generateCppInstrumentationCall( Formatter &out, InstrumentationEvent event, const Method *method) const { generateCppAtraceCall(out, event, method); out << "#ifdef __ANDROID_DEBUGGABLE__\n"; out << "if (UNLIKELY(mEnableInstrumentation)) {\n"; out.indent(); out << "std::vector _hidl_args;\n"; std::string event_str = ""; switch (event) { case SERVER_API_ENTRY: { event_str = "InstrumentationEvent::SERVER_API_ENTRY"; for (const auto &arg : method->args()) { out << "_hidl_args.push_back((void *)" << (arg->type().resultNeedsDeref() ? "" : "&") << arg->name() << ");\n"; } break; } case SERVER_API_EXIT: { event_str = "InstrumentationEvent::SERVER_API_EXIT"; for (const auto &arg : method->results()) { out << "_hidl_args.push_back((void *)&_hidl_out_" << arg->name() << ");\n"; } break; } case CLIENT_API_ENTRY: { event_str = "InstrumentationEvent::CLIENT_API_ENTRY"; for (const auto &arg : method->args()) { out << "_hidl_args.push_back((void *)&" << arg->name() << ");\n"; } break; } case CLIENT_API_EXIT: { event_str = "InstrumentationEvent::CLIENT_API_EXIT"; for (const auto &arg : method->results()) { out << "_hidl_args.push_back((void *)" << (arg->type().resultNeedsDeref() ? "" : "&") << "_hidl_out_" << arg->name() << ");\n"; } break; } case PASSTHROUGH_ENTRY: { event_str = "InstrumentationEvent::PASSTHROUGH_ENTRY"; for (const auto &arg : method->args()) { out << "_hidl_args.push_back((void *)&" << arg->name() << ");\n"; } break; } case PASSTHROUGH_EXIT: { event_str = "InstrumentationEvent::PASSTHROUGH_EXIT"; for (const auto &arg : method->results()) { out << "_hidl_args.push_back((void *)&_hidl_out_" << arg->name() << ");\n"; } break; } default: { CHECK(false) << "Unsupported instrumentation event: " << event; } } const Interface* iface = mRootScope.getInterface(); out << "for (const auto &callback: mInstrumentationCallbacks) {\n"; out.indent(); out << "callback(" << event_str << ", \"" << mPackage.package() << "\", \"" << mPackage.version() << "\", \"" << iface->localName() << "\", \"" << method->name() << "\", &_hidl_args);\n"; out.unindent(); out << "}\n"; out.unindent(); out << "}\n"; out << "#endif // __ANDROID_DEBUGGABLE__\n\n"; } } // namespace android generateCppAdapter.cpp0100644 0000000 0000000 00000015141 13521773237 014063 0ustar000000000 0000000 /* * 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 #include #include #include #include #include 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 \n"; generateCppPackageInclude(out, mPackage, getInterface()->localName()); std::set 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(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 generateCppImpl.cpp0100644 0000000 0000000 00000011333 13521773237 013403 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "Coordinator.h" #include "EnumType.h" #include "Interface.h" #include "Method.h" #include "Reference.h" #include "ScalarType.h" #include "Scope.h" #include #include #include #include #include #include namespace android { void AST::generateFetchSymbol(Formatter &out, const std::string& ifaceName) const { out << "HIDL_FETCH_" << ifaceName; } 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; } method->generateCppSignature(out, className, false /* specifyNamespaces */); out << " {\n"; out.indent(); out << "// TODO implement\n"; const NamedReference* elidedReturn = method->canElideCallback(); if (elidedReturn == nullptr) { out << "return Void();\n"; } else { out << "return " << elidedReturn->type().getCppResultType() << " {};\n"; } out.unindent(); out << "}\n\n"; return; } void AST::generateCppImplHeader(Formatter& out) const { if (!AST::isInterface()) { // types.hal does not get a stub header. return; } const Interface* iface = mRootScope.getInterface(); const std::string baseName = iface->getBaseName(); const std::string guard = makeHeaderGuard(baseName, false /* indicateGenerated */); out << "#ifndef " << guard << "\n"; out << "#define " << guard << "\n\n"; generateCppPackageInclude(out, mPackage, iface->localName()); out << "#include \n"; out << "#include \n\n"; enterLeaveNamespace(out, true /* enter */); out << "namespace implementation {\n\n"; out << "using ::android::hardware::hidl_array;\n"; out << "using ::android::hardware::hidl_memory;\n"; out << "using ::android::hardware::hidl_string;\n"; out << "using ::android::hardware::hidl_vec;\n"; out << "using ::android::hardware::Return;\n"; out << "using ::android::hardware::Void;\n"; out << "using ::android::sp;\n"; out << "\n"; out << "struct " << baseName << " : public " << iface->localName() << " {\n"; out.indent(); generateMethods(out, [&](const Method* method, const Interface*) { // ignore HIDL reserved methods -- implemented in IFoo already. if (method->isHidlReserved()) { return; } method->generateCppSignature(out, "" /* className */, false /* specifyNamespaces */); out << " override;\n"; }); out.unindent(); out << "};\n\n"; out << "// FIXME: most likely delete, this is only for passthrough implementations\n" << "// extern \"C\" " << iface->localName() << "* "; generateFetchSymbol(out, iface->localName()); out << "(const char* name);\n\n"; out << "} // namespace implementation\n"; enterLeaveNamespace(out, false /* leave */); out << "\n#endif // " << guard << "\n"; } void AST::generateCppImplSource(Formatter& out) const { if (!AST::isInterface()) { // types.hal does not get a stub header. return; } const Interface* iface = mRootScope.getInterface(); const std::string baseName = iface->getBaseName(); out << "#include \"" << baseName << ".h\"\n\n"; enterLeaveNamespace(out, true /* enter */); out << "namespace implementation {\n\n"; generateMethods(out, [&](const Method* method, const Interface*) { generateStubImplMethod(out, baseName, method); }); out.setLinePrefix("//"); out << iface->localName() << "* "; generateFetchSymbol(out, iface->localName()); out << "(const char* /* name */) {\n"; out.indent(); out << "return new " << baseName << "();\n"; out.unindent(); out << "}\n\n"; out.unsetLinePrefix(); out << "} // namespace implementation\n"; enterLeaveNamespace(out, false /* leave */); } } // namespace android generateJava.cpp0100644 0000000 0000000 00000046514 13521773237 012731 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "Coordinator.h" #include "Interface.h" #include "Method.h" #include "Reference.h" #include "Scope.h" #include #include namespace android { void AST::emitJavaReaderWriter(Formatter& out, const std::string& parcelObj, const NamedReference* arg, bool isReader, bool addPrefixToName) const { if (isReader) { out << arg->type().getJavaType() << " " << (addPrefixToName ? "_hidl_out_" : "") << arg->name() << " = "; } arg->type().emitJavaReaderWriter(out, parcelObj, (addPrefixToName ? "_hidl_out_" : "") + arg->name(), isReader); } 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 (typeName != limitToType) continue; std::vector packageComponents; getPackageAndVersionComponents( &packageComponents, true /* cpp_compatible */); out << "package " << mPackage.javaPackage() << ";\n\n\n"; type->emitJavaTypeDeclarations(out, true /* atTopLevel */); return; } CHECK(false) << "generateJavaTypes could not find limitToType type"; } 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()) { generateJavaTypes(out, limitToType); return; } const Interface* iface = mRootScope.getInterface(); const std::string ifaceName = iface->localName(); const std::string baseName = iface->getBaseName(); std::vector packageComponents; getPackageAndVersionComponents( &packageComponents, true /* cpp_compatible */); out << "package " << mPackage.javaPackage() << ";\n\n"; out.setNamespace(mPackage.javaPackage() + "."); const Interface *superType = iface->superType(); out << "public interface " << ifaceName << " extends "; if (superType != NULL) { out << superType->fullJavaName(); } else { out << "android.os.IHwInterface"; } out << " {\n"; out.indent(); out << "public static final String kInterfaceName = \"" << mPackage.string() << "::" << ifaceName << "\";\n\n"; out << "/* package private */ static " << ifaceName << " asInterface(android.os.IHwBinder binder) {\n"; out.indent(); out << "if (binder == null) {\n"; out.indent(); out << "return null;\n"; out.unindent(); out << "}\n\n"; out << "android.os.IHwInterface iface =\n"; out.indent(); out.indent(); out << "binder.queryLocalInterface(kInterfaceName);\n\n"; out.unindent(); out.unindent(); out << "if ((iface != null) && (iface instanceof " << ifaceName << ")) {\n"; out.indent(); out << "return (" << ifaceName << ")iface;\n"; out.unindent(); out << "}\n\n"; out << ifaceName << " proxy = new " << ifaceName << ".Proxy(binder);\n\n"; out << "try {\n"; out.indent(); out << "for (String descriptor : proxy.interfaceChain()) {\n"; out.indent(); out << "if (descriptor.equals(kInterfaceName)) {\n"; out.indent(); out << "return proxy;\n"; out.unindent(); out << "}\n"; out.unindent(); out << "}\n"; out.unindent(); out << "} catch (android.os.RemoteException e) {\n"; out.indent(); out.unindent(); out << "}\n\n"; out << "return null;\n"; out.unindent(); out << "}\n\n"; out << "public static " << ifaceName << " castFrom(android.os.IHwInterface iface) {\n"; out.indent(); out << "return (iface == null) ? null : " << ifaceName << ".asInterface(iface.asBinder());\n"; out.unindent(); out << "}\n\n"; out << "@Override\npublic android.os.IHwBinder asBinder();\n\n"; emitGetService(out, ifaceName, iface->fqName().string(), true /* isRetry */); emitGetService(out, ifaceName, iface->fqName().string(), false /* isRetry */); emitJavaTypeDeclarations(out); for (const auto &method : iface->methods()) { if (method->isHiddenFromJava()) { continue; } const bool returnsValue = !method->results().empty(); const bool needsCallback = method->results().size() > 1; if (needsCallback) { out << "\n@java.lang.FunctionalInterface\npublic interface " << method->name() << "Callback {\n"; out.indent(); out << "public void onValues("; method->emitJavaResultSignature(out); out << ");\n"; out.unindent(); out << "}\n\n"; } method->emitDocComment(out); if (returnsValue && !needsCallback) { out << method->results()[0]->type().getJavaType(); } else { out << "void"; } out << " " << method->name() << "("; method->emitJavaArgSignature(out); if (needsCallback) { if (!method->args().empty()) { out << ", "; } out << method->name() << "Callback _hidl_cb"; } out << ")\n"; out.indent(); out << "throws android.os.RemoteException;\n"; out.unindent(); } out << "\npublic static final class Proxy implements " << ifaceName << " {\n"; out.indent(); out << "private android.os.IHwBinder mRemote;\n\n"; out << "public Proxy(android.os.IHwBinder remote) {\n"; out.indent(); out << "mRemote = java.util.Objects.requireNonNull(remote);\n"; out.unindent(); out << "}\n\n"; out << "@Override\npublic android.os.IHwBinder asBinder() {\n"; out.indent(); out << "return mRemote;\n"; out.unindent(); out << "}\n\n"; out << "@Override\npublic String toString() "; out.block([&] { out.sTry([&] { out << "return this.interfaceDescriptor() + \"@Proxy\";\n"; }).sCatch("android.os.RemoteException ex", [&] { out << "/* ignored; handled below. */\n"; }).endl(); out << "return \"[class or subclass of \" + " << 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(); if (method->isHiddenFromJava()) { continue; } const Interface *superInterface = tuple.interface(); if (prevInterface != superInterface) { out << "// Methods from " << superInterface->fullName() << " follow.\n"; prevInterface = superInterface; } const bool returnsValue = !method->results().empty(); const bool needsCallback = method->results().size() > 1; out << "@Override\npublic "; if (returnsValue && !needsCallback) { out << method->results()[0]->type().getJavaType(); } else { out << "void"; } out << " " << method->name() << "("; method->emitJavaArgSignature(out); if (needsCallback) { if (!method->args().empty()) { out << ", "; } out << method->name() << "Callback _hidl_cb"; } out << ")\n"; out.indent(); out.indent(); out << "throws android.os.RemoteException {\n"; out.unindent(); if (method->isHidlReserved() && method->overridesJavaImpl(IMPL_PROXY)) { method->javaImpl(IMPL_PROXY, out); out.unindent(); out << "}\n"; continue; } out << "android.os.HwParcel _hidl_request = new android.os.HwParcel();\n"; out << "_hidl_request.writeInterfaceToken(" << superInterface->fullJavaName() << ".kInterfaceName);\n"; for (const auto &arg : method->args()) { emitJavaReaderWriter( out, "_hidl_request", arg, false /* isReader */, false /* addPrefixToName */); } out << "\nandroid.os.HwParcel _hidl_reply = new android.os.HwParcel();\n"; out.sTry([&] { out << "mRemote.transact(" << method->getSerialId() << " /* " << method->name() << " */, _hidl_request, _hidl_reply, "; if (method->isOneway()) { out << Interface::FLAG_ONEWAY << " /* oneway */"; } else { out << "0 /* flags */"; } out << ");\n"; if (!method->isOneway()) { out << "_hidl_reply.verifySuccess();\n"; } else { CHECK(!returnsValue); } out << "_hidl_request.releaseTemporaryStorage();\n"; if (returnsValue) { out << "\n"; for (const auto &arg : method->results()) { emitJavaReaderWriter( out, "_hidl_reply", arg, true /* isReader */, true /* addPrefixToName */); } if (needsCallback) { out << "_hidl_cb.onValues("; bool firstField = true; for (const auto &arg : method->results()) { if (!firstField) { out << ", "; } out << "_hidl_out_" << arg->name(); firstField = false; } out << ");\n"; } else { const std::string returnName = method->results()[0]->name(); out << "return _hidl_out_" << returnName << ";\n"; } } }).sFinally([&] { out << "_hidl_reply.release();\n"; }).endl(); out.unindent(); out << "}\n\n"; } out.unindent(); out << "}\n"; //////////////////////////////////////////////////////////////////////////// out << "\npublic static abstract class Stub extends android.os.HwBinder " << "implements " << ifaceName << " {\n"; out.indent(); 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"; for (Method *method : iface->hidlReservedMethods()) { if (method->isHiddenFromJava()) { continue; } // b/32383557 this is a hack. We need to change this if we have more reserved methods. CHECK_LE(method->results().size(), 1u); std::string resultType = method->results().size() == 0 ? "void" : method->results()[0]->type().getJavaType(); out << "@Override\npublic final " << resultType << " " << method->name() << "("; method->emitJavaArgSignature(out); out << ") {\n"; out.indent(); method->javaImpl(IMPL_INTERFACE, out); out.unindent(); out << "\n}\n\n"; } out << "@Override\n" << "public android.os.IHwInterface queryLocalInterface(String descriptor) {\n"; out.indent(); // XXX what about potential superClasses? out << "if (kInterfaceName.equals(descriptor)) {\n"; out.indent(); out << "return this;\n"; out.unindent(); out << "}\n"; out << "return null;\n"; out.unindent(); out << "}\n\n"; out << "public void registerAsService(String serviceName) throws android.os.RemoteException {\n"; out.indent(); out << "registerService(serviceName);\n"; out.unindent(); out << "}\n\n"; out << "@Override\npublic String toString() "; out.block([&] { out << "return this.interfaceDescriptor() + \"@Stub\";\n"; }).endl().endl(); out << "@Override\n" << "public void onTransact(" << "int _hidl_code, " << "android.os.HwParcel _hidl_request, " << "final android.os.HwParcel _hidl_reply, " << "int _hidl_flags)\n"; out.indent(); out.indent(); out << "throws android.os.RemoteException {\n"; out.unindent(); out << "switch (_hidl_code) {\n"; out.indent(); for (const auto &tuple : iface->allMethodsFromRoot()) { const Method *method = tuple.method(); const Interface *superInterface = tuple.interface(); const bool returnsValue = !method->results().empty(); const bool needsCallback = method->results().size() > 1; out << "case " << method->getSerialId() << " /* " << method->name() << " */:\n{\n"; 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(); out << "break;\n"; out << "}\n\n"; continue; } out << "_hidl_request.enforceInterface(" << superInterface->fullJavaName() << ".kInterfaceName);\n\n"; if (method->isHiddenFromJava()) { // This is a method hidden from the Java side of things, it must not // return any value and will simply signal success. CHECK(!returnsValue); out << "_hidl_reply.writeStatus(android.os.HwParcel.STATUS_SUCCESS);\n"; out << "_hidl_reply.send();\n"; out << "break;\n"; out.unindent(); out << "}\n\n"; continue; } for (const auto &arg : method->args()) { emitJavaReaderWriter( out, "_hidl_request", arg, true /* isReader */, false /* addPrefixToName */); } if (!needsCallback && returnsValue) { const NamedReference* returnArg = method->results()[0]; out << returnArg->type().getJavaType() << " _hidl_out_" << returnArg->name() << " = "; } out << method->name() << "("; bool firstField = true; for (const auto &arg : method->args()) { if (!firstField) { out << ", "; } out << arg->name(); firstField = false; } if (needsCallback) { if (!firstField) { out << ", "; } out << "new " << method->name() << "Callback() {\n"; out.indent(); out << "@Override\n" << "public void onValues("; method->emitJavaResultSignature(out); out << ") {\n"; out.indent(); out << "_hidl_reply.writeStatus(android.os.HwParcel.STATUS_SUCCESS);\n"; for (const auto &arg : method->results()) { emitJavaReaderWriter( out, "_hidl_reply", arg, false /* isReader */, false /* addPrefixToName */); // no need to add _hidl_out because out vars are are scoped } out << "_hidl_reply.send();\n" << "}}"; out.unindent(); out.unindent(); } out << ");\n"; if (!needsCallback && !method->isOneway()) { out << "_hidl_reply.writeStatus(android.os.HwParcel.STATUS_SUCCESS);\n"; if (returnsValue) { const NamedReference* returnArg = method->results()[0]; emitJavaReaderWriter( out, "_hidl_reply", returnArg, false /* isReader */, true /* addPrefixToName */); } out << "_hidl_reply.send();\n"; } out << "break;\n"; out.unindent(); out << "}\n\n"; } out.unindent(); out << "}\n"; out.unindent(); out << "}\n"; out.unindent(); out << "}\n"; out.unindent(); out << "}\n"; } void AST::emitJavaTypeDeclarations(Formatter& out) const { mRootScope.emitJavaTypeDeclarations(out, false /* atTopLevel */); } } // namespace android generateVts.cpp0100644 0000000 0000000 00000005511 13521773237 012614 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "Annotation.h" #include "Coordinator.h" #include "Interface.h" #include "Method.h" #include "Scope.h" #include #include #include #include namespace android { void AST::emitVtsTypeDeclarations(Formatter& out) const { if (AST::isInterface()) { const Interface* iface = mRootScope.getInterface(); return iface->emitVtsAttributeDeclaration(out); } for (const auto& type : mRootScope.getSubTypes()) { // Skip for TypeDef as it is just an alias of a defined type. if (type->isTypeDef()) { continue; } out << "attribute: {\n"; out.indent(); type->emitVtsTypeDeclarations(out); out.unindent(); out << "}\n\n"; } } void AST::generateVts(Formatter& out) const { std::string baseName = AST::getBaseName(); const Interface *iface = AST::getInterface(); out << "component_class: HAL_HIDL\n"; out << "component_type_version: " << mPackage.version() << "\n"; out << "component_name: \"" << (iface ? iface->localName() : "types") << "\"\n\n"; out << "package: \"" << mPackage.package() << "\"\n\n"; // Generate import statement for all imported interface/types. std::set allImportedNames; getAllImportedNames(&allImportedNames); for (const auto &name : allImportedNames) { // ignore IBase. if (name != gIBaseFqName) { out << "import: \"" << name.string() << "\"\n"; } } out << "\n"; if (isInterface()) { const Interface* iface = mRootScope.getInterface(); out << "interface: {\n"; out.indent(); std::vector chain = iface->typeChain(); // Generate all the attribute declarations first. emitVtsTypeDeclarations(out); // Generate all the method declarations. for (auto it = chain.rbegin(); it != chain.rend(); ++it) { const Interface *superInterface = *it; superInterface->emitVtsMethodDeclaration(out); } out.unindent(); out << "}\n"; } else { emitVtsTypeDeclarations(out); } } } // namespace android hidl-gen_l.h0100644 0000000 0000000 00000001657 13521773237 012003 0ustar000000000 0000000 /* * 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 #include #include 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); } // namespace androidhidl-gen_l.ll0100644 0000000 0000000 00000017347 13521773237 012166 0ustar000000000 0000000 /* * 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. */ 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})* %{ #include "Annotation.h" #include "AST.h" #include "ArrayType.h" #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" #include "FmqType.h" #include "hidl-gen_y.h" #include using namespace android; using token = yy::parser::token; 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" %} %option yylineno %option noyywrap %option nounput %option noinput %option reentrant %option bison-bridge %option bison-locations %x COMMENT_STATE %x DOC_COMMENT_STATE %% "/**" { gCurrentComment.clear(); BEGIN(DOC_COMMENT_STATE); } "*/" { BEGIN(INITIAL); yylval->docComment = new DocComment(gCurrentComment); return token::DOC_COMMENT; } [^*\n]* { gCurrentComment += yytext; } [\n] { gCurrentComment += yytext; yylloc->lines(); } [*] { gCurrentComment += yytext; } "/*" { BEGIN(COMMENT_STATE); } "*/" { BEGIN(INITIAL); } [\n] { yylloc->lines(); } . { } "//"[^\r\n]* { /* skip C++ style comment */ } "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; } "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); } "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; } "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; } "(" { 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('@'); } {COMPONENT} { yylval->str = strdup(yytext); return token::IDENTIFIER; } {FQNAME} { yylval->str = strdup(yytext); return token::FQNAME; } 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; } {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 namespace android { status_t parseFile(AST* ast, std::unique_ptr> file) { yyscan_t scanner; yylex_init(&scanner); yyset_in(file.get(), scanner); Scope* scopeStack = ast->getRootScope(); int res = yy::parser(scanner, ast, &scopeStack).parse(); yylex_destroy(scanner); if (res != 0 || ast->syntaxErrors() != 0) { return UNKNOWN_ERROR; } return OK; } } // namespace android hidl-gen_y.yy0100644 0000000 0000000 00000102722 13521773237 012225 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ %{ #include "AST.h" #include "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 "RefType.h" #include "Scope.h" #include "TypeDef.h" #include "VectorType.h" #include "hidl-gen_y.h" #include #include #include #include using namespace android; 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)); *scope = container; } void leaveScope(AST* ast, Scope** scope) { CHECK((*scope) != ast->getRootScope()); *scope = (*scope)->parent(); } ::android::Location convertYYLoc(const yy::parser::location_type &loc) { return ::android::Location( ::android::Position(*(loc.begin.filename), loc.begin.line, loc.begin.column), ::android::Position(*(loc.end.filename), loc.end.line, loc.end.column) ); } bool isValidInterfaceField(const std::string& identifier, std::string *errorMsg) { static const std::vector reserved({ // Injected names to C++ interfaces by auto-generated code "isRemote", "descriptor", "hidlStaticBlock", "onTransact", "castFrom", "Proxy", "Stub", "getService", // Injected names to Java interfaces by auto-generated code "asInterface", "castFrom", "getService", "toString", // Inherited methods from IBase is detected in addMethod. Not added here // because we need hidl-gen to compile IBase. // Inherited names by interfaces from IInterface / IBinder "onAsBinder", "asBinder", "queryLocalInterface", "getInterfaceDescriptor", "isBinderAlive", "pingBinder", "dump", "transact", "checkSubclass", "attachObject", "findObject", "detachObject", "localBinder", "remoteBinder", "mImpl", // Inherited names from HidlInstrumentor "InstrumentationEvent", "configureInstrumentation", "registerInstrumentationCallbacks", "isInstrumentationLib", "mInstrumentationCal1lbacks", "mEnableInstrumentation", "mInstrumentationLibPackage", "mInterfaceName", // Collide with names in BsFoo "mImpl", "addOnewayTask", "mOnewayQueue", // Inherited names from Java IHwInterface "asBinder", }); if (std::find(reserved.begin(), reserved.end(), identifier) != reserved.end()) { *errorMsg = identifier + " cannot be a name inside an interface"; return false; } return true; } bool isValidStructField(const std::string& identifier, std::string *errorMsg) { static const std::vector reserved({ // Injected names to structs and unions by auto-generated code "readEmbeddedFromParcel", "writeEmbeddedToParcel", "readVectorFromParcel", "writeVectorToParcel", "writeEmbeddedToBlob", }); if (std::find(reserved.begin(), reserved.end(), identifier) != reserved.end()) { *errorMsg = identifier + " cannot be a name inside an struct or union"; return false; } return true; } bool isValidIdentifier(const std::string& identifier, std::string *errorMsg) { static const std::vector keywords({ "uint8_t", "uint16_t", "uint32_t", "uint64_t", "int8_t", "int16_t", "int32_t", "int64_t", "bool", "float", "double", "interface", "struct", "union", "string", "vec", "enum", "ref", "handle", "package", "import", "typedef", "generates", "oneway", "extends", "fmq_sync", "fmq_unsync", }); static const std::vector cppKeywords({ "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class", "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "inline", "int", "import", "long", "module", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq", }); static const std::vector javaKeywords({ "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", "long", "strictfp", "volatile", "const", "float", "native", "super", "while", }); static const std::vector cppCollide({ "size_t", "offsetof", }); // errors if (std::find(keywords.begin(), keywords.end(), identifier) != keywords.end()) { *errorMsg = identifier + " is a HIDL keyword " "and is therefore not a valid identifier"; return false; } if (std::find(cppKeywords.begin(), cppKeywords.end(), identifier) != cppKeywords.end()) { *errorMsg = identifier + " is a C++ keyword " "and is therefore not a valid identifier"; return false; } if (std::find(javaKeywords.begin(), javaKeywords.end(), identifier) != javaKeywords.end()) { *errorMsg = identifier + " is a Java keyword " "and is therefore not a valid identifier"; return false; } if (std::find(cppCollide.begin(), cppCollide.end(), identifier) != cppCollide.end()) { *errorMsg = identifier + " collides with reserved names in C++ code " "and is therefore not a valid identifier"; return false; } if (StringHelper::StartsWith(identifier, "_hidl_")) { *errorMsg = identifier + " starts with _hidl_ " "and is therefore not a valid identifier"; return false; } if (StringHelper::StartsWith(identifier, "hidl_")) { *errorMsg = identifier + " starts with hidl_ " "and is therefore not a valid identifier"; return false; } if (StringHelper::EndsWith(identifier, "_cb")) { *errorMsg = identifier + " ends with _cb " "and is therefore not a valid identifier"; return false; } return true; } // Return true if identifier is an acceptable name for an UDT. bool isValidTypeName(const std::string& identifier, std::string *errorMsg) { if (!isValidIdentifier(identifier, errorMsg)) { return false; } if (identifier == "toString") { *errorMsg = identifier + " is not a valid type name"; return false; } return true; } %} %initial-action { // Initialize the initial location. @$.begin.filename = @$.end.filename = const_cast(&ast->getFilename()); } %parse-param { void* scanner } %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" %expect-rr 0 %error-verbose %debug %token DOC_COMMENT "doc comment" %token ENUM "keyword `enum`" %token EXTENDS "keyword `extends`" %token FQNAME "fully-qualified name" %token GENERATES "keyword `generates`" %token IDENTIFIER "identifier" %token IMPORT "keyword `import`" %token INTEGER "integer value" %token FLOAT "float value" %token INTERFACE "keyword `interface`" %token PACKAGE "keyword `package`" %token TYPE "type" %token STRUCT "keyword `struct`" %token STRING_LITERAL "string literal" %token TYPEDEF "keyword `typedef`" %token UNION "keyword `union`" %token TEMPLATED "templated type" %token ONEWAY "keyword `oneway`" %token UNKNOWN "unknown character" /* Operator precedence and associativity, as per * http://en.cppreference.com/w/cpp/language/operator_precedence */ /* Precedence level 15 ternary operator */ %right '?' ':' /* Precedence level 13 - 14, LTR, logical operators*/ %left LOGICAL_OR %left LOGICAL_AND /* Precedence level 10 - 12, LTR, bitwise operators*/ %left '|' %left '^' %left '&' /* Precedence level 9, LTR */ %left EQUALITY NEQ /* Precedence level 8, LTR */ %left '<' '>' LEQ GEQ /* Precedence level 7, LTR */ %left LSHIFT RSHIFT /* Precedence level 6, LTR */ %left '+' '-' /* Precedence level 5, LTR */ %left '*' '/' '%' /* Precedence level 3, RTL; but we have to use %left here */ %left UNARY_MINUS UNARY_PLUS '!' '~' %type doc_comments %type error_stmt error %type package %type fqname %type fqtype %type valid_identifier valid_type_name %type type enum_storage_type type_or_inplace_compound_declaration %type array_type_base %type array_type %type opt_extends %type type_declaration commentable_type_declaration type_declaration_body %type interface_declaration typedef_declaration %type named_struct_or_union_declaration named_enum_declaration %type compound_declaration annotated_compound_declaration %type field_declaration commentable_field_declaration %type field_declarations struct_or_union_body %type const_expr %type enum_value commentable_enum_value %type enum_values enum_declaration_body %type typed_vars %type typed_var %type method_declaration commentable_method_declaration %type struct_or_union_keyword %type annotation_string_values annotation_string_value %type annotation_const_expr_values annotation_const_expr_value %type annotation_param %type opt_annotation_params annotation_params %type annotation %type opt_annotations %start program %union { const char *str; android::Type* type; android::Reference* referenceToType; android::ArrayType *arrayType; android::TemplatedType *templatedType; android::FQName *fqName; android::CompoundType *compoundType; android::NamedReference* field; std::vector*>* fields; android::EnumValue *enumValue; android::ConstantExpression *constantExpression; std::vector *enumValues; android::NamedReference* typedVar; android::TypedVarVector *typedVars; android::Method *method; android::CompoundType::Style compoundStyle; std::vector *stringVec; std::vector *constExprVec; android::AnnotationParam *annotationParam; android::AnnotationParamVector *annotationParams; android::Annotation *annotation; std::vector *annotations; android::DocComment* docComment; } %% program // 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 : IDENTIFIER { std::string errorMsg; if (!isValidIdentifier($1, &errorMsg)) { std::cerr << "ERROR: " << errorMsg << " at " << @1 << "\n"; YYERROR; } $$ = $1; } ; valid_type_name : IDENTIFIER { std::string errorMsg; if (!isValidTypeName($1, &errorMsg)) { std::cerr << "ERROR: " << errorMsg << " at " << @1 << "\n"; YYERROR; } $$ = $1; } ; opt_annotations : /* empty */ { $$ = new std::vector; } | opt_annotations annotation { $$ = $1; $$->push_back($2); } ; annotation : '@' IDENTIFIER opt_annotation_params { $$ = new Annotation($2, $3); } ; opt_annotation_params : /* empty */ { $$ = new AnnotationParamVector; } | '(' annotation_params ')' { $$ = $2; } ; annotation_params : annotation_param { $$ = new AnnotationParamVector; $$->push_back($1); } | annotation_params ',' annotation_param { $$ = $1; $$->push_back($3); } ; annotation_param : IDENTIFIER '=' annotation_string_value { $$ = new StringAnnotationParam($1, $3); } | IDENTIFIER '=' annotation_const_expr_value { $$ = new ConstantExpressionAnnotationParam($1, $3); } ; annotation_string_value : STRING_LITERAL { $$ = new std::vector; $$->push_back($1); } | '{' annotation_string_values '}' { $$ = $2; } ; annotation_string_values : STRING_LITERAL { $$ = new std::vector; $$->push_back($1); } | annotation_string_values ',' STRING_LITERAL { $$ = $1; $$->push_back($3); } ; annotation_const_expr_value : const_expr { $$ = new std::vector; $$->push_back($1); } | '{' annotation_const_expr_values '}' { $$ = $2; } ; annotation_const_expr_values : const_expr { $$ = new std::vector; $$->push_back($1); } | annotation_const_expr_values ',' const_expr { $$ = $1; $$->push_back($3); } ; error_stmt : error ';' { $$ = $1; ast->addSyntaxError(); } ; require_semicolon : ';' | /* empty */ { std::cerr << "ERROR: missing ; at " << @$ << "\n"; ast->addSyntaxError(); } ; fqname : FQNAME { $$ = new FQName(); if(!FQName::parse($1, $$)) { std::cerr << "ERROR: FQName '" << $1 << "' is not valid at " << @1 << ".\n"; YYERROR; } } | valid_type_name { $$ = new FQName(); if(!FQName::parse($1, $$)) { std::cerr << "ERROR: FQName '" << $1 << "' is not valid at " << @1 << ".\n"; YYERROR; } } ; fqtype : fqname { $$ = new Reference(*$1, convertYYLoc(@1)); } | TYPE { $$ = new Reference($1, convertYYLoc(@1)); } ; package : PACKAGE FQNAME require_semicolon { if (!ast->setPackage($2)) { std::cerr << "ERROR: Malformed package identifier '" << $2 << "' at " << @2 << "\n"; YYERROR; } } | error { std::cerr << "ERROR: Package statement must be at the beginning of the file (" << @1 << ")\n"; $$ = $1; ast->addSyntaxError(); } ; import_stmt : IMPORT FQNAME require_semicolon { if (!ast->addImport($2)) { std::cerr << "ERROR: Unable to import '" << $2 << "' at " << @2 << "\n"; ast->addSyntaxError(); } } | IMPORT valid_type_name require_semicolon { if (!ast->addImport($2)) { std::cerr << "ERROR: Unable to import '" << $2 << "' at " << @2 << "\n"; ast->addSyntaxError(); } } | IMPORT error_stmt ; imports : /* empty */ | imports import_stmt ; opt_extends : /* empty */ { $$ = nullptr; } | EXTENDS fqtype { $$ = $2; } ; interface_declarations : /* empty */ | interface_declarations commentable_type_declaration { CHECK((*scope)->isInterface()); std::string errorMsg; if ($2 != nullptr && $2->isNamedType() && !isValidInterfaceField(static_cast($2)->localName().c_str(), &errorMsg)) { std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n"; YYERROR; } } | interface_declarations commentable_method_declaration { CHECK((*scope)->isInterface()); std::string errorMsg; if ($2 != nullptr && !isValidInterfaceField($2->name().c_str(), &errorMsg)) { std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n"; YYERROR; } if ($2 != nullptr) { Interface *iface = static_cast(*scope); if (!iface->addMethod($2)) { std::cerr << "ERROR: Unable to add method '" << $2->name() << "' at " << @2 << "\n"; YYERROR; } } // ignore if $2 is nullptr (from error recovery) } ; type_declarations : /* empty */ | error_stmt | 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->isTypeDef()) { CHECK($2->isScope()); static_cast($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 << "\n"; YYERROR; } $$ = $2; } ; type_declaration_body : named_struct_or_union_declaration require_semicolon | named_enum_declaration require_semicolon | typedef_declaration require_semicolon | interface_declaration require_semicolon ; interface_declaration : INTERFACE valid_type_name opt_extends { Reference* superType = $3; bool isIBase = ast->package().package() == gIBaseFqName.package(); if (isIBase) { if (superType != nullptr) { std::cerr << "ERROR: IBase must not extend any interface at " << @3 << "\n"; YYERROR; } superType = new Reference(); } else { if (!ast->addImport(gIBaseFqName.string().c_str())) { std::cerr << "ERROR: Unable to automatically import '" << gIBaseFqName.string() << "' at " << @$ << "\n"; YYERROR; } if (superType == nullptr) { superType = new Reference(gIBaseFqName, convertYYLoc(@$)); } } if ($2[0] != 'I') { std::cerr << "ERROR: All interface names must start with an 'I' " << "prefix at " << @2 << "\n"; YYERROR; } if (*scope != ast->getRootScope()) { std::cerr << "ERROR: All interface must declared in " << "global scope at " << @2 << "\n"; YYERROR; } Interface* iface = new Interface( $2, ast->makeFullName($2, *scope), convertYYLoc(@2), *scope, *superType, ast->getFileHash()); enterScope(ast, scope, iface); } '{' interface_declarations '}' { CHECK((*scope)->isInterface()); Interface *iface = static_cast(*scope); CHECK(iface->addAllReservedMethods()); leaveScope(ast, scope); ast->addScopedType(iface, *scope); $$ = iface; } ; typedef_declaration : TYPEDEF type valid_type_name { // 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 { $$ = LiteralConstantExpression::tryParse($1); if ($$ == nullptr) { std::cerr << "ERROR: Could not parse literal: " << $1 << " at " << @1 << ".\n"; YYERROR; } } | fqname { if(!$1->isValidValueName()) { std::cerr << "ERROR: '" << $1->string() << "' does not refer to an enum value at " << @1 << ".\n"; YYERROR; } $$ = new ReferenceConstantExpression( Reference(*$1, convertYYLoc(@1)), $1->string()); } | const_expr '?' const_expr ':' const_expr { $$ = new TernaryConstantExpression($1, $3, $5); } | 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 $$ = 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 /* name */, $4 /* args */, new std::vector*> /* results */, false /* oneway */, $1 /* annotations */, convertYYLoc(@$)); } | opt_annotations ONEWAY valid_identifier '(' typed_vars ')' require_semicolon { $$ = new Method($3 /* name */, $5 /* args */, new std::vector*> /* results */, true /* oneway */, $1 /* annotations */, convertYYLoc(@$)); } | opt_annotations valid_identifier '(' typed_vars ')' GENERATES '(' typed_vars ')' require_semicolon { 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(@$)); } ; typed_vars : /* empty */ { $$ = new TypedVarVector(); } | typed_var { $$ = new TypedVarVector(); if (!$$->add($1)) { std::cerr << "ERROR: duplicated argument or result name " << $1->name() << " at " << @1 << "\n"; ast->addSyntaxError(); } } | typed_vars ',' typed_var { $$ = $1; if (!$$->add($3)) { std::cerr << "ERROR: duplicated argument or result name " << $3->name() << " at " << @3 << "\n"; ast->addSyntaxError(); } } ; typed_var : type valid_identifier { $$ = new NamedReference($2, *$1, convertYYLoc(@2)); } | type { $$ = new NamedReference("", *$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(); } ; struct_or_union_keyword : STRUCT { $$ = CompoundType::STYLE_STRUCT; } | UNION { $$ = CompoundType::STYLE_UNION; } ; named_struct_or_union_declaration : struct_or_union_keyword valid_type_name { CompoundType *container = new CompoundType( $1, $2, ast->makeFullName($2, *scope), convertYYLoc(@2), *scope); enterScope(ast, scope, container); } struct_or_union_body { CHECK((*scope)->isCompoundType()); CompoundType *container = static_cast(*scope); container->setFields($4); leaveScope(ast, scope); ast->addScopedType(container, *scope); $$ = container; } ; struct_or_union_body : '{' field_declarations '}' { $$ = $2; } ; field_declarations : /* empty */ { $$ = new std::vector*>; } | field_declarations commentable_field_declaration { $$ = $1; // 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_or_inplace_compound_declaration valid_identifier require_semicolon { CHECK((*scope)->isCompoundType()); std::string errorMsg; if (static_cast(*scope)->style() == CompoundType::STYLE_STRUCT && !isValidStructField($2, &errorMsg)) { std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n"; YYERROR; } $$ = new NamedReference($2, *$1, convertYYLoc(@2)); } | annotated_compound_declaration ';' { CHECK((*scope)->isCompoundType()); std::string errorMsg; if (static_cast(*scope)->style() == CompoundType::STYLE_STRUCT && $1 != nullptr && $1->isNamedType() && !isValidStructField(static_cast($1)->localName().c_str(), &errorMsg)) { std::cerr << "ERROR: " << errorMsg << " at " << @2 << "\n"; YYERROR; } // Returns fields only $$ = nullptr; } ; annotated_compound_declaration : opt_annotations compound_declaration { CHECK($2->isScope()); static_cast($2)->setAnnotations($1); $$ = $2; } ; compound_declaration : named_struct_or_union_declaration { $$ = $1; } | named_enum_declaration { $$ = $1; } ; enum_storage_type : ':' fqtype { $$ = $2; } | /* empty */ { $$ = nullptr; } ; opt_comma : /* empty */ | ',' ; named_enum_declaration : ENUM valid_type_name enum_storage_type { 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( 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 { CHECK((*scope)->isEnum()); EnumType* enumType = static_cast(*scope); leaveScope(ast, scope); ast->addScopedType(enumType, *scope); $$ = enumType; } ; enum_declaration_body : '{' 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 /* name */, nullptr /* value */, convertYYLoc(@$)); } | valid_identifier '=' const_expr { $$ = new EnumValue($1 /* name */, $3 /* value */, convertYYLoc(@$)); } ; enum_values : /* empty */ { /* do nothing */ } | commentable_enum_value { CHECK((*scope)->isEnum()); static_cast(*scope)->addValue($1); } | enum_values ',' commentable_enum_value { CHECK((*scope)->isEnum()); static_cast(*scope)->addValue($3); } | error ',' commentable_enum_value { ast->addSyntaxError(); CHECK((*scope)->isEnum()); static_cast(*scope)->addValue($3); } | enum_values ',' error ',' commentable_enum_value { ast->addSyntaxError(); CHECK((*scope)->isEnum()); static_cast(*scope)->addValue($5); } ; array_type_base : fqtype { $$ = $1; } | TEMPLATED '<' type '>' { $1->setElementType(*$3); $$ = new Reference($1, convertYYLoc(@1)); } | TEMPLATED '<' TEMPLATED '<' type RSHIFT { $3->setElementType(*$5); $1->setElementType(Reference($3, convertYYLoc(@3))); $$ = new Reference($1, convertYYLoc(@1)); } ; array_type : array_type_base '[' const_expr ']' { $$ = new ArrayType(*$1, $3, *scope); } | array_type '[' const_expr ']' { $$ = $1; $$->appendDimension($3); } ; type : array_type_base { $$ = $1; } | array_type { $$ = new Reference($1, convertYYLoc(@1)); } | INTERFACE { // "interface" is a synonym of android.hidl.base@1.0::IBase $$ = new Reference(gIBaseFqName, convertYYLoc(@1)); } ; type_or_inplace_compound_declaration : type { $$ = $1; } | annotated_compound_declaration { $$ = new Reference($1, convertYYLoc(@1)); } ; %% void yy::parser::error( const yy::parser::location_type &where, const std::string &errstr) { std::cerr << "ERROR: " << errstr << " at " << where << "\n"; } include_hash/0040755 0000000 0000000 00000000000 13521773237 012250 5ustar000000000 0000000 include_hash/hidl-hash/0040755 0000000 0000000 00000000000 13521773237 014111 5ustar000000000 0000000 include_hash/hidl-hash/Hash.h0100644 0000000 0000000 00000003402 13521773237 015141 0ustar000000000 0000000 /* * 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. */ #ifndef HIDL_GEN_HASH_HASH_H_ #define HIDL_GEN_HASH_HASH_H_ #include #include namespace android { struct Hash { static const std::vector 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 lookupHash(const std::string& path, const std::string& interfaceName, std::string* err, bool* fileExists = nullptr); static std::string hexString(const std::vector &hash); std::string hexString() const; const std::vector &raw() const; const std::string &getPath() const; private: Hash(const std::string &path); static Hash& getMutableHash(const std::string& path); const std::string mPath; std::vector mHash; }; } // namespace android #endif // HIDL_GEN_HASH_HASH_H_ main.cpp0100644 0000000 0000000 00000135524 13521773237 011261 0ustar000000000 0000000 /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AST.h" #include "Coordinator.h" #include "Scope.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace android; 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 }; 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 }; // Represents a file that is generated by an -L option for an FQName struct FileGenerator { using ShouldGenerateFunction = std::function; using FileNameForFQName = std::function; using GenerationFunction = std::function; 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* 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; 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 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* targets) const; status_t appendOutputFiles(const FQName& fqName, const Coordinator* coordinator, std::vector* 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* exportedPackageInterfaces) { CHECK(fqName.isFullyQualified()); if (fqName.name() != "types") { exportedPackageInterfaces->push_back(fqName); return OK; } 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 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* 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 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 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* outputFiles) const { std::vector 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 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) { limitToType = fqName.name().substr(strlen("types.")); FQName typesName = fqName.getTypesForPackage(); ast = coordinator->parse(typesName); } else { ast = coordinator->parse(fqName); } 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; }; static status_t dumpDefinedButUnreferencedTypeNames(const FQName& packageFQName, const Coordinator* coordinator) { std::vector packageInterfaces; status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); if (err != OK) return err; std::set unreferencedDefinitions; std::set unreferencedImports; err = coordinator->addUnreferencedTypes(packageInterfaces, &unreferencedDefinitions, &unreferencedImports); 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 : unreferencedImports) { std::cerr << "VERBOSE: IMPORTED-BUT-NOT-REFERENCED " << fqName.string() << std::endl; } return OK; } static std::string makeLibraryName(const FQName &packageFQName) { return packageFQName.string(); } static status_t isPackageJavaCompatible(const FQName& packageFQName, const Coordinator* coordinator, bool* compatible) { std::vector todo; status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &todo); if (err != OK) { return err; } std::set seen; for (const auto &iface : todo) { seen.insert(iface); } // Form the transitive closure of all imported interfaces (and types.hal-s) // If any one of them is not java compatible, this package isn't either. while (!todo.empty()) { const FQName fqName = todo.back(); todo.pop_back(); AST *ast = coordinator->parse(fqName); if (ast == nullptr) { return UNKNOWN_ERROR; } if (!ast->isJavaCompatible()) { *compatible = false; return OK; } std::set importedPackages; ast->getImportedPackages(&importedPackages); for (const auto &package : importedPackages) { std::vector packageInterfaces; status_t err = coordinator->appendPackageInterfacesToVector( package, &packageInterfaces); if (err != OK) { return err; } for (const auto &iface : packageInterfaces) { if (seen.find(iface) != seen.end()) { continue; } todo.push_back(iface); seen.insert(iface); } } } *compatible = true; return OK; } static bool packageNeedsJavaCode( const std::vector &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 || packageInterfaces[0].name() != "types") { return true; } CHECK(typesAST != nullptr); // We'll have to generate Java code if types.hal contains any non-typedef // type declarations. Scope* rootScope = typesAST->getRootScope(); std::vector subTypes = rootScope->getSubTypes(); for (const auto &subType : subTypes) { if (!subType->isTypeDef()) { return true; } } return false; } bool validateIsPackage(const FQName& fqName, const Coordinator*, const std::string& /* language */) { if (fqName.package().empty()) { fprintf(stderr, "ERROR: Expecting package name\n"); return false; } if (fqName.version().empty()) { fprintf(stderr, "ERROR: Expecting package version\n"); return false; } if (!fqName.name().empty()) { fprintf(stderr, "ERROR: Expecting only package name and version.\n"); return false; } return true; } bool isHidlTransportPackage(const FQName& fqName) { 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.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"; } bool isSystemPackage(const FQName &package) { return package.inPackage("android.hidl") || package.inPackage("android.system") || package.inPackage("android.frameworks") || package.inPackage("android.hardware"); } // 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; }; std::string path; status_t err = coordinator->getFilepath(fqName, Coordinator::Location::PACKAGE_ROOT, ".hidl_for_test", &path); if (err != OK) return err; const bool exists = fileExists(path); if (exists) { coordinator->onFileAccess(path, "r"); } *isTestPackage = exists; return OK; } static status_t generateAdapterMainSource(Formatter& out, const FQName& packageFQName, const Coordinator* coordinator) { std::vector packageInterfaces; status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); if (err != OK) { return err; } out << "#include \n"; for (auto &interface : packageInterfaces) { if (interface.name() == "types") { continue; } 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 status_t generateAndroidBpForPackage(Formatter& out, const FQName& packageFQName, const Coordinator* coordinator) { CHECK(packageFQName.isValid() && !packageFQName.isFullyQualified() && packageFQName.name().empty()); std::vector packageInterfaces; status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); if (err != OK) { return err; } std::set importedPackagesHierarchy; std::vector exportedTypes; AST* typesAST = nullptr; 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(&importedPackagesHierarchy); ast->appendToExportedTypesVector(&exportedTypes); } bool needsJavaCode = packageNeedsJavaCode(packageInterfaces, typesAST); bool genJavaConstants = needsJavaCode && !exportedTypes.empty(); bool isJavaCompatible; err = isPackageJavaCompatible(packageFQName, coordinator, &isJavaCompatible); if (err != OK) return err; bool genJavaLibrary = needsJavaCode && isJavaCompatible; bool generateForTest; err = isTestPackage(packageFQName, coordinator, &generateForTest); if (err != OK) return err; bool isVndk = !generateForTest && isSystemPackage(packageFQName); bool isVndkSp = isVndk && isSystemProcessSupportedPackage(packageFQName); std::string packageRoot; err = coordinator->getPackageRoot(packageFQName, &packageRoot); if (err != OK) return err; out << "// This file is autogenerated by hidl-gen -Landroidbp.\n\n"; 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 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(Formatter& out, const FQName& packageFQName, const Coordinator* coordinator) { const std::string libraryName = makeLibraryName(packageFQName) + "-impl"; std::vector packageInterfaces; status_t err = coordinator->appendPackageInterfacesToVector(packageFQName, &packageInterfaces); if (err != OK) { return err; } std::set importedPackages; 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; } ast->getImportedPackages(&importedPackages); } out << "cc_library_shared {\n"; out.indent([&] { 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 '-' 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") { continue; } out << "\"" << fqName.getInterfaceBaseName() << ".cpp\",\n"; } }); out << "],\n" << "shared_libs: [\n"; out.indent([&] { out << "\"libhidlbase\",\n" << "\"libhidltransport\",\n" << "\"libutils\",\n" << "\"" << makeLibraryName(packageFQName) << "\",\n"; for (const auto &importedPackage : importedPackages) { if (isHidlTransportPackage(importedPackage)) { continue; } out << "\"" << makeLibraryName(importedPackage) << "\",\n"; } }); out << "],\n"; }); out << "}\n"; return OK; } 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; } if (fqName.version().empty()) { fprintf(stderr, "ERROR: Expecting package version\n"); return false; } const std::string &name = fqName.name(); if (!name.empty()) { if (name.find('.') == std::string::npos) { return true; } if (language != "java" || name.find("types.") != 0) { // When generating java sources for "types.hal", output can be // constrained to just one of the top-level types declared // by using the extended syntax // android.hardware.Foo@1.0::types.TopLevelTypeName. // In all other cases (different language, not 'types') the dot // notation in the name is illegal in this context. return false; } 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; } 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() && packageFQName.name().empty()); std::vector packageInterfaces; status_t err = coordinator->appendPackageInterfacesToVector( packageFQName, &packageInterfaces); if (err != OK) { return err; } std::vector 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; } ast->appendToExportedTypesVector(&exportedTypes); } if (exportedTypes.empty()) { return OK; } if (!out.isValid()) { return UNKNOWN_ERROR; } 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" << "// Location: " << packagePath << "\n\n"; std::string guard; if (forJava) { out << "package " << packageFQName.javaPackage() << ";\n\n"; out << "public class Constants {\n"; out.indent(); } else { guard = "HIDL_GENERATED_"; guard += StringHelper::Uppercase(packageFQName.tokenName()); guard += "_"; guard += "EXPORTED_CONSTANTS_H_"; out << "#ifndef " << guard << "\n#define " << guard << "\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"; } for (const auto &type : exportedTypes) { type->emitExportedHeader(out, forJava); } if (forJava) { out.unindent(); out << "}\n"; } else { out << "#ifdef __cplusplus\n}\n#endif\n\n#endif // " << guard << "\n"; } return OK; }; } static status_t generateHashOutput(Formatter& out, const FQName& fqName, const Coordinator* coordinator) { CHECK(fqName.isFullyQualified()); AST* ast = coordinator->parse(fqName, {} /* parsed */, Coordinator::Enforce::NO_HASH /* enforcement */); if (ast == NULL) { fprintf(stderr, "ERROR: Could not parse %s. Aborting.\n", fqName.string().c_str()); return UNKNOWN_ERROR; } out << Hash::getHash(ast->getFilename()).hexString() << " " << fqName.string() << "\n"; return OK; } template std::vector operator+(const std::vector& lhs, const std::vector& rhs) { std::vector 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; } // clang-format off static const std::vector kCppHeaderFormats = { { FileGenerator::alwaysGenerate, [](const FQName& fqName) { return fqName.name() + ".h"; }, astGenerationFunction(&AST::generateInterfaceHeader), }, { FileGenerator::alwaysGenerate, [](const FQName& fqName) { return fqName.isInterfaceName() ? fqName.getInterfaceHwName() + ".h" : "hwtypes.h"; }, astGenerationFunction(&AST::generateHwBinderHeader), }, { FileGenerator::generateForInterfaces, [](const FQName& fqName) { return fqName.getInterfaceStubName() + ".h"; }, astGenerationFunction(&AST::generateStubHeader), }, { FileGenerator::generateForInterfaces, [](const FQName& fqName) { return fqName.getInterfaceProxyName() + ".h"; }, astGenerationFunction(&AST::generateProxyHeader), }, { FileGenerator::generateForInterfaces, [](const FQName& fqName) { return fqName.getInterfacePassthroughName() + ".h"; }, astGenerationFunction(&AST::generatePassthroughHeader), }, }; static const std::vector kCppSourceFormats = { { FileGenerator::alwaysGenerate, [](const FQName& fqName) { return fqName.isInterfaceName() ? fqName.getInterfaceBaseName() + "All.cpp" : "types.cpp"; }, astGenerationFunction(&AST::generateCppSource), }, }; static const std::vector kCppImplHeaderFormats = { { FileGenerator::generateForInterfaces, [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".h"; }, astGenerationFunction(&AST::generateCppImplHeader), }, }; static const std::vector kCppImplSourceFormats = { { FileGenerator::generateForInterfaces, [](const FQName& fqName) { return fqName.getInterfaceBaseName() + ".cpp"; }, astGenerationFunction(&AST::generateCppImplSource), }, }; static const std::vector kCppAdapterHeaderFormats = { { FileGenerator::alwaysGenerate, [](const FQName& fqName) { return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".h" : "Atypes.h"; }, astGenerationFunction(&AST::generateCppAdapterHeader), }, }; static const std::vector kCppAdapterSourceFormats = { { FileGenerator::alwaysGenerate, [](const FQName& fqName) { return fqName.isInterfaceName() ? fqName.getInterfaceAdapterName() + ".cpp" : "Atypes.cpp"; }, astGenerationFunction(&AST::generateCppAdapterSource), }, }; static const std::vector 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 ] -o -L [-O ] (-r )+ [-v] [-d ] 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 : The following options are available:\n"); for (auto& e : kFormats) { fprintf(stderr, " %-16s: %s\n", e.name().c_str(), e.description().c_str()); } fprintf(stderr, " -O : The owner of the module for -Landroidbp(-impl)?.\n"); fprintf(stderr, " -o : Location to output files.\n"); fprintf(stderr, " -p : Android build root, defaults to $ANDROID_BUILD_TOP or pwd.\n"); fprintf(stderr, " -r : E.g., android.hardware:hardware/interfaces.\n"); fprintf(stderr, " -v: verbose output.\n"); fprintf(stderr, " -d : location of depfile to write to.\n"); } // hidl is intentionally leaky. Turn off LeakSanitizer by default. extern "C" const char *__asan_default_options() { return "detect_leaks=0"; } int main(int argc, char **argv) { const char *me = argv[0]; 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:O:r:L:vd:")) >= 0) { switch (res) { case 'p': { if (!coordinator.getRootPath().empty()) { fprintf(stderr, "ERROR: -p can only be specified once.\n"); exit(1); } coordinator.setRootPath(optarg); break; } case 'v': { coordinator.setVerbose(true); break; } case 'd': { coordinator.setDepFile(optarg); break; } case 'o': { if (!outputPath.empty()) { fprintf(stderr, "ERROR: -o can only be specified once.\n"); exit(1); } outputPath = optarg; break; } case 'O': { if (!coordinator.getOwner().empty()) { fprintf(stderr, "ERROR: -O 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) { fprintf(stderr, "ERROR: -r option must contain ':': %s\n", val.c_str()); exit(1); } auto root = val.substr(0, index); auto path = val.substr(index + 1); 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': { if (outputFormat != nullptr) { fprintf(stderr, "ERROR: only one -L option allowed. \"%s\" already specified.\n", outputFormat->name().c_str()); exit(1); } for (auto& e : kFormats) { if (e.name() == optarg) { outputFormat = &e; break; } } if (outputFormat == nullptr) { fprintf(stderr, "ERROR: unrecognized -L option: \"%s\".\n", optarg); exit(1); } break; } case '?': case 'h': default: { usage(me); exit(1); break; } } } 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); } argc -= optind; argv += optind; if (argc == 0) { fprintf(stderr, "ERROR: no fqname specified.\n"); usage(me); exit(1); } // Valid options are now in argv[0] .. argv[argc - 1]. switch (outputFormat->mOutputMode) { case OutputMode::NEEDS_DIR: case OutputMode::NEEDS_FILE: { if (outputPath.empty()) { usage(me); exit(1); } if (outputFormat->mOutputMode == OutputMode::NEEDS_DIR) { if (outputPath.back() != '/') { outputPath += "/"; } } break; } case OutputMode::NEEDS_SRC: { if (outputPath.empty()) { outputPath = coordinator.getRootPath(); } if (outputPath.back() != '/') { outputPath += "/"; } break; } default: outputPath.clear(); // Unused. break; } 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; if (!FQName::parse(argv[i], &fqName)) { fprintf(stderr, "ERROR: Invalid fully-qualified name as argument: %s.\n", argv[i]); exit(1); } // 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, &coordinator); if (err != OK) exit(1); err = outputFormat->writeDepFile(fqName, &coordinator); if (err != OK) exit(1); } return 0; } scripts/0040755 0000000 0000000 00000000000 13521773237 011311 5ustar000000000 0000000 scripts/run-tests.sh0100755 0000000 0000000 00000006043 13521773237 013614 0ustar000000000 0000000 #!/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 [-m [...]] [-j ] [-p]" echo " -m : name of test (e.g. VtsHalHealthV2_0TargetTest)" echo " -p: print commands" echo " -j : # 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 $@ test/0040755 0000000 0000000 00000000000 13521773237 010601 5ustar000000000 0000000 test/CleanSpec.mk0100644 0000000 0000000 00000004474 13521773237 012775 0ustar000000000 0000000 # Copyright 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. # # 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 $(PRODUCT_OUT)/data/nativetest64/hidl_test*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest/hidl_test*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/tests.vendor*) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk/tests.vendor*) test/error_test/0040755 0000000 0000000 00000000000 13521773237 012771 5ustar000000000 0000000 test/error_test/Android.bp0100644 0000000 0000000 00000001001 13521773237 014661 0ustar000000000 0000000 genrule { name: "hidl_error_test_gen", 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: [ "hidl_error_test.sh", "**/*.hal", "**/required_error", ], } cc_test_host { name: "hidl_error_test", cflags: ["-Wall", "-Werror"], generated_sources: ["hidl_error_test_gen"], } test/error_test/bad_character/0040755 0000000 0000000 00000000000 13521773237 015533 5ustar000000000 0000000 test/error_test/bad_character/1.0/0040755 0000000 0000000 00000000000 13521773237 016031 5ustar000000000 0000000 test/error_test/bad_character/1.0/IFoo.hal0100644 0000000 0000000 00000001247 13521773237 017354 0ustar000000000 0000000 /* * 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.bad_character@1.0; 😄 interface IFoo { }; test/error_test/bad_character/1.0/required_error0100644 0000000 0000000 00000000052 13521773237 020777 0ustar000000000 0000000 syntax error, unexpected unknown charactertest/error_test/cpp_forward_decl_restriction/0040755 0000000 0000000 00000000000 13521773237 020713 5ustar000000000 0000000 test/error_test/cpp_forward_decl_restriction/1.0/0040755 0000000 0000000 00000000000 13521773237 021211 5ustar000000000 0000000 test/error_test/cpp_forward_decl_restriction/1.0/required_error0100644 0000000 0000000 00000000063 13521773237 024161 0ustar000000000 0000000 C++ forward declaration doesn't support inner typestest/error_test/cpp_forward_decl_restriction/1.0/types.hal0100644 0000000 0000000 00000001476 13521773237 023050 0ustar000000000 0000000 /* * 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.cpp_forward_decl_restriction@1.0; 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 {}; }; }; test/error_test/doc_comment_ends_block/0040755 0000000 0000000 00000000000 13521773237 017443 5ustar000000000 0000000 test/error_test/doc_comment_ends_block/1.0/0040755 0000000 0000000 00000000000 13521773237 017741 5ustar000000000 0000000 test/error_test/doc_comment_ends_block/1.0/IFoo.hal0100644 0000000 0000000 00000001321 13521773237 021255 0ustar000000000 0000000 /* * 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.empty_generates@1.0; interface IFoo { doFoo(); /** * Does foo! */ }; test/error_test/doc_comment_ends_block/1.0/required_error0100644 0000000 0000000 00000000057 13521773237 022714 0ustar000000000 0000000 Doc comments must preceed what they describe attest/error_test/empty_generates/0040755 0000000 0000000 00000000000 13521773237 016164 5ustar000000000 0000000 test/error_test/empty_generates/1.0/0040755 0000000 0000000 00000000000 13521773237 016462 5ustar000000000 0000000 test/error_test/empty_generates/1.0/IFoo.hal0100644 0000000 0000000 00000001273 13521773237 020004 0ustar000000000 0000000 /* * 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.empty_generates@1.0; interface IFoo { foo() generates (); }; test/error_test/empty_generates/1.0/required_error0100644 0000000 0000000 00000000047 13521773237 021434 0ustar000000000 0000000 generates clause used without result attest/error_test/enum_storage/0040755 0000000 0000000 00000000000 13521773237 015461 5ustar000000000 0000000 test/error_test/enum_storage/1.0/0040755 0000000 0000000 00000000000 13521773237 015757 5ustar000000000 0000000 test/error_test/enum_storage/1.0/IFoo.hal0100644 0000000 0000000 00000001316 13521773237 017277 0ustar000000000 0000000 /* * 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.bad_character@1.0; interface IFoo { enum A /* missing storage */ { }; }; test/error_test/enum_storage/1.0/required_error0100644 0000000 0000000 00000000051 13521773237 020724 0ustar000000000 0000000 Must explicitly specify enum storage typetest/error_test/enum_unique_field_names/0040755 0000000 0000000 00000000000 13521773237 017651 5ustar000000000 0000000 test/error_test/enum_unique_field_names/1.0/0040755 0000000 0000000 00000000000 13521773237 020147 5ustar000000000 0000000 test/error_test/enum_unique_field_names/1.0/IFoo.hal0100644 0000000 0000000 00000001412 13521773237 021464 0ustar000000000 0000000 /* * 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.enum_unique_field_names@1.0; interface IFoo { enum E : int32_t { A, B, A, // duplicates first value name }; }; test/error_test/enum_unique_field_names/1.0/required_error0100644 0000000 0000000 00000000031 13521773237 023112 0ustar000000000 0000000 Redefinition of value 'A'test/error_test/enum_unique_field_names_extends/0040755 0000000 0000000 00000000000 13521773237 021403 5ustar000000000 0000000 test/error_test/enum_unique_field_names_extends/1.0/0040755 0000000 0000000 00000000000 13521773237 021701 5ustar000000000 0000000 test/error_test/enum_unique_field_names_extends/1.0/IFoo.hal0100644 0000000 0000000 00000001510 13521773237 023215 0ustar000000000 0000000 /* * 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.enum_unique_field_names_extends@1.0; interface IFoo { enum E1 : int32_t { A, B, C, }; enum E2 : E1 { D, B, // duplicates E1.B name F, }; }; test/error_test/enum_unique_field_names_extends/1.0/required_error0100644 0000000 0000000 00000000051 13521773237 024646 0ustar000000000 0000000 Redefinition of value 'B' defined in enumtest/error_test/hidl_error_test.sh0100755 0000000 0000000 00000001664 13521773237 016524 0ustar000000000 0000000 #!/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 test/error_test/interface_extends_only_interface/0040755 0000000 0000000 00000000000 13521773237 021544 5ustar000000000 0000000 test/error_test/interface_extends_only_interface/1.0/0040755 0000000 0000000 00000000000 13521773237 022042 5ustar000000000 0000000 test/error_test/interface_extends_only_interface/1.0/IFoo.hal0100644 0000000 0000000 00000001340 13521773237 023357 0ustar000000000 0000000 /* * 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.interface_extends_only_interface@1.0; interface IFoo extends IStruct /* struct, not interface */ { }; test/error_test/interface_extends_only_interface/1.0/required_error0100644 0000000 0000000 00000000036 13521773237 025012 0ustar000000000 0000000 You can only extend interfacestest/error_test/interface_extends_only_interface/1.0/types.hal0100644 0000000 0000000 00000001264 13521773237 023674 0ustar000000000 0000000 /* * 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.interface_extends_only_interface@1.0; struct IStruct { }; test/error_test/interface_no_using_reserved_method_names/0040755 0000000 0000000 00000000000 13521773237 023254 5ustar000000000 0000000 test/error_test/interface_no_using_reserved_method_names/1.0/0040755 0000000 0000000 00000000000 13521773237 023552 5ustar000000000 0000000 test/error_test/interface_no_using_reserved_method_names/1.0/IFoo.hal0100644 0000000 0000000 00000001354 13521773237 025074 0ustar000000000 0000000 /* * 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.interface_no_using_reserved_method_names@1.0; interface IFoo { debug(); // duplicates reserved method name }; test/error_test/interface_no_using_reserved_method_names/1.0/required_error0100644 0000000 0000000 00000000047 13521773237 026524 0ustar000000000 0000000 Redefinition of reserved method 'debug'test/error_test/interface_semicolon/0040755 0000000 0000000 00000000000 13521773237 017001 5ustar000000000 0000000 test/error_test/interface_semicolon/1.0/0040755 0000000 0000000 00000000000 13521773237 017277 5ustar000000000 0000000 test/error_test/interface_semicolon/1.0/IFoo.hal0100644 0000000 0000000 00000001276 13521773237 020624 0ustar000000000 0000000 /* * 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.interface_semicolon@1.0; interface IFoo { } // <- missing semicolon test/error_test/interface_semicolon/1.0/required_error0100644 0000000 0000000 00000000014 13521773237 022243 0ustar000000000 0000000 missing ; attest/error_test/interface_unique_method_names/0040755 0000000 0000000 00000000000 13521773237 021042 5ustar000000000 0000000 test/error_test/interface_unique_method_names/1.0/0040755 0000000 0000000 00000000000 13521773237 021340 5ustar000000000 0000000 test/error_test/interface_unique_method_names/1.0/IFoo.hal0100644 0000000 0000000 00000001372 13521773237 022662 0ustar000000000 0000000 /* * 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.interface_unique_method_names@1.0; interface IFoo { foo(int32_t arg); foo(IFoo arg); // duplicates first method name }; test/error_test/interface_unique_method_names/1.0/required_error0100644 0000000 0000000 00000000037 13521773237 024311 0ustar000000000 0000000 Redefinition of method 'foo' attest/error_test/interface_unique_method_names_inheritance/0040755 0000000 0000000 00000000000 13521773237 023413 5ustar000000000 0000000 test/error_test/interface_unique_method_names_inheritance/1.0/0040755 0000000 0000000 00000000000 13521773237 023711 5ustar000000000 0000000 test/error_test/interface_unique_method_names_inheritance/1.0/IBar.hal0100644 0000000 0000000 00000001323 13521773237 025210 0ustar000000000 0000000 /* * 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.interface_unique_method_names_inheritance@1.0; interface IBar { foo(int32_t arg); }; test/error_test/interface_unique_method_names_inheritance/1.0/IFoo.hal0100644 0000000 0000000 00000001425 13521773237 025232 0ustar000000000 0000000 /* * 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.interface_unique_method_names_inheritance@1.0; import IBar; interface IFoo extends IBar { foo(IFoo arg); // duplicates method defined in IBar name }; test/error_test/interface_unique_method_names_inheritance/1.0/required_error0100644 0000000 0000000 00000000034 13521773237 026657 0ustar000000000 0000000 Redefinition of method 'foo'test/error_test/method_ends_in_semicolon/0040755 0000000 0000000 00000000000 13521773237 020020 5ustar000000000 0000000 test/error_test/method_ends_in_semicolon/1.0/0040755 0000000 0000000 00000000000 13521773237 020316 5ustar000000000 0000000 test/error_test/method_ends_in_semicolon/1.0/IFoo.hal0100644 0000000 0000000 00000001316 13521773237 021636 0ustar000000000 0000000 /* * 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.method_ends_in_semicolon@1.0; interface IFoo { foo() generates (int32_t bar) }; test/error_test/method_ends_in_semicolon/1.0/required_error0100644 0000000 0000000 00000000014 13521773237 023262 0ustar000000000 0000000 missing ; attest/error_test/missing_variable_type/0040755 0000000 0000000 00000000000 13521773237 017350 5ustar000000000 0000000 test/error_test/missing_variable_type/1.0/0040755 0000000 0000000 00000000000 13521773237 017646 5ustar000000000 0000000 test/error_test/missing_variable_type/1.0/IFoo.hal0100644 0000000 0000000 00000001352 13521773237 021166 0ustar000000000 0000000 /* * 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.missing_variable_type@1.0; interface IFoo { struct Struct { uint32_t a; }; foo() generates (Struct); }; test/error_test/missing_variable_type/1.0/required_error0100644 0000000 0000000 00000000032 13521773237 022612 0ustar000000000 0000000 is missing a variable nametest/error_test/no_cyclic_declaration_1/0040755 0000000 0000000 00000000000 13521773237 017520 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_1/1.0/0040755 0000000 0000000 00000000000 13521773237 020016 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_1/1.0/IFoo.hal0100644 0000000 0000000 00000001315 13521773237 021335 0ustar000000000 0000000 /* * 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.no_cyclic_declaration_1@1.0; interface IFoo extends IFoo /* extends itself */ { }; test/error_test/no_cyclic_declaration_1/1.0/required_error0100644 0000000 0000000 00000000022 13521773237 022761 0ustar000000000 0000000 Cyclic declarationtest/error_test/no_cyclic_declaration_2/0040755 0000000 0000000 00000000000 13521773237 017521 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_2/1.0/0040755 0000000 0000000 00000000000 13521773237 020017 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_2/1.0/IFoo.hal0100644 0000000 0000000 00000001361 13521773237 021337 0ustar000000000 0000000 /* * 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.no_cyclic_declaration_2@1.0; interface IFoo { struct A { B b; }; struct B { A a; }; }; test/error_test/no_cyclic_declaration_2/1.0/required_error0100644 0000000 0000000 00000000022 13521773237 022762 0ustar000000000 0000000 Cyclic declarationtest/error_test/no_cyclic_declaration_3/0040755 0000000 0000000 00000000000 13521773237 017522 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_3/1.0/0040755 0000000 0000000 00000000000 13521773237 020020 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_3/1.0/required_error0100644 0000000 0000000 00000000022 13521773237 022763 0ustar000000000 0000000 Cyclic declarationtest/error_test/no_cyclic_declaration_3/1.0/types.hal0100644 0000000 0000000 00000001261 13521773237 021647 0ustar000000000 0000000 /* * 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.no_cyclic_declaration_3@1.0; typedef A B; typedef B A; test/error_test/no_cyclic_declaration_4/0040755 0000000 0000000 00000000000 13521773237 017523 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_4/1.0/0040755 0000000 0000000 00000000000 13521773237 020021 5ustar000000000 0000000 test/error_test/no_cyclic_declaration_4/1.0/required_error0100644 0000000 0000000 00000000022 13521773237 022764 0ustar000000000 0000000 Cyclic declarationtest/error_test/no_cyclic_declaration_4/1.0/types.hal0100644 0000000 0000000 00000001277 13521773237 021657 0ustar000000000 0000000 /* * 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.no_cyclic_declaration_4@1.0; typedef S SS; struct S { SS foo; }; test/error_test/no_cyclic_expressions_1/0040755 0000000 0000000 00000000000 13521773237 017615 5ustar000000000 0000000 test/error_test/no_cyclic_expressions_1/1.0/0040755 0000000 0000000 00000000000 13521773237 020113 5ustar000000000 0000000 test/error_test/no_cyclic_expressions_1/1.0/required_error0100644 0000000 0000000 00000000022 13521773237 023056 0ustar000000000 0000000 Cyclic declarationtest/error_test/no_cyclic_expressions_1/1.0/types.hal0100644 0000000 0000000 00000001314 13521773237 021741 0ustar000000000 0000000 /* * 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.no_cyclic_expressions_1@1.0; enum E : int32_t { A = D, B, C, D }; test/error_test/no_cyclic_expressions_2/0040755 0000000 0000000 00000000000 13521773237 017616 5ustar000000000 0000000 test/error_test/no_cyclic_expressions_2/1.0/0040755 0000000 0000000 00000000000 13521773237 020114 5ustar000000000 0000000 test/error_test/no_cyclic_expressions_2/1.0/required_error0100644 0000000 0000000 00000000022 13521773237 023057 0ustar000000000 0000000 Cyclic declarationtest/error_test/no_cyclic_expressions_2/1.0/types.hal0100644 0000000 0000000 00000001340 13521773237 021741 0ustar000000000 0000000 /* * 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.no_cyclic_expressions_2@1.0; enum X : int32_t { A = Y:D, B, }; enum Y : X { C, D, }; test/error_test/no_cyclic_expressions_3/0040755 0000000 0000000 00000000000 13521773237 017617 5ustar000000000 0000000 test/error_test/no_cyclic_expressions_3/1.0/0040755 0000000 0000000 00000000000 13521773237 020115 5ustar000000000 0000000 test/error_test/no_cyclic_expressions_3/1.0/required_error0100644 0000000 0000000 00000000022 13521773237 023060 0ustar000000000 0000000 Cyclic declarationtest/error_test/no_cyclic_expressions_3/1.0/types.hal0100644 0000000 0000000 00000001267 13521773237 021752 0ustar000000000 0000000 /* * 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.no_cyclic_expressions_3@1.0; enum E : int32_t { A = A }; test/error_test/no_data_outside_interface/0040755 0000000 0000000 00000000000 13521773237 020152 5ustar000000000 0000000 test/error_test/no_data_outside_interface/1.0/0040755 0000000 0000000 00000000000 13521773237 020450 5ustar000000000 0000000 test/error_test/no_data_outside_interface/1.0/IFoo.hal0100644 0000000 0000000 00000001347 13521773237 021774 0ustar000000000 0000000 /* * 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.no_data_outside_interface@1.0; struct Bar { // can only have this in types.hal files }; interface IFoo { }; test/error_test/no_data_outside_interface/1.0/required_error0100644 0000000 0000000 00000000075 13521773237 023423 0ustar000000000 0000000 declares types rather than the expected interface type 'IFoo'test/error_test/no_interface_in_types/0040755 0000000 0000000 00000000000 13521773237 017337 5ustar000000000 0000000 test/error_test/no_interface_in_types/1.0/0040755 0000000 0000000 00000000000 13521773237 017635 5ustar000000000 0000000 test/error_test/no_interface_in_types/1.0/IFoo.hal0100644 0000000 0000000 00000001251 13521773237 021153 0ustar000000000 0000000 /* * 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.no_interface_in_types@1.0; interface IFoo { }; test/error_test/no_interface_in_types/1.0/required_error0100644 0000000 0000000 00000000120 13521773237 022577 0ustar000000000 0000000 declares an interface 'IBar' instead of the expected types common to the packagetest/error_test/no_interface_in_types/1.0/types.hal0100644 0000000 0000000 00000001317 13521773237 021466 0ustar000000000 0000000 /* * 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.no_interface_in_types@1.0; // can't have interface in types file interface IBar { }; test/error_test/no_two_interfaces/0040755 0000000 0000000 00000000000 13521773237 016501 5ustar000000000 0000000 test/error_test/no_two_interfaces/1.0/0040755 0000000 0000000 00000000000 13521773237 016777 5ustar000000000 0000000 test/error_test/no_two_interfaces/1.0/IFoo.hal0100644 0000000 0000000 00000001272 13521773237 020320 0ustar000000000 0000000 /* * 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.no_two_interfaces@1.0; interface IFoo { }; interface IBar { }; test/error_test/no_two_interfaces/1.0/required_error0100644 0000000 0000000 00000000075 13521773237 021752 0ustar000000000 0000000 declares types rather than the expected interface type 'IFoo'test/error_test/references_broken_package/0040755 0000000 0000000 00000000000 13521773237 020125 5ustar000000000 0000000 test/error_test/references_broken_package/1.0/0040755 0000000 0000000 00000000000 13521773237 020423 5ustar000000000 0000000 test/error_test/references_broken_package/1.0/IBar.hal0100644 0000000 0000000 00000001353 13521773237 021725 0ustar000000000 0000000 /* * 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. */ package test.references_broken_package@1.0; import test.references_broken_package.bad_package@1.0::IFoo; interface IBar { }; test/error_test/references_broken_package/1.0/required_error0100644 0000000 0000000 00000000014 13521773237 023367 0ustar000000000 0000000 missing ; attest/error_test/references_broken_package/bad_package/0040755 0000000 0000000 00000000000 13521773237 022326 5ustar000000000 0000000 test/error_test/references_broken_package/bad_package/1.0/0040755 0000000 0000000 00000000000 13521773237 022624 5ustar000000000 0000000 test/error_test/references_broken_package/bad_package/1.0/IFoo.hal0100644 0000000 0000000 00000001321 13521773237 024140 0ustar000000000 0000000 /* * 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. */ package test.references_broken_package.bad_package@1.0; interface IFoo { } // no semicolon -> error test/error_test/references_broken_package/bad_package/1.0/types.hal0100644 0000000 0000000 00000001264 13521773237 024456 0ustar000000000 0000000 /* * 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. */ package test.references_broken_package.bad_package@1.0; struct IFoo {};test/error_test/same_name_interface/0040755 0000000 0000000 00000000000 13521773237 016736 5ustar000000000 0000000 test/error_test/same_name_interface/1.0/0040755 0000000 0000000 00000000000 13521773237 017234 5ustar000000000 0000000 test/error_test/same_name_interface/1.0/IFoo.hal0100644 0000000 0000000 00000001276 13521773237 020561 0ustar000000000 0000000 /* * 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.same_name_interface@1.0; // Must be called IFoo interface IBar { }; test/error_test/same_name_interface/1.0/required_error0100644 0000000 0000000 00000000046 13521773237 022205 0ustar000000000 0000000 does not declare interface type 'IFoo'test/error_test/same_package_name/0040755 0000000 0000000 00000000000 13521773237 016371 5ustar000000000 0000000 test/error_test/same_package_name/1.0/0040755 0000000 0000000 00000000000 13521773237 016667 5ustar000000000 0000000 test/error_test/same_package_name/1.0/IFoo.hal0100644 0000000 0000000 00000001236 13521773237 020210 0ustar000000000 0000000 /* * 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.notpackage@1.0; interface IFoo { }; test/error_test/same_package_name/1.0/required_error0100644 0000000 0000000 00000000056 13521773237 021641 0ustar000000000 0000000 does not match expected package and/or versiontest/error_test/scope_unique_type_names/0040755 0000000 0000000 00000000000 13521773237 017714 5ustar000000000 0000000 test/error_test/scope_unique_type_names/1.0/0040755 0000000 0000000 00000000000 13521773237 020212 5ustar000000000 0000000 test/error_test/scope_unique_type_names/1.0/IFoo.hal0100644 0000000 0000000 00000001366 13521773237 021537 0ustar000000000 0000000 /* * 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.scope_unique_type_names@1.0; interface IFoo { struct S {}; enum S : int32_t {}; // duplicates first struct name }; test/error_test/scope_unique_type_names/1.0/required_error0100644 0000000 0000000 00000000064 13521773237 023163 0ustar000000000 0000000 A type named 'S' is already declared in the scope attest/error_test/struct_unique_field_names/0040755 0000000 0000000 00000000000 13521773237 020231 5ustar000000000 0000000 test/error_test/struct_unique_field_names/1.0/0040755 0000000 0000000 00000000000 13521773237 020527 5ustar000000000 0000000 test/error_test/struct_unique_field_names/1.0/IFoo.hal0100644 0000000 0000000 00000001411 13521773237 022043 0ustar000000000 0000000 /* * 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.struct_unique_field_names@1.0; interface IFoo { struct S { int32_t a; int32_t a; // duplicates first field name }; }; test/error_test/struct_unique_field_names/1.0/required_error0100644 0000000 0000000 00000000031 13521773237 023472 0ustar000000000 0000000 Redefinition of field 'a'test/export_test/0040755 0000000 0000000 00000000000 13521773237 013161 5ustar000000000 0000000 test/export_test/1.0/0040755 0000000 0000000 00000000000 13521773237 013457 5ustar000000000 0000000 test/export_test/1.0/IFoo.hal0100644 0000000 0000000 00000001340 13521773237 014774 0ustar000000000 0000000 /* * 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 export@1.0; interface IFoo { @export enum S : uint32_t { X, Y, Z, }; };test/export_test/1.0/types.hal0100644 0000000 0000000 00000001600 13521773237 015303 0ustar000000000 0000000 /* * 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 export@1.0; @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, };test/export_test/Android.bp0100644 0000000 0000000 00000001002 13521773237 015052 0ustar000000000 0000000 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"], } test/export_test/test.cpp0100644 0000000 0000000 00000001764 13521773237 014651 0ustar000000000 0000000 /* * 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 // 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; test/hash_test/0040755 0000000 0000000 00000000000 13521773237 012563 5ustar000000000 0000000 test/hash_test/Android.bp0100644 0000000 0000000 00000002201 13521773237 014456 0ustar000000000 0000000 genrule { name: "hidl_hash_test_gen", tools: [ "hidl-gen", ], cmd: "$(location hidl-gen) -L check " + " -r android.hidl:system/libhidl/transport" + " -r test.hash:system/tools/hidl/test/hash_test/good" + " test.hash.hash@1.0" + "&&" + "!($(location hidl-gen) -L check " + " -r android.hidl:system/libhidl/transport" + " -r test.hash:system/tools/hidl/test/hash_test/bad" + " test.hash.hash@1.0 2> /dev/null)" + "&&" + "$(location hidl-gen) -L hash " + " -r android.hidl:system/libhidl/transport" + " -r test.hash:system/tools/hidl/test/hash_test/bad" + " test.hash.hash@1.0 > /dev/null" + "&&" + "echo 'int main(){return 0;}' > $(genDir)/TODO_b_37575883.cpp", out: ["TODO_b_37575883.cpp"], srcs: [ "bad/hash/1.0/IHash.hal", "bad/current.txt", "good/hash/1.0/IHash.hal", "good/current.txt", ], } cc_test_host { name: "hidl_hash_test", cflags: ["-Wall", "-Werror"], generated_sources: ["hidl_hash_test_gen"], } test/hash_test/bad/0040755 0000000 0000000 00000000000 13521773237 013311 5ustar000000000 0000000 test/hash_test/bad/current.txt0100644 0000000 0000000 00000000133 13521773237 015526 0ustar000000000 0000000 b19939ecb4f877820df49b684f3164f0a3f9aa18743a3521f3bd04e4a06fed64 test.hash.hash@1.0::IHash test/hash_test/bad/hash/0040755 0000000 0000000 00000000000 13521773237 014234 5ustar000000000 0000000 test/hash_test/bad/hash/1.0/0040755 0000000 0000000 00000000000 13521773237 014532 5ustar000000000 0000000 test/hash_test/bad/hash/1.0/IHash.hal0100644 0000000 0000000 00000001273 13521773237 016214 0ustar000000000 0000000 /* * 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.hash.hash@1.0; interface IHash { changeFrozenInterface(); }; test/hash_test/good/0040755 0000000 0000000 00000000000 13521773237 013513 5ustar000000000 0000000 test/hash_test/good/current.txt0100644 0000000 0000000 00000000163 13521773237 015733 0ustar000000000 0000000 b19939ecb4f877820df49b684f3164f0a3f9aa18743a3521f3bd04e4a06fed64 test.hash.hash@1.0::IHash # comments are fine too test/hash_test/good/hash/0040755 0000000 0000000 00000000000 13521773237 014436 5ustar000000000 0000000 test/hash_test/good/hash/1.0/0040755 0000000 0000000 00000000000 13521773237 014734 5ustar000000000 0000000 test/hash_test/good/hash/1.0/IHash.hal0100644 0000000 0000000 00000001236 13521773237 016415 0ustar000000000 0000000 /* * 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.hash.hash@1.0; interface IHash { }; test/hidl_test/0040755 0000000 0000000 00000000000 13521773237 012560 5ustar000000000 0000000 test/hidl_test/Android.bp0100644 0000000 0000000 00000004277 13521773237 014472 0ustar000000000 0000000 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, } test/hidl_test/Android.mk0100644 0000000 0000000 00000002570 13521773237 014472 0ustar000000000 0000000 # # 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 $(CLEAR_VARS) LOCAL_MODULE := hidl_test_helper LOCAL_MODULE_CLASS := NATIVE_TESTS LOCAL_SRC_FILES := hidl_test_helper include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_MODULE := hidl_test LOCAL_MODULE_CLASS := NATIVE_TESTS LOCAL_SRC_FILES := hidl_test 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) include $(CLEAR_VARS) LOCAL_MODULE := VtsHidlUnitTests VTS_CONFIG_SRC_DIR := system/tools/hidl/tests -include test/vts/tools/build/Android.host_config.mk test/hidl_test/AndroidTest.xml0100644 0000000 0000000 00000004245 13521773237 015524 0ustar000000000 0000000 test/hidl_test/FooCallback.cpp0100644 0000000 0000000 00000010546 13521773237 015427 0ustar000000000 0000000 #define LOG_TAG "hidl_test" #include "FooCallback.h" #include #include #include #include namespace android { namespace hardware { namespace tests { namespace foo { namespace V1_0 { namespace implementation { enum { NOT_REPORTED = -1LL }; FooCallback::FooCallback() : mLock{}, mCond{} { for (size_t i = 0; i < invokeInfo.size(); i++) { invokeInfo[i].invoked = false; invokeInfo[i].timeNs = NOT_REPORTED; invokeInfo[i].callerBlockedNs = NOT_REPORTED; } } Return FooCallback::heyItsYou( const sp &_cb) { nsecs_t start = systemTime(); ALOGI("SERVER(FooCallback) 1: heyItsYou cb = %p", _cb.get()); nsecs_t end = systemTime(); { Mutex::Autolock lock(mLock); invokeInfo[0].invoked = true; invokeInfo[0].timeNs = end - start; mCond.signal(); } ALOGI("SERVER(FooCallback) 2: heyItsYou returned"); return Void(); } Return FooCallback::heyItsYouIsntIt(const sp &_cb) { nsecs_t start = systemTime(); ALOGI("SERVER(FooCallback) 3: heyItsYouIsntIt cb = %p sleeping for %" PRId64 " seconds", _cb.get(), DELAY_S); sleep(DELAY_S); ALOGI("SERVER(FooCallback) 4: heyItsYouIsntIt cb = %p responding", _cb.get()); nsecs_t end = systemTime(); { Mutex::Autolock lock(mLock); invokeInfo[1].invoked = true; invokeInfo[1].timeNs = end - start; mCond.signal(); } ALOGI("SERVER(FooCallback) 5: heyItsYouIsntIt cb = %p responding", _cb.get()); return true; } Return FooCallback::heyItsTheMeaningOfLife(uint8_t tmol) { nsecs_t start = systemTime(); ALOGI("SERVER(FooCallback) 6.1: heyItsTheMeaningOfLife = %d sleeping for %" PRId64 " seconds", tmol, DELAY_S); sleep(DELAY_S); ALOGI("SERVER(FooCallback) 6.2: heyItsTheMeaningOfLife = %d done sleeping", tmol); nsecs_t end = systemTime(); { Mutex::Autolock lock(mLock); invokeInfo[2].invoked = true; invokeInfo[2].timeNs = end - start; mCond.signal(); } ALOGI("SERVER(FooCallback) 6.3: heyItsTheMeaningOfLife returned"); return Void(); } Return FooCallback::reportResults(int64_t ns, reportResults_cb cb) { ALOGI("SERVER(FooCallback) 8.1: reportResults(%" PRId64 " seconds)", nanoseconds_to_seconds(ns)); nsecs_t leftToWaitNs = ns; bool cond; { Mutex::Autolock lock(mLock); while ((cond = ((!invokeInfo[0].invoked || !invokeInfo[1].invoked || !invokeInfo[2].invoked || invokeInfo[0].callerBlockedNs == NOT_REPORTED || invokeInfo[1].callerBlockedNs == NOT_REPORTED || invokeInfo[2].callerBlockedNs == NOT_REPORTED) && leftToWaitNs > 0))) { nsecs_t start = systemTime(); ::android::status_t rc = mCond.waitRelative(mLock, leftToWaitNs); if (rc != ::android::OK) { ALOGW("SERVER(FooCallback)::reportResults(%" PRId64 " ns) Condition::waitRelative(%" PRId64 ") returned error (%d)", ns, leftToWaitNs, rc); if (rc == -ETIMEDOUT) { // time's up leftToWaitNs = -INT32_MAX; } break; } ALOGI("SERVER(FooCallback)::reportResults(%" PRId64 " ns) Condition::waitRelative was signalled", ns); leftToWaitNs -= systemTime() - start; } } ALOGI("SERVER(FooCallback) 8.2: reportResults returned;" "invoked? %d, %d, %d; leftToWaitNs = %" PRId64 "; cond = %d", invokeInfo[0].invoked, invokeInfo[1].invoked, invokeInfo[2].invoked, leftToWaitNs, cond); cb(leftToWaitNs, invokeInfo); return Void(); } Return FooCallback::youBlockedMeFor(const hidl_array &ns) { ALOGI("SERVER(FooCallback) 7.1: youBlockedMeFor"); { Mutex::Autolock lock(mLock); for (size_t i = 0; i < 3; i++) { invokeInfo[i].callerBlockedNs = ns[i]; } mCond.signal(); } ALOGI("SERVER(FooCallback) 7.2: returned"); return Void(); } IFooCallback* HIDL_FETCH_IFooCallback(const char* /* name */) { return new FooCallback(); } } // namespace implementation } // namespace V1_0 } // namespace foo } // namespace tests } // namespace hardware } // namespace android test/hidl_test/FooCallback.h0100644 0000000 0000000 00000002710 13521773237 015066 0ustar000000000 0000000 #ifndef ANDROID_HARDWARE_TESTS_FOO_V1_0_FOOCALLBACK_H #define ANDROID_HARDWARE_TESTS_FOO_V1_0_FOOCALLBACK_H #include #include #include #include namespace android { namespace hardware { namespace tests { namespace foo { namespace V1_0 { namespace implementation { using ::android::hardware::tests::foo::V1_0::IFooCallback; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hardware::hidl_vec; using ::android::hardware::hidl_string; using ::android::sp; struct FooCallback : public IFooCallback { FooCallback(); // Methods from ::android::hardware::tests::foo::V1_0::IFooCallback follow. Return heyItsYou(const sp& cb) override; Return heyItsYouIsntIt(const sp& cb) override; Return heyItsTheMeaningOfLife(uint8_t tmol) override; Return reportResults(int64_t ns, reportResults_cb _hidl_cb) override; Return youBlockedMeFor(const hidl_array& callerBlockedInfo) override; hidl_array invokeInfo; Mutex mLock; Condition mCond; }; extern "C" IFooCallback* HIDL_FETCH_IFooCallback(const char* name); } // namespace implementation } // namespace V1_0 } // namespace foo } // namespace tests } // namespace hardware } // namespace android #endif // ANDROID_HARDWARE_TESTS_FOO_V1_0_FOOCALLBACK_H test/hidl_test/hidl_test0100644 0000000 0000000 00000001127 13521773237 014460 0ustar000000000 0000000 source /data/nativetest64/hidl_test_helper && chmod a+x /data/nativetest/hidl_test_servers/hidl_test_servers && chmod a+x /data/nativetest64/hidl_test_servers/hidl_test_servers && chmod a+x /data/nativetest/hidl_test_client/hidl_test_client && chmod a+x /data/nativetest64/hidl_test_client/hidl_test_client && run_all_tests \ "/data/nativetest/hidl_test_servers/hidl_test_servers" \ "/data/nativetest64/hidl_test_servers/hidl_test_servers" \ "/data/nativetest/hidl_test_client/hidl_test_client" \ "/data/nativetest64/hidl_test_client/hidl_test_client" \ "hidl_test" \ "$@" test/hidl_test/hidl_test.h0100644 0000000 0000000 00000003266 13521773237 014714 0ustar000000000 0000000 #ifndef HIDL_TEST_H_ #define HIDL_TEST_H_ #include #include #include #include #include #include #include #include #include #include template