1 /* 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 "optimize/ResourceDeduper.h" 18 19 #include <algorithm> 20 21 #include "DominatorTree.h" 22 #include "ResourceTable.h" 23 #include "trace/TraceBuffer.h" 24 25 using android::ConfigDescription; 26 27 namespace aapt { 28 29 namespace { 30 31 /** 32 * Remove duplicated key-value entries from dominated resources. 33 * 34 * Based on the dominator tree, we can remove a value of an entry if: 35 * 36 * 1. The configuration for the entry's value is dominated by a configuration 37 * with an equivalent entry value. 38 * 2. All compatible configurations for the entry (those not in conflict and 39 * unrelated by domination with the configuration for the entry's value) have 40 * an equivalent entry value. 41 */ 42 class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor { 43 public: 44 using Node = DominatorTree::Node; 45 46 explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry) 47 : context_(context), entry_(entry) {} 48 49 void VisitConfig(Node* node) { 50 Node* parent = node->parent(); 51 if (!parent) { 52 return; 53 } 54 ResourceConfigValue* node_value = node->value(); 55 ResourceConfigValue* parent_value = parent->value(); 56 if (!node_value || !parent_value) { 57 return; 58 } 59 if (!node_value->value->Equals(parent_value->value.get())) { 60 return; 61 } 62 63 // Compare compatible configs for this entry and ensure the values are 64 // equivalent. 65 const ConfigDescription& node_configuration = node_value->config; 66 for (const auto& sibling : entry_->values) { 67 if (!sibling->value) { 68 // Sibling was already removed. 69 continue; 70 } 71 if (node_configuration.IsCompatibleWith(sibling->config) && 72 !node_value->value->Equals(sibling->value.get())) { 73 // The configurations are compatible, but the value is 74 // different, so we can't remove this value. 75 return; 76 } 77 } 78 if (context_->IsVerbose()) { 79 context_->GetDiagnostics()->Note( 80 DiagMessage(node_value->value->GetSource()) 81 << "removing dominated duplicate resource with name \"" 82 << entry_->name << "\""); 83 context_->GetDiagnostics()->Note( 84 DiagMessage(parent_value->value->GetSource()) << "dominated here"); 85 } 86 node_value->value = {}; 87 } 88 89 private: 90 DISALLOW_COPY_AND_ASSIGN(DominatedKeyValueRemover); 91 92 IAaptContext* context_; 93 ResourceEntry* entry_; 94 }; 95 96 static void DedupeEntry(IAaptContext* context, ResourceEntry* entry) { 97 DominatorTree tree(entry->values); 98 DominatedKeyValueRemover remover(context, entry); 99 tree.Accept(&remover); 100 101 // Erase the values that were removed. 102 entry->values.erase( 103 std::remove_if( 104 entry->values.begin(), entry->values.end(), 105 [](const std::unique_ptr<ResourceConfigValue>& val) -> bool { 106 return val == nullptr || val->value == nullptr; 107 }), 108 entry->values.end()); 109 } 110 111 } // namespace 112 113 bool ResourceDeduper::Consume(IAaptContext* context, ResourceTable* table) { 114 TRACE_CALL(); 115 for (auto& package : table->packages) { 116 for (auto& type : package->types) { 117 for (auto& entry : type->entries) { 118 DedupeEntry(context, entry.get()); 119 } 120 } 121 } 122 return true; 123 } 124 125 } // namespace aapt 126