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
521 lines
20 KiB
C++
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
|