blob: a8b7a14256b28c52549d6957442a79c266fdfa2a [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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"
Adam Lesinski24aad162015-04-24 19:19:30 -070019#include "NameMangler.h"
20#include "Resolver.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080021#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>
Adam Lesinskica2fc352015-04-03 12:08:26 -070029#include <bitset>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080030#include <iostream>
31#include <map>
32#include <ostream>
33#include <set>
34#include <sstream>
35#include <tuple>
36#include <vector>
37
38namespace aapt {
39
40Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) {
41}
42
Adam Lesinski24aad162015-04-24 19:19:30 -070043Linker::Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<IResolver> resolver) :
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080044 mTable(table), mResolver(resolver), mError(false) {
45}
46
47bool Linker::linkAndValidate() {
48 std::bitset<256> usedTypeIds;
49 std::array<std::set<uint16_t>, 256> usedIds;
50 usedTypeIds.set(0);
51
52 // First build the graph of references.
53 for (auto& type : *mTable) {
54 if (type->typeId != ResourceTableType::kUnsetTypeId) {
55 // The ID for this type has already been set. We
56 // mark this ID as taken so we don't re-assign it
57 // later.
58 usedTypeIds.set(type->typeId);
59 }
60
61 for (auto& entry : type->entries) {
62 if (type->typeId != ResourceTableType::kUnsetTypeId &&
63 entry->entryId != ResourceEntry::kUnsetEntryId) {
64 // The ID for this entry has already been set. We
65 // mark this ID as taken so we don't re-assign it
66 // later.
67 usedIds[type->typeId].insert(entry->entryId);
68 }
69
Adam Lesinski6ff19662015-04-30 17:40:46 -070070 if (entry->publicStatus.isPublic && entry->values.empty()) {
71 // A public resource has no values. It will not be encoded
72 // properly without a symbol table. This is a unresolved symbol.
73 addUnresolvedSymbol(ResourceNameRef{
74 mTable->getPackage(), type->type, entry->name },
75 entry->publicStatus.source);
76 } else {
77 for (auto& valueConfig : entry->values) {
78 // Dispatch to the right method of this linker
79 // based on the value's type.
80 valueConfig.value->accept(*this, Args{
81 ResourceNameRef{ mTable->getPackage(), type->type, entry->name },
82 valueConfig.source
83 });
84 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080085 }
86 }
87 }
88
89 /*
90 * Assign resource IDs that are available.
91 */
92 size_t nextTypeIndex = 0;
93 for (auto& type : *mTable) {
94 if (type->typeId == ResourceTableType::kUnsetTypeId) {
95 while (nextTypeIndex < usedTypeIds.size() && usedTypeIds[nextTypeIndex]) {
96 nextTypeIndex++;
97 }
98 type->typeId = nextTypeIndex++;
99 }
100
101 const auto endEntryIter = std::end(usedIds[type->typeId]);
102 auto nextEntryIter = std::begin(usedIds[type->typeId]);
103 size_t nextIndex = 0;
104 for (auto& entry : type->entries) {
105 if (entry->entryId == ResourceTableType::kUnsetTypeId) {
106 while (nextEntryIter != endEntryIter &&
107 nextIndex == *nextEntryIter) {
108 nextIndex++;
109 ++nextEntryIter;
110 }
111 entry->entryId = nextIndex++;
112
Adam Lesinski24aad162015-04-24 19:19:30 -0700113 std::u16string unmangledPackage = mTable->getPackage();
114 std::u16string unmangledName = entry->name;
115 NameMangler::unmangle(&unmangledName, &unmangledPackage);
116
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800117 // Update callers of this resource with the right ID.
118 auto callersIter = mGraph.find(ResourceNameRef{
Adam Lesinski24aad162015-04-24 19:19:30 -0700119 unmangledPackage,
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800120 type->type,
Adam Lesinski24aad162015-04-24 19:19:30 -0700121 unmangledName
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800122 });
123
124 if (callersIter != std::end(mGraph)) {
125 for (Node& caller : callersIter->second) {
126 caller.reference->id = ResourceId(mTable->getPackageId(),
127 type->typeId,
128 entry->entryId);
129 }
130 }
131 }
132 }
133 }
134
135 return !mError;
136}
137
138const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const {
139 return mUnresolvedSymbols;
140}
141
142void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
143 Args& args = static_cast<Args&>(a);
144
Adam Lesinski769de982015-04-10 19:43:55 -0700145 if (!reference.name.isValid()) {
146 // We can't have a completely bad reference.
147 assert(reference.id.isValid());
148
149 // This reference has no name but has an ID.
150 // It is a really bad error to have no name and have the same
151 // package ID.
152 assert(reference.id.packageId() != mTable->getPackageId());
153
154 // The reference goes outside this package, let it stay as a
155 // resource ID because it will not change.
156 return;
157 }
158
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800159 Maybe<ResourceId> result = mResolver->findId(reference.name);
160 if (!result) {
161 addUnresolvedSymbol(reference.name, args.source);
162 return;
163 }
164
165 const ResourceId& id = result.value();
166 if (id.isValid()) {
167 reference.id = id;
168 } else {
169 // We need to update the ID when it is set, so add it
170 // to the graph.
171 mGraph[reference.name].push_back(Node{
172 args.referrer,
173 args.source.path,
174 args.source.line,
175 &reference
176 });
177 }
178
179 // TODO(adamlesinski): Verify the referencedType is another reference
180 // or a compatible primitive.
181}
182
183void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
184 const Attribute& attr, std::unique_ptr<Item>& value) {
185 std::unique_ptr<Item> convertedValue;
186 visitFunc<RawString>(*value, [&](RawString& str) {
187 // This is a raw string, so check if it can be converted to anything.
188 // We can NOT swap value with the converted value in here, since
189 // we called through the original value.
190
191 auto onCreateReference = [&](const ResourceName& name) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700192 // We should never get here. All references would have been
193 // parsed in the parser phase.
194 assert(false);
195 //mTable->addResource(name, ConfigDescription{}, source, util::make_unique<Id>());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800196 };
197
Adam Lesinski24aad162015-04-24 19:19:30 -0700198 convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr,
199 onCreateReference);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800200 if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) {
201 // Last effort is to parse as a string.
202 util::StringBuilder builder;
203 builder.append(*str.value);
204 if (builder) {
205 convertedValue = util::make_unique<String>(
206 mTable->getValueStringPool().makeRef(builder.str()));
207 }
208 }
209 });
210
211 if (convertedValue) {
212 value = std::move(convertedValue);
213 }
214
215 // Process this new or old value (it can be a reference!).
216 value->accept(*this, Args{ name, source });
217
218 // Flatten the value to see what resource type it is.
219 android::Res_value resValue;
220 value->flatten(resValue);
221
222 // Always allow references.
223 const uint32_t typeMask = attr.typeMask | android::ResTable_map::TYPE_REFERENCE;
224 if (!(typeMask & ResourceParser::androidTypeToAttributeTypeMask(resValue.dataType))) {
225 Logger::error(source)
226 << *value
227 << " is not compatible with attribute "
228 << attr
229 << "."
230 << std::endl;
231 mError = true;
232 }
233}
234
235void Linker::visit(Style& style, ValueVisitorArgs& a) {
236 Args& args = static_cast<Args&>(a);
237
Adam Lesinski769de982015-04-10 19:43:55 -0700238 if (style.parent.name.isValid() || style.parent.id.isValid()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800239 visit(style.parent, a);
240 }
241
242 for (Style::Entry& styleEntry : style.entries) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700243 Maybe<IResolver::Entry> result = mResolver->findAttribute(styleEntry.key.name);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800244 if (!result || !result.value().attr) {
245 addUnresolvedSymbol(styleEntry.key.name, args.source);
246 continue;
247 }
248
Adam Lesinski24aad162015-04-24 19:19:30 -0700249 const IResolver::Entry& entry = result.value();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800250 if (entry.id.isValid()) {
251 styleEntry.key.id = entry.id;
252 } else {
253 // Create a dependency for the style on this attribute.
254 mGraph[styleEntry.key.name].push_back(Node{
255 args.referrer,
256 args.source.path,
257 args.source.line,
258 &styleEntry.key
259 });
260 }
261 processAttributeValue(args.referrer, args.source, *entry.attr, styleEntry.value);
262 }
263}
264
265void Linker::visit(Attribute& attr, ValueVisitorArgs& a) {
266 static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
267 android::ResTable_map::TYPE_FLAGS;
268 if (attr.typeMask & kMask) {
269 for (auto& symbol : attr.symbols) {
270 visit(symbol.symbol, a);
271 }
272 }
273}
274
275void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) {
276 for (auto& attrRef : styleable.entries) {
277 visit(attrRef, a);
278 }
279}
280
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800281void Linker::visit(Array& array, ValueVisitorArgs& a) {
282 Args& args = static_cast<Args&>(a);
283
284 for (auto& item : array.items) {
285 item->accept(*this, Args{ args.referrer, args.source });
286 }
287}
288
289void Linker::visit(Plural& plural, ValueVisitorArgs& a) {
290 Args& args = static_cast<Args&>(a);
291
292 for (auto& item : plural.values) {
293 if (item) {
294 item->accept(*this, Args{ args.referrer, args.source });
295 }
296 }
297}
298
299void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) {
300 mUnresolvedSymbols[name.toResourceName()].push_back(source);
301}
302
303::std::ostream& operator<<(::std::ostream& out, const Linker::Node& node) {
304 return out << node.name << "(" << node.source << ":" << node.line << ")";
305}
306
307} // namespace aapt