diff options
Diffstat (limited to 'tools/aapt2/Linker.cpp')
-rw-r--r-- | tools/aapt2/Linker.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/tools/aapt2/Linker.cpp b/tools/aapt2/Linker.cpp new file mode 100644 index 0000000..a863197 --- /dev/null +++ b/tools/aapt2/Linker.cpp @@ -0,0 +1,282 @@ +/* + * 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 "ResourceParser.h" +#include "ResourceTable.h" +#include "ResourceValues.h" +#include "StringPiece.h" +#include "Util.h" + +#include <androidfw/AssetManager.h> +#include <array> +#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(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver) : + mTable(table), mResolver(resolver), mError(false) { +} + +bool Linker::linkAndValidate() { + std::bitset<256> usedTypeIds; + std::array<std::set<uint16_t>, 256> usedIds; + usedTypeIds.set(0); + + // First build the graph of references. + 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); + } + + 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 + }); + } + } + } + + /* + * 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++; + + // Update callers of this resource with the right ID. + auto callersIter = mGraph.find(ResourceNameRef{ + mTable->getPackage(), + type->type, + entry->name + }); + + if (callersIter != std::end(mGraph)) { + for (Node& caller : callersIter->second) { + caller.reference->id = ResourceId(mTable->getPackageId(), + type->typeId, + entry->entryId); + } + } + } + } + } + + return !mError; +} + +const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const { + return mUnresolvedSymbols; +} + +void Linker::visit(Reference& reference, ValueVisitorArgs& a) { + Args& args = static_cast<Args&>(a); + + Maybe<ResourceId> result = mResolver->findId(reference.name); + if (!result) { + addUnresolvedSymbol(reference.name, args.source); + return; + } + + const ResourceId& id = result.value(); + if (id.isValid()) { + reference.id = id; + } else { + // We need to update the ID when it is set, so add it + // to the graph. + mGraph[reference.name].push_back(Node{ + args.referrer, + args.source.path, + args.source.line, + &reference + }); + } + + // 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) { + mTable->addResource(name, ConfigDescription{}, + source, util::make_unique<Id>()); + }; + + convertedValue = ResourceParser::parseItemForAttribute( + *str.value, attr, mResolver->getDefaultPackage(), + 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()) { + visit(style.parent, a); + } + + for (Style::Entry& styleEntry : style.entries) { + Maybe<Resolver::Entry> result = mResolver->findAttribute(styleEntry.key.name); + if (!result || !result.value().attr) { + addUnresolvedSymbol(styleEntry.key.name, args.source); + continue; + } + + const Resolver::Entry& entry = result.value(); + if (entry.id.isValid()) { + styleEntry.key.id = entry.id; + } else { + // Create a dependency for the style on this attribute. + mGraph[styleEntry.key.name].push_back(Node{ + args.referrer, + args.source.path, + args.source.line, + &styleEntry.key + }); + } + processAttributeValue(args.referrer, args.source, *entry.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(Sentinel& sentinel, ValueVisitorArgs& a) { + Args& args = static_cast<Args&>(a); + addUnresolvedSymbol(args.referrer, args.source); +} + +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); +} + +::std::ostream& operator<<(::std::ostream& out, const Linker::Node& node) { + return out << node.name << "(" << node.source << ":" << node.line << ")"; +} + +} // namespace aapt |