115 lines
3.7 KiB
C++
115 lines
3.7 KiB
C++
|
/*
|
||
|
* Copyright (C) 2016 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include "DominatorTree.h"
|
||
|
#include "ResourceTable.h"
|
||
|
#include "link/Linkers.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
|
||
|
namespace aapt {
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
/**
|
||
|
* Remove duplicated key-value entries from dominated resources.
|
||
|
*
|
||
|
* Based on the dominator tree, we can remove a value of an entry if:
|
||
|
*
|
||
|
* 1. The configuration for the entry's value is dominated by a configuration
|
||
|
* with an equivalent entry value.
|
||
|
* 2. All compatible configurations for the entry (those not in conflict and
|
||
|
* unrelated by domination with the configuration for the entry's value) have
|
||
|
* an equivalent entry value.
|
||
|
*/
|
||
|
class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
|
||
|
public:
|
||
|
using Node = DominatorTree::Node;
|
||
|
|
||
|
explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry) :
|
||
|
mContext(context), mEntry(entry) {
|
||
|
}
|
||
|
|
||
|
void visitConfig(Node* node) {
|
||
|
Node* parent = node->parent();
|
||
|
if (!parent) {
|
||
|
return;
|
||
|
}
|
||
|
ResourceConfigValue* nodeValue = node->value();
|
||
|
ResourceConfigValue* parentValue = parent->value();
|
||
|
if (!nodeValue || !parentValue) {
|
||
|
return;
|
||
|
}
|
||
|
if (!nodeValue->value->equals(parentValue->value.get())) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Compare compatible configs for this entry and ensure the values are
|
||
|
// equivalent.
|
||
|
const ConfigDescription& nodeConfiguration = nodeValue->config;
|
||
|
for (const auto& sibling : mEntry->values) {
|
||
|
if (!sibling->value) {
|
||
|
// Sibling was already removed.
|
||
|
continue;
|
||
|
}
|
||
|
if (nodeConfiguration.isCompatibleWith(sibling->config)
|
||
|
&& !nodeValue->value->equals(sibling->value.get())) {
|
||
|
// The configurations are compatible, but the value is
|
||
|
// different, so we can't remove this value.
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (mContext->verbose()) {
|
||
|
mContext->getDiagnostics()->note(
|
||
|
DiagMessage(nodeValue->value->getSource())
|
||
|
<< "removing dominated duplicate resource with name \""
|
||
|
<< mEntry->name << "\"");
|
||
|
}
|
||
|
nodeValue->value = {};
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
IAaptContext* mContext;
|
||
|
ResourceEntry* mEntry;
|
||
|
};
|
||
|
|
||
|
static void dedupeEntry(IAaptContext* context, ResourceEntry* entry) {
|
||
|
DominatorTree tree(entry->values);
|
||
|
DominatedKeyValueRemover remover(context, entry);
|
||
|
tree.accept(&remover);
|
||
|
|
||
|
// Erase the values that were removed.
|
||
|
entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
|
||
|
[](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
|
||
|
return val == nullptr || val->value == nullptr;
|
||
|
}), entry->values.end());
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
bool ResourceDeduper::consume(IAaptContext* context, ResourceTable* table) {
|
||
|
for (auto& package : table->packages) {
|
||
|
for (auto& type : package->types) {
|
||
|
for (auto& entry : type->entries) {
|
||
|
dedupeEntry(context, entry.get());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // aapt
|