summaryrefslogtreecommitdiffstats
path: root/tools/aapt2/Linker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt2/Linker.cpp')
-rw-r--r--tools/aapt2/Linker.cpp290
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