diff options
Diffstat (limited to 'tools/aapt2/Linker.cpp')
-rw-r--r-- | tools/aapt2/Linker.cpp | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/tools/aapt2/Linker.cpp b/tools/aapt2/Linker.cpp new file mode 100644 index 0000000..f3f04a5 --- /dev/null +++ b/tools/aapt2/Linker.cpp @@ -0,0 +1,290 @@ +/* + * 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 "Linker.h" +#include "Logger.h" +#include "NameMangler.h" +#include "Resolver.h" +#include "ResourceParser.h" +#include "ResourceTable.h" +#include "ResourceValues.h" +#include "StringPiece.h" +#include "Util.h" + +#include <androidfw/AssetManager.h> +#include <array> +#include <bitset> +#include <iostream> +#include <map> +#include <ostream> +#include <set> +#include <sstream> +#include <tuple> +#include <vector> + +namespace aapt { + +Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) { +} + +Linker::Linker(const std::shared_ptr<ResourceTable>& table, + const std::shared_ptr<IResolver>& resolver, const Options& options) : + mResolver(resolver), mTable(table), mOptions(options), mError(false) { +} + +bool Linker::linkAndValidate() { + std::bitset<256> usedTypeIds; + std::array<std::set<uint16_t>, 256> usedIds; + usedTypeIds.set(0); + + // Collect which resource IDs are already taken. + for (auto& type : *mTable) { + if (type->typeId != ResourceTableType::kUnsetTypeId) { + // The ID for this type has already been set. We + // mark this ID as taken so we don't re-assign it + // later. + usedTypeIds.set(type->typeId); + } + + for (auto& entry : type->entries) { + if (type->typeId != ResourceTableType::kUnsetTypeId && + entry->entryId != ResourceEntry::kUnsetEntryId) { + // The ID for this entry has already been set. We + // mark this ID as taken so we don't re-assign it + // later. + usedIds[type->typeId].insert(entry->entryId); + } + } + } + + // Assign resource IDs that are available. + size_t nextTypeIndex = 0; + for (auto& type : *mTable) { + if (type->typeId == ResourceTableType::kUnsetTypeId) { + while (nextTypeIndex < usedTypeIds.size() && usedTypeIds[nextTypeIndex]) { + nextTypeIndex++; + } + type->typeId = nextTypeIndex++; + } + + const auto endEntryIter = std::end(usedIds[type->typeId]); + auto nextEntryIter = std::begin(usedIds[type->typeId]); + size_t nextIndex = 0; + for (auto& entry : type->entries) { + if (entry->entryId == ResourceTableType::kUnsetTypeId) { + while (nextEntryIter != endEntryIter && + nextIndex == *nextEntryIter) { + nextIndex++; + ++nextEntryIter; + } + entry->entryId = nextIndex++; + } + } + } + + // Now do reference linking. + for (auto& type : *mTable) { + for (auto& entry : type->entries) { + if (entry->publicStatus.isPublic && entry->values.empty()) { + // A public resource has no values. It will not be encoded + // properly without a symbol table. This is a unresolved symbol. + addUnresolvedSymbol(ResourceNameRef{ + mTable->getPackage(), type->type, entry->name }, + entry->publicStatus.source); + continue; + } + + for (auto& valueConfig : entry->values) { + // Dispatch to the right method of this linker + // based on the value's type. + valueConfig.value->accept(*this, Args{ + ResourceNameRef{ mTable->getPackage(), type->type, entry->name }, + valueConfig.source + }); + } + } + } + return !mError; +} + +const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const { + return mUnresolvedSymbols; +} + +void Linker::doResolveReference(Reference& reference, const SourceLine& source) { + Maybe<ResourceId> result = mResolver->findId(reference.name); + if (!result) { + addUnresolvedSymbol(reference.name, source); + return; + } + assert(result.value().isValid()); + + if (mOptions.linkResourceIds) { + reference.id = result.value(); + } else { + reference.id = 0; + } +} + +const Attribute* Linker::doResolveAttribute(Reference& attribute, const SourceLine& source) { + Maybe<IResolver::Entry> result = mResolver->findAttribute(attribute.name); + if (!result || !result.value().attr) { + addUnresolvedSymbol(attribute.name, source); + return nullptr; + } + + const IResolver::Entry& entry = result.value(); + assert(entry.id.isValid()); + + if (mOptions.linkResourceIds) { + attribute.id = entry.id; + } else { + attribute.id = 0; + } + return entry.attr; +} + +void Linker::visit(Reference& reference, ValueVisitorArgs& a) { + Args& args = static_cast<Args&>(a); + + if (!reference.name.isValid()) { + // We can't have a completely bad reference. + if (!reference.id.isValid()) { + Logger::error() << "srsly? " << args.referrer << std::endl; + assert(reference.id.isValid()); + } + + // This reference has no name but has an ID. + // It is a really bad error to have no name and have the same + // package ID. + assert(reference.id.packageId() != mTable->getPackageId()); + + // The reference goes outside this package, let it stay as a + // resource ID because it will not change. + return; + } + + doResolveReference(reference, args.source); + + // TODO(adamlesinski): Verify the referencedType is another reference + // or a compatible primitive. +} + +void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine& source, + const Attribute& attr, std::unique_ptr<Item>& value) { + std::unique_ptr<Item> convertedValue; + visitFunc<RawString>(*value, [&](RawString& str) { + // This is a raw string, so check if it can be converted to anything. + // We can NOT swap value with the converted value in here, since + // we called through the original value. + + auto onCreateReference = [&](const ResourceName& name) { + // We should never get here. All references would have been + // parsed in the parser phase. + assert(false); + }; + + convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr, + onCreateReference); + if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) { + // Last effort is to parse as a string. + util::StringBuilder builder; + builder.append(*str.value); + if (builder) { + convertedValue = util::make_unique<String>( + mTable->getValueStringPool().makeRef(builder.str())); + } + } + }); + + if (convertedValue) { + value = std::move(convertedValue); + } + + // Process this new or old value (it can be a reference!). + value->accept(*this, Args{ name, source }); + + // Flatten the value to see what resource type it is. + android::Res_value resValue; + value->flatten(resValue); + + // Always allow references. + const uint32_t typeMask = attr.typeMask | android::ResTable_map::TYPE_REFERENCE; + if (!(typeMask & ResourceParser::androidTypeToAttributeTypeMask(resValue.dataType))) { + Logger::error(source) + << *value + << " is not compatible with attribute " + << attr + << "." + << std::endl; + mError = true; + } +} + +void Linker::visit(Style& style, ValueVisitorArgs& a) { + Args& args = static_cast<Args&>(a); + + if (style.parent.name.isValid() || style.parent.id.isValid()) { + visit(style.parent, a); + } + + for (Style::Entry& styleEntry : style.entries) { + const Attribute* attr = doResolveAttribute(styleEntry.key, args.source); + if (attr) { + processAttributeValue(args.referrer, args.source, *attr, styleEntry.value); + } + } +} + +void Linker::visit(Attribute& attr, ValueVisitorArgs& a) { + static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM | + android::ResTable_map::TYPE_FLAGS; + if (attr.typeMask & kMask) { + for (auto& symbol : attr.symbols) { + visit(symbol.symbol, a); + } + } +} + +void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) { + for (auto& attrRef : styleable.entries) { + visit(attrRef, a); + } +} + +void Linker::visit(Array& array, ValueVisitorArgs& a) { + Args& args = static_cast<Args&>(a); + + for (auto& item : array.items) { + item->accept(*this, Args{ args.referrer, args.source }); + } +} + +void Linker::visit(Plural& plural, ValueVisitorArgs& a) { + Args& args = static_cast<Args&>(a); + + for (auto& item : plural.values) { + if (item) { + item->accept(*this, Args{ args.referrer, args.source }); + } + } +} + +void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) { + mUnresolvedSymbols[name.toResourceName()].push_back(source); +} + +} // namespace aapt |