1 /*
2  * Copyright (C) 2015 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 "Linker.h"
18 #include "Logger.h"
19 #include "NameMangler.h"
20 #include "Resolver.h"
21 #include "ResourceParser.h"
22 #include "ResourceTable.h"
23 #include "ResourceValues.h"
24 #include "StringPiece.h"
25 #include "Util.h"
26 
27 #include <androidfw/AssetManager.h>
28 #include <array>
29 #include <bitset>
30 #include <iostream>
31 #include <map>
32 #include <ostream>
33 #include <set>
34 #include <sstream>
35 #include <tuple>
36 #include <vector>
37 
38 namespace aapt {
39 
Args(const ResourceNameRef & r,const SourceLine & s)40 Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) {
41 }
42 
Linker(const std::shared_ptr<ResourceTable> & table,const std::shared_ptr<IResolver> & resolver,const Options & options)43 Linker::Linker(const std::shared_ptr<ResourceTable>& table,
44                const std::shared_ptr<IResolver>& resolver, const Options& options) :
45         mResolver(resolver), mTable(table), mOptions(options), mError(false) {
46 }
47 
linkAndValidate()48 bool Linker::linkAndValidate() {
49     std::bitset<256> usedTypeIds;
50     std::array<std::set<uint16_t>, 256> usedIds;
51     usedTypeIds.set(0);
52 
53     // Collect which resource IDs are already taken.
54     for (auto& type : *mTable) {
55         if (type->typeId != ResourceTableType::kUnsetTypeId) {
56             // The ID for this type has already been set. We
57             // mark this ID as taken so we don't re-assign it
58             // later.
59             usedTypeIds.set(type->typeId);
60         }
61 
62         for (auto& entry : type->entries) {
63             if (type->typeId != ResourceTableType::kUnsetTypeId &&
64                     entry->entryId != ResourceEntry::kUnsetEntryId) {
65                 // The ID for this entry has already been set. We
66                 // mark this ID as taken so we don't re-assign it
67                 // later.
68                 usedIds[type->typeId].insert(entry->entryId);
69             }
70         }
71     }
72 
73     // Assign resource IDs that are available.
74     size_t nextTypeIndex = 0;
75     for (auto& type : *mTable) {
76         if (type->typeId == ResourceTableType::kUnsetTypeId) {
77             while (nextTypeIndex < usedTypeIds.size() && usedTypeIds[nextTypeIndex]) {
78                 nextTypeIndex++;
79             }
80             type->typeId = nextTypeIndex++;
81         }
82 
83         const auto endEntryIter = std::end(usedIds[type->typeId]);
84         auto nextEntryIter = std::begin(usedIds[type->typeId]);
85         size_t nextIndex = 0;
86         for (auto& entry : type->entries) {
87             if (entry->entryId == ResourceTableType::kUnsetTypeId) {
88                 while (nextEntryIter != endEntryIter &&
89                         nextIndex == *nextEntryIter) {
90                     nextIndex++;
91                     ++nextEntryIter;
92                 }
93                 entry->entryId = nextIndex++;
94             }
95         }
96     }
97 
98     // Now do reference linking.
99     for (auto& type : *mTable) {
100         for (auto& entry : type->entries) {
101             if (entry->publicStatus.isPublic && entry->values.empty()) {
102                 // A public resource has no values. It will not be encoded
103                 // properly without a symbol table. This is a unresolved symbol.
104                 addUnresolvedSymbol(ResourceNameRef{
105                         mTable->getPackage(), type->type, entry->name },
106                         entry->publicStatus.source);
107                 continue;
108             }
109 
110             for (auto& valueConfig : entry->values) {
111                 // Dispatch to the right method of this linker
112                 // based on the value's type.
113                 valueConfig.value->accept(*this, Args{
114                         ResourceNameRef{ mTable->getPackage(), type->type, entry->name },
115                         valueConfig.source
116                 });
117             }
118         }
119     }
120     return !mError;
121 }
122 
getUnresolvedReferences() const123 const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const {
124     return mUnresolvedSymbols;
125 }
126 
doResolveReference(Reference & reference,const SourceLine & source)127 void Linker::doResolveReference(Reference& reference, const SourceLine& source) {
128     Maybe<ResourceId> result = mResolver->findId(reference.name);
129     if (!result) {
130         addUnresolvedSymbol(reference.name, source);
131         return;
132     }
133     assert(result.value().isValid());
134 
135     if (mOptions.linkResourceIds) {
136         reference.id = result.value();
137     } else {
138         reference.id = 0;
139     }
140 }
141 
doResolveAttribute(Reference & attribute,const SourceLine & source)142 const Attribute* Linker::doResolveAttribute(Reference& attribute, const SourceLine& source) {
143     Maybe<IResolver::Entry> result = mResolver->findAttribute(attribute.name);
144     if (!result || !result.value().attr) {
145         addUnresolvedSymbol(attribute.name, source);
146         return nullptr;
147     }
148 
149     const IResolver::Entry& entry = result.value();
150     assert(entry.id.isValid());
151 
152     if (mOptions.linkResourceIds) {
153         attribute.id = entry.id;
154     } else {
155         attribute.id = 0;
156     }
157     return entry.attr;
158 }
159 
visit(Reference & reference,ValueVisitorArgs & a)160 void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
161     Args& args = static_cast<Args&>(a);
162 
163     if (reference.name.entry.empty()) {
164         // We can't have a completely bad reference.
165         if (!reference.id.isValid()) {
166             Logger::error() << "srsly? " << args.referrer << std::endl;
167             assert(reference.id.isValid());
168         }
169 
170         // This reference has no name but has an ID.
171         // It is a really bad error to have no name and have the same
172         // package ID.
173         assert(reference.id.packageId() != mTable->getPackageId());
174 
175         // The reference goes outside this package, let it stay as a
176         // resource ID because it will not change.
177         return;
178     }
179 
180     doResolveReference(reference, args.source);
181 
182     // TODO(adamlesinski): Verify the referencedType is another reference
183     // or a compatible primitive.
184 }
185 
processAttributeValue(const ResourceNameRef & name,const SourceLine & source,const Attribute & attr,std::unique_ptr<Item> & value)186 void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
187         const Attribute& attr, std::unique_ptr<Item>& value) {
188     std::unique_ptr<Item> convertedValue;
189     visitFunc<RawString>(*value, [&](RawString& str) {
190         // This is a raw string, so check if it can be converted to anything.
191         // We can NOT swap value with the converted value in here, since
192         // we called through the original value.
193 
194         auto onCreateReference = [&](const ResourceName& name) {
195             // We should never get here. All references would have been
196             // parsed in the parser phase.
197             assert(false);
198         };
199 
200         convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr,
201                                                                onCreateReference);
202         if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) {
203             // Last effort is to parse as a string.
204             util::StringBuilder builder;
205             builder.append(*str.value);
206             if (builder) {
207                 convertedValue = util::make_unique<String>(
208                         mTable->getValueStringPool().makeRef(builder.str()));
209             }
210         }
211     });
212 
213     if (convertedValue) {
214         value = std::move(convertedValue);
215     }
216 
217     // Process this new or old value (it can be a reference!).
218     value->accept(*this, Args{ name, source });
219 
220     // Flatten the value to see what resource type it is.
221     android::Res_value resValue;
222     value->flatten(resValue);
223 
224     // Always allow references.
225     const uint32_t typeMask = attr.typeMask | android::ResTable_map::TYPE_REFERENCE;
226     if (!(typeMask & ResourceParser::androidTypeToAttributeTypeMask(resValue.dataType))) {
227         Logger::error(source)
228                 << *value
229                 << " is not compatible with attribute "
230                 << attr
231                 << "."
232                 << std::endl;
233         mError = true;
234     }
235 }
236 
visit(Style & style,ValueVisitorArgs & a)237 void Linker::visit(Style& style, ValueVisitorArgs& a) {
238     Args& args = static_cast<Args&>(a);
239 
240     if (style.parent.name.isValid() || style.parent.id.isValid()) {
241         visit(style.parent, a);
242     }
243 
244     for (Style::Entry& styleEntry : style.entries) {
245         const Attribute* attr = doResolveAttribute(styleEntry.key, args.source);
246         if (attr) {
247             processAttributeValue(args.referrer, args.source, *attr, styleEntry.value);
248         }
249     }
250 }
251 
visit(Attribute & attr,ValueVisitorArgs & a)252 void Linker::visit(Attribute& attr, ValueVisitorArgs& a) {
253     static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
254             android::ResTable_map::TYPE_FLAGS;
255     if (attr.typeMask & kMask) {
256         for (auto& symbol : attr.symbols) {
257             visit(symbol.symbol, a);
258         }
259     }
260 }
261 
visit(Styleable & styleable,ValueVisitorArgs & a)262 void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) {
263     for (auto& attrRef : styleable.entries) {
264         visit(attrRef, a);
265     }
266 }
267 
visit(Array & array,ValueVisitorArgs & a)268 void Linker::visit(Array& array, ValueVisitorArgs& a) {
269     Args& args = static_cast<Args&>(a);
270 
271     for (auto& item : array.items) {
272         item->accept(*this, Args{ args.referrer, args.source });
273     }
274 }
275 
visit(Plural & plural,ValueVisitorArgs & a)276 void Linker::visit(Plural& plural, ValueVisitorArgs& a) {
277     Args& args = static_cast<Args&>(a);
278 
279     for (auto& item : plural.values) {
280         if (item) {
281             item->accept(*this, Args{ args.referrer, args.source });
282         }
283     }
284 }
285 
addUnresolvedSymbol(const ResourceNameRef & name,const SourceLine & source)286 void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) {
287     mUnresolvedSymbols[name.toResourceName()].push_back(source);
288 }
289 
290 } // namespace aapt
291