From 1ab598f46c3ff520a67f9d80194847741f3467ab Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Fri, 14 Aug 2015 14:26:04 -0700 Subject: AAPT2: Separate out the various steps An early refactor. Some ideas became clearer as development continued. Now the various phases are much clearer and more easily reusable. Also added a ton of tests! Change-Id: Ic8f0a70c8222370352e63533b329c40457c0903e --- tools/aapt2/ResourceTable.cpp | 467 ++++++++++++++++++++---------------------- 1 file changed, 221 insertions(+), 246 deletions(-) (limited to 'tools/aapt2/ResourceTable.cpp') diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index c93ecc768022..a1e7d36d91d9 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -15,11 +15,11 @@ */ #include "ConfigDescription.h" -#include "Logger.h" #include "NameMangler.h" #include "ResourceTable.h" #include "ResourceValues.h" -#include "Util.h" +#include "ValueVisitor.h" +#include "util/Util.h" #include #include @@ -37,65 +37,109 @@ static bool lessThanType(const std::unique_ptr& lhs, Resource return lhs->type < rhs; } -static bool lessThanEntry(const std::unique_ptr& lhs, const StringPiece16& rhs) { +template +static bool lessThanStructWithName(const std::unique_ptr& lhs, + const StringPiece16& rhs) { return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; } -ResourceTable::ResourceTable() : mPackageId(kUnsetPackageId) { - // Make sure attrs always have type ID 1. - findOrCreateType(ResourceType::kAttr)->typeId = 1; +ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& name) { + const auto last = packages.end(); + auto iter = std::lower_bound(packages.begin(), last, name, + lessThanStructWithName); + if (iter != last && name == (*iter)->name) { + return iter->get(); + } + return nullptr; } -std::unique_ptr& ResourceTable::findOrCreateType(ResourceType type) { - auto last = mTypes.end(); - auto iter = std::lower_bound(mTypes.begin(), last, type, lessThanType); - if (iter != last) { - if ((*iter)->type == type) { - return *iter; +ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) { + for (auto& package : packages) { + if (package->id && package->id.value() == id) { + return package.get(); } } - return *mTypes.emplace(iter, new ResourceTableType{ type }); + return nullptr; } -std::unique_ptr& ResourceTable::findOrCreateEntry( - std::unique_ptr& type, const StringPiece16& name) { - auto last = type->entries.end(); - auto iter = std::lower_bound(type->entries.begin(), last, name, lessThanEntry); - if (iter != last) { - if (name == (*iter)->name) { - return *iter; - } +ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, uint8_t id) { + ResourceTablePackage* package = findOrCreatePackage(name); + if (!package->id) { + package->id = id; + return package; } - return *type->entries.emplace(iter, new ResourceEntry{ name }); + + if (package->id.value() == id) { + return package; + } + return nullptr; } -struct IsAttributeVisitor : ConstValueVisitor { - bool isAttribute = false; +ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece16& name) { + const auto last = packages.end(); + auto iter = std::lower_bound(packages.begin(), last, name, + lessThanStructWithName); + if (iter != last && name == (*iter)->name) { + return iter->get(); + } - void visit(const Attribute&, ValueVisitorArgs&) override { - isAttribute = true; + std::unique_ptr newPackage = util::make_unique(); + newPackage->name = name.toString(); + return packages.emplace(iter, std::move(newPackage))->get(); +} + +ResourceTableType* ResourceTablePackage::findType(ResourceType type) { + const auto last = types.end(); + auto iter = std::lower_bound(types.begin(), last, type, lessThanType); + if (iter != last && (*iter)->type == type) { + return iter->get(); } + return nullptr; +} - operator bool() { - return isAttribute; +ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) { + const auto last = types.end(); + auto iter = std::lower_bound(types.begin(), last, type, lessThanType); + if (iter != last && (*iter)->type == type) { + return iter->get(); } -}; + return types.emplace(iter, new ResourceTableType{ type })->get(); +} + +ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) { + const auto last = entries.end(); + auto iter = std::lower_bound(entries.begin(), last, name, + lessThanStructWithName); + if (iter != last && name == (*iter)->name) { + return iter->get(); + } + return nullptr; +} + +ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece16& name) { + auto last = entries.end(); + auto iter = std::lower_bound(entries.begin(), last, name, + lessThanStructWithName); + if (iter != last && name == (*iter)->name) { + return iter->get(); + } + return entries.emplace(iter, new ResourceEntry{ name })->get(); +} /** * The default handler for collisions. A return value of -1 means keep the * existing value, 0 means fail, and +1 means take the incoming value. */ -static int defaultCollisionHandler(const Value& existing, const Value& incoming) { - IsAttributeVisitor existingIsAttr, incomingIsAttr; - existing.accept(existingIsAttr, {}); - incoming.accept(incomingIsAttr, {}); +int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) { + Attribute* existingAttr = valueCast(existing); + Attribute* incomingAttr = valueCast(incoming); - if (!incomingIsAttr) { - if (incoming.isWeak()) { + if (!incomingAttr) { + if (incoming->isWeak()) { // We're trying to add a weak resource but a resource // already exists. Keep the existing. return -1; - } else if (existing.isWeak()) { + } else if (existing->isWeak()) { // Override the weak resource with the new strong resource. return 1; } @@ -104,8 +148,8 @@ static int defaultCollisionHandler(const Value& existing, const Value& incoming) return 0; } - if (!existingIsAttr) { - if (existing.isWeak()) { + if (!existingAttr) { + if (existing->isWeak()) { // The existing value is not an attribute and it is weak, // so take the incoming attribute value. return 1; @@ -115,27 +159,27 @@ static int defaultCollisionHandler(const Value& existing, const Value& incoming) return 0; } + assert(incomingAttr && existingAttr); + // // Attribute specific handling. At this point we know both // values are attributes. Since we can declare and define // attributes all-over, we do special handling to see // which definition sticks. // - const Attribute& existingAttr = static_cast(existing); - const Attribute& incomingAttr = static_cast(incoming); - if (existingAttr.typeMask == incomingAttr.typeMask) { + if (existingAttr->typeMask == incomingAttr->typeMask) { // The two attributes are both DECLs, but they are plain attributes // with the same formats. // Keep the strongest one. - return existingAttr.isWeak() ? 1 : -1; + return existingAttr->isWeak() ? 1 : -1; } - if (existingAttr.isWeak() && existingAttr.typeMask == android::ResTable_map::TYPE_ANY) { + if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) { // Any incoming attribute is better than this. return 1; } - if (incomingAttr.isWeak() && incomingAttr.typeMask == android::ResTable_map::TYPE_ANY) { + if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) { // The incoming attribute may be a USE instead of a DECL. // Keep the existing attribute. return -1; @@ -147,180 +191,183 @@ static constexpr const char16_t* kValidNameChars = u"._-"; static constexpr const char16_t* kValidNameMangledChars = u"._-$"; bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config, - const SourceLine& source, std::unique_ptr value) { - return addResourceImpl(name, ResourceId{}, config, source, std::move(value), kValidNameChars); + const Source& source, std::unique_ptr value, + IDiagnostics* diag) { + return addResourceImpl(name, ResourceId{}, config, source, std::move(value), kValidNameChars, + diag); } bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId, - const ConfigDescription& config, const SourceLine& source, - std::unique_ptr value) { - return addResourceImpl(name, resId, config, source, std::move(value), kValidNameChars); + const ConfigDescription& config, const Source& source, + std::unique_ptr value, IDiagnostics* diag) { + return addResourceImpl(name, resId, config, source, std::move(value), kValidNameChars, diag); +} + +bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config, + const Source& source, const StringPiece16& path, + IDiagnostics* diag) { + return addResourceImpl(name, ResourceId{}, config, source, + util::make_unique(stringPool.makeRef(path)), + kValidNameChars, diag); } bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config, - const SourceLine& source, - std::unique_ptr value) { + const Source& source, + std::unique_ptr value, + IDiagnostics* diag) { return addResourceImpl(name, ResourceId{}, config, source, std::move(value), - kValidNameMangledChars); + kValidNameMangledChars, diag); } bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId, - const ConfigDescription& config, const SourceLine& source, - std::unique_ptr value, const char16_t* validChars) { - if (!name.package.empty() && name.package != mPackage) { - Logger::error(source) - << "resource '" - << name - << "' has incompatible package. Must be '" - << mPackage - << "'." - << std::endl; + const ConfigDescription& config, const Source& source, + std::unique_ptr value, const char16_t* validChars, + IDiagnostics* diag) { + auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); + if (badCharIter != name.entry.end()) { + diag->error(DiagMessage(source) + << "resource '" + << name + << "' has invalid entry name '" + << name.entry + << "'. Invalid character '" + << StringPiece16(badCharIter, 1) + << "'"); return false; } - auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); - if (badCharIter != name.entry.end()) { - Logger::error(source) - << "resource '" - << name - << "' has invalid entry name '" - << name.entry - << "'. Invalid character '" - << StringPiece16(badCharIter, 1) - << "'." - << std::endl; + ResourceTablePackage* package = findOrCreatePackage(name.package); + if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { + diag->error(DiagMessage(source) + << "trying to add resource '" + << name + << "' with ID " + << resId + << " but package '" + << package->name + << "' already has ID " + << std::hex << (int) package->id.value() << std::dec); return false; } - std::unique_ptr& type = findOrCreateType(name.type); - if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && - type->typeId != resId.typeId()) { - Logger::error(source) - << "trying to add resource '" - << name - << "' with ID " - << resId - << " but type '" - << type->type - << "' already has ID " - << std::hex << type->typeId << std::dec - << "." - << std::endl; + ResourceTableType* type = package->findOrCreateType(name.type); + if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { + diag->error(DiagMessage(source) + << "trying to add resource '" + << name + << "' with ID " + << resId + << " but type '" + << type->type + << "' already has ID " + << std::hex << (int) type->id.value() << std::dec); return false; } - std::unique_ptr& entry = findOrCreateEntry(type, name.entry); - if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && - entry->entryId != resId.entryId()) { - Logger::error(source) - << "trying to add resource '" - << name - << "' with ID " - << resId - << " but resource already has ID " - << ResourceId(mPackageId, type->typeId, entry->entryId) - << "." - << std::endl; + ResourceEntry* entry = type->findOrCreateEntry(name.entry); + if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { + diag->error(DiagMessage(source) + << "trying to add resource '" + << name + << "' with ID " + << resId + << " but resource already has ID " + << ResourceId(package->id.value(), type->id.value(), entry->id.value())); return false; } - const auto endIter = std::end(entry->values); - auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs); + const auto endIter = entry->values.end(); + auto iter = std::lower_bound(entry->values.begin(), endIter, config, compareConfigs); if (iter == endIter || iter->config != config) { // This resource did not exist before, add it. entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) }); } else { - int collisionResult = defaultCollisionHandler(*iter->value, *value); + int collisionResult = resolveValueCollision(iter->value.get(), value.get()); if (collisionResult > 0) { // Take the incoming value. *iter = ResourceConfigValue{ config, source, {}, std::move(value) }; } else if (collisionResult == 0) { - Logger::error(source) - << "duplicate value for resource '" << name << "' " - << "with config '" << iter->config << "'." - << std::endl; - - Logger::error(iter->source) - << "resource previously defined here." - << std::endl; + diag->error(DiagMessage(source) + << "duplicate value for resource '" << name << "' " + << "with config '" << iter->config << "'"); + diag->error(DiagMessage(iter->source) + << "resource previously defined here"); return false; } } if (resId.isValid()) { - type->typeId = resId.typeId(); - entry->entryId = resId.entryId(); + package->id = resId.packageId(); + type->id = resId.typeId(); + entry->id = resId.entryId(); } return true; } bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId, - const SourceLine& source) { - return markPublicImpl(name, resId, source, kValidNameChars); + const Source& source, IDiagnostics* diag) { + return markPublicImpl(name, resId, source, kValidNameChars, diag); } bool ResourceTable::markPublicAllowMangled(const ResourceNameRef& name, const ResourceId resId, - const SourceLine& source) { - return markPublicImpl(name, resId, source, kValidNameMangledChars); + const Source& source, IDiagnostics* diag) { + return markPublicImpl(name, resId, source, kValidNameMangledChars, diag); } bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId resId, - const SourceLine& source, const char16_t* validChars) { - if (!name.package.empty() && name.package != mPackage) { - Logger::error(source) - << "resource '" - << name - << "' has incompatible package. Must be '" - << mPackage - << "'." - << std::endl; + const Source& source, const char16_t* validChars, + IDiagnostics* diag) { + auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); + if (badCharIter != name.entry.end()) { + diag->error(DiagMessage(source) + << "resource '" + << name + << "' has invalid entry name '" + << name.entry + << "'. Invalid character '" + << StringPiece16(badCharIter, 1) + << "'"); return false; } - auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); - if (badCharIter != name.entry.end()) { - Logger::error(source) - << "resource '" - << name - << "' has invalid entry name '" - << name.entry - << "'. Invalid character '" - << StringPiece16(badCharIter, 1) - << "'." - << std::endl; + ResourceTablePackage* package = findOrCreatePackage(name.package); + if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { + diag->error(DiagMessage(source) + << "trying to add resource '" + << name + << "' with ID " + << resId + << " but package '" + << package->name + << "' already has ID " + << std::hex << (int) package->id.value() << std::dec); return false; } - std::unique_ptr& type = findOrCreateType(name.type); - if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && - type->typeId != resId.typeId()) { - Logger::error(source) - << "trying to make resource '" - << name - << "' public with ID " - << resId - << " but type '" - << type->type - << "' already has ID " - << std::hex << type->typeId << std::dec - << "." - << std::endl; + ResourceTableType* type = package->findOrCreateType(name.type); + if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { + diag->error(DiagMessage(source) + << "trying to add resource '" + << name + << "' with ID " + << resId + << " but type '" + << type->type + << "' already has ID " + << std::hex << (int) type->id.value() << std::dec); return false; } - std::unique_ptr& entry = findOrCreateEntry(type, name.entry); - if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && - entry->entryId != resId.entryId()) { - Logger::error(source) - << "trying to make resource '" - << name - << "' public with ID " - << resId - << " but resource already has ID " - << ResourceId(mPackageId, type->typeId, entry->entryId) - << "." - << std::endl; + ResourceEntry* entry = type->findOrCreateEntry(name.entry); + if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { + diag->error(DiagMessage(source) + << "trying to add resource '" + << name + << "' with ID " + << resId + << " but resource already has ID " + << ResourceId(package->id.value(), type->id.value(), entry->id.value())); return false; } @@ -329,102 +376,30 @@ bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId entry->publicStatus.source = source; if (resId.isValid()) { - type->typeId = resId.typeId(); - entry->entryId = resId.entryId(); - } - return true; -} - -bool ResourceTable::merge(ResourceTable&& other) { - const bool mangleNames = mPackage != other.getPackage(); - std::u16string mangledName; - - for (auto& otherType : other) { - std::unique_ptr& type = findOrCreateType(otherType->type); - if (otherType->publicStatus.isPublic) { - if (type->publicStatus.isPublic && type->typeId != otherType->typeId) { - Logger::error() << "can not merge type '" << type->type - << "': conflicting public IDs " - << "(" << type->typeId << " vs " << otherType->typeId << ")." - << std::endl; - return false; - } - type->publicStatus = std::move(otherType->publicStatus); - type->typeId = otherType->typeId; - } - - for (auto& otherEntry : otherType->entries) { - const std::u16string* nameToAdd = &otherEntry->name; - if (mangleNames) { - mangledName = otherEntry->name; - NameMangler::mangle(other.getPackage(), &mangledName); - nameToAdd = &mangledName; - } - - std::unique_ptr& entry = findOrCreateEntry(type, *nameToAdd); - if (otherEntry->publicStatus.isPublic) { - if (entry->publicStatus.isPublic && entry->entryId != otherEntry->entryId) { - Logger::error() << "can not merge entry '" << type->type << "/" << entry->name - << "': conflicting public IDs " - << "(" << entry->entryId << " vs " << entry->entryId << ")." - << std::endl; - return false; - } - entry->publicStatus = std::move(otherEntry->publicStatus); - entry->entryId = otherEntry->entryId; - } - - for (ResourceConfigValue& otherValue : otherEntry->values) { - auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), - otherValue.config, compareConfigs); - if (iter != entry->values.end() && iter->config == otherValue.config) { - int collisionResult = defaultCollisionHandler(*iter->value, *otherValue.value); - if (collisionResult > 0) { - // Take the incoming value. - iter->source = std::move(otherValue.source); - iter->comment = std::move(otherValue.comment); - iter->value = std::unique_ptr(otherValue.value->clone(&mValuePool)); - } else if (collisionResult == 0) { - ResourceNameRef resourceName = { mPackage, type->type, entry->name }; - Logger::error(otherValue.source) - << "resource '" << resourceName << "' has a conflicting value for " - << "configuration (" << otherValue.config << ")." - << std::endl; - Logger::note(iter->source) << "originally defined here." << std::endl; - return false; - } - } else { - entry->values.insert(iter, ResourceConfigValue{ - otherValue.config, - std::move(otherValue.source), - std::move(otherValue.comment), - std::unique_ptr(otherValue.value->clone(&mValuePool)), - }); - } - } - } + package->id = resId.packageId(); + type->id = resId.typeId(); + entry->id = resId.entryId(); } return true; } -std::tuple -ResourceTable::findResource(const ResourceNameRef& name) const { - if (name.package != mPackage) { +Maybe +ResourceTable::findResource(const ResourceNameRef& name) { + ResourceTablePackage* package = findPackage(name.package); + if (!package) { return {}; } - auto iter = std::lower_bound(mTypes.begin(), mTypes.end(), name.type, lessThanType); - if (iter == mTypes.end() || (*iter)->type != name.type) { + ResourceTableType* type = package->findType(name.type); + if (!type) { return {}; } - const std::unique_ptr& type = *iter; - auto iter2 = std::lower_bound(type->entries.begin(), type->entries.end(), name.entry, - lessThanEntry); - if (iter2 == type->entries.end() || name.entry != (*iter2)->name) { + ResourceEntry* entry = type->findEntry(name.entry); + if (!entry) { return {}; } - return std::make_tuple(iter->get(), iter2->get()); + return SearchResult{ package, type, entry }; } } // namespace aapt -- cgit v1.2.3-59-g8ed1b From 9ba47d813075fcb05c5e1532c137c93b394631cb Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Tue, 13 Oct 2015 11:37:10 -0700 Subject: Filter products during compile phase Unfortunately there is no good way to deal with products in the link phase. Products are like preprocessor defines in that they are processed early and change the composition of the compiled unit. Change-Id: I6d5e15ef60d29df8e83e059ba857c09333993779 --- tools/aapt2/Diagnostics.h | 13 +- tools/aapt2/Resource.h | 7 + tools/aapt2/ResourceParser.cpp | 306 +++++++++++++++---------- tools/aapt2/ResourceParser.h | 36 +-- tools/aapt2/ResourceParser_test.cpp | 46 +++- tools/aapt2/ResourceTable.cpp | 10 +- tools/aapt2/ResourceTable.h | 8 +- tools/aapt2/compile/Compile.cpp | 19 +- tools/aapt2/flatten/TableFlattener.cpp | 270 ++++++++++++---------- tools/aapt2/link/Link.cpp | 12 +- tools/aapt2/link/TableMerger.cpp | 2 +- tools/aapt2/test/Common.h | 1 + tools/aapt2/unflatten/BinaryResourceParser.cpp | 33 +-- 13 files changed, 473 insertions(+), 290 deletions(-) (limited to 'tools/aapt2/ResourceTable.cpp') diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h index d20ae1b92191..7ea26b3ed0df 100644 --- a/tools/aapt2/Diagnostics.h +++ b/tools/aapt2/Diagnostics.h @@ -51,12 +51,6 @@ public: mMessage << value; return *this; } -/* - template DiagMessage& operator<<( - const ::std::function<::std::ostream&(::std::ostream&)>& f) { - f(mMessage); - return *this; - }*/ DiagMessageActual build() const { return DiagMessageActual{ mSource, mMessage.str() }; @@ -72,6 +66,8 @@ struct IDiagnostics { }; struct StdErrDiagnostics : public IDiagnostics { + size_t mNumErrors = 0; + void emit(const DiagMessage& msg, const char* tag) { DiagMessageActual actual = msg.build(); if (!actual.source.path.empty()) { @@ -81,7 +77,10 @@ struct StdErrDiagnostics : public IDiagnostics { } void error(const DiagMessage& msg) override { - emit(msg, "error: "); + if (mNumErrors < 20) { + emit(msg, "error: "); + } + mNumErrors++; } void warn(const DiagMessage& msg) override { diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index 31fe298670ae..7ef18973d89b 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -78,6 +78,9 @@ struct ResourceName { ResourceType type; std::u16string entry; + ResourceName() = default; + ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e); + bool isValid() const; bool operator<(const ResourceName& rhs) const; bool operator==(const ResourceName& rhs) const; @@ -226,6 +229,10 @@ inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val) // ResourceName implementation. // +inline ResourceName::ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e) : + package(p.toString()), type(t), entry(e.toString()) { +} + inline bool ResourceName::isValid() const { return !package.empty() && !entry.empty(); } diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 5e5fc5338e29..63629f0d6c10 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -49,8 +49,9 @@ static Maybe findNonEmptyAttribute(XmlPullParser* parser, } ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source, - const ConfigDescription& config) : - mDiag(diag), mTable(table), mSource(source), mConfig(config) { + const ConfigDescription& config, + const ResourceParserOptions& options) : + mDiag(diag), mTable(table), mSource(source), mConfig(config), mOptions(options) { } /** @@ -157,7 +158,62 @@ bool ResourceParser::parse(XmlPullParser* parser) { return !error; } +static bool shouldStripResource(XmlPullParser* parser, const Maybe productToMatch) { + assert(parser->getEvent() == XmlPullParser::Event::kStartElement); + + if (Maybe maybeProduct = findNonEmptyAttribute(parser, u"product")) { + if (!productToMatch) { + if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") { + // We didn't specify a product and this is not a default product, so skip. + return true; + } + } else { + if (productToMatch && maybeProduct.value() != productToMatch.value()) { + // We specified a product, but they don't match. + return true; + } + } + } + return false; +} + +/** + * A parsed resource ready to be added to the ResourceTable. + */ +struct ParsedResource { + ResourceName name; + Source source; + ResourceId id; + bool markPublic = false; + std::unique_ptr value; + std::list childResources; +}; + +// Recursively adds resources to the ResourceTable. +static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config, + IDiagnostics* diag, ParsedResource* res) { + if (res->markPublic && !table->markPublic(res->name, res->id, res->source, diag)) { + return false; + } + + if (!res->value) { + return true; + } + + if (!table->addResource(res->name, res->id, config, res->source, std::move(res->value), diag)) { + return false; + } + + bool error = false; + for (ParsedResource& child : res->childResources) { + error |= !addResourcesToTable(table, config, diag, &child); + } + return !error; +} + bool ResourceParser::parseResources(XmlPullParser* parser) { + std::set strippedResources; + bool error = false; std::u16string comment; const size_t depth = parser->getDepth(); @@ -198,9 +254,8 @@ bool ResourceParser::parseResources(XmlPullParser* parser) { continue; } - // Copy because our iterator will go out of scope when - // we parse more XML. - std::u16string name = maybeName.value().toString(); + // Check if we should skip this product. + const bool stripResource = shouldStripResource(parser, mOptions.product); if (elementName == u"item") { // Items simply have their type encoded in the type attribute. @@ -214,48 +269,85 @@ bool ResourceParser::parseResources(XmlPullParser* parser) { } } - if (elementName == u"id") { - error |= !mTable->addResource(ResourceNameRef{ {}, ResourceType::kId, name }, - {}, mSource.withLine(parser->getLineNumber()), - util::make_unique(), mDiag); + ParsedResource parsedResource; + parsedResource.name.entry = maybeName.value().toString(); + parsedResource.source = mSource.withLine(parser->getLineNumber()); + bool result = true; + if (elementName == u"id") { + parsedResource.name.type = ResourceType::kId; + parsedResource.value = util::make_unique(); } else if (elementName == u"string") { - error |= !parseString(parser, ResourceNameRef{ {}, ResourceType::kString, name }); + parsedResource.name.type = ResourceType::kString; + result = parseString(parser, &parsedResource); } else if (elementName == u"color") { - error |= !parseColor(parser, ResourceNameRef{ {}, ResourceType::kColor, name }); + parsedResource.name.type = ResourceType::kColor; + result = parseColor(parser, &parsedResource); } else if (elementName == u"drawable") { - error |= !parseColor(parser, ResourceNameRef{ {}, ResourceType::kDrawable, name }); + parsedResource.name.type = ResourceType::kDrawable; + result = parseColor(parser, &parsedResource); } else if (elementName == u"bool") { - error |= !parsePrimitive(parser, ResourceNameRef{ {}, ResourceType::kBool, name }); + parsedResource.name.type = ResourceType::kBool; + result = parsePrimitive(parser, &parsedResource); } else if (elementName == u"integer") { - error |= !parsePrimitive(parser, ResourceNameRef{ {}, ResourceType::kInteger, name }); + parsedResource.name.type = ResourceType::kInteger; + result = parsePrimitive(parser, &parsedResource); } else if (elementName == u"dimen") { - error |= !parsePrimitive(parser, ResourceNameRef{ {}, ResourceType::kDimen, name }); + parsedResource.name.type = ResourceType::kDimen; + result = parsePrimitive(parser, &parsedResource); } else if (elementName == u"style") { - error |= !parseStyle(parser, ResourceNameRef{ {}, ResourceType::kStyle, name }); + parsedResource.name.type = ResourceType::kStyle; + result = parseStyle(parser, &parsedResource); } else if (elementName == u"plurals") { - error |= !parsePlural(parser, ResourceNameRef{ {}, ResourceType::kPlurals, name }); + parsedResource.name.type = ResourceType::kPlurals; + result = parsePlural(parser, &parsedResource); } else if (elementName == u"array") { - error |= !parseArray(parser, ResourceNameRef{ {}, ResourceType::kArray, name }, - android::ResTable_map::TYPE_ANY); + parsedResource.name.type = ResourceType::kArray; + result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_ANY); } else if (elementName == u"string-array") { - error |= !parseArray(parser, ResourceNameRef{ {}, ResourceType::kArray, name }, - android::ResTable_map::TYPE_STRING); + parsedResource.name.type = ResourceType::kArray; + result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_STRING); } else if (elementName == u"integer-array") { - error |= !parseArray(parser, ResourceNameRef{ {}, ResourceType::kArray, name }, - android::ResTable_map::TYPE_INTEGER); - } else if (elementName == u"public") { - error |= !parsePublic(parser, name); + parsedResource.name.type = ResourceType::kIntegerArray; + result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_INTEGER); } else if (elementName == u"declare-styleable") { - error |= !parseDeclareStyleable(parser, - ResourceNameRef{ {}, ResourceType::kStyleable, name }); + parsedResource.name.type = ResourceType::kStyleable; + result = parseDeclareStyleable(parser, &parsedResource); } else if (elementName == u"attr") { - error |= !parseAttr(parser, ResourceNameRef{ {}, ResourceType::kAttr, name }); + parsedResource.name.type = ResourceType::kAttr; + result = parseAttr(parser, &parsedResource); + } else if (elementName == u"public") { + result = parsePublic(parser, &parsedResource); } else { mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber())) << "unknown resource type '" << elementName << "'"); } + + if (result) { + // We successfully parsed the resource. + + if (stripResource) { + // Record that we stripped out this resource name. + // We will check that at least one variant of this resource was included. + strippedResources.insert(parsedResource.name); + } else { + error |= !addResourcesToTable(mTable, mConfig, mDiag, &parsedResource); + } + } else { + error = true; + } } + + // Check that we included at least one variant of each stripped resource. + for (const ResourceName& strippedResource : strippedResources) { + if (!mTable->findResource(strippedResource)) { + // Failed to find the resource. + mDiag->error(DiagMessage(mSource) << "resource '" << strippedResource << "' " + "was filtered out but no product variant remains"); + error = true; + } + } + return !error; } @@ -322,53 +414,43 @@ std::unique_ptr ResourceParser::parseXml(XmlPullParser* parser, uint32_t t return {}; } -bool ResourceParser::parseString(XmlPullParser* parser, const ResourceNameRef& resourceName) { +bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); // TODO(adamlesinski): Read "untranslateable" attribute. - if (Maybe maybeProduct = findAttribute(parser, u"product")) { - if (maybeProduct.value() != u"default" && maybeProduct.value() != u"phone") { - // TODO(adamlesinski): Actually match product. - return true; - } - } - - std::unique_ptr processedItem = parseXml(parser, android::ResTable_map::TYPE_STRING, - kNoRawString); - if (!processedItem) { + outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString); + if (!outResource->value) { mDiag->error(DiagMessage(source) << "not a valid string"); return false; } - return mTable->addResource(resourceName, mConfig, source, std::move(processedItem), - mDiag); + return true; } -bool ResourceParser::parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName) { +bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); - std::unique_ptr item = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString); - if (!item) { + outResource->value = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString); + if (!outResource->value) { mDiag->error(DiagMessage(source) << "invalid color"); return false; } - return mTable->addResource(resourceName, mConfig, source, std::move(item), - mDiag); + return true; } -bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName) { +bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); uint32_t typeMask = 0; - switch (resourceName.type) { + switch (outResource->name.type) { case ResourceType::kInteger: typeMask |= android::ResTable_map::TYPE_INTEGER; break; case ResourceType::kDimen: typeMask |= android::ResTable_map::TYPE_DIMENSION - | android::ResTable_map::TYPE_FLOAT - | android::ResTable_map::TYPE_FRACTION; + | android::ResTable_map::TYPE_FLOAT + | android::ResTable_map::TYPE_FRACTION; break; case ResourceType::kBool: @@ -380,16 +462,15 @@ bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef break; } - std::unique_ptr item = parseXml(parser, typeMask, kNoRawString); - if (!item) { - mDiag->error(DiagMessage(source) << "invalid " << resourceName.type); + outResource->value = parseXml(parser, typeMask, kNoRawString); + if (!outResource->value) { + mDiag->error(DiagMessage(source) << "invalid " << outResource->name.type); return false; } - return mTable->addResource(resourceName, mConfig, source, std::move(item), - mDiag); + return true; } -bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& name) { +bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); Maybe maybeType = findNonEmptyAttribute(parser, u"type"); @@ -405,27 +486,28 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& nam return false; } - ResourceNameRef resourceName { {}, *parsedType, name }; - ResourceId resourceId; + outResource->name.type = *parsedType; if (Maybe maybeId = findNonEmptyAttribute(parser, u"id")) { android::Res_value val; bool result = android::ResTable::stringToInt(maybeId.value().data(), maybeId.value().size(), &val); - resourceId.id = val.data; + ResourceId resourceId(val.data); if (!result || !resourceId.isValid()) { mDiag->error(DiagMessage(source) << "invalid resource ID '" << maybeId.value() << "' in "); return false; } + outResource->id = resourceId; } if (*parsedType == ResourceType::kId) { // An ID marked as public is also the definition of an ID. - mTable->addResource(resourceName, {}, source, util::make_unique(), - mDiag); + outResource->value = util::make_unique(); } - return mTable->markPublic(resourceName, resourceId, source, mDiag); + + outResource->markPublic = true; + return true; } static uint32_t parseFormatType(const StringPiece16& piece) { @@ -455,20 +537,13 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) { return mask; } -bool ResourceParser::parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName) { - const Source source = mSource.withLine(parser->getLineNumber()); - ResourceName actualName = resourceName.toResourceName(); - std::unique_ptr attr = parseAttrImpl(parser, &actualName, false); - if (!attr) { - return false; - } - return mTable->addResource(actualName, mConfig, source, std::move(attr), - mDiag); + +bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) { + outResource->source = mSource.withLine(parser->getLineNumber()); + return parseAttrImpl(parser, outResource, false); } -std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, - ResourceName* resourceName, - bool weak) { +bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak) { uint32_t typeMask = 0; Maybe maybeFormat = findAttribute(parser, u"format"); @@ -477,7 +552,7 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, if (typeMask == 0) { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) << "invalid attribute format '" << maybeFormat.value() << "'"); - return {}; + return false; } } @@ -486,10 +561,10 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, // No format attribute is allowed. if (weak && !maybeFormat) { StringPiece16 package, type, name; - ResourceUtils::extractResourceName(resourceName->entry, &package, &type, &name); + ResourceUtils::extractResourceName(outResource->name.entry, &package, &type, &name); if (type.empty() && !package.empty()) { - resourceName->package = package.toString(); - resourceName->entry = name.toString(); + outResource->name.package = package.toString(); + outResource->name.entry = name.toString(); } } @@ -526,14 +601,12 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, } if (Maybe s = parseEnumOrFlagItem(parser, elementName)) { - if (mTable->addResource(s.value().symbol.name.value(), mConfig, - mSource.withLine(parser->getLineNumber()), - util::make_unique(), - mDiag)) { - items.push_back(std::move(s.value())); - } else { - error = true; - } + ParsedResource childResource; + childResource.name = s.value().symbol.name.value(); + childResource.source = mSource.withLine(parser->getLineNumber()); + childResource.value = util::make_unique(); + outResource->childResources.push_back(std::move(childResource)); + items.push_back(std::move(s.value())); } else { error = true; } @@ -548,13 +621,14 @@ std::unique_ptr ResourceParser::parseAttrImpl(XmlPullParser* parser, } if (error) { - return {}; + return false; } std::unique_ptr attr = util::make_unique(weak); attr->symbols.swap(items); attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY); - return attr; + outResource->value = std::move(attr); + return true; } Maybe ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser, @@ -582,8 +656,8 @@ Maybe ResourceParser::parseEnumOrFlagItem(XmlPullParser* pars } return Attribute::Symbol{ - Reference(ResourceName{ {}, ResourceType::kId, maybeName.value().toString() }), - val.data }; + Reference(ResourceName{ {}, ResourceType::kId, maybeName.value().toString() }), + val.data }; } static Maybe parseXmlAttributeName(StringPiece16 str) { @@ -604,7 +678,7 @@ static Maybe parseXmlAttributeName(StringPiece16 str) { } return ResourceName{ package.toString(), ResourceType::kAttr, - name.empty() ? str.toString() : name.toString() }; + name.empty() ? str.toString() : name.toString() }; } @@ -637,7 +711,7 @@ bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) { return true; } -bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName) { +bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); std::unique_ptr