1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2016 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include "optimize/ResourceDeduper.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <algorithm>
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker #include "DominatorTree.h"
22*d57664e9SAndroid Build Coastguard Worker #include "ResourceTable.h"
23*d57664e9SAndroid Build Coastguard Worker #include "trace/TraceBuffer.h"
24*d57664e9SAndroid Build Coastguard Worker
25*d57664e9SAndroid Build Coastguard Worker using android::ConfigDescription;
26*d57664e9SAndroid Build Coastguard Worker
27*d57664e9SAndroid Build Coastguard Worker namespace aapt {
28*d57664e9SAndroid Build Coastguard Worker
29*d57664e9SAndroid Build Coastguard Worker namespace {
30*d57664e9SAndroid Build Coastguard Worker
31*d57664e9SAndroid Build Coastguard Worker /**
32*d57664e9SAndroid Build Coastguard Worker * Remove duplicated key-value entries from dominated resources.
33*d57664e9SAndroid Build Coastguard Worker *
34*d57664e9SAndroid Build Coastguard Worker * Based on the dominator tree, we can remove a value of an entry if:
35*d57664e9SAndroid Build Coastguard Worker *
36*d57664e9SAndroid Build Coastguard Worker * 1. The configuration for the entry's value is dominated by a configuration
37*d57664e9SAndroid Build Coastguard Worker * with an equivalent entry value.
38*d57664e9SAndroid Build Coastguard Worker * 2. All compatible configurations for the entry (those not in conflict and
39*d57664e9SAndroid Build Coastguard Worker * unrelated by domination with the configuration for the entry's value) have
40*d57664e9SAndroid Build Coastguard Worker * an equivalent entry value.
41*d57664e9SAndroid Build Coastguard Worker */
42*d57664e9SAndroid Build Coastguard Worker class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
43*d57664e9SAndroid Build Coastguard Worker public:
44*d57664e9SAndroid Build Coastguard Worker using Node = DominatorTree::Node;
45*d57664e9SAndroid Build Coastguard Worker
DominatedKeyValueRemover(IAaptContext * context,ResourceEntry * entry)46*d57664e9SAndroid Build Coastguard Worker explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry)
47*d57664e9SAndroid Build Coastguard Worker : context_(context), entry_(entry) {}
48*d57664e9SAndroid Build Coastguard Worker
VisitConfig(Node * node)49*d57664e9SAndroid Build Coastguard Worker void VisitConfig(Node* node) {
50*d57664e9SAndroid Build Coastguard Worker Node* parent = node->parent();
51*d57664e9SAndroid Build Coastguard Worker if (!parent) {
52*d57664e9SAndroid Build Coastguard Worker return;
53*d57664e9SAndroid Build Coastguard Worker }
54*d57664e9SAndroid Build Coastguard Worker ResourceConfigValue* node_value = node->value();
55*d57664e9SAndroid Build Coastguard Worker ResourceConfigValue* parent_value = parent->value();
56*d57664e9SAndroid Build Coastguard Worker if (!node_value || !parent_value) {
57*d57664e9SAndroid Build Coastguard Worker return;
58*d57664e9SAndroid Build Coastguard Worker }
59*d57664e9SAndroid Build Coastguard Worker if (!node_value->value->Equals(parent_value->value.get())) {
60*d57664e9SAndroid Build Coastguard Worker return;
61*d57664e9SAndroid Build Coastguard Worker }
62*d57664e9SAndroid Build Coastguard Worker
63*d57664e9SAndroid Build Coastguard Worker // Compare compatible configs for this entry and ensure the values are
64*d57664e9SAndroid Build Coastguard Worker // equivalent.
65*d57664e9SAndroid Build Coastguard Worker const ConfigDescription& node_configuration = node_value->config;
66*d57664e9SAndroid Build Coastguard Worker for (const auto& sibling : parent->children()) {
67*d57664e9SAndroid Build Coastguard Worker ResourceConfigValue* sibling_value = sibling->value();
68*d57664e9SAndroid Build Coastguard Worker if (!sibling_value->value) {
69*d57664e9SAndroid Build Coastguard Worker // Sibling was already removed.
70*d57664e9SAndroid Build Coastguard Worker continue;
71*d57664e9SAndroid Build Coastguard Worker }
72*d57664e9SAndroid Build Coastguard Worker if (node_configuration.IsCompatibleWith(sibling_value->config) &&
73*d57664e9SAndroid Build Coastguard Worker !node_value->value->Equals(sibling_value->value.get())) {
74*d57664e9SAndroid Build Coastguard Worker // The configurations are compatible, but the value is
75*d57664e9SAndroid Build Coastguard Worker // different, so we can't remove this value.
76*d57664e9SAndroid Build Coastguard Worker return;
77*d57664e9SAndroid Build Coastguard Worker }
78*d57664e9SAndroid Build Coastguard Worker }
79*d57664e9SAndroid Build Coastguard Worker if (context_->IsVerbose()) {
80*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage(node_value->value->GetSource())
81*d57664e9SAndroid Build Coastguard Worker << "removing dominated duplicate resource with name \""
82*d57664e9SAndroid Build Coastguard Worker << entry_->name << "\"");
83*d57664e9SAndroid Build Coastguard Worker context_->GetDiagnostics()->Note(android::DiagMessage(parent_value->value->GetSource())
84*d57664e9SAndroid Build Coastguard Worker << "dominated here");
85*d57664e9SAndroid Build Coastguard Worker }
86*d57664e9SAndroid Build Coastguard Worker node_value->value = {};
87*d57664e9SAndroid Build Coastguard Worker }
88*d57664e9SAndroid Build Coastguard Worker
89*d57664e9SAndroid Build Coastguard Worker private:
90*d57664e9SAndroid Build Coastguard Worker DISALLOW_COPY_AND_ASSIGN(DominatedKeyValueRemover);
91*d57664e9SAndroid Build Coastguard Worker
92*d57664e9SAndroid Build Coastguard Worker IAaptContext* context_;
93*d57664e9SAndroid Build Coastguard Worker ResourceEntry* entry_;
94*d57664e9SAndroid Build Coastguard Worker };
95*d57664e9SAndroid Build Coastguard Worker
DedupeEntry(IAaptContext * context,ResourceEntry * entry)96*d57664e9SAndroid Build Coastguard Worker static void DedupeEntry(IAaptContext* context, ResourceEntry* entry) {
97*d57664e9SAndroid Build Coastguard Worker DominatorTree tree(entry->values);
98*d57664e9SAndroid Build Coastguard Worker DominatedKeyValueRemover remover(context, entry);
99*d57664e9SAndroid Build Coastguard Worker tree.Accept(&remover);
100*d57664e9SAndroid Build Coastguard Worker
101*d57664e9SAndroid Build Coastguard Worker // Erase the values that were removed.
102*d57664e9SAndroid Build Coastguard Worker entry->values.erase(
103*d57664e9SAndroid Build Coastguard Worker std::remove_if(
104*d57664e9SAndroid Build Coastguard Worker entry->values.begin(), entry->values.end(),
105*d57664e9SAndroid Build Coastguard Worker [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
106*d57664e9SAndroid Build Coastguard Worker return val == nullptr || val->value == nullptr;
107*d57664e9SAndroid Build Coastguard Worker }),
108*d57664e9SAndroid Build Coastguard Worker entry->values.end());
109*d57664e9SAndroid Build Coastguard Worker }
110*d57664e9SAndroid Build Coastguard Worker
111*d57664e9SAndroid Build Coastguard Worker } // namespace
112*d57664e9SAndroid Build Coastguard Worker
Consume(IAaptContext * context,ResourceTable * table)113*d57664e9SAndroid Build Coastguard Worker bool ResourceDeduper::Consume(IAaptContext* context, ResourceTable* table) {
114*d57664e9SAndroid Build Coastguard Worker TRACE_CALL();
115*d57664e9SAndroid Build Coastguard Worker for (auto& package : table->packages) {
116*d57664e9SAndroid Build Coastguard Worker for (auto& type : package->types) {
117*d57664e9SAndroid Build Coastguard Worker for (auto& entry : type->entries) {
118*d57664e9SAndroid Build Coastguard Worker DedupeEntry(context, entry.get());
119*d57664e9SAndroid Build Coastguard Worker }
120*d57664e9SAndroid Build Coastguard Worker }
121*d57664e9SAndroid Build Coastguard Worker }
122*d57664e9SAndroid Build Coastguard Worker return true;
123*d57664e9SAndroid Build Coastguard Worker }
124*d57664e9SAndroid Build Coastguard Worker
125*d57664e9SAndroid Build Coastguard Worker } // namespace aapt
126