1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <android-base/logging.h> 18 #include <android-base/parseint.h> 19 #include <android-base/strings.h> 20 #include <hidl-util/FQName.h> 21 #include <hidl-util/Formatter.h> 22 #include <hidl-util/StringHelper.h> 23 #include <cstddef> 24 #include <vector> 25 26 #include "AidlHelper.h" 27 #include "Coordinator.h" 28 #include "DocComment.h" 29 #include "FormattingConstants.h" 30 #include "Interface.h" 31 #include "Location.h" 32 #include "Method.h" 33 #include "NamedType.h" 34 #include "Reference.h" 35 #include "Type.h" 36 37 namespace android { 38 39 static void emitAidlMethodParams(WrappedOutput* wrappedOutput, 40 const std::vector<NamedReference<Type>*> args, 41 const std::string& prefix, const std::string& attachToLast, 42 const Interface& iface) { 43 if (args.size() == 0) { 44 *wrappedOutput << attachToLast; 45 return; 46 } 47 48 for (size_t i = 0; i < args.size(); i++) { 49 const NamedReference<Type>* arg = args[i]; 50 std::string out = 51 prefix + AidlHelper::getAidlType(*arg->get(), iface.fqName()) + " " + arg->name(); 52 wrappedOutput->group([&] { 53 if (i != 0) wrappedOutput->printUnlessWrapped(" "); 54 *wrappedOutput << out; 55 if (i == args.size() - 1) { 56 if (!attachToLast.empty()) *wrappedOutput << attachToLast; 57 } else { 58 *wrappedOutput << ","; 59 } 60 }); 61 } 62 } 63 64 std::vector<const Method*> AidlHelper::getUserDefinedMethods(const Interface& interface) { 65 std::vector<const Method*> methods; 66 for (const Interface* iface : interface.typeChain()) { 67 const std::vector<Method*> userDefined = iface->userDefinedMethods(); 68 methods.insert(methods.end(), userDefined.begin(), userDefined.end()); 69 } 70 71 return methods; 72 } 73 74 struct MethodWithVersion { 75 size_t major; 76 size_t minor; 77 const Method* method; 78 std::string name; 79 }; 80 81 static void pushVersionedMethodOntoMap(MethodWithVersion versionedMethod, 82 std::map<std::string, MethodWithVersion>* map, 83 std::vector<const MethodWithVersion*>* ignored) { 84 const Method* method = versionedMethod.method; 85 std::string name = method->name(); 86 size_t underscore = name.find('_'); 87 if (underscore != std::string::npos) { 88 std::string version = name.substr(underscore + 1); // don't include _ 89 std::string nameWithoutVersion = name.substr(0, underscore); 90 underscore = version.find('_'); 91 92 size_t major, minor; 93 if (underscore != std::string::npos && 94 base::ParseUint(version.substr(0, underscore), &major) && 95 base::ParseUint(version.substr(underscore + 1), &minor)) { 96 // contains major and minor version. consider it's nameWithoutVersion now. 97 name = nameWithoutVersion; 98 versionedMethod.name = nameWithoutVersion; 99 } 100 } 101 102 // push name onto map 103 auto [it, inserted] = map->emplace(std::move(name), versionedMethod); 104 if (!inserted) { 105 auto* current = &it->second; 106 107 // Method in the map is more recent 108 if ((current->major > versionedMethod.major) || 109 (current->major == versionedMethod.major && current->minor > versionedMethod.minor)) { 110 // ignoring versionedMethod 111 ignored->push_back(&versionedMethod); 112 return; 113 } 114 115 // Either current.major < versioned.major OR versioned.minor >= current.minor 116 ignored->push_back(current); 117 *current = std::move(versionedMethod); 118 } 119 } 120 121 struct ResultTransformation { 122 enum class TransformType { 123 MOVED, // Moved to the front of the method name 124 REMOVED, // Removed the result 125 }; 126 127 std::string resultName; 128 TransformType type; 129 }; 130 131 static bool shouldWarnStatusType(const std::string& typeName) { 132 static const std::vector<std::string> kUppercaseIgnoreStatusTypes = {"ERROR", "STATUS"}; 133 134 const std::string uppercase = StringHelper::Uppercase(typeName); 135 for (const std::string& ignore : kUppercaseIgnoreStatusTypes) { 136 if (uppercase.find(ignore) != std::string::npos) return true; 137 } 138 return false; 139 } 140 141 void AidlHelper::emitAidl(const Interface& interface, const Coordinator& coordinator) { 142 for (const NamedType* type : interface.getSubTypes()) { 143 emitAidl(*type, coordinator); 144 } 145 146 Formatter out = getFileWithHeader(interface, coordinator); 147 148 interface.emitDocComment(out); 149 if (interface.superType() && interface.superType()->fqName() != gIBaseFqName) { 150 out << "// Interface inherits from " << interface.superType()->fqName().string() 151 << " but AIDL does not support interface inheritance.\n"; 152 } 153 154 out << "interface " << getAidlName(interface.fqName()) << " "; 155 out.block([&] { 156 for (const NamedType* type : interface.getSubTypes()) { 157 emitAidl(*type, coordinator); 158 } 159 160 std::map<std::string, MethodWithVersion> methodMap; 161 std::vector<const MethodWithVersion*> ignoredMethods; 162 std::vector<std::string> methodNames; 163 std::vector<const Interface*> typeChain = interface.typeChain(); 164 for (auto iface = typeChain.rbegin(); iface != typeChain.rend(); ++iface) { 165 for (const Method* method : (*iface)->userDefinedMethods()) { 166 pushVersionedMethodOntoMap( 167 {(*iface)->fqName().getPackageMajorVersion(), 168 (*iface)->fqName().getPackageMinorVersion(), method, method->name()}, 169 &methodMap, &ignoredMethods); 170 methodNames.push_back(method->name()); 171 } 172 } 173 174 std::set<std::string> ignoredMethodNames; 175 out.join(ignoredMethods.begin(), ignoredMethods.end(), "\n", 176 [&](const MethodWithVersion* versionedMethod) { 177 out << "// Ignoring method " << versionedMethod->method->name() << " from " 178 << versionedMethod->major << "." << versionedMethod->minor 179 << "::" << getAidlName(interface.fqName()) 180 << " since a newer alternative is available."; 181 ignoredMethodNames.insert(versionedMethod->method->name()); 182 }); 183 if (!ignoredMethods.empty()) out << "\n\n"; 184 185 out.join(methodNames.begin(), methodNames.end(), "\n", [&](const std::string& name) { 186 const Method* method = methodMap[name].method; 187 if (method == nullptr) { 188 CHECK(ignoredMethodNames.count(name) == 1); 189 return; 190 } 191 192 std::vector<NamedReference<Type>*> results; 193 std::vector<ResultTransformation> transformations; 194 for (NamedReference<Type>* res : method->results()) { 195 const std::string aidlType = getAidlType(*res->get(), interface.fqName()); 196 197 if (shouldWarnStatusType(aidlType)) { 198 out << "// FIXME: AIDL has built-in status types. Do we need the status type " 199 "here?\n"; 200 } 201 results.push_back(res); 202 } 203 204 if (method->name() != name) { 205 out << "// Changing method name from " << method->name() << " to " << name << "\n"; 206 } 207 208 std::string returnType = "void"; 209 if (results.size() == 1) { 210 returnType = getAidlType(*results[0]->get(), interface.fqName()); 211 212 out << "// Adding return type to method instead of out param " << returnType << " " 213 << results[0]->name() << " since there is only one return value.\n"; 214 transformations.emplace_back(ResultTransformation{ 215 results[0]->name(), ResultTransformation::TransformType::MOVED}); 216 results.clear(); 217 } 218 219 if (method->getDocComment() != nullptr) { 220 std::vector<std::string> modifiedDocComment; 221 for (const std::string& line : method->getDocComment()->lines()) { 222 std::vector<std::string> tokens = base::Split(line, " "); 223 if (tokens.size() <= 1 || tokens[0] != "@return") { 224 // unimportant line 225 modifiedDocComment.emplace_back(line); 226 continue; 227 } 228 229 const std::string& res = tokens[1]; 230 bool transformed = false; 231 for (const ResultTransformation& transform : transformations) { 232 if (transform.resultName != res) continue; 233 234 // Some transform was done to it 235 if (transform.type == ResultTransformation::TransformType::MOVED) { 236 // remove the name 237 tokens.erase(++tokens.begin()); 238 transformed = true; 239 } else { 240 CHECK(transform.type == ResultTransformation::TransformType::REMOVED); 241 tokens.insert(tokens.begin(), 242 "FIXME: The following return was removed\n"); 243 transformed = true; 244 } 245 } 246 247 if (!transformed) { 248 tokens.erase(tokens.begin()); 249 tokens.insert(tokens.begin(), "@param out"); 250 } 251 252 modifiedDocComment.emplace_back(base::Join(tokens, " ")); 253 } 254 255 DocComment(modifiedDocComment, HIDL_LOCATION_HERE).emit(out); 256 } 257 258 WrappedOutput wrappedOutput(MAX_LINE_LENGTH); 259 260 if (method->isOneway()) wrappedOutput << "oneway "; 261 wrappedOutput << returnType << " " << name << "("; 262 263 if (results.empty()) { 264 emitAidlMethodParams(&wrappedOutput, method->args(), /* prefix */ "in ", 265 /* attachToLast */ ");\n", interface); 266 } else { 267 if (!method->args().empty()) { 268 emitAidlMethodParams(&wrappedOutput, method->args(), /* prefix */ "in ", 269 /* attachToLast */ ",", interface); 270 wrappedOutput.printUnlessWrapped(" "); 271 } 272 273 // TODO: Emit warning if a primitive is given as a out param. 274 emitAidlMethodParams(&wrappedOutput, results, /* prefix */ "out ", 275 /* attachToLast */ ");\n", interface); 276 } 277 278 out << wrappedOutput; 279 }); 280 }); 281 } 282 283 } // namespace android 284