/* * Copyright (C) 2019 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 #include #include #include #include #include #include #include "AidlHelper.h" #include "Coordinator.h" #include "DocComment.h" #include "FormattingConstants.h" #include "Interface.h" #include "Location.h" #include "Method.h" #include "NamedType.h" #include "Reference.h" #include "Type.h" namespace android { static void emitAidlMethodParams(WrappedOutput* wrappedOutput, const std::vector*> args, const std::string& prefix, const std::string& attachToLast, const Interface& iface) { if (args.size() == 0) { *wrappedOutput << attachToLast; return; } for (size_t i = 0; i < args.size(); i++) { const NamedReference* arg = args[i]; std::string out = prefix + AidlHelper::getAidlType(*arg->get(), iface.fqName()) + " " + arg->name(); wrappedOutput->group([&] { if (i != 0) wrappedOutput->printUnlessWrapped(" "); *wrappedOutput << out; if (i == args.size() - 1) { if (!attachToLast.empty()) *wrappedOutput << attachToLast; } else { *wrappedOutput << ","; } }); } } std::vector AidlHelper::getUserDefinedMethods(Formatter& out, const Interface& interface) { std::vector methods; for (const Interface* iface : interface.typeChain()) { if (!AidlHelper::shouldBeExpanded(interface.fqName(), iface->fqName()) && iface->fqName() != gIBaseFqName) { out << "// Types from " << iface->fqName().string() << " are not included because it is in a separate package, and it is expected to " "be a separate AIDL interface. To include these methods, use the '-e' argument. " "\n"; break; } const std::vector userDefined = iface->userDefinedMethods(); methods.insert(methods.end(), userDefined.begin(), userDefined.end()); } return methods; } // Represents a node which is potentially overriding another node. // e.g. if this is 'foo_1_4' template struct NodeWithVersion { size_t major; // 1 size_t minor; // 4 const NODE* node; // HIDL object representing foo_1_4. std::string baseName; // foo }; std::string getBaseName(const std::string& rawName) { size_t underscore = rawName.find('_'); if (underscore != std::string::npos) { std::string version = rawName.substr(underscore + 1); // don't include _ std::string baseName = rawName.substr(0, underscore); underscore = version.find('_'); size_t major, minor; if (underscore != std::string::npos && base::ParseUint(version.substr(0, underscore), &major) && base::ParseUint(version.substr(underscore + 1), &minor)) { // contains major and minor version. consider it's baseName now. return baseName; } } return rawName; } template static void pushVersionedNodeOntoMap(const NODE& versionedNode, std::map* latestNodeForBaseName, std::vector* supersededNode) { // attempt to push name onto latestNodeForBaseName auto [it, inserted] = latestNodeForBaseName->emplace(std::move(versionedNode.baseName), versionedNode); if (!inserted) { auto* current = &it->second; // Node in the latestNodeForBaseName is more recent if ((current->major > versionedNode.major) || (current->major == versionedNode.major && current->minor > versionedNode.minor)) { supersededNode->push_back(versionedNode); return; } // Either current.major < versioned.major OR versioned.minor >= current.minor supersededNode->push_back(*current); *current = std::move(versionedNode); } } struct ResultTransformation { enum class TransformType { MOVED, // Moved to the front of the method name REMOVED, // Removed the result }; std::string resultName; TransformType type; }; static bool shouldWarnStatusType(const std::string& typeName) { static const std::vector kUppercaseIgnoreStatusTypes = {"ERROR", "STATUS"}; const std::string uppercase = StringHelper::Uppercase(typeName); for (const std::string& ignore : kUppercaseIgnoreStatusTypes) { if (uppercase.find(ignore) != std::string::npos) return true; } return false; } static bool shouldWarnOutParam(const std::string& typeName) { static const std::vector kNoOutParamTypes = {"ParcelFileDescriptor", "FileDescriptor", "ParcelableHolder", "IBinder", "String", "CharacterSequence", "void", "boolean", "byte", "char", "int", "long", "float", "double"}; return std::find(kNoOutParamTypes.begin(), kNoOutParamTypes.end(), typeName) != kNoOutParamTypes.end(); } void AidlHelper::emitAidl( const Interface& interface, const Coordinator& coordinator, const std::map& processedTypes) { Formatter out = getFileWithHeader(interface, coordinator, processedTypes); interface.emitDocComment(out); if (interface.superType() && interface.superType()->fqName() != gIBaseFqName) { out << "// Interface inherits from " << interface.superType()->fqName().string() << " but AIDL does not support interface inheritance.\n"; } out << "@VintfStability\n"; out << "interface " << getAidlName(interface.fqName()) << " "; out.block([&] { std::map> latestTypeForBaseName; std::vector> supersededNamedTypes; std::map> latestMethodForBaseName; std::vector> supersededMethods; for (const Interface* iface : interface.typeChain()) { if (!AidlHelper::shouldBeExpanded(interface.fqName(), iface->fqName())) { // Stop traversing extended interfaces once they leave this package break; } for (const Method* method : iface->userDefinedMethods()) { pushVersionedNodeOntoMap({iface->fqName().getPackageMajorVersion(), iface->fqName().getPackageMinorVersion(), method, getBaseName(method->name())}, &latestMethodForBaseName, &supersededMethods); } // Types from other interfaces will be handled while those interfaces // are being emitted. if (iface->getBaseName() != interface.getBaseName()) { continue; } for (const NamedType* type : iface->getSubTypes()) { // The baseName for types is not being stripped of the version // numbers like that of the methods. If a type was named // BigStruct_1_1 and the previous version was named BigStruct, // they will be treated as two different types. pushVersionedNodeOntoMap({iface->fqName().getPackageMajorVersion(), iface->fqName().getPackageMinorVersion(), type, getAidlName(type->fqName())}, &latestTypeForBaseName, &supersededNamedTypes); } } // Add comment for superseded types out.join(supersededNamedTypes.begin(), supersededNamedTypes.end(), "\n", [&](const NodeWithVersion& versionedType) { out << "// Ignoring type " << getAidlName(versionedType.node->fqName()) << " from " << versionedType.major << "." << versionedType.minor << "::" << getAidlName(interface.fqName()) << " since a newer alternative is available."; }); if (!supersededNamedTypes.empty()) out << "\n\n"; // Add comment for superseded methods out.join(supersededMethods.begin(), supersededMethods.end(), "\n", [&](const NodeWithVersion& versionedMethod) { out << "// Ignoring method " << versionedMethod.node->name() << " from " << versionedMethod.major << "." << versionedMethod.minor << "::" << getAidlName(interface.fqName()) << " since a newer alternative is available."; }); if (!supersededMethods.empty()) out << "\n\n"; // Emit latest methods defined for this interface out.join(latestMethodForBaseName.begin(), latestMethodForBaseName.end(), "\n", [&](const std::pair>& methodPair) { const Method* method = methodPair.second.node; const std::string& baseName = methodPair.first; std::vector*> results; std::vector transformations; for (NamedReference* res : method->results()) { const std::string aidlType = getAidlType(*res->get(), interface.fqName()); if (shouldWarnStatusType(aidlType)) { out << "// FIXME: AIDL has built-in status types. Do we need the " "status type here?\n"; } if (method->results().size() > 1 && shouldWarnOutParam(aidlType)) { out << "// FIXME: AIDL does not allow " << aidlType << " to be an out parameter.\n"; out << "// Move it to return, or add it to a Parcelable.\n"; } results.push_back(res); } if (method->name() != baseName) { out << "// Changing method name from " << method->name() << " to " << baseName << "\n"; } std::string returnType = "void"; if (results.size() == 1) { returnType = getAidlType(*results[0]->get(), interface.fqName()); out << "// Adding return type to method instead of out param " << returnType << " " << results[0]->name() << " since there is only one return value.\n"; transformations.emplace_back(ResultTransformation{ results[0]->name(), ResultTransformation::TransformType::MOVED}); results.clear(); } if (method->getDocComment() != nullptr) { std::vector modifiedDocComment; for (const std::string& line : method->getDocComment()->lines()) { std::vector tokens = base::Split(line, " "); if (tokens.size() <= 1 || tokens[0] != "@return") { // unimportant line modifiedDocComment.emplace_back(line); continue; } const std::string& res = tokens[1]; bool transformed = false; for (const ResultTransformation& transform : transformations) { if (transform.resultName != res) continue; // Some transform was done to it if (transform.type == ResultTransformation::TransformType::MOVED) { // remove the name tokens.erase(++tokens.begin()); transformed = true; } else { CHECK(transform.type == ResultTransformation::TransformType::REMOVED); tokens.insert(tokens.begin(), "FIXME: The following return was removed\n"); transformed = true; } } if (!transformed) { tokens.erase(tokens.begin()); tokens.insert(tokens.begin(), "@param out"); } modifiedDocComment.emplace_back(base::Join(tokens, " ")); } DocComment(modifiedDocComment, HIDL_LOCATION_HERE).emit(out); } WrappedOutput wrappedOutput(MAX_LINE_LENGTH); if (method->isOneway()) wrappedOutput << "oneway "; wrappedOutput << returnType << " " << baseName << "("; if (results.empty()) { emitAidlMethodParams(&wrappedOutput, method->args(), /* prefix */ "in ", /* attachToLast */ ");\n", interface); } else { const bool emitArgs = !method->args().empty(); if (emitArgs) { emitAidlMethodParams(&wrappedOutput, method->args(), /* prefix */ "in ", /* attachToLast */ ",", interface); } wrappedOutput.group([&] { if (emitArgs) wrappedOutput.printUnlessWrapped(" "); emitAidlMethodParams(&wrappedOutput, results, /* prefix */ "out ", /* attachToLast */ ");\n", interface); }); } out << wrappedOutput; }); }); } } // namespace android