blob: 1cfb2975b1dd5ad1a462cd13581a341246ad1852 [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
131 Maybe<ResourceId> result = mResolver->findId(reference.name);
132 if (!result) {
133 addUnresolvedSymbol(reference.name, args.source);
134 return;
135 }
136
137 const ResourceId& id = result.value();
138 if (id.isValid()) {
139 reference.id = id;
140 } else {
141 // We need to update the ID when it is set, so add it
142 // to the graph.
143 mGraph[reference.name].push_back(Node{
144 args.referrer,
145 args.source.path,
146 args.source.line,
147 &reference
148 });
149 }
150
151 // TODO(adamlesinski): Verify the referencedType is another reference
152 // or a compatible primitive.
153}
154
155void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
156 const Attribute& attr, std::unique_ptr<Item>& value) {
157 std::unique_ptr<Item> convertedValue;
158 visitFunc<RawString>(*value, [&](RawString& str) {
159 // This is a raw string, so check if it can be converted to anything.
160 // We can NOT swap value with the converted value in here, since
161 // we called through the original value.
162
163 auto onCreateReference = [&](const ResourceName& name) {
164 mTable->addResource(name, ConfigDescription{},
165 source, util::make_unique<Id>());
166 };
167
168 convertedValue = ResourceParser::parseItemForAttribute(
169 *str.value, attr, mResolver->getDefaultPackage(),
170 onCreateReference);
171 if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) {
172 // Last effort is to parse as a string.
173 util::StringBuilder builder;
174 builder.append(*str.value);
175 if (builder) {
176 convertedValue = util::make_unique<String>(
177 mTable->getValueStringPool().makeRef(builder.str()));
178 }
179 }
180 });
181
182 if (convertedValue) {
183 value = std::move(convertedValue);
184 }
185
186 // Process this new or old value (it can be a reference!).
187 value->accept(*this, Args{ name, source });
188
189 // Flatten the value to see what resource type it is.
190 android::Res_value resValue;
191 value->flatten(resValue);
192
193 // Always allow references.
194 const uint32_t typeMask = attr.typeMask | android::ResTable_map::TYPE_REFERENCE;
195 if (!(typeMask & ResourceParser::androidTypeToAttributeTypeMask(resValue.dataType))) {
196 Logger::error(source)
197 << *value
198 << " is not compatible with attribute "
199 << attr
200 << "."
201 << std::endl;
202 mError = true;
203 }
204}
205
206void Linker::visit(Style& style, ValueVisitorArgs& a) {
207 Args& args = static_cast<Args&>(a);
208
209 if (style.parent.name.isValid()) {
210 visit(style.parent, a);
211 }
212
213 for (Style::Entry& styleEntry : style.entries) {
214 Maybe<Resolver::Entry> result = mResolver->findAttribute(styleEntry.key.name);
215 if (!result || !result.value().attr) {
216 addUnresolvedSymbol(styleEntry.key.name, args.source);
217 continue;
218 }
219
220 const Resolver::Entry& entry = result.value();
221 if (entry.id.isValid()) {
222 styleEntry.key.id = entry.id;
223 } else {
224 // Create a dependency for the style on this attribute.
225 mGraph[styleEntry.key.name].push_back(Node{
226 args.referrer,
227 args.source.path,
228 args.source.line,
229 &styleEntry.key
230 });
231 }
232 processAttributeValue(args.referrer, args.source, *entry.attr, styleEntry.value);
233 }
234}
235
236void Linker::visit(Attribute& attr, ValueVisitorArgs& a) {
237 static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
238 android::ResTable_map::TYPE_FLAGS;
239 if (attr.typeMask & kMask) {
240 for (auto& symbol : attr.symbols) {
241 visit(symbol.symbol, a);
242 }
243 }
244}
245
246void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) {
247 for (auto& attrRef : styleable.entries) {
248 visit(attrRef, a);
249 }
250}
251
252void Linker::visit(Sentinel& sentinel, ValueVisitorArgs& a) {
253 Args& args = static_cast<Args&>(a);
254 addUnresolvedSymbol(args.referrer, args.source);
255}
256
257void Linker::visit(Array& array, ValueVisitorArgs& a) {
258 Args& args = static_cast<Args&>(a);
259
260 for (auto& item : array.items) {
261 item->accept(*this, Args{ args.referrer, args.source });
262 }
263}
264
265void Linker::visit(Plural& plural, ValueVisitorArgs& a) {
266 Args& args = static_cast<Args&>(a);
267
268 for (auto& item : plural.values) {
269 if (item) {
270 item->accept(*this, Args{ args.referrer, args.source });
271 }
272 }
273}
274
275void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) {
276 mUnresolvedSymbols[name.toResourceName()].push_back(source);
277}
278
279::std::ostream& operator<<(::std::ostream& out, const Linker::Node& node) {
280 return out << node.name << "(" << node.source << ":" << node.line << ")";
281}
282
283} // namespace aapt