blob: c37cc932cd3b6b740da1dd64a96cba7ed57aae43 [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 Lesinski330edcd2015-05-04 17:40:56 -070043Linker::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) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080046}
47
48bool Linker::linkAndValidate() {
49 std::bitset<256> usedTypeIds;
50 std::array<std::set<uint16_t>, 256> usedIds;
51 usedTypeIds.set(0);
52
Adam Lesinski330edcd2015-05-04 17:40:56 -070053 // Collect which resource IDs are already taken.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054 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 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080070 }
71 }
72
Adam Lesinski330edcd2015-05-04 17:40:56 -070073 // Assign resource IDs that are available.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080074 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++;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080094 }
95 }
96 }
97
Adam Lesinski330edcd2015-05-04 17:40:56 -070098 // 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 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800120 return !mError;
121}
122
123const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const {
124 return mUnresolvedSymbols;
125}
126
Adam Lesinski330edcd2015-05-04 17:40:56 -0700127void 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
142const 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
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800160void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
161 Args& args = static_cast<Args&>(a);
162
Adam Lesinski6cc479b2015-06-12 15:45:48 -0700163 if (reference.name.entry.empty()) {
Adam Lesinski769de982015-04-10 19:43:55 -0700164 // We can't have a completely bad reference.
Adam Lesinski330edcd2015-05-04 17:40:56 -0700165 if (!reference.id.isValid()) {
166 Logger::error() << "srsly? " << args.referrer << std::endl;
167 assert(reference.id.isValid());
168 }
Adam Lesinski769de982015-04-10 19:43:55 -0700169
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
Adam Lesinski330edcd2015-05-04 17:40:56 -0700180 doResolveReference(reference, args.source);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800181
182 // TODO(adamlesinski): Verify the referencedType is another reference
183 // or a compatible primitive.
184}
185
186void 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) {
Adam Lesinski24aad162015-04-24 19:19:30 -0700195 // We should never get here. All references would have been
196 // parsed in the parser phase.
197 assert(false);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800198 };
199
Adam Lesinski24aad162015-04-24 19:19:30 -0700200 convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr,
201 onCreateReference);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800202 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
237void Linker::visit(Style& style, ValueVisitorArgs& a) {
238 Args& args = static_cast<Args&>(a);
239
Adam Lesinski769de982015-04-10 19:43:55 -0700240 if (style.parent.name.isValid() || style.parent.id.isValid()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800241 visit(style.parent, a);
242 }
243
244 for (Style::Entry& styleEntry : style.entries) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700245 const Attribute* attr = doResolveAttribute(styleEntry.key, args.source);
246 if (attr) {
247 processAttributeValue(args.referrer, args.source, *attr, styleEntry.value);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800248 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800249 }
250}
251
252void 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
262void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) {
263 for (auto& attrRef : styleable.entries) {
264 visit(attrRef, a);
265 }
266}
267
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800268void 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
276void 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
286void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) {
287 mUnresolvedSymbols[name.toResourceName()].push_back(source);
288}
289
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800290} // namespace aapt