• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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