blob: 84674e841063dd34fd88aef9da7dd17df6adbb4b [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 "ConfigDescription.h"
Adam Lesinski769de982015-04-10 19:43:55 -070018#include "NameMangler.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080019#include "ResourceTable.h"
20#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "ValueVisitor.h"
22#include "util/Util.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080023
24#include <algorithm>
25#include <androidfw/ResourceTypes.h>
26#include <memory>
27#include <string>
28#include <tuple>
29
30namespace aapt {
31
32static bool compareConfigs(const ResourceConfigValue& lhs, const ConfigDescription& rhs) {
33 return lhs.config < rhs;
34}
35
36static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
37 return lhs->type < rhs;
38}
39
Adam Lesinski1ab598f2015-08-14 14:26:04 -070040template <typename T>
41static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
42 const StringPiece16& rhs) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080043 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
44}
45
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& name) {
47 const auto last = packages.end();
48 auto iter = std::lower_bound(packages.begin(), last, name,
49 lessThanStructWithName<ResourceTablePackage>);
50 if (iter != last && name == (*iter)->name) {
51 return iter->get();
52 }
53 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054}
55
Adam Lesinski1ab598f2015-08-14 14:26:04 -070056ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
57 for (auto& package : packages) {
58 if (package->id && package->id.value() == id) {
59 return package.get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080060 }
61 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 return nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080063}
64
Adam Lesinski9ba47d82015-10-13 11:37:10 -070065ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, Maybe<uint8_t> id) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070066 ResourceTablePackage* package = findOrCreatePackage(name);
Adam Lesinski9ba47d82015-10-13 11:37:10 -070067 if (id && !package->id) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070068 package->id = id;
69 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080070 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070071
Adam Lesinski9ba47d82015-10-13 11:37:10 -070072 if (id && package->id && package->id.value() != id.value()) {
73 return nullptr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070074 }
Adam Lesinski9ba47d82015-10-13 11:37:10 -070075 return package;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080076}
77
Adam Lesinski1ab598f2015-08-14 14:26:04 -070078ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece16& name) {
79 const auto last = packages.end();
80 auto iter = std::lower_bound(packages.begin(), last, name,
81 lessThanStructWithName<ResourceTablePackage>);
82 if (iter != last && name == (*iter)->name) {
83 return iter->get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080084 }
85
Adam Lesinski1ab598f2015-08-14 14:26:04 -070086 std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>();
87 newPackage->name = name.toString();
88 return packages.emplace(iter, std::move(newPackage))->get();
89}
90
91ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
92 const auto last = types.end();
93 auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
94 if (iter != last && (*iter)->type == type) {
95 return iter->get();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080096 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070097 return nullptr;
98}
99
100ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
101 const auto last = types.end();
102 auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
103 if (iter != last && (*iter)->type == type) {
104 return iter->get();
105 }
106 return types.emplace(iter, new ResourceTableType{ type })->get();
107}
108
109ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) {
110 const auto last = entries.end();
111 auto iter = std::lower_bound(entries.begin(), last, name,
112 lessThanStructWithName<ResourceEntry>);
113 if (iter != last && name == (*iter)->name) {
114 return iter->get();
115 }
116 return nullptr;
117}
118
119ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece16& name) {
120 auto last = entries.end();
121 auto iter = std::lower_bound(entries.begin(), last, name,
122 lessThanStructWithName<ResourceEntry>);
123 if (iter != last && name == (*iter)->name) {
124 return iter->get();
125 }
126 return entries.emplace(iter, new ResourceEntry{ name })->get();
127}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800128
129/**
130 * The default handler for collisions. A return value of -1 means keep the
131 * existing value, 0 means fail, and +1 means take the incoming value.
132 */
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700133int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) {
134 Attribute* existingAttr = valueCast<Attribute>(existing);
135 Attribute* incomingAttr = valueCast<Attribute>(incoming);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800136
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700137 if (!incomingAttr) {
138 if (incoming->isWeak()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800139 // We're trying to add a weak resource but a resource
140 // already exists. Keep the existing.
141 return -1;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700142 } else if (existing->isWeak()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800143 // Override the weak resource with the new strong resource.
144 return 1;
145 }
146 // The existing and incoming values are strong, this is an error
147 // if the values are not both attributes.
148 return 0;
149 }
150
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700151 if (!existingAttr) {
152 if (existing->isWeak()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800153 // The existing value is not an attribute and it is weak,
154 // so take the incoming attribute value.
155 return 1;
156 }
157 // The existing value is not an attribute and it is strong,
158 // so the incoming attribute value is an error.
159 return 0;
160 }
161
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700162 assert(incomingAttr && existingAttr);
163
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800164 //
165 // Attribute specific handling. At this point we know both
166 // values are attributes. Since we can declare and define
167 // attributes all-over, we do special handling to see
168 // which definition sticks.
169 //
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700170 if (existingAttr->typeMask == incomingAttr->typeMask) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800171 // The two attributes are both DECLs, but they are plain attributes
172 // with the same formats.
173 // Keep the strongest one.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700174 return existingAttr->isWeak() ? 1 : -1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800175 }
176
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700177 if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800178 // Any incoming attribute is better than this.
179 return 1;
180 }
181
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700182 if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800183 // The incoming attribute may be a USE instead of a DECL.
184 // Keep the existing attribute.
185 return -1;
186 }
187 return 0;
188}
189
190static constexpr const char16_t* kValidNameChars = u"._-";
Adam Lesinski330edcd2015-05-04 17:40:56 -0700191static constexpr const char16_t* kValidNameMangledChars = u"._-$";
192
193bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700194 const Source& source, std::unique_ptr<Value> value,
195 IDiagnostics* diag) {
196 return addResourceImpl(name, ResourceId{}, config, source, std::move(value), kValidNameChars,
197 diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700198}
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800199
200bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700201 const ConfigDescription& config, const Source& source,
202 std::unique_ptr<Value> value, IDiagnostics* diag) {
203 return addResourceImpl(name, resId, config, source, std::move(value), kValidNameChars, diag);
204}
205
206bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config,
207 const Source& source, const StringPiece16& path,
208 IDiagnostics* diag) {
209 return addResourceImpl(name, ResourceId{}, config, source,
210 util::make_unique<FileReference>(stringPool.makeRef(path)),
211 kValidNameChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700212}
213
214bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
215 const ConfigDescription& config,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700216 const Source& source,
217 std::unique_ptr<Value> value,
218 IDiagnostics* diag) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700219 return addResourceImpl(name, ResourceId{}, config, source, std::move(value),
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700220 kValidNameMangledChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700221}
222
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700223bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
224 const ResourceId id,
225 const ConfigDescription& config,
226 const Source& source,
227 std::unique_ptr<Value> value,
228 IDiagnostics* diag) {
229 return addResourceImpl(name, id, config, source, std::move(value),
230 kValidNameMangledChars, diag);
231}
232
Adam Lesinski330edcd2015-05-04 17:40:56 -0700233bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId,
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700234 const ConfigDescription& config, const Source& source,
235 std::unique_ptr<Value> value, const char16_t* validChars,
236 IDiagnostics* diag) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700237 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800238 if (badCharIter != name.entry.end()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700239 diag->error(DiagMessage(source)
240 << "resource '"
241 << name
242 << "' has invalid entry name '"
243 << name.entry
244 << "'. Invalid character '"
245 << StringPiece16(badCharIter, 1)
246 << "'");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800247 return false;
248 }
249
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700250 ResourceTablePackage* package = findOrCreatePackage(name.package);
251 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
252 diag->error(DiagMessage(source)
253 << "trying to add resource '"
254 << name
255 << "' with ID "
256 << resId
257 << " but package '"
258 << package->name
259 << "' already has ID "
260 << std::hex << (int) package->id.value() << std::dec);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800261 return false;
262 }
263
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700264 ResourceTableType* type = package->findOrCreateType(name.type);
265 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
266 diag->error(DiagMessage(source)
267 << "trying to add resource '"
268 << name
269 << "' with ID "
270 << resId
271 << " but type '"
272 << type->type
273 << "' already has ID "
274 << std::hex << (int) type->id.value() << std::dec);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800275 return false;
276 }
277
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700278 ResourceEntry* entry = type->findOrCreateEntry(name.entry);
279 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
280 diag->error(DiagMessage(source)
281 << "trying to add resource '"
282 << name
283 << "' with ID "
284 << resId
285 << " but resource already has ID "
286 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
287 return false;
288 }
289
290 const auto endIter = entry->values.end();
291 auto iter = std::lower_bound(entry->values.begin(), endIter, config, compareConfigs);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800292 if (iter == endIter || iter->config != config) {
293 // This resource did not exist before, add it.
294 entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) });
295 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700296 int collisionResult = resolveValueCollision(iter->value.get(), value.get());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800297 if (collisionResult > 0) {
298 // Take the incoming value.
299 *iter = ResourceConfigValue{ config, source, {}, std::move(value) };
300 } else if (collisionResult == 0) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700301 diag->error(DiagMessage(source)
302 << "duplicate value for resource '" << name << "' "
303 << "with config '" << iter->config << "'");
304 diag->error(DiagMessage(iter->source)
305 << "resource previously defined here");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800306 return false;
307 }
308 }
309
310 if (resId.isValid()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700311 package->id = resId.packageId();
312 type->id = resId.typeId();
313 entry->id = resId.entryId();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800314 }
315 return true;
316}
317
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700318bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId,
319 const Source& source, SymbolState state, IDiagnostics* diag) {
320 return setSymbolStateImpl(name, resId, source, state, kValidNameChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700321}
322
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700323bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name, const ResourceId resId,
324 const Source& source, SymbolState state,
325 IDiagnostics* diag) {
326 return setSymbolStateImpl(name, resId, source, state, kValidNameMangledChars, diag);
Adam Lesinski330edcd2015-05-04 17:40:56 -0700327}
328
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700329bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId,
330 const Source& source, SymbolState state,
331 const char16_t* validChars, IDiagnostics* diag) {
332 if (state == SymbolState::kUndefined) {
333 // Nothing to do.
334 return true;
335 }
336
Adam Lesinski330edcd2015-05-04 17:40:56 -0700337 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800338 if (badCharIter != name.entry.end()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700339 diag->error(DiagMessage(source)
340 << "resource '"
341 << name
342 << "' has invalid entry name '"
343 << name.entry
344 << "'. Invalid character '"
345 << StringPiece16(badCharIter, 1)
346 << "'");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800347 return false;
348 }
349
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700350 ResourceTablePackage* package = findOrCreatePackage(name.package);
351 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
352 diag->error(DiagMessage(source)
353 << "trying to add resource '"
354 << name
355 << "' with ID "
356 << resId
357 << " but package '"
358 << package->name
359 << "' already has ID "
360 << std::hex << (int) package->id.value() << std::dec);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800361 return false;
362 }
363
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700364 ResourceTableType* type = package->findOrCreateType(name.type);
365 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
366 diag->error(DiagMessage(source)
367 << "trying to add resource '"
368 << name
369 << "' with ID "
370 << resId
371 << " but type '"
372 << type->type
373 << "' already has ID "
374 << std::hex << (int) type->id.value() << std::dec);
375 return false;
376 }
377
378 ResourceEntry* entry = type->findOrCreateEntry(name.entry);
379 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
380 diag->error(DiagMessage(source)
381 << "trying to add resource '"
382 << name
383 << "' with ID "
384 << resId
385 << " but resource already has ID "
386 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800387 return false;
388 }
389
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700390 // Only mark the type state as public, it doesn't care about being private.
391 if (state == SymbolState::kPublic) {
392 type->symbolStatus.state = SymbolState::kPublic;
393 }
394
395 // Downgrading to a private symbol from a public one is not allowed.
396 if (entry->symbolStatus.state != SymbolState::kPublic) {
397 if (entry->symbolStatus.state != state) {
398 entry->symbolStatus.state = state;
399 entry->symbolStatus.source = source;
400 }
401 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800402
403 if (resId.isValid()) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700404 package->id = resId.packageId();
405 type->id = resId.typeId();
406 entry->id = resId.entryId();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800407 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800408 return true;
409}
410
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700411Maybe<ResourceTable::SearchResult>
412ResourceTable::findResource(const ResourceNameRef& name) {
413 ResourceTablePackage* package = findPackage(name.package);
414 if (!package) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700415 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800416 }
417
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700418 ResourceTableType* type = package->findType(name.type);
419 if (!type) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700420 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800421 }
422
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700423 ResourceEntry* entry = type->findEntry(name.entry);
424 if (!entry) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700425 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800426 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700427 return SearchResult{ package, type, entry };
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800428}
429
430} // namespace aapt