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
emitAidlMethodParams(WrappedOutput * wrappedOutput,const std::vector<NamedReference<Type> * > args,const std::string & prefix,const std::string & attachToLast,const Interface & iface)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
getUserDefinedMethods(Formatter & out,const Interface & interface)64 std::vector<const Method*> AidlHelper::getUserDefinedMethods(Formatter& out,
65 const Interface& interface) {
66 std::vector<const Method*> methods;
67 for (const Interface* iface : interface.typeChain()) {
68 if (!AidlHelper::shouldBeExpanded(interface.fqName(), iface->fqName()) &&
69 iface->fqName() != gIBaseFqName) {
70 out << "// Types from " << iface->fqName().string()
71 << " are not included because it is in a separate package, and it is expected to "
72 "be a separate AIDL interface. To include these methods, use the '-e' argument. "
73 "\n";
74 break;
75 }
76 const std::vector<Method*> userDefined = iface->userDefinedMethods();
77 methods.insert(methods.end(), userDefined.begin(), userDefined.end());
78 }
79
80 return methods;
81 }
82
83 // Represents a node which is potentially overriding another node.
84 // e.g. if this is 'foo_1_4'
85 template <class NODE>
86 struct NodeWithVersion {
87 size_t major; // 1
88 size_t minor; // 4
89 const NODE* node; // HIDL object representing foo_1_4.
90 std::string baseName; // foo
91 };
92
getBaseName(const std::string & rawName)93 std::string getBaseName(const std::string& rawName) {
94 size_t underscore = rawName.find('_');
95 if (underscore != std::string::npos) {
96 std::string version = rawName.substr(underscore + 1); // don't include _
97 std::string baseName = rawName.substr(0, underscore);
98 underscore = version.find('_');
99
100 size_t major, minor;
101 if (underscore != std::string::npos &&
102 base::ParseUint(version.substr(0, underscore), &major) &&
103 base::ParseUint(version.substr(underscore + 1), &minor)) {
104 // contains major and minor version. consider it's baseName now.
105 return baseName;
106 }
107 }
108 return rawName;
109 }
110
111 template <class NODE>
pushVersionedNodeOntoMap(const NODE & versionedNode,std::map<std::string,NODE> * latestNodeForBaseName,std::vector<const NODE> * supersededNode)112 static void pushVersionedNodeOntoMap(const NODE& versionedNode,
113 std::map<std::string, NODE>* latestNodeForBaseName,
114 std::vector<const NODE>* supersededNode) {
115 // attempt to push name onto latestNodeForBaseName
116 auto [it, inserted] =
117 latestNodeForBaseName->emplace(std::move(versionedNode.baseName), versionedNode);
118 if (!inserted) {
119 auto* current = &it->second;
120
121 // Node in the latestNodeForBaseName is more recent
122 if ((current->major > versionedNode.major) ||
123 (current->major == versionedNode.major && current->minor > versionedNode.minor)) {
124 supersededNode->push_back(versionedNode);
125 return;
126 }
127
128 // Either current.major < versioned.major OR versioned.minor >= current.minor
129 supersededNode->push_back(*current);
130 *current = std::move(versionedNode);
131 }
132 }
133
134 struct ResultTransformation {
135 enum class TransformType {
136 MOVED, // Moved to the front of the method name
137 REMOVED, // Removed the result
138 };
139
140 std::string resultName;
141 TransformType type;
142 };
143
shouldWarnStatusType(const std::string & typeName)144 static bool shouldWarnStatusType(const std::string& typeName) {
145 static const std::vector<std::string> kUppercaseIgnoreStatusTypes = {"ERROR", "STATUS"};
146
147 const std::string uppercase = StringHelper::Uppercase(typeName);
148 for (const std::string& ignore : kUppercaseIgnoreStatusTypes) {
149 if (uppercase.find(ignore) != std::string::npos) return true;
150 }
151 return false;
152 }
153
shouldWarnOutParam(const std::string & typeName)154 static bool shouldWarnOutParam(const std::string& typeName) {
155 static const std::vector<std::string> kNoOutParamTypes = {"ParcelFileDescriptor",
156 "FileDescriptor",
157 "ParcelableHolder",
158 "IBinder",
159 "String",
160 "CharacterSequence",
161 "void",
162 "boolean",
163 "byte",
164 "char",
165 "int",
166 "long",
167 "float",
168 "double"};
169 return std::find(kNoOutParamTypes.begin(), kNoOutParamTypes.end(), typeName) !=
170 kNoOutParamTypes.end();
171 }
172
emitAidl(const Interface & interface,Formatter & out,const std::map<const NamedType *,const ProcessedCompoundType> & processedTypes)173 void AidlHelper::emitAidl(
174 const Interface& interface, Formatter& out,
175 const std::map<const NamedType*, const ProcessedCompoundType>& processedTypes) {
176 interface.emitDocComment(out);
177 if (interface.superType() && interface.superType()->fqName() != gIBaseFqName) {
178 out << "// Interface inherits from " << interface.superType()->fqName().string()
179 << " but AIDL does not support interface inheritance (methods have been flattened).\n";
180 }
181
182 out << "@VintfStability\n";
183 out << "interface " << getAidlName(interface.fqName()) << " ";
184 out.block([&] {
185 std::map<std::string, NodeWithVersion<NamedType>> latestTypeForBaseName;
186 std::vector<const NodeWithVersion<NamedType>> supersededNamedTypes;
187 std::map<std::string, NodeWithVersion<Method>> latestMethodForBaseName;
188 std::vector<const NodeWithVersion<Method>> supersededMethods;
189 for (const Interface* iface : interface.typeChain()) {
190 if (!AidlHelper::shouldBeExpanded(interface.fqName(), iface->fqName())) {
191 // Stop traversing extended interfaces once they leave this package
192 break;
193 }
194 for (const Method* method : iface->userDefinedMethods()) {
195 pushVersionedNodeOntoMap({iface->fqName().getPackageMajorVersion(),
196 iface->fqName().getPackageMinorVersion(), method,
197 getBaseName(method->name())},
198 &latestMethodForBaseName, &supersededMethods);
199 }
200 // Types from other interfaces will be handled while those interfaces
201 // are being emitted.
202 if (iface->getBaseName() != interface.getBaseName()) {
203 continue;
204 }
205 for (const NamedType* type : iface->getSubTypes()) {
206 // The baseName for types is not being stripped of the version
207 // numbers like that of the methods. If a type was named
208 // BigStruct_1_1 and the previous version was named BigStruct,
209 // they will be treated as two different types.
210 pushVersionedNodeOntoMap({iface->fqName().getPackageMajorVersion(),
211 iface->fqName().getPackageMinorVersion(), type,
212 getAidlName(type->fqName())},
213 &latestTypeForBaseName, &supersededNamedTypes);
214 }
215 }
216
217 // Add comment for superseded types
218 out.join(supersededNamedTypes.begin(), supersededNamedTypes.end(), "\n",
219 [&](const NodeWithVersion<NamedType>& versionedType) {
220 out << "// Ignoring type " << getAidlName(versionedType.node->fqName())
221 << " from " << versionedType.major << "." << versionedType.minor
222 << "::" << getAidlName(interface.fqName())
223 << " since a newer alternative is available.";
224 });
225 if (!supersededNamedTypes.empty()) out << "\n\n";
226 // Emit the nested type definitions
227 for (const auto& [name, type] : latestTypeForBaseName) {
228 emitAidl(*type.node, out, processedTypes);
229 }
230 // Add comment for superseded methods
231 out.join(supersededMethods.begin(), supersededMethods.end(), "\n",
232 [&](const NodeWithVersion<Method>& versionedMethod) {
233 out << "// Ignoring method " << versionedMethod.node->name() << " from "
234 << versionedMethod.major << "." << versionedMethod.minor
235 << "::" << getAidlName(interface.fqName())
236 << " since a newer alternative is available.";
237 });
238 if (!supersededMethods.empty()) out << "\n\n";
239
240 // Emit latest methods defined for this interface
241 out.join(latestMethodForBaseName.begin(), latestMethodForBaseName.end(), "\n",
242 [&](const std::pair<std::string, NodeWithVersion<Method>>& methodPair) {
243 const Method* method = methodPair.second.node;
244 const std::string& baseName = methodPair.first;
245 std::vector<NamedReference<Type>*> results;
246 std::vector<ResultTransformation> transformations;
247 for (NamedReference<Type>* res : method->results()) {
248 const std::string aidlType = getAidlType(*res->get(), interface.fqName());
249
250 if (shouldWarnStatusType(aidlType)) {
251 out << "// FIXME: AIDL has built-in status types. Do we need the "
252 "status type here?\n";
253 }
254 if (method->results().size() > 1 && shouldWarnOutParam(aidlType)) {
255 out << "// FIXME: AIDL does not allow " << aidlType
256 << " to be an out parameter.\n";
257 out << "// Move it to return, or add it to a Parcelable.\n";
258 }
259 results.push_back(res);
260 }
261
262 if (method->name() != baseName) {
263 out << "// Changing method name from " << method->name() << " to "
264 << baseName << "\n";
265 }
266
267 std::string returnType = "void";
268 if (results.size() == 1) {
269 returnType = getAidlType(*results[0]->get(), interface.fqName());
270
271 out << "// Adding return type to method instead of out param "
272 << returnType << " " << results[0]->name()
273 << " since there is only one return value.\n";
274 transformations.emplace_back(ResultTransformation{
275 results[0]->name(), ResultTransformation::TransformType::MOVED});
276 results.clear();
277 }
278
279 if (method->getDocComment() != nullptr) {
280 std::vector<std::string> modifiedDocComment;
281 for (const std::string& line : method->getDocComment()->lines()) {
282 std::vector<std::string> tokens = base::Split(line, " ");
283 if (tokens.size() <= 1 || tokens[0] != "@return") {
284 // unimportant line
285 modifiedDocComment.emplace_back(line);
286 continue;
287 }
288
289 const std::string& res = tokens[1];
290 bool transformed = false;
291 for (const ResultTransformation& transform : transformations) {
292 if (transform.resultName != res) continue;
293
294 // Some transform was done to it
295 if (transform.type == ResultTransformation::TransformType::MOVED) {
296 // remove the name
297 tokens.erase(++tokens.begin());
298 transformed = true;
299 } else {
300 CHECK(transform.type ==
301 ResultTransformation::TransformType::REMOVED);
302 tokens.insert(tokens.begin(),
303 "FIXME: The following return was removed\n");
304 transformed = true;
305 }
306 }
307
308 if (!transformed) {
309 tokens.erase(tokens.begin());
310 tokens.insert(tokens.begin(), "@param out");
311 }
312
313 modifiedDocComment.emplace_back(base::Join(tokens, " "));
314 }
315
316 DocComment(modifiedDocComment, HIDL_LOCATION_HERE).emit(out);
317 }
318
319 WrappedOutput wrappedOutput(MAX_LINE_LENGTH);
320
321 if (method->isOneway()) wrappedOutput << "oneway ";
322 wrappedOutput << returnType << " " << baseName << "(";
323
324 if (results.empty()) {
325 emitAidlMethodParams(&wrappedOutput, method->args(), /* prefix */ "in ",
326 /* attachToLast */ ");\n", interface);
327 } else {
328 const bool emitArgs = !method->args().empty();
329 if (emitArgs) {
330 emitAidlMethodParams(&wrappedOutput, method->args(),
331 /* prefix */ "in ",
332 /* attachToLast */ ",", interface);
333 }
334 wrappedOutput.group([&] {
335 if (emitArgs) wrappedOutput.printUnlessWrapped(" ");
336 emitAidlMethodParams(&wrappedOutput, results, /* prefix */ "out ",
337 /* attachToLast */ ");\n", interface);
338 });
339 }
340
341 out << wrappedOutput;
342 });
343 });
344 out << "\n";
345 }
346
347 } // namespace android
348