ResourceTable.cpp revision 5eeaaddffd23d8d85aeb321e3ceea626e42cf9de
1/*
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"
18#include "NameMangler.h"
19#include "ResourceTable.h"
20#include "ResourceValues.h"
21#include "ValueVisitor.h"
22#include "util/Util.h"
23
24#include <algorithm>
25#include <androidfw/ResourceTypes.h>
26#include <memory>
27#include <string>
28#include <tuple>
29
30namespace aapt {
31
32static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
33    return lhs->type < rhs;
34}
35
36template <typename T>
37static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
38                                   const StringPiece& rhs) {
39    return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
40}
41
42ResourceTablePackage* ResourceTable::findPackage(const StringPiece& name) {
43    const auto last = packages.end();
44    auto iter = std::lower_bound(packages.begin(), last, name,
45                                 lessThanStructWithName<ResourceTablePackage>);
46    if (iter != last && name == (*iter)->name) {
47        return iter->get();
48    }
49    return nullptr;
50}
51
52ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
53    for (auto& package : packages) {
54        if (package->id && package->id.value() == id) {
55            return package.get();
56        }
57    }
58    return nullptr;
59}
60
61ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name, Maybe<uint8_t> id) {
62    ResourceTablePackage* package = findOrCreatePackage(name);
63    if (id && !package->id) {
64        package->id = id;
65        return package;
66    }
67
68    if (id && package->id && package->id.value() != id.value()) {
69        return nullptr;
70    }
71    return package;
72}
73
74ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece& name) {
75    const auto last = packages.end();
76    auto iter = std::lower_bound(packages.begin(), last, name,
77                                 lessThanStructWithName<ResourceTablePackage>);
78    if (iter != last && name == (*iter)->name) {
79        return iter->get();
80    }
81
82    std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>();
83    newPackage->name = name.toString();
84    return packages.emplace(iter, std::move(newPackage))->get();
85}
86
87ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
88    const auto last = types.end();
89    auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
90    if (iter != last && (*iter)->type == type) {
91        return iter->get();
92    }
93    return nullptr;
94}
95
96ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
97    const auto last = types.end();
98    auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
99    if (iter != last && (*iter)->type == type) {
100        return iter->get();
101    }
102    return types.emplace(iter, new ResourceTableType(type))->get();
103}
104
105ResourceEntry* ResourceTableType::findEntry(const StringPiece& name) {
106    const auto last = entries.end();
107    auto iter = std::lower_bound(entries.begin(), last, name,
108                                 lessThanStructWithName<ResourceEntry>);
109    if (iter != last && name == (*iter)->name) {
110        return iter->get();
111    }
112    return nullptr;
113}
114
115ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece& name) {
116    auto last = entries.end();
117    auto iter = std::lower_bound(entries.begin(), last, name,
118                                 lessThanStructWithName<ResourceEntry>);
119    if (iter != last && name == (*iter)->name) {
120        return iter->get();
121    }
122    return entries.emplace(iter, new ResourceEntry(name))->get();
123}
124
125ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
126    return findValue(config, StringPiece());
127}
128
129struct ConfigKey {
130    const ConfigDescription* config;
131    const StringPiece& product;
132};
133
134bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
135    int cmp = lhs->config.compare(*rhs.config);
136    if (cmp == 0) {
137        cmp = StringPiece(lhs->product).compare(rhs.product);
138    }
139    return cmp < 0;
140}
141
142ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
143                                              const StringPiece& product) {
144    auto iter = std::lower_bound(values.begin(), values.end(),
145                                 ConfigKey{ &config, product }, ltConfigKeyRef);
146    if (iter != values.end()) {
147        ResourceConfigValue* value = iter->get();
148        if (value->config == config && StringPiece(value->product) == product) {
149            return value;
150        }
151    }
152    return nullptr;
153}
154
155ResourceConfigValue* ResourceEntry::findOrCreateValue(const ConfigDescription& config,
156                                                      const StringPiece& product) {
157    auto iter = std::lower_bound(values.begin(), values.end(),
158                                 ConfigKey{ &config, product }, ltConfigKeyRef);
159    if (iter != values.end()) {
160        ResourceConfigValue* value = iter->get();
161        if (value->config == config && StringPiece(value->product) == product) {
162            return value;
163        }
164    }
165    ResourceConfigValue* newValue = values.insert(
166            iter, util::make_unique<ResourceConfigValue>(config, product))->get();
167    return newValue;
168}
169
170std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescription& config) {
171    std::vector<ResourceConfigValue*> results;
172
173    auto iter = values.begin();
174    for (; iter != values.end(); ++iter) {
175        ResourceConfigValue* value = iter->get();
176        if (value->config == config) {
177            results.push_back(value);
178            ++iter;
179            break;
180        }
181    }
182
183    for (; iter != values.end(); ++iter) {
184        ResourceConfigValue* value = iter->get();
185        if (value->config == config) {
186            results.push_back(value);
187        }
188    }
189    return results;
190}
191
192std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
193        const std::function<bool(ResourceConfigValue*)>& f) {
194    std::vector<ResourceConfigValue*> results;
195    for (auto& configValue : values) {
196        if (f(configValue.get())) {
197            results.push_back(configValue.get());
198        }
199    }
200    return results;
201}
202
203/**
204 * The default handler for collisions.
205 *
206 * Typically, a weak value will be overridden by a strong value. An existing weak
207 * value will not be overridden by an incoming weak value.
208 *
209 * There are some exceptions:
210 *
211 * Attributes: There are two types of Attribute values: USE and DECL.
212 *
213 * USE is anywhere an Attribute is declared without a format, and in a place that would
214 * be legal to declare if the Attribute already existed. This is typically in a
215 * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
216 *
217 * DECL is an absolute declaration of an Attribute and specifies an explicit format.
218 *
219 * A DECL will override a USE without error. Two DECLs must match in their format for there to be
220 * no error.
221 */
222ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
223        Value* existing, Value* incoming) {
224    Attribute* existingAttr = valueCast<Attribute>(existing);
225    Attribute* incomingAttr = valueCast<Attribute>(incoming);
226    if (!incomingAttr) {
227        if (incoming->isWeak()) {
228            // We're trying to add a weak resource but a resource
229            // already exists. Keep the existing.
230            return CollisionResult::kKeepOriginal;
231        } else if (existing->isWeak()) {
232            // Override the weak resource with the new strong resource.
233            return CollisionResult::kTakeNew;
234        }
235        // The existing and incoming values are strong, this is an error
236        // if the values are not both attributes.
237        return CollisionResult::kConflict;
238    }
239
240    if (!existingAttr) {
241        if (existing->isWeak()) {
242            // The existing value is not an attribute and it is weak,
243            // so take the incoming attribute value.
244            return CollisionResult::kTakeNew;
245        }
246        // The existing value is not an attribute and it is strong,
247        // so the incoming attribute value is an error.
248        return CollisionResult::kConflict;
249    }
250
251    assert(incomingAttr && existingAttr);
252
253    //
254    // Attribute specific handling. At this point we know both
255    // values are attributes. Since we can declare and define
256    // attributes all-over, we do special handling to see
257    // which definition sticks.
258    //
259    if (existingAttr->typeMask == incomingAttr->typeMask) {
260        // The two attributes are both DECLs, but they are plain attributes
261        // with the same formats.
262        // Keep the strongest one.
263        return existingAttr->isWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
264    }
265
266    if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
267        // Any incoming attribute is better than this.
268        return CollisionResult::kTakeNew;
269    }
270
271    if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
272        // The incoming attribute may be a USE instead of a DECL.
273        // Keep the existing attribute.
274        return CollisionResult::kKeepOriginal;
275    }
276    return CollisionResult::kConflict;
277}
278
279static constexpr const char* kValidNameChars = "._-";
280static constexpr const char* kValidNameMangledChars = "._-$";
281
282bool ResourceTable::addResource(const ResourceNameRef& name,
283                                const ConfigDescription& config,
284                                const StringPiece& product,
285                                std::unique_ptr<Value> value,
286                                IDiagnostics* diag) {
287    return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
288                           resolveValueCollision, diag);
289}
290
291bool ResourceTable::addResource(const ResourceNameRef& name,
292                                const ResourceId& resId,
293                                const ConfigDescription& config,
294                                const StringPiece& product,
295                                std::unique_ptr<Value> value,
296                                IDiagnostics* diag) {
297    return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
298                           resolveValueCollision, diag);
299}
300
301bool ResourceTable::addFileReference(const ResourceNameRef& name,
302                                     const ConfigDescription& config,
303                                     const Source& source,
304                                     const StringPiece& path,
305                                     IDiagnostics* diag) {
306    return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag);
307}
308
309bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name,
310                                                 const ConfigDescription& config,
311                                                 const Source& source,
312                                                 const StringPiece& path,
313                                                 io::IFile* file,
314                                                 IDiagnostics* diag) {
315    return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag);
316}
317
318bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name,
319                                         const ConfigDescription& config,
320                                         const Source& source,
321                                         const StringPiece& path,
322                                         io::IFile* file,
323                                         const char* validChars,
324                                         IDiagnostics* diag) {
325    std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
326            stringPool.makeRef(path));
327    fileRef->setSource(source);
328    fileRef->file = file;
329    return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
330                           validChars, resolveValueCollision, diag);
331}
332
333bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
334                                            const ConfigDescription& config,
335                                            const StringPiece& product,
336                                            std::unique_ptr<Value> value,
337                                            IDiagnostics* diag) {
338    return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
339                           kValidNameMangledChars, resolveValueCollision, diag);
340}
341
342bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
343                                            const ResourceId& id,
344                                            const ConfigDescription& config,
345                                            const StringPiece& product,
346                                            std::unique_ptr<Value> value,
347                                            IDiagnostics* diag) {
348    return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
349                           resolveValueCollision, diag);
350}
351
352bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
353                                    const ResourceId& resId,
354                                    const ConfigDescription& config,
355                                    const StringPiece& product,
356                                    std::unique_ptr<Value> value,
357                                    const char* validChars,
358                                    const CollisionResolverFunc& conflictResolver,
359                                    IDiagnostics* diag) {
360    assert(value && "value can't be nullptr");
361    assert(diag && "diagnostics can't be nullptr");
362
363    auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
364    if (badCharIter != name.entry.end()) {
365        diag->error(DiagMessage(value->getSource())
366                    << "resource '"
367                    << name
368                    << "' has invalid entry name '"
369                    << name.entry
370                    << "'. Invalid character '"
371                    << StringPiece(badCharIter, 1)
372                    << "'");
373        return false;
374    }
375
376    ResourceTablePackage* package = findOrCreatePackage(name.package);
377    if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
378        diag->error(DiagMessage(value->getSource())
379                    << "trying to add resource '"
380                    << name
381                    << "' with ID "
382                    << resId
383                    << " but package '"
384                    << package->name
385                    << "' already has ID "
386                    << std::hex << (int) package->id.value() << std::dec);
387        return false;
388    }
389
390    ResourceTableType* type = package->findOrCreateType(name.type);
391    if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
392        diag->error(DiagMessage(value->getSource())
393                    << "trying to add resource '"
394                    << name
395                    << "' with ID "
396                    << resId
397                    << " but type '"
398                    << type->type
399                    << "' already has ID "
400                    << std::hex << (int) type->id.value() << std::dec);
401        return false;
402    }
403
404    ResourceEntry* entry = type->findOrCreateEntry(name.entry);
405    if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
406        diag->error(DiagMessage(value->getSource())
407                    << "trying to add resource '"
408                    << name
409                    << "' with ID "
410                    << resId
411                    << " but resource already has ID "
412                    << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
413        return false;
414    }
415
416    ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
417    if (!configValue->value) {
418        // Resource does not exist, add it now.
419        configValue->value = std::move(value);
420
421    } else {
422        switch (conflictResolver(configValue->value.get(), value.get())) {
423        case CollisionResult::kTakeNew:
424            // Take the incoming value.
425            configValue->value = std::move(value);
426            break;
427
428        case CollisionResult::kConflict:
429            diag->error(DiagMessage(value->getSource())
430                                    << "duplicate value for resource '" << name << "' "
431                                    << "with config '" << config << "'");
432            diag->error(DiagMessage(configValue->value->getSource())
433                                    << "resource previously defined here");
434            return false;
435
436        case CollisionResult::kKeepOriginal:
437            break;
438        }
439    }
440
441    if (resId.isValid()) {
442        package->id = resId.packageId();
443        type->id = resId.typeId();
444        entry->id = resId.entryId();
445    }
446    return true;
447}
448
449bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId& resId,
450                                   const Symbol& symbol, IDiagnostics* diag) {
451    return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
452}
453
454bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
455                                               const ResourceId& resId,
456                                               const Symbol& symbol, IDiagnostics* diag) {
457    return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
458}
459
460bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId,
461                                       const Symbol& symbol, const char* validChars,
462                                       IDiagnostics* diag) {
463    assert(diag && "diagnostics can't be nullptr");
464
465    auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
466    if (badCharIter != name.entry.end()) {
467        diag->error(DiagMessage(symbol.source)
468                    << "resource '"
469                    << name
470                    << "' has invalid entry name '"
471                    << name.entry
472                    << "'. Invalid character '"
473                    << StringPiece(badCharIter, 1)
474                    << "'");
475        return false;
476    }
477
478    ResourceTablePackage* package = findOrCreatePackage(name.package);
479    if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
480        diag->error(DiagMessage(symbol.source)
481                    << "trying to add resource '"
482                    << name
483                    << "' with ID "
484                    << resId
485                    << " but package '"
486                    << package->name
487                    << "' already has ID "
488                    << std::hex << (int) package->id.value() << std::dec);
489        return false;
490    }
491
492    ResourceTableType* type = package->findOrCreateType(name.type);
493    if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
494        diag->error(DiagMessage(symbol.source)
495                    << "trying to add resource '"
496                    << name
497                    << "' with ID "
498                    << resId
499                    << " but type '"
500                    << type->type
501                    << "' already has ID "
502                    << std::hex << (int) type->id.value() << std::dec);
503        return false;
504    }
505
506    ResourceEntry* entry = type->findOrCreateEntry(name.entry);
507    if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
508        diag->error(DiagMessage(symbol.source)
509                    << "trying to add resource '"
510                    << name
511                    << "' with ID "
512                    << resId
513                    << " but resource already has ID "
514                    << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
515        return false;
516    }
517
518    if (resId.isValid()) {
519        package->id = resId.packageId();
520        type->id = resId.typeId();
521        entry->id = resId.entryId();
522    }
523
524    // Only mark the type state as public, it doesn't care about being private.
525    if (symbol.state == SymbolState::kPublic) {
526        type->symbolStatus.state = SymbolState::kPublic;
527    }
528
529    if (symbol.state == SymbolState::kUndefined &&
530            entry->symbolStatus.state != SymbolState::kUndefined) {
531        // We can't undefine a symbol (remove its visibility). Ignore.
532        return true;
533    }
534
535    if (symbol.state == SymbolState::kPrivate &&
536            entry->symbolStatus.state == SymbolState::kPublic) {
537        // We can't downgrade public to private. Ignore.
538        return true;
539    }
540
541    entry->symbolStatus = std::move(symbol);
542    return true;
543}
544
545Maybe<ResourceTable::SearchResult>
546ResourceTable::findResource(const ResourceNameRef& name) {
547    ResourceTablePackage* package = findPackage(name.package);
548    if (!package) {
549        return {};
550    }
551
552    ResourceTableType* type = package->findType(name.type);
553    if (!type) {
554        return {};
555    }
556
557    ResourceEntry* entry = type->findEntry(name.entry);
558    if (!entry) {
559        return {};
560    }
561    return SearchResult{ package, type, entry };
562}
563
564} // namespace aapt
565