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/file.h>
18 #include <android-base/logging.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 <iostream>
24 #include <set>
25 #include <string>
26 #include <vector>
27 
28 #include "AidlHelper.h"
29 #include "ArrayType.h"
30 #include "CompoundType.h"
31 #include "Coordinator.h"
32 #include "Interface.h"
33 #include "Method.h"
34 #include "NamedType.h"
35 #include "Reference.h"
36 #include "Scope.h"
37 
38 namespace android {
39 
40 Formatter* AidlHelper::notesFormatter = nullptr;
41 std::string AidlHelper::fileHeader = "";
42 bool AidlHelper::expandExtended = false;
43 
notes()44 Formatter& AidlHelper::notes() {
45     CHECK(notesFormatter != nullptr);
46     return *notesFormatter;
47 }
48 
setNotes(Formatter * formatter)49 void AidlHelper::setNotes(Formatter* formatter) {
50     CHECK(formatter != nullptr);
51     notesFormatter = formatter;
52 }
53 
getAidlName(const FQName & fqName)54 std::string AidlHelper::getAidlName(const FQName& fqName) {
55     std::vector<std::string> names;
56     for (const std::string& name : fqName.names()) {
57         names.push_back(StringHelper::Capitalize(name));
58     }
59     return StringHelper::JoinStrings(names, "");
60 }
61 
getAidlPackage(const FQName & fqName)62 std::string AidlHelper::getAidlPackage(const FQName& fqName) {
63     std::string aidlPackage = fqName.package();
64     if (fqName.getPackageMajorVersion() != 1) {
65         aidlPackage += std::to_string(fqName.getPackageMajorVersion());
66     }
67 
68     return aidlPackage;
69 }
70 
getAidlPackagePath(const FQName & fqName)71 std::string AidlHelper::getAidlPackagePath(const FQName& fqName) {
72     return base::Join(base::Split(AidlHelper::getAidlPackage(fqName), "."), "/");
73 }
74 
getAidlFQName(const FQName & fqName)75 std::optional<std::string> AidlHelper::getAidlFQName(const FQName& fqName) {
76     std::optional<const ReplacedTypeInfo> type = getAidlReplacedType(fqName);
77     if (type) {
78         return type.value().aidlReplacedFQName;
79     }
80     return getAidlPackage(fqName) + "." + getAidlName(fqName);
81 }
82 
importLocallyReferencedType(const Type & type,std::set<std::string> * imports)83 void AidlHelper::importLocallyReferencedType(const Type& type, std::set<std::string>* imports) {
84     if (type.isArray()) {
85         return importLocallyReferencedType(*static_cast<const ArrayType*>(&type)->getElementType(),
86                                            imports);
87     }
88     if (type.isTemplatedType()) {
89         return importLocallyReferencedType(
90                 *static_cast<const TemplatedType*>(&type)->getElementType(), imports);
91     }
92 
93     if (!type.isNamedType()) return;
94     const NamedType& namedType = *static_cast<const NamedType*>(&type);
95 
96     std::optional<std::string> import = AidlHelper::getAidlFQName(namedType.fqName());
97     if (import) {
98         imports->insert(import.value());
99     }
100 }
101 
102 // This tries iterating over the HIDL AST which is a bit messy because
103 // it has to encode the logic in the rest of hidl2aidl. It would be better
104 // if we could iterate over the AIDL structure which has already been
105 // processed.
emitFileHeader(Formatter & out,const NamedType & type,const std::map<const NamedType *,const ProcessedCompoundType> & processedTypes)106 void AidlHelper::emitFileHeader(
107         Formatter& out, const NamedType& type,
108         const std::map<const NamedType*, const ProcessedCompoundType>& processedTypes) {
109     AidlHelper::emitFileHeader(out);
110     out << "package " << getAidlPackage(type.fqName()) << ";\n\n";
111 
112     std::set<std::string> imports;
113 
114     // Import all the defined types since they will now be in a different file
115     if (type.isScope()) {
116         const Scope& scope = static_cast<const Scope&>(type);
117         for (const NamedType* namedType : scope.getSubTypes()) {
118             importLocallyReferencedType(*namedType, &imports);
119         }
120     }
121 
122     // Import all the referenced types
123     if (type.isInterface()) {
124         // This is a separate case because getReferences doesn't traverse all the superTypes and
125         // sometimes includes references to types that would not exist on AIDL
126         const std::vector<const Method*>& methods =
127                 getUserDefinedMethods(out, static_cast<const Interface&>(type));
128         for (const Method* method : methods) {
129             for (const Reference<Type>* ref : method->getReferences()) {
130                 importLocallyReferencedType(*ref->get(), &imports);
131             }
132         }
133     } else if (type.isCompoundType()) {
134         // Get all of the imports for the flattened compound type that may
135         // include additional fields and subtypes from older versions
136         const auto& it = processedTypes.find(&type);
137         CHECK(it != processedTypes.end()) << "Failed to find " << type.fullName();
138         const ProcessedCompoundType& processedType = it->second;
139 
140         for (const auto& field : processedType.fields) {
141             importLocallyReferencedType(*field.field->get(), &imports);
142         }
143         for (const auto& subType : processedType.subTypes) {
144             importLocallyReferencedType(*subType, &imports);
145         }
146     } else {
147         for (const Reference<Type>* ref : type.getReferences()) {
148             if (ref->get()->definedName() == type.fqName().name()) {
149                 // Don't import the referenced type if this is referencing itself
150                 continue;
151             }
152             importLocallyReferencedType(*ref->get(), &imports);
153         }
154     }
155 
156     for (const std::string& import : imports) {
157         out << "import " << import << ";\n";
158     }
159 
160     if (imports.size() > 0) {
161         out << "\n";
162     }
163 }
164 
getFileWithHeader(const NamedType & namedType,const Coordinator & coordinator,const std::map<const NamedType *,const ProcessedCompoundType> & processedTypes)165 Formatter AidlHelper::getFileWithHeader(
166         const NamedType& namedType, const Coordinator& coordinator,
167         const std::map<const NamedType*, const ProcessedCompoundType>& processedTypes) {
168     Formatter out =
169             coordinator.getFormatter(namedType.fqName(), Coordinator::Location::DIRECT,
170                                      AidlHelper::getAidlPackagePath(namedType.fqName()) + "/" +
171                                              getAidlName(namedType.fqName()) + ".aidl");
172     emitFileHeader(out, namedType, processedTypes);
173     return out;
174 }
175 
processCompoundType(const CompoundType & compoundType,ProcessedCompoundType * processedType,const std::string & fieldNamePrefix)176 void AidlHelper::processCompoundType(const CompoundType& compoundType,
177                                      ProcessedCompoundType* processedType,
178                                      const std::string& fieldNamePrefix) {
179     // Gather all of the subtypes defined in this type
180     for (const NamedType* subType : compoundType.getSubTypes()) {
181         processedType->subTypes.insert(subType);
182     }
183     std::pair<size_t, size_t> version = compoundType.fqName().hasVersion()
184                                                 ? compoundType.fqName().getVersion()
185                                                 : std::pair<size_t, size_t>{0, 0};
186     for (const NamedReference<Type>* field : compoundType.getFields()) {
187         // Check for references to another version of itself
188         if (field->get()->typeName() == compoundType.typeName()) {
189             if (AidlHelper::shouldBeExpanded(
190                         static_cast<const CompoundType&>(*field->get()).fqName(),
191                         compoundType.fqName())) {
192                 processCompoundType(static_cast<const CompoundType&>(*field->get()), processedType,
193                                     fieldNamePrefix + field->name() + ".");
194             } else {
195                 // Keep this field as is
196                 processedType->fields.push_back({field, fieldNamePrefix + field->name(), version});
197             }
198         } else {
199             // Handle duplicate field names. Keep only the most recent definitions.
200             auto it = std::find_if(processedType->fields.begin(), processedType->fields.end(),
201                                    [field](auto& processedField) {
202                                        return processedField.field->name() == field->name();
203                                    });
204             if (it != processedType->fields.end()) {
205                 AidlHelper::notes()
206                         << "Found conflicting field name \"" << field->name()
207                         << "\" in different versions of " << compoundType.fqName().name() << ". ";
208 
209                 if (version.first > it->version.first ||
210                     (version.first == it->version.first && version.second > it->version.second)) {
211                     AidlHelper::notes()
212                             << "Keeping " << field->get()->typeName() << " from " << version.first
213                             << "." << version.second << " and discarding "
214                             << (it->field)->get()->typeName() << " from " << it->version.first
215                             << "." << it->version.second << ".\n";
216                     it->fullName = fieldNamePrefix + field->name();
217                     it->field = field;
218                     it->version = version;
219                 } else {
220                     AidlHelper::notes()
221                             << "Keeping " << (it->field)->get()->typeName() << " from "
222                             << it->version.first << "." << it->version.second << " and discarding "
223                             << field->get()->typeName() << " from " << version.first << "."
224                             << version.second << ".\n";
225                 }
226             } else {
227                 processedType->fields.push_back({field, fieldNamePrefix + field->name(), version});
228             }
229         }
230     }
231 }
232 
setFileHeader(const std::string & file)233 void AidlHelper::setFileHeader(const std::string& file) {
234     if (!file.empty()) {
235         if (!android::base::ReadFileToString(file, &fileHeader)) {
236             std::cerr << "ERROR: Failed to find license file: " << file << "\n";
237             exit(1);
238         }
239     }
240 }
241 
emitFileHeader(Formatter & out)242 void AidlHelper::emitFileHeader(Formatter& out) {
243     if (fileHeader.empty()) {
244         out << "// FIXME: license file, or use the -l option to generate the files with the "
245                "header.\n\n";
246     } else {
247         out << fileHeader << "\n";
248     }
249 }
250 
shouldBeExpanded(const FQName & a,const FQName & b)251 bool AidlHelper::shouldBeExpanded(const FQName& a, const FQName& b) {
252     return a.package() == b.package() || expandExtended;
253 }
254 
255 }  // namespace android
256