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 StringPiece16& rhs) {
39    return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
40}
41
42ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& 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 StringPiece16& 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 StringPiece16& 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 StringPiece16& 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 StringPiece16& 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
192/**
193 * The default handler for collisions. A return value of -1 means keep the
194 * existing value, 0 means fail, and +1 means take the incoming value.
195 */
196int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) {
197    Attribute* existingAttr = valueCast<Attribute>(existing);
198    Attribute* incomingAttr = valueCast<Attribute>(incoming);
199
200    if (!incomingAttr) {
201        if (incoming->isWeak()) {
202            // We're trying to add a weak resource but a resource
203            // already exists. Keep the existing.
204            return -1;
205        } else if (existing->isWeak()) {
206            // Override the weak resource with the new strong resource.
207            return 1;
208        }
209        // The existing and incoming values are strong, this is an error
210        // if the values are not both attributes.
211        return 0;
212    }
213
214    if (!existingAttr) {
215        if (existing->isWeak()) {
216            // The existing value is not an attribute and it is weak,
217            // so take the incoming attribute value.
218            return 1;
219        }
220        // The existing value is not an attribute and it is strong,
221        // so the incoming attribute value is an error.
222        return 0;
223    }
224
225    assert(incomingAttr && existingAttr);
226
227    //
228    // Attribute specific handling. At this point we know both
229    // values are attributes. Since we can declare and define
230    // attributes all-over, we do special handling to see
231    // which definition sticks.
232    //
233    if (existingAttr->typeMask == incomingAttr->typeMask) {
234        // The two attributes are both DECLs, but they are plain attributes
235        // with the same formats.
236        // Keep the strongest one.
237        return existingAttr->isWeak() ? 1 : -1;
238    }
239
240    if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
241        // Any incoming attribute is better than this.
242        return 1;
243    }
244
245    if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
246        // The incoming attribute may be a USE instead of a DECL.
247        // Keep the existing attribute.
248        return -1;
249    }
250    return 0;
251}
252
253static constexpr const char16_t* kValidNameChars = u"._-";
254static constexpr const char16_t* kValidNameMangledChars = u"._-$";
255
256bool ResourceTable::addResource(const ResourceNameRef& name,
257                                const ConfigDescription& config,
258                                const StringPiece& product,
259                                std::unique_ptr<Value> value,
260                                IDiagnostics* diag) {
261    return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
262                           resolveValueCollision, diag);
263}
264
265bool ResourceTable::addResource(const ResourceNameRef& name,
266                                const ResourceId resId,
267                                const ConfigDescription& config,
268                                const StringPiece& product,
269                                std::unique_ptr<Value> value,
270                                IDiagnostics* diag) {
271    return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
272                           resolveValueCollision, diag);
273}
274
275bool ResourceTable::addFileReference(const ResourceNameRef& name,
276                                     const ConfigDescription& config,
277                                     const Source& source,
278                                     const StringPiece16& path,
279                                     IDiagnostics* diag) {
280    return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag);
281}
282
283bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name,
284                                                 const ConfigDescription& config,
285                                                 const Source& source,
286                                                 const StringPiece16& path,
287                                                 io::IFile* file,
288                                                 IDiagnostics* diag) {
289    return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag);
290}
291
292bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name,
293                                         const ConfigDescription& config,
294                                         const Source& source,
295                                         const StringPiece16& path,
296                                         io::IFile* file,
297                                         const char16_t* validChars,
298                                         IDiagnostics* diag) {
299    std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
300            stringPool.makeRef(path));
301    fileRef->setSource(source);
302    fileRef->file = file;
303    return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
304                           kValidNameChars, resolveValueCollision, diag);
305}
306
307bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
308                                            const ConfigDescription& config,
309                                            const StringPiece& product,
310                                            std::unique_ptr<Value> value,
311                                            IDiagnostics* diag) {
312    return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
313                           kValidNameMangledChars, resolveValueCollision, diag);
314}
315
316bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
317                                            const ResourceId id,
318                                            const ConfigDescription& config,
319                                            const StringPiece& product,
320                                            std::unique_ptr<Value> value,
321                                            IDiagnostics* diag) {
322    return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
323                           resolveValueCollision, diag);
324}
325
326bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
327                                    const ResourceId resId,
328                                    const ConfigDescription& config,
329                                    const StringPiece& product,
330                                    std::unique_ptr<Value> value,
331                                    const char16_t* validChars,
332                                    std::function<int(Value*,Value*)> conflictResolver,
333                                    IDiagnostics* diag) {
334    assert(value && "value can't be nullptr");
335    assert(diag && "diagnostics can't be nullptr");
336
337    auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
338    if (badCharIter != name.entry.end()) {
339        diag->error(DiagMessage(value->getSource())
340                    << "resource '"
341                    << name
342                    << "' has invalid entry name '"
343                    << name.entry
344                    << "'. Invalid character '"
345                    << StringPiece16(badCharIter, 1)
346                    << "'");
347        return false;
348    }
349
350    ResourceTablePackage* package = findOrCreatePackage(name.package);
351    if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
352        diag->error(DiagMessage(value->getSource())
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);
361        return false;
362    }
363
364    ResourceTableType* type = package->findOrCreateType(name.type);
365    if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
366        diag->error(DiagMessage(value->getSource())
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(value->getSource())
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()));
387        return false;
388    }
389
390    ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
391    if (!configValue->value) {
392        // Resource does not exist, add it now.
393        configValue->value = std::move(value);
394
395    } else {
396        int collisionResult = conflictResolver(configValue->value.get(), value.get());
397        if (collisionResult > 0) {
398            // Take the incoming value.
399            configValue->value = std::move(value);
400        } else if (collisionResult == 0) {
401            diag->error(DiagMessage(value->getSource())
402                        << "duplicate value for resource '" << name << "' "
403                        << "with config '" << config << "'");
404            diag->error(DiagMessage(configValue->value->getSource())
405                        << "resource previously defined here");
406            return false;
407        }
408    }
409
410    if (resId.isValid()) {
411        package->id = resId.packageId();
412        type->id = resId.typeId();
413        entry->id = resId.entryId();
414    }
415    return true;
416}
417
418bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId,
419                                   const Symbol& symbol, IDiagnostics* diag) {
420    return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
421}
422
423bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
424                                               const ResourceId resId,
425                                               const Symbol& symbol, IDiagnostics* diag) {
426    return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
427}
428
429bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId,
430                                       const Symbol& symbol, const char16_t* validChars,
431                                       IDiagnostics* diag) {
432    assert(diag && "diagnostics can't be nullptr");
433
434    auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
435    if (badCharIter != name.entry.end()) {
436        diag->error(DiagMessage(symbol.source)
437                    << "resource '"
438                    << name
439                    << "' has invalid entry name '"
440                    << name.entry
441                    << "'. Invalid character '"
442                    << StringPiece16(badCharIter, 1)
443                    << "'");
444        return false;
445    }
446
447    ResourceTablePackage* package = findOrCreatePackage(name.package);
448    if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
449        diag->error(DiagMessage(symbol.source)
450                    << "trying to add resource '"
451                    << name
452                    << "' with ID "
453                    << resId
454                    << " but package '"
455                    << package->name
456                    << "' already has ID "
457                    << std::hex << (int) package->id.value() << std::dec);
458        return false;
459    }
460
461    ResourceTableType* type = package->findOrCreateType(name.type);
462    if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
463        diag->error(DiagMessage(symbol.source)
464                    << "trying to add resource '"
465                    << name
466                    << "' with ID "
467                    << resId
468                    << " but type '"
469                    << type->type
470                    << "' already has ID "
471                    << std::hex << (int) type->id.value() << std::dec);
472        return false;
473    }
474
475    ResourceEntry* entry = type->findOrCreateEntry(name.entry);
476    if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
477        diag->error(DiagMessage(symbol.source)
478                    << "trying to add resource '"
479                    << name
480                    << "' with ID "
481                    << resId
482                    << " but resource already has ID "
483                    << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
484        return false;
485    }
486
487    if (resId.isValid()) {
488        package->id = resId.packageId();
489        type->id = resId.typeId();
490        entry->id = resId.entryId();
491    }
492
493    // Only mark the type state as public, it doesn't care about being private.
494    if (symbol.state == SymbolState::kPublic) {
495        type->symbolStatus.state = SymbolState::kPublic;
496    }
497
498    if (symbol.state == SymbolState::kUndefined &&
499            entry->symbolStatus.state != SymbolState::kUndefined) {
500        // We can't undefine a symbol (remove its visibility). Ignore.
501        return true;
502    }
503
504    if (symbol.state == SymbolState::kPrivate &&
505            entry->symbolStatus.state == SymbolState::kPublic) {
506        // We can't downgrade public to private. Ignore.
507        return true;
508    }
509
510    entry->symbolStatus = std::move(symbol);
511    return true;
512}
513
514Maybe<ResourceTable::SearchResult>
515ResourceTable::findResource(const ResourceNameRef& name) {
516    ResourceTablePackage* package = findPackage(name.package);
517    if (!package) {
518        return {};
519    }
520
521    ResourceTableType* type = package->findType(name.type);
522    if (!type) {
523        return {};
524    }
525
526    ResourceEntry* entry = type->findEntry(name.entry);
527    if (!entry) {
528        return {};
529    }
530    return SearchResult{ package, type, entry };
531}
532
533} // namespace aapt
534