blob: f565359daacab8319bc8c610926791873e5ad339 [file] [log] [blame]
Alexandria Cornwall77788eb2016-09-06 15:16:49 -07001/*
2 * Copyright (C) 2016 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 "DominatorTree.h"
18#include "ResourceTable.h"
19#include "link/Linkers.h"
20
21#include <algorithm>
22
23namespace aapt {
24
25namespace {
26
27/**
28 * Remove duplicated key-value entries from dominated resources.
29 *
30 * Based on the dominator tree, we can remove a value of an entry if:
31 *
32 * 1. The configuration for the entry's value is dominated by a configuration
33 * with an equivalent entry value.
34 * 2. All compatible configurations for the entry (those not in conflict and
35 * unrelated by domination with the configuration for the entry's value) have
36 * an equivalent entry value.
37 */
38class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070039 public:
40 using Node = DominatorTree::Node;
Alexandria Cornwall77788eb2016-09-06 15:16:49 -070041
Adam Lesinskicacb28f2016-10-19 12:18:14 -070042 explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry)
43 : mContext(context), mEntry(entry) {}
44
45 void visitConfig(Node* node) {
46 Node* parent = node->parent();
47 if (!parent) {
48 return;
49 }
50 ResourceConfigValue* nodeValue = node->value();
51 ResourceConfigValue* parentValue = parent->value();
52 if (!nodeValue || !parentValue) {
53 return;
54 }
55 if (!nodeValue->value->equals(parentValue->value.get())) {
56 return;
Alexandria Cornwall77788eb2016-09-06 15:16:49 -070057 }
58
Adam Lesinskicacb28f2016-10-19 12:18:14 -070059 // Compare compatible configs for this entry and ensure the values are
60 // equivalent.
61 const ConfigDescription& nodeConfiguration = nodeValue->config;
62 for (const auto& sibling : mEntry->values) {
63 if (!sibling->value) {
64 // Sibling was already removed.
65 continue;
66 }
67 if (nodeConfiguration.isCompatibleWith(sibling->config) &&
68 !nodeValue->value->equals(sibling->value.get())) {
69 // The configurations are compatible, but the value is
70 // different, so we can't remove this value.
71 return;
72 }
Alexandria Cornwall77788eb2016-09-06 15:16:49 -070073 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070074 if (mContext->verbose()) {
75 mContext->getDiagnostics()->note(
76 DiagMessage(nodeValue->value->getSource())
77 << "removing dominated duplicate resource with name \""
78 << mEntry->name << "\"");
79 }
80 nodeValue->value = {};
81 }
Alexandria Cornwall77788eb2016-09-06 15:16:49 -070082
Adam Lesinskicacb28f2016-10-19 12:18:14 -070083 private:
84 IAaptContext* mContext;
85 ResourceEntry* mEntry;
Alexandria Cornwall77788eb2016-09-06 15:16:49 -070086};
87
88static void dedupeEntry(IAaptContext* context, ResourceEntry* entry) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070089 DominatorTree tree(entry->values);
90 DominatedKeyValueRemover remover(context, entry);
91 tree.accept(&remover);
Alexandria Cornwall77788eb2016-09-06 15:16:49 -070092
Adam Lesinskicacb28f2016-10-19 12:18:14 -070093 // Erase the values that were removed.
94 entry->values.erase(
95 std::remove_if(
96 entry->values.begin(), entry->values.end(),
97 [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
98 return val == nullptr || val->value == nullptr;
99 }),
100 entry->values.end());
Alexandria Cornwall77788eb2016-09-06 15:16:49 -0700101}
102
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700103} // namespace
Alexandria Cornwall77788eb2016-09-06 15:16:49 -0700104
105bool ResourceDeduper::consume(IAaptContext* context, ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700106 for (auto& package : table->packages) {
107 for (auto& type : package->types) {
108 for (auto& entry : type->entries) {
109 dedupeEntry(context, entry.get());
110 }
Alexandria Cornwall77788eb2016-09-06 15:16:49 -0700111 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700112 }
113 return true;
Alexandria Cornwall77788eb2016-09-06 15:16:49 -0700114}
115
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700116} // aapt