xref: /aosp_15_r20/frameworks/base/tools/aapt2/optimize/ResourceDeduper.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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