android_frameworks_base/tools/aapt2/proto/TableProtoDeserializer.cpp
Adam Lesinski 803c7c8079 AAPT2: Enable windows build and make sure it builds
Mingw32 4.8 is kind of picky with macros and some complicated template
stuff. Luckily there was another way to represent the
SFINAE code that works on all platforms. Yay!

Change-Id: Idc2e38f47bfdc57b394550bfa0f53cc0b825df25
2016-04-06 16:24:02 -07:00

521 lines
20 KiB
C++

/*
* Copyright (C) 2016 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 "ResourceTable.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
#include "proto/ProtoHelpers.h"
#include "proto/ProtoSerialize.h"
#include <androidfw/ResourceTypes.h>
namespace aapt {
namespace {
class ReferenceIdToNameVisitor : public ValueVisitor {
public:
using ValueVisitor::visit;
ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) :
mMapping(mapping) {
assert(mMapping);
}
void visit(Reference* reference) override {
if (!reference->id || !reference->id.value().isValid()) {
return;
}
ResourceId id = reference->id.value();
auto cacheIter = mMapping->find(id);
if (cacheIter != mMapping->end()) {
reference->name = cacheIter->second.toResourceName();
}
}
private:
const std::map<ResourceId, ResourceNameRef>* mMapping;
};
class PackagePbDeserializer {
public:
PackagePbDeserializer(const android::ResStringPool* valuePool,
const android::ResStringPool* sourcePool,
const android::ResStringPool* symbolPool,
const Source& source, IDiagnostics* diag) :
mValuePool(valuePool), mSourcePool(sourcePool), mSymbolPool(symbolPool),
mSource(source), mDiag(diag) {
}
public:
bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
Maybe<uint8_t> id;
if (pbPackage.has_package_id()) {
id = static_cast<uint8_t>(pbPackage.package_id());
}
std::map<ResourceId, ResourceNameRef> idIndex;
ResourceTablePackage* pkg = table->createPackage(
util::utf8ToUtf16(pbPackage.package_name()), id);
for (const pb::Type& pbType : pbPackage.types()) {
const ResourceType* resType = parseResourceType(util::utf8ToUtf16(pbType.name()));
if (!resType) {
mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'");
return {};
}
ResourceTableType* type = pkg->findOrCreateType(*resType);
for (const pb::Entry& pbEntry : pbType.entries()) {
ResourceEntry* entry = type->findOrCreateEntry(util::utf8ToUtf16(pbEntry.name()));
// Deserialize the symbol status (public/private with source and comments).
if (pbEntry.has_symbol_status()) {
const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
if (pbStatus.has_source()) {
deserializeSourceFromPb(pbStatus.source(), *mSourcePool,
&entry->symbolStatus.source);
}
if (pbStatus.has_comment()) {
entry->symbolStatus.comment = util::utf8ToUtf16(pbStatus.comment());
}
SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility());
entry->symbolStatus.state = visibility;
if (visibility == SymbolState::kPublic) {
// This is a public symbol, we must encode the ID now if there is one.
if (pbEntry.has_id()) {
entry->id = static_cast<uint16_t>(pbEntry.id());
}
if (type->symbolStatus.state != SymbolState::kPublic) {
// If the type has not been made public, do so now.
type->symbolStatus.state = SymbolState::kPublic;
if (pbType.has_id()) {
type->id = static_cast<uint8_t>(pbType.id());
}
}
} else if (visibility == SymbolState::kPrivate) {
if (type->symbolStatus.state == SymbolState::kUndefined) {
type->symbolStatus.state = SymbolState::kPrivate;
}
}
}
ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
if (resId.isValid()) {
idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
}
for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
const pb::ConfigDescription& pbConfig = pbConfigValue.config();
ConfigDescription config;
if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
mDiag->error(DiagMessage(mSource) << "invalid configuration");
return {};
}
ResourceConfigValue* configValue = entry->findOrCreateValue(config,
pbConfig.product());
if (configValue->value) {
// Duplicate config.
mDiag->error(DiagMessage(mSource) << "duplicate configuration");
return {};
}
configValue->value = deserializeValueFromPb(pbConfigValue.value(),
config, &table->stringPool);
if (!configValue->value) {
return {};
}
}
}
}
ReferenceIdToNameVisitor visitor(&idIndex);
visitAllValuesInPackage(pkg, &visitor);
return true;
}
private:
std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem,
const ConfigDescription& config,
StringPool* pool) {
if (pbItem.has_ref()) {
const pb::Reference& pbRef = pbItem.ref();
std::unique_ptr<Reference> ref = util::make_unique<Reference>();
if (!deserializeReferenceFromPb(pbRef, ref.get())) {
return {};
}
return std::move(ref);
} else if (pbItem.has_prim()) {
const pb::Primitive& pbPrim = pbItem.prim();
android::Res_value prim = {};
prim.dataType = static_cast<uint8_t>(pbPrim.type());
prim.data = pbPrim.data();
return util::make_unique<BinaryPrimitive>(prim);
} else if (pbItem.has_id()) {
return util::make_unique<Id>();
} else if (pbItem.has_str()) {
const uint32_t idx = pbItem.str().idx();
StringPiece16 str = util::getString(*mValuePool, idx);
const android::ResStringPool_span* spans = mValuePool->styleAt(idx);
if (spans && spans->name.index != android::ResStringPool_span::END) {
StyleString styleStr = { str.toString() };
while (spans->name.index != android::ResStringPool_span::END) {
styleStr.spans.push_back(Span{
util::getString(*mValuePool, spans->name.index).toString(),
spans->firstChar,
spans->lastChar
});
spans++;
}
return util::make_unique<StyledString>(
pool->makeRef(styleStr, StringPool::Context{ 1, config }));
}
return util::make_unique<String>(
pool->makeRef(str, StringPool::Context{ 1, config }));
} else if (pbItem.has_raw_str()) {
const uint32_t idx = pbItem.raw_str().idx();
StringPiece16 str = util::getString(*mValuePool, idx);
return util::make_unique<RawString>(
pool->makeRef(str, StringPool::Context{ 1, config }));
} else if (pbItem.has_file()) {
const uint32_t idx = pbItem.file().path_idx();
StringPiece16 str = util::getString(*mValuePool, idx);
return util::make_unique<FileReference>(
pool->makeRef(str, StringPool::Context{ 0, config }));
} else {
mDiag->error(DiagMessage(mSource) << "unknown item");
}
return {};
}
std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
const ConfigDescription& config,
StringPool* pool) {
const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
std::unique_ptr<Value> value;
if (pbValue.has_item()) {
value = deserializeItemFromPb(pbValue.item(), config, pool);
if (!value) {
return {};
}
} else if (pbValue.has_compound_value()) {
const pb::CompoundValue pbCompoundValue = pbValue.compound_value();
if (pbCompoundValue.has_attr()) {
const pb::Attribute& pbAttr = pbCompoundValue.attr();
std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
attr->typeMask = pbAttr.format_flags();
for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
Attribute::Symbol symbol;
deserializeItemCommon(pbSymbol, &symbol.symbol);
if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
return {};
}
symbol.value = pbSymbol.value();
attr->symbols.push_back(std::move(symbol));
}
value = std::move(attr);
} else if (pbCompoundValue.has_style()) {
const pb::Style& pbStyle = pbCompoundValue.style();
std::unique_ptr<Style> style = util::make_unique<Style>();
if (pbStyle.has_parent()) {
style->parent = Reference();
if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) {
return {};
}
if (pbStyle.has_parent_source()) {
Source parentSource;
deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
&parentSource);
style->parent.value().setSource(std::move(parentSource));
}
}
for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
Style::Entry entry;
deserializeItemCommon(pbEntry, &entry.key);
if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
return {};
}
entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
if (!entry.value) {
return {};
}
deserializeItemCommon(pbEntry, entry.value.get());
style->entries.push_back(std::move(entry));
}
value = std::move(style);
} else if (pbCompoundValue.has_styleable()) {
const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
Reference attrRef;
deserializeItemCommon(pbEntry, &attrRef);
deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
styleable->entries.push_back(std::move(attrRef));
}
value = std::move(styleable);
} else if (pbCompoundValue.has_array()) {
const pb::Array& pbArray = pbCompoundValue.array();
std::unique_ptr<Array> array = util::make_unique<Array>();
for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config,
pool);
if (!item) {
return {};
}
deserializeItemCommon(pbEntry, item.get());
array->items.push_back(std::move(item));
}
value = std::move(array);
} else if (pbCompoundValue.has_plural()) {
const pb::Plural& pbPlural = pbCompoundValue.plural();
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config,
pool);
if (!plural->values[pluralIdx]) {
return {};
}
deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
}
value = std::move(plural);
} else {
mDiag->error(DiagMessage(mSource) << "unknown compound value");
return {};
}
} else {
mDiag->error(DiagMessage(mSource) << "unknown value");
return {};
}
assert(value && "forgot to set value");
value->setWeak(isWeak);
deserializeItemCommon(pbValue, value.get());
return value;
}
bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
outRef->privateReference = pbRef.private_();
if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
return false;
}
if (pbRef.has_id()) {
outRef->id = ResourceId(pbRef.id());
}
if (pbRef.has_symbol_idx()) {
StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx());
ResourceNameRef nameRef;
if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
mDiag->error(DiagMessage(mSource) << "invalid reference name '"
<< strSymbol << "'");
return false;
}
outRef->name = nameRef.toResourceName();
}
return true;
}
template <typename T>
void deserializeItemCommon(const T& pbItem, Value* outValue) {
if (pbItem.has_source()) {
Source source;
deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
outValue->setSource(std::move(source));
}
if (pbItem.has_comment()) {
outValue->setComment(util::utf8ToUtf16(pbItem.comment()));
}
}
private:
const android::ResStringPool* mValuePool;
const android::ResStringPool* mSourcePool;
const android::ResStringPool* mSymbolPool;
const Source mSource;
IDiagnostics* mDiag;
};
} // namespace
std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
const Source& source,
IDiagnostics* diag) {
// We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
// causes errors when qualifying it with android::
using namespace android;
std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
if (!pbTable.has_string_pool()) {
diag->error(DiagMessage(source) << "no string pool found");
return {};
}
ResStringPool valuePool;
status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
pbTable.string_pool().data().size());
if (result != NO_ERROR) {
diag->error(DiagMessage(source) << "invalid string pool");
return {};
}
ResStringPool sourcePool;
if (pbTable.has_source_pool()) {
result = sourcePool.setTo(pbTable.source_pool().data().data(),
pbTable.source_pool().data().size());
if (result != NO_ERROR) {
diag->error(DiagMessage(source) << "invalid source pool");
return {};
}
}
ResStringPool symbolPool;
if (pbTable.has_symbol_pool()) {
result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
pbTable.symbol_pool().data().size());
if (result != NO_ERROR) {
diag->error(DiagMessage(source) << "invalid symbol pool");
return {};
}
}
PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
for (const pb::Package& pbPackage : pbTable.packages()) {
if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
return {};
}
}
return table;
}
std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
const Source& source,
IDiagnostics* diag) {
std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
ResourceNameRef nameRef;
// Need to create an lvalue here so that nameRef can point to something real.
std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name());
if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
<< pbFile.resource_name());
return {};
}
file->name = nameRef.toResourceName();
file->source.path = pbFile.source_path();
deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
// Need to create an lvalue here so that nameRef can point to something real.
utf16Name = util::utf8ToUtf16(pbSymbol.resource_name());
if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
diag->error(DiagMessage(source) << "invalid resource name for exported symbol in "
"compiled file header: "
<< pbFile.resource_name());
return {};
}
file->exportedSymbols.push_back(
SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() });
}
return file;
}
CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
mIn(static_cast<const uint8_t*>(data), size), mPbFile(),
mData(static_cast<const uint8_t*>(data)), mSize(size) {
}
const pb::CompiledFile* CompiledFileInputStream::CompiledFile() {
if (!mPbFile) {
std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
uint64_t pbSize = 0u;
if (!mIn.ReadLittleEndian64(&pbSize)) {
return nullptr;
}
mIn.PushLimit(static_cast<int>(pbSize));
if (!pbFile->ParsePartialFromCodedStream(&mIn)) {
return nullptr;
}
const size_t padding = 4 - (pbSize & 0x03);
const size_t offset = sizeof(uint64_t) + pbSize + padding;
if (offset > mSize) {
return nullptr;
}
mData += offset;
mSize -= offset;
mPbFile = std::move(pbFile);
}
return mPbFile.get();
}
const void* CompiledFileInputStream::data() {
if (!mPbFile) {
if (!CompiledFile()) {
return nullptr;
}
}
return mData;
}
size_t CompiledFileInputStream::size() {
if (!mPbFile) {
if (!CompiledFile()) {
return 0;
}
}
return mSize;
}
} // namespace aapt