summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/aapt2/ResourceParser.cpp7
-rw-r--r--tools/aapt2/StringPool.h17
-rw-r--r--tools/aapt2/flatten/XmlFlattener.cpp4
-rw-r--r--tools/aapt2/proto/TableProtoDeserializer.cpp791
-rw-r--r--tools/aapt2/unflatten/BinaryResourceParser.cpp978
5 files changed, 923 insertions, 874 deletions
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 51aed135a39e..7d50e1d38816 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -533,7 +533,8 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser,
if (!styleString.spans.empty()) {
// This can only be a StyledString.
return util::make_unique<StyledString>(mTable->stringPool.makeRef(
- styleString, StringPool::Context{1, mConfig}));
+ styleString,
+ StringPool::Context(StringPool::Context::kStylePriority, mConfig)));
}
auto onCreateReference = [&](const ResourceName& name) {
@@ -559,13 +560,13 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser,
if (typeMask & android::ResTable_map::TYPE_STRING) {
// Use the trimmed, escaped string.
return util::make_unique<String>(mTable->stringPool.makeRef(
- styleString.str, StringPool::Context{1, mConfig}));
+ styleString.str, StringPool::Context(mConfig)));
}
if (allowRawValue) {
// We can't parse this so return a RawString if we are allowed.
return util::make_unique<RawString>(
- mTable->stringPool.makeRef(rawValue, StringPool::Context{1, mConfig}));
+ mTable->stringPool.makeRef(rawValue, StringPool::Context(mConfig)));
}
return {};
}
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 05c26e7e4ea1..6e0d646cae12 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -42,9 +42,22 @@ struct StyleString {
class StringPool {
public:
- struct Context {
- uint32_t priority;
+ class Context {
+ public:
+ enum : uint32_t {
+ kStylePriority = 0u,
+ kHighPriority = 1u,
+ kNormalPriority = 0x7fffffffu,
+ kLowPriority = 0xffffffffu,
+ };
+ uint32_t priority = kNormalPriority;
ConfigDescription config;
+
+ Context() = default;
+ Context(uint32_t p, const ConfigDescription& c) : priority(p), config(c) {}
+ explicit Context(uint32_t p) : priority(p) {}
+ explicit Context(const ConfigDescription& c)
+ : priority(kNormalPriority), config(c) {}
};
class Entry;
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index c296dde0ca1e..b1536d5f21d2 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -63,7 +63,7 @@ struct XmlFlattenerVisitor : public xml::Visitor {
dest->index = util::deviceToHost32(-1);
} else {
mStringRefs.push_back(StringFlattenDest{
- mPool.makeRef(str, StringPool::Context{priority}), dest});
+ mPool.makeRef(str, StringPool::Context(priority)), dest});
}
}
@@ -256,7 +256,7 @@ struct XmlFlattenerVisitor : public xml::Visitor {
StringPool::Ref nameRef =
mPackagePools[aaptAttr.id.value().packageId()].makeRef(
- xmlAttr->name, StringPool::Context{aaptAttr.id.value().id});
+ xmlAttr->name, StringPool::Context(aaptAttr.id.value().id));
// Add it to the list of strings to flatten.
addString(nameRef, &flatAttr->name);
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 595fa6f29fac..0dfb01c181c6 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -27,445 +27,460 @@ namespace aapt {
namespace {
class ReferenceIdToNameVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
-
- explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) :
- mMapping(mapping) {
- assert(mMapping);
+ public:
+ using ValueVisitor::visit;
+
+ explicit ReferenceIdToNameVisitor(
+ const std::map<ResourceId, ResourceNameRef>* mapping)
+ : mMapping(mapping) {
+ assert(mMapping);
+ }
+
+ void visit(Reference* reference) override {
+ if (!reference->id || !reference->id.value().isValid()) {
+ return;
}
- 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();
- }
+ 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;
+ 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:
+ 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());
}
-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;
+ std::map<ResourceId, ResourceNameRef> idIndex;
- ResourceTablePackage* pkg = table->createPackage(pbPackage.package_name(), id);
- for (const pb::Type& pbType : pbPackage.types()) {
- const ResourceType* resType = parseResourceType(pbType.name());
- if (!resType) {
- mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'");
- return {};
+ ResourceTablePackage* pkg =
+ table->createPackage(pbPackage.package_name(), id);
+ for (const pb::Type& pbType : pbPackage.types()) {
+ const ResourceType* resType = parseResourceType(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(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 = 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());
}
- ResourceTableType* type = pkg->findOrCreateType(*resType);
-
- for (const pb::Entry& pbEntry : pbType.entries()) {
- ResourceEntry* entry = type->findOrCreateEntry(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 = 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 {};
- }
- }
+ 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;
}
+ }
}
- 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();
- const std::string 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 };
- while (spans->name.index != android::ResStringPool_span::END) {
- styleStr.spans.push_back(Span{
- util::getString(*mValuePool, spans->name.index),
- 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();
- const std::string 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();
- const std::string str = util::getString(*mValuePool, idx);
- return util::make_unique<FileReference>(
- pool->makeRef(str, StringPool::Context{ 0, config }));
-
- } else {
- mDiag->error(DiagMessage(mSource) << "unknown item");
+ ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
+ if (resId.isValid()) {
+ idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
}
- return {};
- }
- std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
- const ConfigDescription& config,
- StringPool* pool) {
- const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
+ for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
+ const pb::ConfigDescription& pbConfig = pbConfigValue.config();
- std::unique_ptr<Value> value;
- if (pbValue.has_item()) {
- value = deserializeItemFromPb(pbValue.item(), config, pool);
- if (!value) {
- return {};
- }
+ ConfigDescription config;
+ if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
+ mDiag->error(DiagMessage(mSource) << "invalid configuration");
+ 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();
- attr->minInt = pbAttr.min_int();
- attr->maxInt = pbAttr.max_int();
- 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");
+ ResourceConfigValue* configValue =
+ entry->findOrCreateValue(config, pbConfig.product());
+ if (configValue->value) {
+ // Duplicate config.
+ mDiag->error(DiagMessage(mSource) << "duplicate configuration");
return {};
- }
+ }
- assert(value && "forgot to set value");
+ configValue->value = deserializeValueFromPb(
+ pbConfigValue.value(), config, &table->stringPool);
+ if (!configValue->value) {
+ return {};
+ }
+ }
+ }
+ }
- value->setWeak(isWeak);
- deserializeItemCommon(pbValue, value.get());
- return value;
+ 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();
+ const std::string 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};
+ while (spans->name.index != android::ResStringPool_span::END) {
+ styleStr.spans.push_back(
+ Span{util::getString(*mValuePool, spans->name.index),
+ spans->firstChar, spans->lastChar});
+ spans++;
+ }
+ return util::make_unique<StyledString>(pool->makeRef(
+ styleStr,
+ StringPool::Context(StringPool::Context::kStylePriority, config)));
+ }
+ return util::make_unique<String>(
+ pool->makeRef(str, StringPool::Context(config)));
+
+ } else if (pbItem.has_raw_str()) {
+ const uint32_t idx = pbItem.raw_str().idx();
+ const std::string str = util::getString(*mValuePool, idx);
+ return util::make_unique<RawString>(
+ pool->makeRef(str, StringPool::Context(config)));
+
+ } else if (pbItem.has_file()) {
+ const uint32_t idx = pbItem.file().path_idx();
+ const std::string str = util::getString(*mValuePool, idx);
+ return util::make_unique<FileReference>(pool->makeRef(
+ str,
+ StringPool::Context(StringPool::Context::kHighPriority, config)));
+
+ } else {
+ mDiag->error(DiagMessage(mSource) << "unknown item");
}
+ return {};
+ }
- bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
- outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
- outRef->privateReference = pbRef.private_();
+ std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
+ const ConfigDescription& config,
+ StringPool* pool) {
+ const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
- if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
- return 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();
+ attr->minInt = pbAttr.min_int();
+ attr->maxInt = pbAttr.max_int();
+ 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));
}
-
- if (pbRef.has_id()) {
- outRef->id = ResourceId(pbRef.id());
+ 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));
+ }
}
- if (pbRef.has_symbol_idx()) {
- const std::string 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;
- }
+ 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 {};
+ }
- outRef->name = nameRef.toResourceName();
+ deserializeItemCommon(pbEntry, entry.value.get());
+ style->entries.push_back(std::move(entry));
}
- return true;
- }
+ 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 {};
+ }
- 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));
+ 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 {};
+ }
- if (pbItem.has_comment()) {
- outValue->setComment(pbItem.comment());
+ deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
}
- }
+ value = std::move(plural);
-private:
- const android::ResStringPool* mValuePool;
- const android::ResStringPool* mSourcePool;
- const android::ResStringPool* mSymbolPool;
- const Source mSource;
- IDiagnostics* mDiag;
-};
+ } else {
+ mDiag->error(DiagMessage(mSource) << "unknown compound value");
+ return {};
+ }
+ } else {
+ mDiag->error(DiagMessage(mSource) << "unknown value");
+ return {};
+ }
-} // namespace
+ assert(value && "forgot to set value");
-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;
+ value->setWeak(isWeak);
+ deserializeItemCommon(pbValue, value.get());
+ return value;
+ }
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ bool deserializeReferenceFromPb(const pb::Reference& pbRef,
+ Reference* outRef) {
+ outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
+ outRef->privateReference = pbRef.private_();
- if (!pbTable.has_string_pool()) {
- diag->error(DiagMessage(source) << "no string pool found");
- return {};
+ if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
+ return false;
}
- 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 {};
+ if (pbRef.has_id()) {
+ outRef->id = ResourceId(pbRef.id());
}
- 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 {};
- }
+ if (pbRef.has_symbol_idx()) {
+ const std::string 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();
}
-
- 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 {};
- }
+ 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));
}
- PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
- for (const pb::Package& pbPackage : pbTable.packages()) {
- if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
- return {};
- }
+ if (pbItem.has_comment()) {
+ outValue->setComment(pbItem.comment());
}
- return table;
-}
+ }
+
+ private:
+ const android::ResStringPool* mValuePool;
+ const android::ResStringPool* mSourcePool;
+ const android::ResStringPool* mSymbolPool;
+ const Source mSource;
+ IDiagnostics* mDiag;
+};
-std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
- const Source& source,
- IDiagnostics* diag) {
- std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
+} // 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 {};
+ }
+ }
- ResourceNameRef nameRef;
+ 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 {};
+ }
+ }
- // Need to create an lvalue here so that nameRef can point to something real.
- if (!ResourceUtils::parseResourceName(pbFile.resource_name(), &nameRef)) {
- diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
- << pbFile.resource_name());
- return {};
+ PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool,
+ &symbolPool, source, diag);
+ for (const pb::Package& pbPackage : pbTable.packages()) {
+ if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
+ 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.
- if (!ResourceUtils::parseResourceName(pbSymbol.resource_name(), &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 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.
+ if (!ResourceUtils::parseResourceName(pbFile.resource_name(), &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.
+ if (!ResourceUtils::parseResourceName(pbSymbol.resource_name(), &nameRef)) {
+ diag->error(DiagMessage(source)
+ << "invalid resource name for exported symbol in "
+ "compiled file header: "
+ << pbFile.resource_name());
+ return {};
}
- return file;
+ file->exportedSymbols.push_back(
+ SourcedResourceName{nameRef.toResourceName(), pbSymbol.line_no()});
+ }
+ return file;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 4fd77c83a891..546b6078ddb0 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
+#include "unflatten/BinaryResourceParser.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "Source.h"
#include "ValueVisitor.h"
-#include "unflatten/BinaryResourceParser.h"
#include "unflatten/ResChunkPullParser.h"
#include "util/Util.h"
+#include <android-base/macros.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
-#include <android-base/macros.h>
#include <algorithm>
#include <map>
#include <string>
@@ -41,533 +41,553 @@ namespace {
* given a mapping from resource ID to resource name.
*/
class ReferenceIdToNameVisitor : public ValueVisitor {
-private:
- const std::map<ResourceId, ResourceName>* mMapping;
+ private:
+ const std::map<ResourceId, ResourceName>* mMapping;
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::visit;
- explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) :
- mMapping(mapping) {
- assert(mMapping);
- }
+ explicit ReferenceIdToNameVisitor(
+ const std::map<ResourceId, ResourceName>* mapping)
+ : mMapping(mapping) {
+ assert(mMapping);
+ }
- void visit(Reference* reference) override {
- if (!reference->id || !reference->id.value().isValid()) {
- return;
- }
+ 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;
- }
+ ResourceId id = reference->id.value();
+ auto cacheIter = mMapping->find(id);
+ if (cacheIter != mMapping->end()) {
+ reference->name = cacheIter->second;
}
+ }
};
-} // namespace
+} // namespace
-BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
- const Source& source, const void* data, size_t len) :
- mContext(context), mTable(table), mSource(source), mData(data), mDataLen(len) {
-}
+BinaryResourceParser::BinaryResourceParser(IAaptContext* context,
+ ResourceTable* table,
+ const Source& source,
+ const void* data, size_t len)
+ : mContext(context),
+ mTable(table),
+ mSource(source),
+ mData(data),
+ mDataLen(len) {}
bool BinaryResourceParser::parse() {
- ResChunkPullParser parser(mData, mDataLen);
-
- bool error = false;
- while(ResChunkPullParser::isGoodEvent(parser.next())) {
- if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
- mContext->getDiagnostics()->warn(DiagMessage(mSource)
- << "unknown chunk of type '"
- << (int) parser.getChunk()->type << "'");
- continue;
- }
+ ResChunkPullParser parser(mData, mDataLen);
- if (!parseTable(parser.getChunk())) {
- error = true;
- }
+ bool error = false;
+ while (ResChunkPullParser::isGoodEvent(parser.next())) {
+ if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
+ mContext->getDiagnostics()->warn(DiagMessage(mSource)
+ << "unknown chunk of type '"
+ << (int)parser.getChunk()->type << "'");
+ continue;
}
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt resource table: "
- << parser.getLastError());
- return false;
+ if (!parseTable(parser.getChunk())) {
+ error = true;
}
- return !error;
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt resource table: "
+ << parser.getLastError());
+ return false;
+ }
+ return !error;
}
/**
- * Parses the resource table, which contains all the packages, types, and entries.
+ * Parses the resource table, which contains all the packages, types, and
+ * entries.
*/
bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
- const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
- if (!tableHeader) {
- mContext->getDiagnostics()->error(DiagMessage(mSource) << "corrupt ResTable_header chunk");
- return false;
- }
+ const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
+ if (!tableHeader) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_header chunk");
+ return false;
+ }
+
+ ResChunkPullParser parser(getChunkData(&tableHeader->header),
+ getChunkDataLen(&tableHeader->header));
+ while (ResChunkPullParser::isGoodEvent(parser.next())) {
+ switch (util::deviceToHost16(parser.getChunk()->type)) {
+ case android::RES_STRING_POOL_TYPE:
+ if (mValuePool.getError() == NO_INIT) {
+ status_t err = mValuePool.setTo(
+ parser.getChunk(), util::deviceToHost32(parser.getChunk()->size));
+ if (err != NO_ERROR) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(mSource) << "corrupt string pool in ResTable: "
+ << mValuePool.getError());
+ return false;
+ }
- ResChunkPullParser parser(getChunkData(&tableHeader->header),
- getChunkDataLen(&tableHeader->header));
- while (ResChunkPullParser::isGoodEvent(parser.next())) {
- switch (util::deviceToHost16(parser.getChunk()->type)) {
- case android::RES_STRING_POOL_TYPE:
- if (mValuePool.getError() == NO_INIT) {
- status_t err = mValuePool.setTo(parser.getChunk(),
- util::deviceToHost32(parser.getChunk()->size));
- if (err != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt string pool in ResTable: "
- << mValuePool.getError());
- return false;
- }
-
- // Reserve some space for the strings we are going to add.
- mTable->stringPool.hintWillAdd(mValuePool.size(), mValuePool.styleCount());
- } else {
- mContext->getDiagnostics()->warn(DiagMessage(mSource)
- << "unexpected string pool in ResTable");
- }
- break;
-
- case android::RES_TABLE_PACKAGE_TYPE:
- if (!parsePackage(parser.getChunk())) {
- return false;
- }
- break;
-
- default:
- mContext->getDiagnostics()
- ->warn(DiagMessage(mSource)
- << "unexpected chunk type "
- << (int) util::deviceToHost16(parser.getChunk()->type));
- break;
+ // Reserve some space for the strings we are going to add.
+ mTable->stringPool.hintWillAdd(mValuePool.size(),
+ mValuePool.styleCount());
+ } else {
+ mContext->getDiagnostics()->warn(
+ DiagMessage(mSource) << "unexpected string pool in ResTable");
}
- }
+ break;
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt resource table: " << parser.getLastError());
- return false;
- }
- return true;
+ case android::RES_TABLE_PACKAGE_TYPE:
+ if (!parsePackage(parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ default:
+ mContext->getDiagnostics()->warn(
+ DiagMessage(mSource)
+ << "unexpected chunk type "
+ << (int)util::deviceToHost16(parser.getChunk()->type));
+ break;
+ }
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt resource table: "
+ << parser.getLastError());
+ return false;
+ }
+ return true;
}
-
bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
- const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
- if (!packageHeader) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_package chunk");
- return false;
- }
-
- uint32_t packageId = util::deviceToHost32(packageHeader->id);
- if (packageId > std::numeric_limits<uint8_t>::max()) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "package ID is too big (" << packageId << ")");
- return false;
- }
-
- // Extract the package name.
- size_t len = strnlen16((const char16_t*) packageHeader->name, arraysize(packageHeader->name));
- std::u16string packageName;
- packageName.resize(len);
- for (size_t i = 0; i < len; i++) {
- packageName[i] = util::deviceToHost16(packageHeader->name[i]);
- }
-
- ResourceTablePackage* package = mTable->createPackage(util::utf16ToUtf8(packageName),
- static_cast<uint8_t>(packageId));
- if (!package) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "incompatible package '" << packageName
- << "' with ID " << packageId);
- return false;
- }
-
- // There can be multiple packages in a table, so
- // clear the type and key pool in case they were set from a previous package.
- mTypePool.uninit();
- mKeyPool.uninit();
-
- ResChunkPullParser parser(getChunkData(&packageHeader->header),
- getChunkDataLen(&packageHeader->header));
- while (ResChunkPullParser::isGoodEvent(parser.next())) {
- switch (util::deviceToHost16(parser.getChunk()->type)) {
- case android::RES_STRING_POOL_TYPE:
- if (mTypePool.getError() == NO_INIT) {
- status_t err = mTypePool.setTo(parser.getChunk(),
- util::deviceToHost32(parser.getChunk()->size));
- if (err != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt type string pool in "
- << "ResTable_package: "
- << mTypePool.getError());
- return false;
- }
- } else if (mKeyPool.getError() == NO_INIT) {
- status_t err = mKeyPool.setTo(parser.getChunk(),
- util::deviceToHost32(parser.getChunk()->size));
- if (err != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt key string pool in "
- << "ResTable_package: "
- << mKeyPool.getError());
- return false;
- }
- } else {
- mContext->getDiagnostics()->warn(DiagMessage(mSource) << "unexpected string pool");
- }
- break;
-
- case android::RES_TABLE_TYPE_SPEC_TYPE:
- if (!parseTypeSpec(parser.getChunk())) {
- return false;
- }
- break;
-
- case android::RES_TABLE_TYPE_TYPE:
- if (!parseType(package, parser.getChunk())) {
- return false;
- }
- break;
-
- default:
- mContext->getDiagnostics()
- ->warn(DiagMessage(mSource)
- << "unexpected chunk type "
- << (int) util::deviceToHost16(parser.getChunk()->type));
- break;
+ const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
+ if (!packageHeader) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_package chunk");
+ return false;
+ }
+
+ uint32_t packageId = util::deviceToHost32(packageHeader->id);
+ if (packageId > std::numeric_limits<uint8_t>::max()) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(mSource) << "package ID is too big (" << packageId << ")");
+ return false;
+ }
+
+ // Extract the package name.
+ size_t len = strnlen16((const char16_t*)packageHeader->name,
+ arraysize(packageHeader->name));
+ std::u16string packageName;
+ packageName.resize(len);
+ for (size_t i = 0; i < len; i++) {
+ packageName[i] = util::deviceToHost16(packageHeader->name[i]);
+ }
+
+ ResourceTablePackage* package = mTable->createPackage(
+ util::utf16ToUtf8(packageName), static_cast<uint8_t>(packageId));
+ if (!package) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "incompatible package '" << packageName
+ << "' with ID " << packageId);
+ return false;
+ }
+
+ // There can be multiple packages in a table, so
+ // clear the type and key pool in case they were set from a previous package.
+ mTypePool.uninit();
+ mKeyPool.uninit();
+
+ ResChunkPullParser parser(getChunkData(&packageHeader->header),
+ getChunkDataLen(&packageHeader->header));
+ while (ResChunkPullParser::isGoodEvent(parser.next())) {
+ switch (util::deviceToHost16(parser.getChunk()->type)) {
+ case android::RES_STRING_POOL_TYPE:
+ if (mTypePool.getError() == NO_INIT) {
+ status_t err = mTypePool.setTo(
+ parser.getChunk(), util::deviceToHost32(parser.getChunk()->size));
+ if (err != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt type string pool in "
+ << "ResTable_package: "
+ << mTypePool.getError());
+ return false;
+ }
+ } else if (mKeyPool.getError() == NO_INIT) {
+ status_t err = mKeyPool.setTo(
+ parser.getChunk(), util::deviceToHost32(parser.getChunk()->size));
+ if (err != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt key string pool in "
+ << "ResTable_package: "
+ << mKeyPool.getError());
+ return false;
+ }
+ } else {
+ mContext->getDiagnostics()->warn(DiagMessage(mSource)
+ << "unexpected string pool");
}
- }
+ break;
- if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_package: "
- << parser.getLastError());
- return false;
- }
+ case android::RES_TABLE_TYPE_SPEC_TYPE:
+ if (!parseTypeSpec(parser.getChunk())) {
+ return false;
+ }
+ break;
- // Now go through the table and change local resource ID references to
- // symbolic references.
- ReferenceIdToNameVisitor visitor(&mIdIndex);
- visitAllValuesInTable(mTable, &visitor);
- return true;
+ case android::RES_TABLE_TYPE_TYPE:
+ if (!parseType(package, parser.getChunk())) {
+ return false;
+ }
+ break;
+
+ default:
+ mContext->getDiagnostics()->warn(
+ DiagMessage(mSource)
+ << "unexpected chunk type "
+ << (int)util::deviceToHost16(parser.getChunk()->type));
+ break;
+ }
+ }
+
+ if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_package: "
+ << parser.getLastError());
+ return false;
+ }
+
+ // Now go through the table and change local resource ID references to
+ // symbolic references.
+ ReferenceIdToNameVisitor visitor(&mIdIndex);
+ visitAllValuesInTable(mTable, &visitor);
+ return true;
}
bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
- if (mTypePool.getError() != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "missing type string pool");
- return false;
- }
-
- const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
- if (!typeSpec) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_typeSpec chunk");
- return false;
- }
-
- if (typeSpec->id == 0) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "ResTable_typeSpec has invalid id: " << typeSpec->id);
- return false;
- }
- return true;
+ if (mTypePool.getError() != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "missing type string pool");
+ return false;
+ }
+
+ const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
+ if (!typeSpec) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_typeSpec chunk");
+ return false;
+ }
+
+ if (typeSpec->id == 0) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "ResTable_typeSpec has invalid id: "
+ << typeSpec->id);
+ return false;
+ }
+ return true;
}
bool BinaryResourceParser::parseType(const ResourceTablePackage* package,
const ResChunk_header* chunk) {
- if (mTypePool.getError() != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "missing type string pool");
+ if (mTypePool.getError() != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "missing type string pool");
+ return false;
+ }
+
+ if (mKeyPool.getError() != NO_ERROR) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "missing key string pool");
+ return false;
+ }
+
+ const ResTable_type* type = convertTo<ResTable_type>(chunk);
+ if (!type) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "corrupt ResTable_type chunk");
+ return false;
+ }
+
+ if (type->id == 0) {
+ mContext->getDiagnostics()->error(DiagMessage(mSource)
+ << "ResTable_type has invalid id: "
+ << (int)type->id);
+ return false;
+ }
+
+ ConfigDescription config;
+ config.copyFromDtoH(type->config);
+
+ const std::string typeStr = util::getString(mTypePool, type->id - 1);
+
+ const ResourceType* parsedType = parseResourceType(typeStr);
+ if (!parsedType) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(mSource) << "invalid type name '" << typeStr
+ << "' for type with ID " << (int)type->id);
+ return false;
+ }
+
+ TypeVariant tv(type);
+ for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
+ const ResTable_entry* entry = *it;
+ if (!entry) {
+ continue;
+ }
+
+ const ResourceName name(
+ package->name, *parsedType,
+ util::getString(mKeyPool, util::deviceToHost32(entry->key.index)));
+
+ const ResourceId resId(package->id.value(), type->id,
+ static_cast<uint16_t>(it.index()));
+
+ std::unique_ptr<Value> resourceValue;
+ if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
+ const ResTable_map_entry* mapEntry =
+ static_cast<const ResTable_map_entry*>(entry);
+
+ // TODO(adamlesinski): Check that the entry count is valid.
+ resourceValue = parseMapEntry(name, config, mapEntry);
+ } else {
+ const Res_value* value =
+ (const Res_value*)((const uint8_t*)entry +
+ util::deviceToHost32(entry->size));
+ resourceValue = parseValue(name, config, value, entry->flags);
+ }
+
+ if (!resourceValue) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(mSource) << "failed to parse value for resource " << name
+ << " (" << resId << ") with configuration '"
+ << config << "'");
+ return false;
+ }
+
+ if (!mTable->addResourceAllowMangled(name, config, {},
+ std::move(resourceValue),
+ mContext->getDiagnostics())) {
+ return false;
+ }
+
+ if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
+ Symbol symbol;
+ symbol.state = SymbolState::kPublic;
+ symbol.source = mSource.withLine(0);
+ if (!mTable->setSymbolStateAllowMangled(name, resId, symbol,
+ mContext->getDiagnostics())) {
return false;
+ }
}
- if (mKeyPool.getError() != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "missing key string pool");
- return false;
- }
-
- const ResTable_type* type = convertTo<ResTable_type>(chunk);
- if (!type) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt ResTable_type chunk");
- return false;
- }
-
- if (type->id == 0) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "ResTable_type has invalid id: " << (int) type->id);
- return false;
+ // Add this resource name->id mapping to the index so
+ // that we can resolve all ID references to name references.
+ auto cacheIter = mIdIndex.find(resId);
+ if (cacheIter == mIdIndex.end()) {
+ mIdIndex.insert({resId, name});
}
-
- ConfigDescription config;
- config.copyFromDtoH(type->config);
-
- const std::string typeStr = util::getString(mTypePool, type->id - 1);
-
- const ResourceType* parsedType = parseResourceType(typeStr);
- if (!parsedType) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "invalid type name '" << typeStr
- << "' for type with ID " << (int) type->id);
- return false;
- }
-
- TypeVariant tv(type);
- for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
- const ResTable_entry* entry = *it;
- if (!entry) {
- continue;
- }
-
- const ResourceName name(package->name, *parsedType,
- util::getString(mKeyPool,
- util::deviceToHost32(entry->key.index)));
-
- const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
-
- std::unique_ptr<Value> resourceValue;
- if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
- const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
-
- // TODO(adamlesinski): Check that the entry count is valid.
- resourceValue = parseMapEntry(name, config, mapEntry);
- } else {
- const Res_value* value = (const Res_value*)(
- (const uint8_t*) entry + util::deviceToHost32(entry->size));
- resourceValue = parseValue(name, config, value, entry->flags);
- }
-
- if (!resourceValue) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "failed to parse value for resource " << name
- << " (" << resId << ") with configuration '"
- << config << "'");
- return false;
- }
-
- if (!mTable->addResourceAllowMangled(name, config, {}, std::move(resourceValue),
- mContext->getDiagnostics())) {
- return false;
- }
-
- if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
- Symbol symbol;
- symbol.state = SymbolState::kPublic;
- symbol.source = mSource.withLine(0);
- if (!mTable->setSymbolStateAllowMangled(name, resId, symbol,
- mContext->getDiagnostics())) {
- return false;
- }
- }
-
- // Add this resource name->id mapping to the index so
- // that we can resolve all ID references to name references.
- auto cacheIter = mIdIndex.find(resId);
- if (cacheIter == mIdIndex.end()) {
- mIdIndex.insert({ resId, name });
- }
- }
- return true;
+ }
+ return true;
}
-std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Res_value* value,
- uint16_t flags) {
- if (name.type == ResourceType::kId) {
- return util::make_unique<Id>();
- }
-
- const uint32_t data = util::deviceToHost32(value->data);
-
- if (value->dataType == Res_value::TYPE_STRING) {
- const std::string str = util::getString(mValuePool, data);
-
- const ResStringPool_span* spans = mValuePool.styleAt(data);
-
- // Check if the string has a valid style associated with it.
- if (spans != nullptr && spans->name.index != ResStringPool_span::END) {
- StyleString styleStr = { str };
- while (spans->name.index != ResStringPool_span::END) {
- styleStr.spans.push_back(Span{
- util::getString(mValuePool, spans->name.index),
- spans->firstChar,
- spans->lastChar
- });
- spans++;
- }
- return util::make_unique<StyledString>(mTable->stringPool.makeRef(
- styleStr, StringPool::Context{1, config}));
- } else {
- if (name.type != ResourceType::kString &&
- util::stringStartsWith(str, "res/")) {
- // This must be a FileReference.
- return util::make_unique<FileReference>(mTable->stringPool.makeRef(
- str, StringPool::Context{ 0, config }));
- }
-
- // There are no styles associated with this string, so treat it as
- // a simple string.
- return util::make_unique<String>(mTable->stringPool.makeRef(
- str, StringPool::Context{1, config}));
- }
- }
-
- if (value->dataType == Res_value::TYPE_REFERENCE ||
- value->dataType == Res_value::TYPE_ATTRIBUTE) {
- const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
- Reference::Type::kResource : Reference::Type::kAttribute;
-
- if (data == 0) {
- // A reference of 0, must be the magic @null reference.
- Res_value nullType = {};
- nullType.dataType = Res_value::TYPE_REFERENCE;
- return util::make_unique<BinaryPrimitive>(nullType);
- }
-
- // This is a normal reference.
- return util::make_unique<Reference>(data, type);
- }
-
- // Treat this as a raw binary primitive.
- return util::make_unique<BinaryPrimitive>(*value);
+std::unique_ptr<Item> BinaryResourceParser::parseValue(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const Res_value* value, uint16_t flags) {
+ if (name.type == ResourceType::kId) {
+ return util::make_unique<Id>();
+ }
+
+ const uint32_t data = util::deviceToHost32(value->data);
+
+ if (value->dataType == Res_value::TYPE_STRING) {
+ const std::string str = util::getString(mValuePool, data);
+
+ const ResStringPool_span* spans = mValuePool.styleAt(data);
+
+ // Check if the string has a valid style associated with it.
+ if (spans != nullptr && spans->name.index != ResStringPool_span::END) {
+ StyleString styleStr = {str};
+ while (spans->name.index != ResStringPool_span::END) {
+ styleStr.spans.push_back(
+ Span{util::getString(mValuePool, spans->name.index),
+ spans->firstChar, spans->lastChar});
+ spans++;
+ }
+ return util::make_unique<StyledString>(mTable->stringPool.makeRef(
+ styleStr,
+ StringPool::Context(StringPool::Context::kStylePriority, config)));
+ } else {
+ if (name.type != ResourceType::kString &&
+ util::stringStartsWith(str, "res/")) {
+ // This must be a FileReference.
+ return util::make_unique<FileReference>(mTable->stringPool.makeRef(
+ str,
+ StringPool::Context(StringPool::Context::kHighPriority, config)));
+ }
+
+ // There are no styles associated with this string, so treat it as
+ // a simple string.
+ return util::make_unique<String>(
+ mTable->stringPool.makeRef(str, StringPool::Context(config)));
+ }
+ }
+
+ if (value->dataType == Res_value::TYPE_REFERENCE ||
+ value->dataType == Res_value::TYPE_ATTRIBUTE) {
+ const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE)
+ ? Reference::Type::kResource
+ : Reference::Type::kAttribute;
+
+ if (data == 0) {
+ // A reference of 0, must be the magic @null reference.
+ Res_value nullType = {};
+ nullType.dataType = Res_value::TYPE_REFERENCE;
+ return util::make_unique<BinaryPrimitive>(nullType);
+ }
+
+ // This is a normal reference.
+ return util::make_unique<Reference>(data, type);
+ }
+
+ // Treat this as a raw binary primitive.
+ return util::make_unique<BinaryPrimitive>(*value);
}
-std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- switch (name.type) {
- case ResourceType::kStyle:
- return parseStyle(name, config, map);
- case ResourceType::kAttrPrivate:
- // fallthrough
- case ResourceType::kAttr:
- return parseAttr(name, config, map);
- case ResourceType::kArray:
- return parseArray(name, config, map);
- case ResourceType::kPlurals:
- return parsePlural(name, config, map);
- default:
- assert(false && "unknown map type");
- break;
- }
- return {};
+std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ switch (name.type) {
+ case ResourceType::kStyle:
+ return parseStyle(name, config, map);
+ case ResourceType::kAttrPrivate:
+ // fallthrough
+ case ResourceType::kAttr:
+ return parseAttr(name, config, map);
+ case ResourceType::kArray:
+ return parseArray(name, config, map);
+ case ResourceType::kPlurals:
+ return parsePlural(name, config, map);
+ default:
+ assert(false && "unknown map type");
+ break;
+ }
+ return {};
}
-std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Style> style = util::make_unique<Style>();
- if (util::deviceToHost32(map->parent.ident) != 0) {
- // The parent is a regular reference to a resource.
- style->parent = Reference(util::deviceToHost32(map->parent.ident));
- }
-
- for (const ResTable_map& mapEntry : map) {
- if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
- continue;
- }
-
- Style::Entry styleEntry;
- styleEntry.key = Reference(util::deviceToHost32(mapEntry.name.ident));
- styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
- if (!styleEntry.value) {
- return {};
- }
- style->entries.push_back(std::move(styleEntry));
- }
- return style;
+std::unique_ptr<Style> BinaryResourceParser::parseStyle(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (util::deviceToHost32(map->parent.ident) != 0) {
+ // The parent is a regular reference to a resource.
+ style->parent = Reference(util::deviceToHost32(map->parent.ident));
+ }
+
+ for (const ResTable_map& mapEntry : map) {
+ if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
+ continue;
+ }
+
+ Style::Entry styleEntry;
+ styleEntry.key = Reference(util::deviceToHost32(mapEntry.name.ident));
+ styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
+ if (!styleEntry.value) {
+ return {};
+ }
+ style->entries.push_back(std::move(styleEntry));
+ }
+ return style;
}
-std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- const bool isWeak = (util::deviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
-
- // First we must discover what type of attribute this is. Find the type mask.
- auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
- return util::deviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
- });
-
- if (typeMaskIter != end(map)) {
- attr->typeMask = util::deviceToHost32(typeMaskIter->value.data);
- }
-
- for (const ResTable_map& mapEntry : map) {
- if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
- switch (util::deviceToHost32(mapEntry.name.ident)) {
- case ResTable_map::ATTR_MIN:
- attr->minInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- case ResTable_map::ATTR_MAX:
- attr->maxInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- }
- continue;
- }
-
- if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
- Attribute::Symbol symbol;
- symbol.value = util::deviceToHost32(mapEntry.value.data);
- symbol.symbol = Reference(util::deviceToHost32(mapEntry.name.ident));
- attr->symbols.push_back(std::move(symbol));
- }
- }
-
- // TODO(adamlesinski): Find i80n, attributes.
- return attr;
+std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ const bool isWeak =
+ (util::deviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
+
+ // First we must discover what type of attribute this is. Find the type mask.
+ auto typeMaskIter =
+ std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+ return util::deviceToHost32(entry.name.ident) ==
+ ResTable_map::ATTR_TYPE;
+ });
+
+ if (typeMaskIter != end(map)) {
+ attr->typeMask = util::deviceToHost32(typeMaskIter->value.data);
+ }
+
+ for (const ResTable_map& mapEntry : map) {
+ if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
+ switch (util::deviceToHost32(mapEntry.name.ident)) {
+ case ResTable_map::ATTR_MIN:
+ attr->minInt = static_cast<int32_t>(mapEntry.value.data);
+ break;
+ case ResTable_map::ATTR_MAX:
+ attr->maxInt = static_cast<int32_t>(mapEntry.value.data);
+ break;
+ }
+ continue;
+ }
+
+ if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+ Attribute::Symbol symbol;
+ symbol.value = util::deviceToHost32(mapEntry.value.data);
+ symbol.symbol = Reference(util::deviceToHost32(mapEntry.name.ident));
+ attr->symbols.push_back(std::move(symbol));
+ }
+ }
+
+ // TODO(adamlesinski): Find i80n, attributes.
+ return attr;
}
-std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Array> array = util::make_unique<Array>();
- for (const ResTable_map& mapEntry : map) {
- array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
- }
- return array;
+std::unique_ptr<Array> BinaryResourceParser::parseArray(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const ResTable_map& mapEntry : map) {
+ array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
+ }
+ return array;
}
-std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- for (const ResTable_map& mapEntry : map) {
- std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
- if (!item) {
- return {};
- }
-
- switch (util::deviceToHost32(mapEntry.name.ident)) {
- case ResTable_map::ATTR_ZERO:
- plural->values[Plural::Zero] = std::move(item);
- break;
- case ResTable_map::ATTR_ONE:
- plural->values[Plural::One] = std::move(item);
- break;
- case ResTable_map::ATTR_TWO:
- plural->values[Plural::Two] = std::move(item);
- break;
- case ResTable_map::ATTR_FEW:
- plural->values[Plural::Few] = std::move(item);
- break;
- case ResTable_map::ATTR_MANY:
- plural->values[Plural::Many] = std::move(item);
- break;
- case ResTable_map::ATTR_OTHER:
- plural->values[Plural::Other] = std::move(item);
- break;
- }
- }
- return plural;
+std::unique_ptr<Plural> BinaryResourceParser::parsePlural(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const ResTable_map_entry* map) {
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const ResTable_map& mapEntry : map) {
+ std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
+ if (!item) {
+ return {};
+ }
+
+ switch (util::deviceToHost32(mapEntry.name.ident)) {
+ case ResTable_map::ATTR_ZERO:
+ plural->values[Plural::Zero] = std::move(item);
+ break;
+ case ResTable_map::ATTR_ONE:
+ plural->values[Plural::One] = std::move(item);
+ break;
+ case ResTable_map::ATTR_TWO:
+ plural->values[Plural::Two] = std::move(item);
+ break;
+ case ResTable_map::ATTR_FEW:
+ plural->values[Plural::Few] = std::move(item);
+ break;
+ case ResTable_map::ATTR_MANY:
+ plural->values[Plural::Many] = std::move(item);
+ break;
+ case ResTable_map::ATTR_OTHER:
+ plural->values[Plural::Other] = std::move(item);
+ break;
+ }
+ }
+ return plural;
}
-} // namespace aapt
+} // namespace aapt