Adam Lesinski 6cc479b76e AAPT2: Remove the need for specifying package name in compile phase
The compile phase doesn't use the AndroidManifest, so we had to specify the
package name on the command line.

We can omit the package name, since we don't resolve external references
in the compile phase. Packages that reference the current package will be encoded
with no package name. When loaded by the link phase, the package name will be supplied
and all the references with no package name will use that one.

Change-Id: I9fe4902b747b06899b45c968f30ba1aa05c5cd69
2015-06-12 17:12:04 -07:00

291 lines
9.4 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 "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.entry.empty()) {
// 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