330edcdf13
When a static library A references static library B, and app C references both A and B, we get the following symbol merging, symbols from library B get imported twice. We must only check that symbol references to library B are valid when building library A. We should only merge all the symbols when building final app C. Change-Id: I23cba33b0901dcbb5328d9c9dfaa6a979c073c36
203 lines
7.2 KiB
C++
203 lines
7.2 KiB
C++
/*
|
|
* Copyright (C) 2015 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 "Maybe.h"
|
|
#include "NameMangler.h"
|
|
#include "Resource.h"
|
|
#include "ResourceTable.h"
|
|
#include "ResourceTableResolver.h"
|
|
#include "ResourceValues.h"
|
|
#include "Util.h"
|
|
|
|
#include <androidfw/AssetManager.h>
|
|
#include <androidfw/ResourceTypes.h>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
namespace aapt {
|
|
|
|
ResourceTableResolver::ResourceTableResolver(
|
|
std::shared_ptr<const ResourceTable> table,
|
|
const std::vector<std::shared_ptr<const android::AssetManager>>& sources) :
|
|
mTable(table), mSources(sources) {
|
|
for (const auto& assetManager : mSources) {
|
|
const android::ResTable& resTable = assetManager->getResources(false);
|
|
const size_t packageCount = resTable.getBasePackageCount();
|
|
for (size_t i = 0; i < packageCount; i++) {
|
|
std::u16string packageName = resTable.getBasePackageName(i).string();
|
|
mIncludedPackages.insert(std::move(packageName));
|
|
}
|
|
}
|
|
}
|
|
|
|
Maybe<ResourceId> ResourceTableResolver::findId(const ResourceName& name) {
|
|
Maybe<Entry> result = findAttribute(name);
|
|
if (result) {
|
|
return result.value().id;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Maybe<IResolver::Entry> ResourceTableResolver::findAttribute(const ResourceName& name) {
|
|
auto cacheIter = mCache.find(name);
|
|
if (cacheIter != std::end(mCache)) {
|
|
return Entry{ cacheIter->second.id, cacheIter->second.attr.get() };
|
|
}
|
|
|
|
ResourceName mangledName;
|
|
const ResourceName* nameToSearch = &name;
|
|
if (name.package != mTable->getPackage()) {
|
|
// This may be a reference to an included resource or
|
|
// to a mangled resource.
|
|
if (mIncludedPackages.find(name.package) == mIncludedPackages.end()) {
|
|
// This is not in our included set, so mangle the name and
|
|
// check for that.
|
|
mangledName.entry = name.entry;
|
|
NameMangler::mangle(name.package, &mangledName.entry);
|
|
mangledName.package = mTable->getPackage();
|
|
mangledName.type = name.type;
|
|
nameToSearch = &mangledName;
|
|
} else {
|
|
const CacheEntry* cacheEntry = buildCacheEntry(name);
|
|
if (cacheEntry) {
|
|
return Entry{ cacheEntry->id, cacheEntry->attr.get() };
|
|
}
|
|
return {};
|
|
}
|
|
}
|
|
|
|
const ResourceTableType* type;
|
|
const ResourceEntry* entry;
|
|
std::tie(type, entry) = mTable->findResource(*nameToSearch);
|
|
if (type && entry) {
|
|
Entry result = {};
|
|
if (mTable->getPackageId() != ResourceTable::kUnsetPackageId &&
|
|
type->typeId != ResourceTableType::kUnsetTypeId &&
|
|
entry->entryId != ResourceEntry::kUnsetEntryId) {
|
|
result.id = ResourceId(mTable->getPackageId(), type->typeId, entry->entryId);
|
|
}
|
|
|
|
if (!entry->values.empty()) {
|
|
visitFunc<Attribute>(*entry->values.front().value, [&result](Attribute& attr) {
|
|
result.attr = &attr;
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Maybe<ResourceName> ResourceTableResolver::findName(ResourceId resId) {
|
|
for (const auto& assetManager : mSources) {
|
|
const android::ResTable& table = assetManager->getResources(false);
|
|
|
|
android::ResTable::resource_name resourceName;
|
|
if (!table.getResourceName(resId.id, false, &resourceName)) {
|
|
continue;
|
|
}
|
|
|
|
const ResourceType* type = parseResourceType(StringPiece16(resourceName.type,
|
|
resourceName.typeLen));
|
|
assert(type);
|
|
return ResourceName{
|
|
{ resourceName.package, resourceName.packageLen },
|
|
*type,
|
|
{ resourceName.name, resourceName.nameLen } };
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* This is called when we need to lookup a resource name in the AssetManager.
|
|
* Since the values in the AssetManager are not parsed like in a ResourceTable,
|
|
* we must create Attribute objects here if we find them.
|
|
*/
|
|
const ResourceTableResolver::CacheEntry* ResourceTableResolver::buildCacheEntry(
|
|
const ResourceName& name) {
|
|
for (const auto& assetManager : mSources) {
|
|
const android::ResTable& table = assetManager->getResources(false);
|
|
|
|
const StringPiece16 type16 = toString(name.type);
|
|
ResourceId resId {
|
|
table.identifierForName(
|
|
name.entry.data(), name.entry.size(),
|
|
type16.data(), type16.size(),
|
|
name.package.data(), name.package.size())
|
|
};
|
|
|
|
if (!resId.isValid()) {
|
|
continue;
|
|
}
|
|
|
|
CacheEntry& entry = mCache[name];
|
|
entry.id = resId;
|
|
|
|
//
|
|
// Now check to see if this resource is an Attribute.
|
|
//
|
|
|
|
const android::ResTable::bag_entry* bagBegin;
|
|
ssize_t bags = table.lockBag(resId.id, &bagBegin);
|
|
if (bags < 1) {
|
|
table.unlockBag(bagBegin);
|
|
return &entry;
|
|
}
|
|
|
|
// Look for the ATTR_TYPE key in the bag and check the types it supports.
|
|
uint32_t attrTypeMask = 0;
|
|
for (ssize_t i = 0; i < bags; i++) {
|
|
if (bagBegin[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
|
|
attrTypeMask = bagBegin[i].map.value.data;
|
|
}
|
|
}
|
|
|
|
entry.attr = util::make_unique<Attribute>(false);
|
|
|
|
if (attrTypeMask & android::ResTable_map::TYPE_ENUM ||
|
|
attrTypeMask & android::ResTable_map::TYPE_FLAGS) {
|
|
for (ssize_t i = 0; i < bags; i++) {
|
|
if (Res_INTERNALID(bagBegin[i].map.name.ident)) {
|
|
// Internal IDs are special keys, which are not enum/flag symbols, so skip.
|
|
continue;
|
|
}
|
|
|
|
android::ResTable::resource_name symbolName;
|
|
bool result = table.getResourceName(bagBegin[i].map.name.ident, false,
|
|
&symbolName);
|
|
assert(result);
|
|
const ResourceType* type = parseResourceType(
|
|
StringPiece16(symbolName.type, symbolName.typeLen));
|
|
assert(type);
|
|
|
|
entry.attr->symbols.push_back(Attribute::Symbol{
|
|
Reference(ResourceNameRef(
|
|
StringPiece16(symbolName.package, symbolName.packageLen),
|
|
*type,
|
|
StringPiece16(symbolName.name, symbolName.nameLen))),
|
|
bagBegin[i].map.value.data
|
|
});
|
|
}
|
|
}
|
|
|
|
entry.attr->typeMask |= attrTypeMask;
|
|
table.unlockBag(bagBegin);
|
|
return &entry;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace aapt
|