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