117 lines
3.4 KiB
C++
117 lines
3.4 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
|