Home | History | Annotate | Download | only in optimize
      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