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