diff options
author | Adam Lesinski <adamlesinski@google.com> | 2015-11-16 17:35:44 -0800 |
---|---|---|
committer | Adam Lesinski <adamlesinski@google.com> | 2015-11-19 14:46:53 -0800 |
commit | 467f171315f9c2037fcd3eb5edcfabc40671bf7b (patch) | |
tree | 3b14762e3d9e42a660479d0d5722883b391f835b | |
parent | abf83cbe4f63cd76043aab89cd0e08525560fea2 (diff) |
AAPT2: Fail compiling when private symbols are referenced
Also moved some XML specific stuff into its own directory,
and refactored ReferenceLinker a bit.
Change-Id: I912247a82023c1bbf72dc191fbdaf62858cbec0c
54 files changed, 1305 insertions, 764 deletions
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index ec29c3818bdc..d8e0aac678f0 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -58,8 +58,9 @@ sources := \ ResourceValues.cpp \ SdkConstants.cpp \ StringPool.cpp \ - XmlDom.cpp \ - XmlPullParser.cpp + xml/XmlDom.cpp \ + xml/XmlPullParser.cpp \ + xml/XmlUtil.cpp testSources := \ compile/IdAssigner_test.cpp \ @@ -90,8 +91,9 @@ testSources := \ ResourceUtils_test.cpp \ StringPool_test.cpp \ ValueVisitor_test.cpp \ - XmlDom_test.cpp \ - XmlPullParser_test.cpp + xml/XmlDom_test.cpp \ + xml/XmlPullParser_test.cpp \ + xml/XmlUtil_test.cpp toolSources := \ compile/Compile.cpp \ diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index d292f623a33e..02fe59c0a03b 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -19,9 +19,8 @@ #include "ResourceUtils.h" #include "ResourceValues.h" #include "ValueVisitor.h" -#include "XmlPullParser.h" - #include "util/Util.h" +#include "xml/XmlPullParser.h" #include <sstream> @@ -29,26 +28,6 @@ namespace aapt { constexpr const char16_t* sXliffNamespaceUri = u"urn:oasis:names:tc:xliff:document:1.2"; -static Maybe<StringPiece16> findAttribute(XmlPullParser* parser, const StringPiece16& name) { - auto iter = parser->findAttribute(u"", name); - if (iter != parser->endAttributes()) { - return StringPiece16(util::trimWhitespace(iter->value)); - } - return {}; -} - -static Maybe<StringPiece16> findNonEmptyAttribute(XmlPullParser* parser, - const StringPiece16& name) { - auto iter = parser->findAttribute(u"", name); - if (iter != parser->endAttributes()) { - StringPiece16 trimmed = util::trimWhitespace(iter->value); - if (!trimmed.empty()) { - return trimmed; - } - } - return {}; -} - /** * Returns true if the element is <skip> or <eat-comment> and can be safely ignored. */ @@ -65,7 +44,7 @@ ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const S /** * Build a string from XML that converts nested elements into Span objects. */ -bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString, +bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString, StyleString* outStyleString) { std::vector<Span> spanStack; @@ -74,9 +53,9 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou outStyleString->spans.clear(); util::StringBuilder builder; size_t depth = 1; - while (XmlPullParser::isGoodEvent(parser->next())) { - const XmlPullParser::Event event = parser->getEvent(); - if (event == XmlPullParser::Event::kEndElement) { + while (xml::XmlPullParser::isGoodEvent(parser->next())) { + const xml::XmlPullParser::Event event = parser->getEvent(); + if (event == xml::XmlPullParser::Event::kEndElement) { if (!parser->getElementNamespace().empty()) { // We already warned and skipped the start element, so just skip here too continue; @@ -91,11 +70,11 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou outStyleString->spans.push_back(spanStack.back()); spanStack.pop_back(); - } else if (event == XmlPullParser::Event::kText) { + } else if (event == xml::XmlPullParser::Event::kText) { outRawString->append(parser->getText()); builder.append(parser->getText()); - } else if (event == XmlPullParser::Event::kStartElement) { + } else if (event == xml::XmlPullParser::Event::kStartElement) { if (!parser->getElementNamespace().empty()) { if (parser->getElementNamespace() != sXliffNamespaceUri) { // Only warn if this isn't an xliff namespace. @@ -128,7 +107,7 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) }); } - } else if (event == XmlPullParser::Event::kComment) { + } else if (event == xml::XmlPullParser::Event::kComment) { // Skip } else { assert(false); @@ -140,11 +119,11 @@ bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* ou return !error; } -bool ResourceParser::parse(XmlPullParser* parser) { +bool ResourceParser::parse(xml::XmlPullParser* parser) { bool error = false; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - if (parser->getEvent() != XmlPullParser::Event::kStartElement) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) { // Skip comments and text. continue; } @@ -159,7 +138,7 @@ bool ResourceParser::parse(XmlPullParser* parser) { break; }; - if (parser->getEvent() == XmlPullParser::Event::kBadDocument) { + if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) << "xml parser error: " << parser->getLastError()); return false; @@ -167,10 +146,11 @@ bool ResourceParser::parse(XmlPullParser* parser) { return !error; } -static bool shouldStripResource(XmlPullParser* parser, const Maybe<std::u16string> productToMatch) { - assert(parser->getEvent() == XmlPullParser::Event::kStartElement); +static bool shouldStripResource(const xml::XmlPullParser* parser, + const Maybe<std::u16string> productToMatch) { + assert(parser->getEvent() == xml::XmlPullParser::Event::kStartElement); - if (Maybe<StringPiece16> maybeProduct = findNonEmptyAttribute(parser, u"product")) { + if (Maybe<StringPiece16> maybeProduct = xml::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. @@ -229,20 +209,20 @@ static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& c return !error; } -bool ResourceParser::parseResources(XmlPullParser* parser) { +bool ResourceParser::parseResources(xml::XmlPullParser* parser) { std::set<ResourceName> strippedResources; bool error = false; std::u16string comment; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - const XmlPullParser::Event event = parser->getEvent(); - if (event == XmlPullParser::Event::kComment) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + const xml::XmlPullParser::Event event = parser->getEvent(); + if (event == xml::XmlPullParser::Event::kComment) { comment = parser->getComment(); continue; } - if (event == XmlPullParser::Event::kText) { + if (event == xml::XmlPullParser::Event::kText) { if (!util::trimWhitespace(parser->getText()).empty()) { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) << "plain text not allowed here"); @@ -251,7 +231,7 @@ bool ResourceParser::parseResources(XmlPullParser* parser) { continue; } - assert(event == XmlPullParser::Event::kStartElement); + assert(event == xml::XmlPullParser::Event::kStartElement); if (!parser->getElementNamespace().empty()) { // Skip unknown namespace. @@ -266,7 +246,7 @@ bool ResourceParser::parseResources(XmlPullParser* parser) { if (elementName == u"item") { // Items simply have their type encoded in the type attribute. - if (Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type")) { + if (Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type")) { elementName = maybeType.value().toString(); } else { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) @@ -280,7 +260,7 @@ bool ResourceParser::parseResources(XmlPullParser* parser) { parsedResource.source = mSource.withLine(parser->getLineNumber()); parsedResource.comment = std::move(comment); - if (Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name")) { + if (Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name")) { parsedResource.name.entry = maybeName.value().toString(); } else if (elementName != u"public-group") { @@ -403,7 +383,7 @@ enum { * an Item. If allowRawValue is false, nullptr is returned in this * case. */ -std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint32_t typeMask, +std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const uint32_t typeMask, const bool allowRawValue) { const size_t beginXmlLine = parser->getLineNumber(); @@ -432,10 +412,7 @@ std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint if (processedItem) { // Fix up the reference. if (Reference* ref = valueCast<Reference>(processedItem.get())) { - if (Maybe<ResourceName> transformedName = - parser->transformPackage(ref->name.value(), u"")) { - ref->name = std::move(transformedName); - } + transformReferenceFromNamespace(parser, u"", ref); } return processedItem; } @@ -456,11 +433,11 @@ std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, const uint return {}; } -bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); bool formatted = true; - if (Maybe<StringPiece16> formattedAttr = findAttribute(parser, u"formatted")) { + if (Maybe<StringPiece16> formattedAttr = xml::findAttribute(parser, u"formatted")) { if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) { mDiag->error(DiagMessage(source) << "invalid value for 'formatted'. Must be a boolean"); return false; @@ -468,7 +445,7 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou } bool translateable = mOptions.translatable; - if (Maybe<StringPiece16> translateableAttr = findAttribute(parser, u"translatable")) { + if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) { if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { mDiag->error(DiagMessage(source) << "invalid value for 'translatable'. Must be a boolean"); @@ -495,7 +472,7 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou return true; } -bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parseColor(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); outResource->value = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString); @@ -506,7 +483,7 @@ bool ResourceParser::parseColor(XmlPullParser* parser, ParsedResource* outResour return true; } -bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); uint32_t typeMask = 0; @@ -540,10 +517,10 @@ bool ResourceParser::parsePrimitive(XmlPullParser* parser, ParsedResource* outRe return true; } -bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); - Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type"); + Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type"); if (!maybeType) { mDiag->error(DiagMessage(source) << "<public> must have a 'type' attribute"); return false; @@ -558,7 +535,7 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResou outResource->name.type = *parsedType; - if (Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"id")) { + if (Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"id")) { android::Res_value val; bool result = android::ResTable::stringToInt(maybeId.value().data(), maybeId.value().size(), &val); @@ -580,10 +557,10 @@ bool ResourceParser::parsePublic(XmlPullParser* parser, ParsedResource* outResou return true; } -bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); - Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type"); + Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type"); if (!maybeType) { mDiag->error(DiagMessage(source) << "<public-group> must have a 'type' attribute"); return false; @@ -596,7 +573,7 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out return false; } - Maybe<StringPiece16> maybeId = findNonEmptyAttribute(parser, u"first-id"); + Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"first-id"); if (!maybeId) { mDiag->error(DiagMessage(source) << "<public-group> must have a 'first-id' attribute"); return false; @@ -615,11 +592,11 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out std::u16string comment; bool error = false; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - if (parser->getEvent() == XmlPullParser::Event::kComment) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + if (parser->getEvent() == xml::XmlPullParser::Event::kComment) { comment = util::trimWhitespace(parser->getComment()).toString(); continue; - } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) { + } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) { // Skip text. continue; } @@ -628,20 +605,20 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out const std::u16string& elementNamespace = parser->getElementNamespace(); const std::u16string& elementName = parser->getElementName(); if (elementNamespace.empty() && elementName == u"public") { - Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name"); + Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); if (!maybeName) { mDiag->error(DiagMessage(itemSource) << "<public> must have a 'name' attribute"); error = true; continue; } - if (findNonEmptyAttribute(parser, u"id")) { + if (xml::findNonEmptyAttribute(parser, u"id")) { mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>"); error = true; continue; } - if (findNonEmptyAttribute(parser, u"type")) { + if (xml::findNonEmptyAttribute(parser, u"type")) { mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>"); error = true; continue; @@ -666,10 +643,10 @@ bool ResourceParser::parsePublicGroup(XmlPullParser* parser, ParsedResource* out return !error; } -bool ResourceParser::parseSymbol(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); - Maybe<StringPiece16> maybeType = findNonEmptyAttribute(parser, u"type"); + Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type"); if (!maybeType) { mDiag->error(DiagMessage(source) << "<" << parser->getElementName() << "> must have a " "'type' attribute"); @@ -715,17 +692,16 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) { return mask; } - - -bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) { outResource->source = mSource.withLine(parser->getLineNumber()); return parseAttrImpl(parser, outResource, false); } -bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak) { +bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, + bool weak) { uint32_t typeMask = 0; - Maybe<StringPiece16> maybeFormat = findAttribute(parser, u"format"); + Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format"); if (maybeFormat) { typeMask = parseFormatAttribute(maybeFormat.value()); if (typeMask == 0) { @@ -735,18 +711,6 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes } } - // If this is a declaration, the package name may be in the name. Separate these out. - // Eg. <attr name="android:text" /> - // No format attribute is allowed. - if (weak && !maybeFormat) { - StringPiece16 package, type, name; - ResourceUtils::extractResourceName(outResource->name.entry, &package, &type, &name); - if (type.empty() && !package.empty()) { - outResource->name.package = package.toString(); - outResource->name.entry = name.toString(); - } - } - struct SymbolComparator { bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) { return a.symbol.name.value() < b.symbol.name.value(); @@ -758,11 +722,11 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes std::u16string comment; bool error = false; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - if (parser->getEvent() == XmlPullParser::Event::kComment) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + if (parser->getEvent() == xml::XmlPullParser::Event::kComment) { comment = util::trimWhitespace(parser->getComment()).toString(); continue; - } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) { + } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) { // Skip text. continue; } @@ -834,17 +798,17 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes return true; } -Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser, +Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser, const StringPiece16& tag) { const Source source = mSource.withLine(parser->getLineNumber()); - Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name"); + Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); if (!maybeName) { mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">"); return {}; } - Maybe<StringPiece16> maybeValue = findNonEmptyAttribute(parser, u"value"); + Maybe<StringPiece16> maybeValue = xml::findNonEmptyAttribute(parser, u"value"); if (!maybeValue) { mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">"); return {}; @@ -863,12 +827,19 @@ Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(XmlPullParser* pars val.data }; } -static Maybe<ResourceName> parseXmlAttributeName(StringPiece16 str) { +static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) { str = util::trimWhitespace(str); - const char16_t* const start = str.data(); + const char16_t* start = str.data(); const char16_t* const end = start + str.size(); const char16_t* p = start; + Reference ref; + if (p != end && *p == u'*') { + ref.privateReference = true; + start++; + p++; + } + StringPiece16 package; StringPiece16 name; while (p != end) { @@ -880,28 +851,27 @@ static Maybe<ResourceName> parseXmlAttributeName(StringPiece16 str) { p++; } - return ResourceName(package.toString(), ResourceType::kAttr, + ref.name = ResourceName(package.toString(), ResourceType::kAttr, name.empty() ? str.toString() : name.toString()); + return Maybe<Reference>(std::move(ref)); } -bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) { +bool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) { const Source source = mSource.withLine(parser->getLineNumber()); - Maybe<StringPiece16> maybeName = findNonEmptyAttribute(parser, u"name"); + Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); if (!maybeName) { mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute"); return false; } - Maybe<ResourceName> maybeKey = parseXmlAttributeName(maybeName.value()); + Maybe<Reference> maybeKey = parseXmlAttributeName(maybeName.value()); if (!maybeKey) { mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'"); return false; } - if (Maybe<ResourceName> transformedName = parser->transformPackage(maybeKey.value(), u"")) { - maybeKey = std::move(transformedName); - } + transformReferenceFromNamespace(parser, u"", &maybeKey.value()); std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString); if (!value) { @@ -909,15 +879,15 @@ bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) { return false; } - style->entries.push_back(Style::Entry{ Reference(maybeKey.value()), std::move(value) }); + style->entries.push_back(Style::Entry{ std::move(maybeKey.value()), std::move(value) }); return true; } -bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); std::unique_ptr<Style> style = util::make_unique<Style>(); - Maybe<StringPiece16> maybeParent = findAttribute(parser, u"parent"); + Maybe<StringPiece16> maybeParent = xml::findAttribute(parser, u"parent"); if (maybeParent) { // If the parent is empty, we don't have a parent, but we also don't infer either. if (!maybeParent.value().empty()) { @@ -928,10 +898,9 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResour return false; } - if (Maybe<ResourceName> transformedName = - parser->transformPackage(style->parent.value().name.value(), u"")) { - style->parent.value().name = std::move(transformedName); - } + // Transform the namespace prefix to the actual package name, and mark the reference as + // private if appropriate. + transformReferenceFromNamespace(parser, u"", &style->parent.value()); } } else { @@ -940,15 +909,15 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResour size_t pos = styleName.find_last_of(u'.'); if (pos != std::string::npos) { style->parentInferred = true; - style->parent = Reference( - ResourceName({}, ResourceType::kStyle, styleName.substr(0, pos))); + style->parent = Reference(ResourceName({}, ResourceType::kStyle, + styleName.substr(0, pos))); } } bool error = false; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - if (parser->getEvent() != XmlPullParser::Event::kStartElement) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) { // Skip text and comments. continue; } @@ -973,15 +942,15 @@ bool ResourceParser::parseStyle(XmlPullParser* parser, ParsedResource* outResour return true; } -bool ResourceParser::parseArray(XmlPullParser* parser, ParsedResource* outResource, +bool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask) { const Source source = mSource.withLine(parser->getLineNumber()); std::unique_ptr<Array> array = util::make_unique<Array>(); bool error = false; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - if (parser->getEvent() != XmlPullParser::Event::kStartElement) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) { // Skip text and comments. continue; } @@ -1014,14 +983,14 @@ bool ResourceParser::parseArray(XmlPullParser* parser, ParsedResource* outResour return true; } -bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); std::unique_ptr<Plural> plural = util::make_unique<Plural>(); bool error = false; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - if (parser->getEvent() != XmlPullParser::Event::kStartElement) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) { // Skip text and comments. continue; } @@ -1030,16 +999,15 @@ bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResou const std::u16string& elementNamespace = parser->getElementNamespace(); const std::u16string& elementName = parser->getElementName(); if (elementNamespace.empty() && elementName == u"item") { - const auto endAttrIter = parser->endAttributes(); - auto attrIter = parser->findAttribute(u"", u"quantity"); - if (attrIter == endAttrIter || attrIter->value.empty()) { + Maybe<StringPiece16> maybeQuantity = xml::findNonEmptyAttribute(parser, u"quantity"); + if (!maybeQuantity) { mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute " << "'quantity'"); error = true; continue; } - StringPiece16 trimmedQuantity = util::trimWhitespace(attrIter->value); + StringPiece16 trimmedQuantity = util::trimWhitespace(maybeQuantity.value()); size_t index = 0; if (trimmedQuantity == u"zero") { index = Plural::Zero; @@ -1089,7 +1057,7 @@ bool ResourceParser::parsePlural(XmlPullParser* parser, ParsedResource* outResou return true; } -bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource) { +bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource) { const Source source = mSource.withLine(parser->getLineNumber()); std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>(); @@ -1099,11 +1067,11 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource std::u16string comment; bool error = false; const size_t depth = parser->getDepth(); - while (XmlPullParser::nextChildNode(parser, depth)) { - if (parser->getEvent() == XmlPullParser::Event::kComment) { + while (xml::XmlPullParser::nextChildNode(parser, depth)) { + if (parser->getEvent() == xml::XmlPullParser::Event::kComment) { comment = util::trimWhitespace(parser->getComment()).toString(); continue; - } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) { + } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) { // Ignore text. continue; } @@ -1112,17 +1080,29 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource const std::u16string& elementNamespace = parser->getElementNamespace(); const std::u16string& elementName = parser->getElementName(); if (elementNamespace.empty() && elementName == u"attr") { - const auto endAttrIter = parser->endAttributes(); - auto attrIter = parser->findAttribute(u"", u"name"); - if (attrIter == endAttrIter || attrIter->value.empty()) { + Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); + if (!maybeName) { mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute"); error = true; continue; } + // If this is a declaration, the package name may be in the name. Separate these out. + // Eg. <attr name="android:text" /> + Maybe<Reference> maybeRef = parseXmlAttributeName(maybeName.value()); + if (!maybeRef) { + mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '" + << maybeName.value() << "'"); + error = true; + continue; + } + + Reference& childRef = maybeRef.value(); + xml::transformReferenceFromNamespace(parser, u"", &childRef); + // Create the ParsedResource that will add the attribute to the table. ParsedResource childResource; - childResource.name = ResourceName({}, ResourceType::kAttr, attrIter->value); + childResource.name = childRef.name.value(); childResource.source = itemSource; childResource.comment = std::move(comment); @@ -1132,7 +1112,6 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource } // Create the reference to this attribute. - Reference childRef(childResource.name); childRef.setComment(childResource.comment); childRef.setSource(itemSource); styleable->entries.push_back(std::move(childRef)); diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index 18101ee111c9..1150758b930e 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -22,10 +22,9 @@ #include "ResourceTable.h" #include "ResourceValues.h" #include "StringPool.h" -#include "XmlPullParser.h" - #include "util/Maybe.h" #include "util/StringPiece.h" +#include "xml/XmlPullParser.h" #include <memory> @@ -57,7 +56,7 @@ public: ResourceParser(const ResourceParser&) = delete; // No copy. - bool parse(XmlPullParser* parser); + bool parse(xml::XmlPullParser* parser); private: /* @@ -66,7 +65,7 @@ private: * contains the escaped and whitespace trimmed text, while `outRawString` * contains the unescaped text. Returns true on success. */ - bool flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString, + bool flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString, StyleString* outStyleString); /* @@ -75,24 +74,25 @@ private: * If `allowRawValue` is true and the subtree can not be parsed as a regular Item, then a * RawString is returned. Otherwise this returns false; */ - std::unique_ptr<Item> parseXml(XmlPullParser* parser, const uint32_t typeMask, + std::unique_ptr<Item> parseXml(xml::XmlPullParser* parser, const uint32_t typeMask, const bool allowRawValue); - bool parseResources(XmlPullParser* parser); - bool parseString(XmlPullParser* parser, ParsedResource* outResource); - bool parseColor(XmlPullParser* parser, ParsedResource* outResource); - bool parsePrimitive(XmlPullParser* parser, ParsedResource* outResource); - bool parsePublic(XmlPullParser* parser, ParsedResource* outResource); - bool parsePublicGroup(XmlPullParser* parser, ParsedResource* outResource); - bool parseSymbol(XmlPullParser* parser, ParsedResource* outResource); - bool parseAttr(XmlPullParser* parser, ParsedResource* outResource); - bool parseAttrImpl(XmlPullParser* parser, ParsedResource* outResource, bool weak); - Maybe<Attribute::Symbol> parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag); - bool parseStyle(XmlPullParser* parser, ParsedResource* outResource); - bool parseStyleItem(XmlPullParser* parser, Style* style); - bool parseDeclareStyleable(XmlPullParser* parser, ParsedResource* outResource); - bool parseArray(XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask); - bool parsePlural(XmlPullParser* parser, ParsedResource* outResource); + bool parseResources(xml::XmlPullParser* parser); + bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parseColor(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak); + Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser, + const StringPiece16& tag); + bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parseStyleItem(xml::XmlPullParser* parser, Style* style); + bool parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource); + bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask); + bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource); IDiagnostics* mDiag; ResourceTable* mTable; diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index b59eb95e7448..ab16424db709 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -18,9 +18,8 @@ #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" -#include "XmlPullParser.h" - #include "test/Context.h" +#include "xml/XmlPullParser.h" #include <gtest/gtest.h> #include <sstream> @@ -36,7 +35,7 @@ TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) { input << "<attr name=\"foo\"/>" << std::endl; ResourceTable table; ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {}); - XmlPullParser xmlParser(input); + xml::XmlPullParser xmlParser(input); ASSERT_FALSE(parser.parse(&xmlParser)); } @@ -56,7 +55,7 @@ struct ResourceParserTest : public ::testing::Test { parserOptions.product = product; ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {}, parserOptions); - XmlPullParser xmlParser(input); + xml::XmlPullParser xmlParser(input); if (parser.parse(&xmlParser)) { return ::testing::AssertionSuccess(); } @@ -360,6 +359,25 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value()); } +TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) { + std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n" + " <attr name=\"*android:bar\" />\n" + " <attr name=\"privAndroid:bat\" />\n" + "</declare-styleable>"; + ASSERT_TRUE(testParse(input)); + Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); + ASSERT_NE(nullptr, styleable); + ASSERT_EQ(2u, styleable->entries.size()); + + EXPECT_TRUE(styleable->entries[0].privateReference); + AAPT_ASSERT_TRUE(styleable->entries[0].name); + EXPECT_EQ(std::u16string(u"android"), styleable->entries[0].name.value().package); + + EXPECT_TRUE(styleable->entries[1].privateReference); + AAPT_ASSERT_TRUE(styleable->entries[1].name); + EXPECT_EQ(std::u16string(u"android"), styleable->entries[1].name.value().package); +} + TEST_F(ResourceParserTest, ParseArray) { std::string input = "<array name=\"foo\">\n" " <item>@string/ref</item>\n" diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index b1a4c7dee73f..ffe6595cf0b6 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -15,6 +15,7 @@ */ #include "ResourceUtils.h" +#include "flatten/ResourceTypeExtensions.h" #include "util/Util.h" #include <androidfw/ResourceTypes.h> @@ -47,6 +48,42 @@ bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty()); } +bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) { + size_t offset = 0; + bool priv = false; + if (str.data()[0] == u'*') { + priv = true; + offset = 1; + } + + StringPiece16 package; + StringPiece16 type; + StringPiece16 entry; + if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) { + return false; + } + + const ResourceType* parsedType = parseResourceType(type); + if (!parsedType) { + return false; + } + + if (entry.empty()) { + return false; + } + + if (outRef) { + outRef->package = package; + outRef->type = *parsedType; + outRef->entry = entry; + } + + if (outPrivate) { + *outPrivate = priv; + } + return true; +} + bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate, bool* outPrivate) { StringPiece16 trimmedStr(util::trimWhitespace(str)); @@ -61,35 +98,24 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* if (trimmedStr.data()[1] == u'+') { create = true; offset += 1; - } else if (trimmedStr.data()[1] == u'*') { - priv = true; - offset += 1; - } - StringPiece16 package; - StringPiece16 type; - StringPiece16 entry; - if (!extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), - &package, &type, &entry)) { - return false; } - const ResourceType* parsedType = parseResourceType(type); - if (!parsedType) { + ResourceNameRef name; + if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), + &name, &priv)) { return false; } - if (entry.empty()) { + if (create && priv) { return false; } - if (create && *parsedType != ResourceType::kId) { + if (create && name.type != ResourceType::kId) { return false; } if (outRef) { - outRef->package = package; - outRef->type = *parsedType; - outRef->entry = entry; + *outRef = name; } if (outCreate) { diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index 34daa66b1546..f93a4c762706 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -39,6 +39,13 @@ namespace ResourceUtils { bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, StringPiece16* outType, StringPiece16* outEntry); +/** + * Returns true if the string was parsed as a resource name ([*][package:]type/name), with + * `outResource` set to the parsed resource name and `outPrivate` set to true if a '*' prefix + * was present. + */ +bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource, bool* outPrivate); + /* * Returns true if the string was parsed as a reference (@[+][package:]type/name), with * `outReference` set to the parsed reference. diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index 3d2a6e18e2bc..4bbfc32b9b37 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -16,15 +16,30 @@ #include "Resource.h" #include "ResourceUtils.h" - #include "test/Common.h" #include <gtest/gtest.h> namespace aapt { +TEST(ResourceUtilsTest, ParseResourceName) { + ResourceNameRef actual; + bool actualPriv = false; + EXPECT_TRUE(ResourceUtils::parseResourceName(u"android:color/foo", &actual, &actualPriv)); + EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual); + EXPECT_FALSE(actualPriv); + + EXPECT_TRUE(ResourceUtils::parseResourceName(u"color/foo", &actual, &actualPriv)); + EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, u"foo"), actual); + EXPECT_FALSE(actualPriv); + + EXPECT_TRUE(ResourceUtils::parseResourceName(u"*android:color/foo", &actual, &actualPriv)); + EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual); + EXPECT_TRUE(actualPriv); +} + TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) { - ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" }; + ResourceNameRef expected({}, ResourceType::kColor, u"foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; @@ -35,7 +50,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) { } TEST(ResourceUtilsTest, ParseReferenceWithPackage) { - ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" }; + ResourceNameRef expected(u"android", ResourceType::kColor, u"foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; @@ -47,7 +62,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithPackage) { } TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) { - ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" }; + ResourceNameRef expected(u"android", ResourceType::kColor, u"foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; @@ -59,7 +74,7 @@ TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) { } TEST(ResourceUtilsTest, ParseAutoCreateIdReference) { - ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" }; + ResourceNameRef expected(u"android", ResourceType::kId, u"foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; @@ -71,7 +86,7 @@ TEST(ResourceUtilsTest, ParseAutoCreateIdReference) { } TEST(ResourceUtilsTest, ParsePrivateReference) { - ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" }; + ResourceNameRef expected(u"android", ResourceType::kId, u"foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; @@ -111,8 +126,8 @@ TEST(ResourceUtilsTest, FailParseIncompleteReference) { } TEST(ResourceUtilsTest, ParseStyleParentReference) { - const ResourceName kAndroidStyleFooName = { u"android", ResourceType::kStyle, u"foo" }; - const ResourceName kStyleFooName = { {}, ResourceType::kStyle, u"foo" }; + const ResourceName kAndroidStyleFooName(u"android", ResourceType::kStyle, u"foo"); + const ResourceName kStyleFooName({}, ResourceType::kStyle, u"foo"); std::string errStr; Maybe<Reference> ref = ResourceUtils::parseStyleParentReference(u"@android:style/foo", &errStr); diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index 8acff0d39019..5550f192d51a 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -71,27 +71,23 @@ Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type } bool Reference::flatten(android::Res_value* outValue) const { - outValue->dataType = (referenceType == Reference::Type::kResource) - ? android::Res_value::TYPE_REFERENCE - : android::Res_value::TYPE_ATTRIBUTE; + outValue->dataType = (referenceType == Reference::Type::kResource) ? + android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE; outValue->data = util::hostToDevice32(id ? id.value().id : 0); return true; } Reference* Reference::clone(StringPool* /*newPool*/) const { - Reference* ref = new Reference(); - ref->mComment = mComment; - ref->mSource = mSource; - ref->referenceType = referenceType; - ref->name = name; - ref->id = id; - return ref; + return new Reference(*this); } void Reference::print(std::ostream* out) const { *out << "(reference) "; if (referenceType == Reference::Type::kResource) { *out << "@"; + if (privateReference) { + *out << "*"; + } } else { *out << "?"; } @@ -116,10 +112,7 @@ bool Id::flatten(android::Res_value* out) const { } Id* Id::clone(StringPool* /*newPool*/) const { - Id* id = new Id(); - id->mComment = mComment; - id->mSource = mSource; - return id; + return new Id(*this); } void Id::print(std::ostream* out) const { @@ -214,10 +207,7 @@ bool BinaryPrimitive::flatten(android::Res_value* outValue) const { } BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const { - BinaryPrimitive* bp = new BinaryPrimitive(value); - bp->mComment = mComment; - bp->mSource = mSource; - return bp; + return new BinaryPrimitive(*this); } void BinaryPrimitive::print(std::ostream* out) const { @@ -255,12 +245,7 @@ bool Attribute::isWeak() const { } Attribute* Attribute::clone(StringPool* /*newPool*/) const { - Attribute* attr = new Attribute(weak); - attr->mComment = mComment; - attr->mSource = mSource; - attr->typeMask = typeMask; - std::copy(symbols.begin(), symbols.end(), std::back_inserter(attr->symbols)); - return attr; + return new Attribute(*this); } void Attribute::printMask(std::ostream* out) const { @@ -450,11 +435,7 @@ static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Ite } Styleable* Styleable::clone(StringPool* /*newPool*/) const { - Styleable* styleable = new Styleable(); - styleable->mComment = mComment; - styleable->mSource = mSource; - std::copy(entries.begin(), entries.end(), std::back_inserter(styleable->entries)); - return styleable; + return new Styleable(*this); } void Styleable::print(std::ostream* out) const { diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp index 39088bcee140..17a658ee27cf 100644 --- a/tools/aapt2/compile/Compile.cpp +++ b/tools/aapt2/compile/Compile.cpp @@ -19,9 +19,6 @@ #include "Flags.h" #include "ResourceParser.h" #include "ResourceTable.h" -#include "XmlDom.h" -#include "XmlPullParser.h" - #include "compile/IdAssigner.h" #include "compile/Png.h" #include "compile/XmlIdCollector.h" @@ -31,6 +28,8 @@ #include "util/Files.h" #include "util/Maybe.h" #include "util/Util.h" +#include "xml/XmlDom.h" +#include "xml/XmlPullParser.h" #include <fstream> #include <string> @@ -131,7 +130,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options, // Parse the values file from XML. - XmlPullParser xmlParser(fin); + xml::XmlPullParser xmlParser(fin); ResourceParserOptions parserOptions; parserOptions.product = options.product; @@ -191,7 +190,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options, static bool compileXml(IAaptContext* context, const CompileOptions& options, const ResourcePathData& pathData, const std::string& outputPath) { - std::unique_ptr<XmlResource> xmlRes; + std::unique_ptr<xml::XmlResource> xmlRes; { std::ifstream fin(pathData.source.path, std::ifstream::binary); diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp index dfdf710967f9..f40689eaeb47 100644 --- a/tools/aapt2/compile/XmlIdCollector.cpp +++ b/tools/aapt2/compile/XmlIdCollector.cpp @@ -16,9 +16,8 @@ #include "ResourceUtils.h" #include "ResourceValues.h" -#include "XmlDom.h" - #include "compile/XmlIdCollector.h" +#include "xml/XmlDom.h" #include <algorithm> #include <vector> @@ -61,7 +60,7 @@ struct IdCollector : public xml::Visitor { } // namespace -bool XmlIdCollector::consume(IAaptContext* context, XmlResource* xmlRes) { +bool XmlIdCollector::consume(IAaptContext* context, xml::XmlResource* xmlRes) { xmlRes->file.exportedSymbols.clear(); IdCollector collector(&xmlRes->file.exportedSymbols); xmlRes->root->accept(&collector); diff --git a/tools/aapt2/compile/XmlIdCollector.h b/tools/aapt2/compile/XmlIdCollector.h index 96a58f22fab6..1b149449de2c 100644 --- a/tools/aapt2/compile/XmlIdCollector.h +++ b/tools/aapt2/compile/XmlIdCollector.h @@ -18,11 +18,12 @@ #define AAPT_XMLIDCOLLECTOR_H #include "process/IResourceTableConsumer.h" +#include "xml/XmlDom.h" namespace aapt { struct XmlIdCollector : public IXmlResourceConsumer { - bool consume(IAaptContext* context, XmlResource* xmlRes) override; + bool consume(IAaptContext* context, xml::XmlResource* xmlRes) override; }; } // namespace aapt diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp index c703f451f05e..45b7af240abe 100644 --- a/tools/aapt2/compile/XmlIdCollector_test.cpp +++ b/tools/aapt2/compile/XmlIdCollector_test.cpp @@ -26,7 +26,7 @@ namespace aapt { TEST(XmlIdCollectorTest, CollectsIds) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF( <View xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/foo" text="@+id/bar"> @@ -50,7 +50,7 @@ TEST(XmlIdCollectorTest, CollectsIds) { TEST(XmlIdCollectorTest, DontCollectNonIds) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); - std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>"); + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>"); XmlIdCollector collector; ASSERT_TRUE(collector.consume(context.get(), doc.get())); diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/flatten/ResourceTypeExtensions.h index c1ff556ac0cf..acf5bb5b9bcb 100644 --- a/tools/aapt2/flatten/ResourceTypeExtensions.h +++ b/tools/aapt2/flatten/ResourceTypeExtensions.h @@ -62,7 +62,7 @@ struct ExtendedTypes { * A raw string value that hasn't had its escape sequences * processed nor whitespace removed. */ - TYPE_RAW_STRING = 0xfe + TYPE_RAW_STRING = 0xfe, }; }; diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp index 6b90fb276b6d..636e9774156e 100644 --- a/tools/aapt2/flatten/TableFlattener.cpp +++ b/tools/aapt2/flatten/TableFlattener.cpp @@ -78,10 +78,18 @@ public: explicit SymbolWriter(StringPool* pool) : mPool(pool) { } - void addSymbol(const ResourceNameRef& name, size_t offset) { - symbols.push_back(Entry{ mPool->makeRef(name.package.toString() + u":" + - toString(name.type).toString() + u"/" + - name.entry.toString()), offset }); + void addSymbol(const Reference& ref, size_t offset) { + const ResourceName& name = ref.name.value(); + std::u16string fullName; + if (ref.privateReference) { + fullName += u"*"; + } + + if (!name.package.empty()) { + fullName += name.package + u":"; + } + fullName += toString(name.type).toString() + u"/" + name.entry; + symbols.push_back(Entry{ mPool->makeRef(fullName), offset }); } void shiftAllOffsets(size_t offset) { @@ -100,20 +108,23 @@ struct MapFlattenVisitor : public RawValueVisitor { SymbolWriter* mSymbols; FlatEntry* mEntry; BigBuffer* mBuffer; + bool mUseExtendedChunks; size_t mEntryCount = 0; Maybe<uint32_t> mParentIdent; Maybe<ResourceNameRef> mParentName; - MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer) : - mSymbols(symbols), mEntry(entry), mBuffer(buffer) { + MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer, + bool useExtendedChunks) : + mSymbols(symbols), mEntry(entry), mBuffer(buffer), + mUseExtendedChunks(useExtendedChunks) { } void flattenKey(Reference* key, ResTable_map* outEntry) { - if (!key->id) { + if (!key->id || (key->privateReference && mUseExtendedChunks)) { assert(key->name && "reference must have a name"); outEntry->name.ident = util::hostToDevice32(0); - mSymbols->addSymbol(key->name.value(), (mBuffer->size() - sizeof(ResTable_map)) + + mSymbols->addSymbol(*key, (mBuffer->size() - sizeof(ResTable_map)) + offsetof(ResTable_map, name)); } else { outEntry->name.ident = util::hostToDevice32(key->id.value().id); @@ -121,16 +132,21 @@ struct MapFlattenVisitor : public RawValueVisitor { } void flattenValue(Item* value, ResTable_map* outEntry) { + bool privateRef = false; if (Reference* ref = valueCast<Reference>(value)) { - if (!ref->id) { + privateRef = ref->privateReference && mUseExtendedChunks; + if (!ref->id || privateRef) { assert(ref->name && "reference must have a name"); - mSymbols->addSymbol(ref->name.value(), (mBuffer->size() - sizeof(ResTable_map)) + + mSymbols->addSymbol(*ref, (mBuffer->size() - sizeof(ResTable_map)) + offsetof(ResTable_map, value) + offsetof(Res_value, data)); } } bool result = value->flatten(&outEntry->value); + if (privateRef) { + outEntry->value.data = 0; + } assert(result && "flatten failed"); } @@ -169,7 +185,8 @@ struct MapFlattenVisitor : public RawValueVisitor { void visit(Style* style) override { if (style->parent) { - if (!style->parent.value().id) { + bool privateRef = style->parent.value().privateReference && mUseExtendedChunks; + if (!style->parent.value().id || privateRef) { assert(style->parent.value().name && "reference must have a name"); mParentName = style->parent.value().name; } else { @@ -339,21 +356,28 @@ private: bool flattenValue(FlatEntry* entry, BigBuffer* buffer) { if (Item* item = valueCast<Item>(entry->value)) { writeEntry<ResTable_entry, true>(entry, buffer); + bool privateRef = false; if (Reference* ref = valueCast<Reference>(entry->value)) { - if (!ref->id) { + // If there is no ID or the reference is private and we allow extended chunks, + // write out a 0 and mark the symbol table with the name of the reference. + privateRef = (ref->privateReference && mOptions.useExtendedChunks); + if (!ref->id || privateRef) { assert(ref->name && "reference must have at least a name"); - mSymbols->addSymbol(ref->name.value(), - buffer->size() + offsetof(Res_value, data)); + mSymbols->addSymbol(*ref, buffer->size() + offsetof(Res_value, data)); } } Res_value* outValue = buffer->nextBlock<Res_value>(); bool result = item->flatten(outValue); assert(result && "flatten failed"); + if (privateRef) { + // Force the value of 0 so we look up the symbol at unflatten time. + outValue->data = 0; + } outValue->size = util::hostToDevice16(sizeof(*outValue)); } else { const size_t beforeEntry = buffer->size(); ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer); - MapFlattenVisitor visitor(mSymbols, entry, buffer); + MapFlattenVisitor visitor(mSymbols, entry, buffer, mOptions.useExtendedChunks); entry->value->accept(&visitor); outEntry->count = util::hostToDevice32(visitor.mEntryCount); if (visitor.mParentName) { diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp index 68a1f478c34d..4ffb980b122d 100644 --- a/tools/aapt2/flatten/TableFlattener_test.cpp +++ b/tools/aapt2/flatten/TableFlattener_test.cpp @@ -15,11 +15,11 @@ */ #include "flatten/TableFlattener.h" +#include "test/Builders.h" +#include "test/Context.h" #include "unflatten/BinaryResourceParser.h" #include "util/Util.h" -#include "test/Builders.h" -#include "test/Context.h" #include <gtest/gtest.h> diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp index 4efb08bfe58d..8219462f8a73 100644 --- a/tools/aapt2/flatten/XmlFlattener.cpp +++ b/tools/aapt2/flatten/XmlFlattener.cpp @@ -15,15 +15,14 @@ */ #include "SdkConstants.h" -#include "XmlDom.h" - #include "flatten/ChunkWriter.h" #include "flatten/ResourceTypeExtensions.h" #include "flatten/XmlFlattener.h" +#include "xml/XmlDom.h" #include <androidfw/ResourceTypes.h> -#include <vector> #include <utils/misc.h> +#include <vector> using namespace android; @@ -306,7 +305,7 @@ bool XmlFlattener::flatten(IAaptContext* context, xml::Node* node) { return true; } -bool XmlFlattener::consume(IAaptContext* context, XmlResource* resource) { +bool XmlFlattener::consume(IAaptContext* context, xml::XmlResource* resource) { if (!resource->root) { return false; } diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/flatten/XmlFlattener.h index b1fb3a7cef27..a688ac965b0d 100644 --- a/tools/aapt2/flatten/XmlFlattener.h +++ b/tools/aapt2/flatten/XmlFlattener.h @@ -17,16 +17,12 @@ #ifndef AAPT_FLATTEN_XMLFLATTENER_H #define AAPT_FLATTEN_XMLFLATTENER_H -#include "util/BigBuffer.h" - #include "process/IResourceTableConsumer.h" +#include "util/BigBuffer.h" +#include "xml/XmlDom.h" namespace aapt { -namespace xml { -struct Node; -} - struct XmlFlattenerOptions { /** * Keep attribute raw string values along with typed values. @@ -45,7 +41,7 @@ public: mBuffer(buffer), mOptions(options) { } - bool consume(IAaptContext* context, XmlResource* resource) override; + bool consume(IAaptContext* context, xml::XmlResource* resource) override; private: BigBuffer* mBuffer; diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp index 318bcddf44ec..864887927328 100644 --- a/tools/aapt2/flatten/XmlFlattener_test.cpp +++ b/tools/aapt2/flatten/XmlFlattener_test.cpp @@ -16,11 +16,10 @@ #include "flatten/XmlFlattener.h" #include "link/Linkers.h" -#include "util/BigBuffer.h" -#include "util/Util.h" - #include "test/Builders.h" #include "test/Context.h" +#include "util/BigBuffer.h" +#include "util/Util.h" #include <androidfw/ResourceTypes.h> #include <gtest/gtest.h> @@ -45,7 +44,7 @@ public: .build(); } - ::testing::AssertionResult flatten(XmlResource* doc, android::ResXMLTree* outTree, + ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree, XmlFlattenerOptions options = {}) { BigBuffer buffer(1024); XmlFlattener flattener(&buffer, options); @@ -65,7 +64,7 @@ protected: }; TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF( <View xmlns:test="http://com.test" attr="hey"> <Layout test:hello="hi" /> @@ -144,7 +143,7 @@ TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) { } TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF( <View xmlns:android="http://schemas.android.com/apk/res/android" android:paddingStart="1dp" android:colorAccent="#ffffff"/>)EOF"); @@ -169,7 +168,7 @@ TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) { } TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF( <View xmlns:android="http://schemas.android.com/apk/res/android" android:id="@id/id" class="str" @@ -192,7 +191,7 @@ TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) { * namespace. */ TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>"); + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>"); android::ResXMLTree tree; ASSERT_TRUE(flatten(doc.get(), &tree)); diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp index d5a2b38bcbc4..da96b84fd4ea 100644 --- a/tools/aapt2/java/AnnotationProcessor_test.cpp +++ b/tools/aapt2/java/AnnotationProcessor_test.cpp @@ -17,12 +17,10 @@ #include "ResourceParser.h" #include "ResourceTable.h" #include "ResourceValues.h" -#include "XmlPullParser.h" - #include "java/AnnotationProcessor.h" - #include "test/Builders.h" #include "test/Context.h" +#include "xml/XmlPullParser.h" #include <gtest/gtest.h> @@ -42,7 +40,7 @@ struct AnnotationProcessorTest : public ::testing::Test { options); std::stringstream in; in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str; - XmlPullParser xmlParser(in); + xml::XmlPullParser xmlParser(in); if (parser.parse(&xmlParser)) { return ::testing::AssertionSuccess(); } diff --git a/tools/aapt2/java/ClassDefinitionWriter.h b/tools/aapt2/java/ClassDefinitionWriter.h index b8886f90c6c5..04e1274c3a97 100644 --- a/tools/aapt2/java/ClassDefinitionWriter.h +++ b/tools/aapt2/java/ClassDefinitionWriter.h @@ -17,6 +17,7 @@ #ifndef AAPT_JAVA_CLASSDEFINITION_H #define AAPT_JAVA_CLASSDEFINITION_H +#include "Resource.h" #include "java/AnnotationProcessor.h" #include "util/StringPiece.h" #include "util/Util.h" diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp index d963d8948f8d..a9b4c14337fe 100644 --- a/tools/aapt2/java/ManifestClassGenerator.cpp +++ b/tools/aapt2/java/ManifestClassGenerator.cpp @@ -15,12 +15,11 @@ */ #include "Source.h" -#include "XmlDom.h" - #include "java/AnnotationProcessor.h" #include "java/ClassDefinitionWriter.h" #include "java/ManifestClassGenerator.h" #include "util/Maybe.h" +#include "xml/XmlDom.h" #include <algorithm> @@ -80,7 +79,7 @@ static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef, } bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package, - XmlResource* res, std::ostream* out) { + xml::XmlResource* res, std::ostream* out) { xml::Element* el = xml::findRootElement(res->root.get()); if (!el) { return false; diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h index 0f0998f8e2ba..226ed23b85f8 100644 --- a/tools/aapt2/java/ManifestClassGenerator.h +++ b/tools/aapt2/java/ManifestClassGenerator.h @@ -18,15 +18,15 @@ #define AAPT_JAVA_MANIFESTCLASSGENERATOR_H #include "Diagnostics.h" -#include "process/IResourceTableConsumer.h" #include "util/StringPiece.h" +#include "xml/XmlDom.h" #include <iostream> namespace aapt { struct ManifestClassGenerator { - bool generate(IDiagnostics* diag, const StringPiece16& package, XmlResource* res, + bool generate(IDiagnostics* diag, const StringPiece16& package, xml::XmlResource* res, std::ostream* out); }; diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp index 4081287a1852..fc57ae6fd8ff 100644 --- a/tools/aapt2/java/ManifestClassGenerator_test.cpp +++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp @@ -15,7 +15,6 @@ */ #include "java/ManifestClassGenerator.h" - #include "test/Builders.h" #include "test/Context.h" @@ -25,7 +24,7 @@ namespace aapt { TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); - std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <permission android:name="android.permission.ACCESS_INTERNET" /> <permission android:name="android.DO_DANGEROUS_THINGS" /> @@ -75,7 +74,7 @@ TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) { TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); - std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Required to access the internet. Added in API 1. --> diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp index 44314772fbd4..c0968545ad81 100644 --- a/tools/aapt2/java/ProguardRules.cpp +++ b/tools/aapt2/java/ProguardRules.cpp @@ -14,10 +14,9 @@ * limitations under the License. */ -#include "XmlDom.h" - #include "java/ProguardRules.h" #include "util/Util.h" +#include "xml/XmlDom.h" #include <memory> #include <string> @@ -40,11 +39,11 @@ public: virtual void visit(xml::Element* node) override { if (!node->namespaceUri.empty()) { - Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace( + Maybe<xml::ExtractedPackage> maybePackage = xml::extractPackageFromNamespace( node->namespaceUri); if (maybePackage) { // This is a custom view, let's figure out the class name from this. - std::u16string package = maybePackage.value() + u"." + node->name; + std::u16string package = maybePackage.value().package + u"." + node->name; if (util::isJavaClassName(package)) { addClass(node->lineNumber, package); } @@ -185,7 +184,8 @@ struct ManifestVisitor : public BaseVisitor { std::u16string mPackage; }; -bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) { +bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, + KeepSet* keepSet) { ManifestVisitor visitor(source, keepSet); if (res->root) { res->root->accept(&visitor); @@ -194,7 +194,7 @@ bool collectProguardRulesForManifest(const Source& source, XmlResource* res, Kee return false; } -bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) { +bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet) { if (!res->root) { return false; } diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h index be61eb9095c2..aafffd39d84e 100644 --- a/tools/aapt2/java/ProguardRules.h +++ b/tools/aapt2/java/ProguardRules.h @@ -19,8 +19,7 @@ #include "Resource.h" #include "Source.h" - -#include "process/IResourceTableConsumer.h" +#include "xml/XmlDom.h" #include <map> #include <ostream> @@ -47,8 +46,8 @@ private: std::map<std::u16string, std::set<Source>> mKeepMethodSet; }; -bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet); -bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet); +bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keepSet); +bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet); bool writeKeepSet(std::ostream* out, const KeepSet& keepSet); diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 97be774acaf1..9850ae5cf57b 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -18,8 +18,6 @@ #include "Debug.h" #include "Flags.h" #include "NameMangler.h" -#include "XmlDom.h" - #include "compile/IdAssigner.h" #include "flatten/Archive.h" #include "flatten/TableFlattener.h" @@ -28,6 +26,7 @@ #include "java/ManifestClassGenerator.h" #include "java/ProguardRules.h" #include "link/Linkers.h" +#include "link/ReferenceLinker.h" #include "link/ManifestFixer.h" #include "link/TableMerger.h" #include "process/IResourceTableConsumer.h" @@ -36,6 +35,7 @@ #include "unflatten/FileExportHeaderReader.h" #include "util/Files.h" #include "util/StringPiece.h" +#include "xml/XmlDom.h" #include <fstream> #include <sys/stat.h> @@ -159,7 +159,7 @@ public: /** * Inflates an XML file from the source path. */ - std::unique_ptr<XmlResource> loadXml(const std::string& path) { + std::unique_ptr<xml::XmlResource> loadXml(const std::string& path) { std::ifstream fin(path, std::ifstream::binary); if (!fin) { mContext.getDiagnostics()->error(DiagMessage(path) << strerror(errno)); @@ -172,7 +172,7 @@ public: /** * Inflates a binary XML file from the source path. */ - std::unique_ptr<XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) { + std::unique_ptr<xml::XmlResource> loadBinaryXmlSkipFileExport(const std::string& path) { // Read header for symbol info and export info. std::string errorStr; Maybe<android::FileMap> maybeF = file::mmapPath(path, &errorStr); @@ -188,7 +188,7 @@ public: return {}; } - std::unique_ptr<XmlResource> xmlRes = xml::inflate( + std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate( (const uint8_t*) maybeF.value().getDataPtr() + (size_t) offset, maybeF.value().getDataLength() - offset, mContext.getDiagnostics(), Source(path)); @@ -245,7 +245,7 @@ public: return true; } - Maybe<AppInfo> extractAppInfoFromManifest(XmlResource* xmlRes) { + Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) { // Make sure the first element is <manifest> with package attribute. if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) { if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") { @@ -309,7 +309,7 @@ public: return true; } - bool flattenXml(XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel, + bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel, IArchiveWriter* writer) { BigBuffer buffer(1024); XmlFlattenerOptions options = {}; @@ -354,7 +354,7 @@ public: return true; } - bool writeManifestJavaFile(XmlResource* manifestXml) { + bool writeManifestJavaFile(xml::XmlResource* manifestXml) { if (!mOptions.generateJavaClassPath) { return true; } @@ -502,7 +502,7 @@ public: int run(const std::vector<std::string>& inputFiles) { // Load the AndroidManifest.xml - std::unique_ptr<XmlResource> manifestXml = loadXml(mOptions.manifestPath); + std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath); if (!manifestXml) { return 1; } @@ -545,25 +545,21 @@ public: << "with package ID " << std::hex << (int) mContext.mPackageId); } - bool error = false; for (const std::string& input : inputFiles) { if (!processFile(input, false)) { - error = true; + mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input"); + return 1; } } for (const std::string& input : mOptions.overlayFiles) { if (!processFile(input, true)) { - error = true; + mContext.getDiagnostics()->error(DiagMessage() << "failed parsing overlays"); + return 1; } } - if (error) { - mContext.getDiagnostics()->error(DiagMessage() << "failed parsing input"); - return 1; - } - if (!verifyNoExternalPackages()) { return 1; } @@ -608,6 +604,7 @@ public: return 1; } + bool error = false; { ManifestFixerOptions manifestFixerOptions; manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault; @@ -617,6 +614,11 @@ public: error = true; } + // AndroidManifest.xml has no resource name, but the CallSite is built from the name + // (aka, which package the AndroidManifest.xml is coming from). + // So we give it a package name so it can see local resources. + manifestXml->file.name.package = mContext.getCompilationPackage().toString(); + XmlReferenceLinker manifestLinker; if (manifestLinker.consume(&mContext, manifestXml.get())) { if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath), @@ -640,14 +642,21 @@ public: } } + if (error) { + mContext.getDiagnostics()->error(DiagMessage() << "failed processing manifest"); + return 1; + } + for (const FileToProcess& file : mFilesToProcess) { if (file.file.name.type != ResourceType::kRaw && util::stringEndsWith<char>(file.source.path, ".xml.flat")) { if (mOptions.verbose) { - mContext.getDiagnostics()->note(DiagMessage() << "linking " << file.source.path); + mContext.getDiagnostics()->note(DiagMessage() + << "linking " << file.source.path); } - std::unique_ptr<XmlResource> xmlRes = loadBinaryXmlSkipFileExport(file.source.path); + std::unique_ptr<xml::XmlResource> xmlRes = loadBinaryXmlSkipFileExport( + file.source.path); if (!xmlRes) { return 1; } diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h index 7b3fc358fca4..4d3a483c6b82 100644 --- a/tools/aapt2/link/Linkers.h +++ b/tools/aapt2/link/Linkers.h @@ -17,7 +17,9 @@ #ifndef AAPT_LINKER_LINKERS_H #define AAPT_LINKER_LINKERS_H +#include "Resource.h" #include "process/IResourceTableConsumer.h" +#include "xml/XmlDom.h" #include <set> @@ -28,6 +30,14 @@ struct ResourceEntry; struct ConfigDescription; /** + * Defines the location in which a value exists. This determines visibility of other + * package's private symbols. + */ +struct CallSite { + ResourceNameRef resource; +}; + +/** * Determines whether a versioned resource should be created. If a versioned resource already * exists, it takes precedence. */ @@ -39,7 +49,7 @@ struct AutoVersioner : public IResourceTableConsumer { }; struct XmlAutoVersioner : public IXmlResourceConsumer { - bool consume(IAaptContext* context, XmlResource* resource) override; + bool consume(IAaptContext* context, xml::XmlResource* resource) override; }; /** @@ -69,15 +79,6 @@ struct PrivateAttributeMover : public IResourceTableConsumer { }; /** - * Resolves all references to resources in the ResourceTable and assigns them IDs. - * The ResourceTable must already have IDs assigned to each resource. - * Once the ResourceTable is processed by this linker, it is ready to be flattened. - */ -struct ReferenceLinker : public IResourceTableConsumer { - bool consume(IAaptContext* context, ResourceTable* table) override; -}; - -/** * Resolves attributes in the XmlResource and compiles string values to resource values. * Once an XmlResource is processed by this linker, it is ready to be flattened. */ @@ -86,7 +87,7 @@ private: std::set<int> mSdkLevelsFound; public: - bool consume(IAaptContext* context, XmlResource* resource) override; + bool consume(IAaptContext* context, xml::XmlResource* resource) override; /** * Once the XmlResource has been consumed, this returns the various SDK levels in which diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 52d942670243..2034c5701492 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -15,10 +15,9 @@ */ #include "ResourceUtils.h" -#include "XmlDom.h" - #include "link/ManifestFixer.h" #include "util/Util.h" +#include "xml/XmlDom.h" namespace aapt { @@ -63,7 +62,7 @@ static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element return true; } -bool ManifestFixer::consume(IAaptContext* context, XmlResource* doc) { +bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) { xml::Element* root = xml::findRootElement(doc->root.get()); if (!root || !root->namespaceUri.empty() || root->name != u"manifest") { context->getDiagnostics()->error(DiagMessage(doc->file.source) diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h index 16e161dc40d8..a77e6d5f709c 100644 --- a/tools/aapt2/link/ManifestFixer.h +++ b/tools/aapt2/link/ManifestFixer.h @@ -18,6 +18,10 @@ #define AAPT_LINK_MANIFESTFIXER_H #include "process/IResourceTableConsumer.h" +#include "util/Maybe.h" +#include "xml/XmlDom.h" + +#include <string> namespace aapt { @@ -36,7 +40,7 @@ struct ManifestFixer : public IXmlResourceConsumer { ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) { } - bool consume(IAaptContext* context, XmlResource* doc) override; + bool consume(IAaptContext* context, xml::XmlResource* doc) override; }; } // namespace aapt diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index 5c5d8afa610d..f6bf895a7f57 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -15,7 +15,6 @@ */ #include "link/ManifestFixer.h" - #include "test/Builders.h" #include "test/Context.h" @@ -51,13 +50,13 @@ struct ManifestFixerTest : public ::testing::Test { .build(); } - std::unique_ptr<XmlResource> verify(const StringPiece& str) { + std::unique_ptr<xml::XmlResource> verify(const StringPiece& str) { return verifyWithOptions(str, {}); } - std::unique_ptr<XmlResource> verifyWithOptions(const StringPiece& str, - const ManifestFixerOptions& options) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(str); + std::unique_ptr<xml::XmlResource> verifyWithOptions(const StringPiece& str, + const ManifestFixerOptions& options) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(str); ManifestFixer fixer(options); if (fixer.consume(mContext.get(), doc.get())) { return doc; @@ -88,7 +87,7 @@ TEST_F(ManifestFixerTest, EnsureManifestHasPackage) { TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") }; - std::unique_ptr<XmlResource> doc = verifyWithOptions(R"EOF( + std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" /> diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp index 5a2f5f0771d2..3c8af4f81ffe 100644 --- a/tools/aapt2/link/PrivateAttributeMover.cpp +++ b/tools/aapt2/link/PrivateAttributeMover.cpp @@ -15,7 +15,6 @@ */ #include "ResourceTable.h" - #include "link/Linkers.h" #include <algorithm> diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 8c924b5b0f99..4b8253752d96 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -14,17 +14,18 @@ * limitations under the License. */ +#include "ReferenceLinker.h" + #include "Diagnostics.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" -#include "util/Util.h" #include "ValueVisitor.h" - #include "link/Linkers.h" -#include "link/ReferenceLinkerVisitor.h" #include "process/IResourceTableConsumer.h" #include "process/SymbolTable.h" +#include "util/Util.h" +#include "xml/XmlUtil.h" #include <androidfw/ResourceTypes.h> #include <cassert> @@ -41,52 +42,15 @@ namespace { * * NOTE: All of the entries in the ResourceTable must be assigned IDs. */ -class StyleAndReferenceLinkerVisitor : public ValueVisitor { +class ReferenceLinkerVisitor : public ValueVisitor { private: - ReferenceLinkerVisitor mReferenceVisitor; IAaptContext* mContext; ISymbolTable* mSymbols; - IPackageDeclStack* mPackageDecls; + xml::IPackageDeclStack* mPackageDecls; StringPool* mStringPool; + CallSite* mCallSite; bool mError = false; - const ISymbolTable::Symbol* findAttributeSymbol(Reference* reference) { - assert(reference); - assert(reference->name || reference->id); - - if (reference->name) { - // Transform the package name if it is an alias. - Maybe<ResourceName> realName = mPackageDecls->transformPackage( - reference->name.value(), mContext->getCompilationPackage()); - - // Mangle the reference name if it should be mangled. - Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName( - realName ? realName.value() : reference->name.value()); - - const ISymbolTable::Symbol* s = nullptr; - if (mangledName) { - s = mSymbols->findByName(mangledName.value()); - } else if (realName) { - s = mSymbols->findByName(realName.value()); - } else { - s = mSymbols->findByName(reference->name.value()); - } - - if (s && s->attribute) { - return s; - } - } - - if (reference->id) { - if (const ISymbolTable::Symbol* s = mSymbols->findById(reference->id.value())) { - if (s->attribute) { - return s; - } - } - } - return nullptr; - } - /** * Transform a RawString value into a more specific, appropriate value, based on the * Attribute. If a non RawString value is passed in, this is an identity transform. @@ -94,8 +58,8 @@ private: std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value, const Attribute* attr) { if (RawString* rawString = valueCast<RawString>(value.get())) { - std::unique_ptr<Item> transformed = ResourceUtils::parseItemForAttribute( - *rawString->value, attr); + std::unique_ptr<Item> transformed = + ResourceUtils::parseItemForAttribute(*rawString->value, attr); // If we could not parse as any specific type, try a basic STRING. if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) { @@ -163,14 +127,16 @@ private: public: using ValueVisitor::visit; - StyleAndReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, - StringPool* stringPool, IPackageDeclStack* decl) : - mReferenceVisitor(context, symbols, decl), mContext(context), mSymbols(symbols), - mPackageDecls(decl), mStringPool(stringPool) { + ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool, + xml::IPackageDeclStack* decl,CallSite* callSite) : + mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool), + mCallSite(callSite) { } - void visit(Reference* reference) override { - mReferenceVisitor.visit(reference); + void visit(Reference* ref) override { + if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) { + mError = true; + } } /** @@ -184,13 +150,26 @@ public: } for (Style::Entry& entry : style->entries) { - if (const ISymbolTable::Symbol* s = findAttributeSymbol(&entry.key)) { + std::string errStr; + + // Transform the attribute reference so that it is using the fully qualified package + // name. This will also mark the reference as being able to see private resources if + // there was a '*' in the reference or if the package came from the private namespace. + Reference transformedReference = entry.key; + transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(), + &transformedReference); + + // Find the attribute in the symbol table and check if it is visible from this callsite. + const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility( + transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr); + if (symbol) { // Assign our style key the correct ID. - entry.key.id = s->id; + entry.key.id = symbol->id; // Try to convert the value to a more specific, typed value based on the // attribute it is set to. - entry.value = parseValueWithAttribute(std::move(entry.value), s->attribute.get()); + entry.value = parseValueWithAttribute(std::move(entry.value), + symbol->attribute.get()); // Link/resolve the final value (mostly if it's a reference). entry.value->accept(this); @@ -201,13 +180,13 @@ public: entry.value->flatten(&val); // Always allow references. - const uint32_t typeMask = s->attribute->typeMask | + const uint32_t typeMask = symbol->attribute->typeMask | android::ResTable_map::TYPE_REFERENCE; if (!(typeMask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) { // The actual type of this item is incompatible with the attribute. DiagMessage msg(style->getSource()); - buildAttributeMismatchMessage(&msg, s->attribute.get(), entry.value.get()); + buildAttributeMismatchMessage(&msg, symbol->attribute.get(), entry.value.get()); mContext->getDiagnostics()->error(msg); mError = true; } @@ -219,23 +198,151 @@ public: } else { msg << entry.key.id.value(); } - msg << "' not found"; + msg << "' " << errStr; mContext->getDiagnostics()->error(msg); + mContext->getDiagnostics()->note(DiagMessage(style->getSource()) << entry.key); mError = true; } } } - inline bool hasError() { - return mError || mReferenceVisitor.hasError(); + bool hasError() { + return mError; } }; -struct EmptyDeclStack : public IPackageDeclStack { - Maybe<ResourceName> transformPackage(const ResourceName& name, - const StringPiece16& localPackage) const override { - if (name.package.empty()) { - return ResourceName{ localPackage.toString(), name.type, name.entry }; +} // namespace + +/** + * The symbol is visible if it is public, or if the reference to it is requesting private access + * or if the callsite comes from the same package. + */ +bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref, + const CallSite& callSite) { + if (!symbol.isPublic && !ref.privateReference) { + if (ref.name) { + return callSite.resource.package == ref.name.value().package; + } else if (ref.id) { + return ref.id.value().packageId() == symbol.id.packageId(); + } else { + return false; + } + } + return true; +} + +const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference, + NameMangler* mangler, + ISymbolTable* symbols) { + if (reference.name) { + Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value()); + return symbols->findByName(mangled ? mangled.value() : reference.name.value()); + } else if (reference.id) { + return symbols->findById(reference.id.value()); + } else { + return nullptr; + } +} + +const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility( + const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols, + CallSite* callSite, std::string* outError) { + const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols); + if (!symbol) { + std::stringstream errStr; + errStr << "not found"; + if (outError) *outError = errStr.str(); + return nullptr; + } + + if (!isSymbolVisible(*symbol, reference, *callSite)) { + std::stringstream errStr; + errStr << "is private"; + if (outError) *outError = errStr.str(); + return nullptr; + } + return symbol; +} + +const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility( + const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols, + CallSite* callSite, std::string* outError) { + const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler, + symbols, callSite, + outError); + if (!symbol) { + return nullptr; + } + + if (!symbol->attribute) { + std::stringstream errStr; + errStr << "is not an attribute"; + if (outError) *outError = errStr.str(); + return nullptr; + } + return symbol; +} + +Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference, + NameMangler* nameMangler, + ISymbolTable* symbols, + CallSite* callSite, + std::string* outError) { + const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols); + if (!symbol) { + return {}; + } + + if (!symbol->attribute) { + std::stringstream errStr; + errStr << "is not an attribute"; + if (outError) *outError = errStr.str(); + return {}; + } + return xml::AaptAttribute{ symbol->id, *symbol->attribute }; +} + +bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context, + ISymbolTable* symbols, xml::IPackageDeclStack* decls, + CallSite* callSite) { + assert(reference); + assert(reference->name || reference->id); + + Reference transformedReference = *reference; + transformReferenceFromNamespace(decls, context->getCompilationPackage(), + &transformedReference); + + std::string errStr; + const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility( + transformedReference, context->getNameMangler(), symbols, callSite, &errStr); + if (s) { + reference->id = s->id; + return true; + } + + DiagMessage errorMsg(reference->getSource()); + errorMsg << "resource "; + if (reference->name) { + errorMsg << reference->name.value(); + if (transformedReference.name.value() != reference->name.value()) { + errorMsg << " (aka " << transformedReference.name.value() << ")"; + } + } else { + errorMsg << reference->id.value(); + } + + errorMsg << " " << errStr; + context->getDiagnostics()->error(errorMsg); + return false; +} + +namespace { + +struct EmptyDeclStack : public xml::IPackageDeclStack { + Maybe<xml::ExtractedPackage> transformPackageAlias( + const StringPiece16& alias, const StringPiece16& localPackage) const override { + if (alias.empty()) { + return xml::ExtractedPackage{ localPackage.toString(), true /* private */ }; } return {}; } @@ -259,14 +366,16 @@ bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) { error = true; } + CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) }; + ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), + &table->stringPool, &declStack, &callSite); + for (auto& configValue : entry->values) { - StyleAndReferenceLinkerVisitor visitor(context, - context->getExternalSymbols(), - &table->stringPool, &declStack); configValue.value->accept(&visitor); - if (visitor.hasError()) { - error = true; - } + } + + if (visitor.hasError()) { + error = true; } } } diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h new file mode 100644 index 000000000000..6f11d588d47e --- /dev/null +++ b/tools/aapt2/link/ReferenceLinker.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT_LINKER_REFERENCELINKER_H +#define AAPT_LINKER_REFERENCELINKER_H + +#include "Resource.h" +#include "ResourceValues.h" +#include "ValueVisitor.h" +#include "link/Linkers.h" +#include "process/IResourceTableConsumer.h" +#include "process/SymbolTable.h" +#include "xml/XmlDom.h" + +#include <cassert> + +namespace aapt { + +/** + * Resolves all references to resources in the ResourceTable and assigns them IDs. + * The ResourceTable must already have IDs assigned to each resource. + * Once the ResourceTable is processed by this linker, it is ready to be flattened. + */ +struct ReferenceLinker : public IResourceTableConsumer { + /** + * Returns true if the symbol is visible by the reference and from the callsite. + */ + static bool isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref, + const CallSite& callSite); + + /** + * Performs name mangling and looks up the resource in the symbol table. Returns nullptr + * if the symbol was not found. + */ + static const ISymbolTable::Symbol* resolveSymbol(const Reference& reference, + NameMangler* mangler, ISymbolTable* symbols); + + /** + * Performs name mangling and looks up the resource in the symbol table. If the symbol is + * not visible by the reference at the callsite, nullptr is returned. outError holds + * the error message. + */ + static const ISymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference, + NameMangler* nameMangler, + ISymbolTable* symbols, + CallSite* callSite, + std::string* outError); + + /** + * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute. + * That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute. + */ + static const ISymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference, + NameMangler* nameMangler, + ISymbolTable* symbols, + CallSite* callSite, + std::string* outError); + + /** + * Resolves the attribute reference and returns an xml::AaptAttribute if successful. + * If resolution fails, outError holds the error message. + */ + static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference, + NameMangler* nameMangler, + ISymbolTable* symbols, + CallSite* callSite, + std::string* outError); + + /** + * Transforms the package name of the reference to the fully qualified package name using + * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible + * to the reference at the callsite, the reference is updated with an ID. + * Returns false on failure, and an error message is logged to the IDiagnostics in the context. + */ + static bool linkReference(Reference* reference, IAaptContext* context, ISymbolTable* symbols, + xml::IPackageDeclStack* decls, CallSite* callSite); + + /** + * Links all references in the ResourceTable. + */ + bool consume(IAaptContext* context, ResourceTable* table) override; +}; + +} // namespace aapt + +#endif /* AAPT_LINKER_REFERENCELINKER_H */ diff --git a/tools/aapt2/link/ReferenceLinkerVisitor.h b/tools/aapt2/link/ReferenceLinkerVisitor.h deleted file mode 100644 index a4cb596eb5ec..000000000000 --- a/tools/aapt2/link/ReferenceLinkerVisitor.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAPT_LINKER_REFERENCELINKERVISITOR_H -#define AAPT_LINKER_REFERENCELINKERVISITOR_H - -#include "Resource.h" -#include "ResourceValues.h" -#include "ValueVisitor.h" - -#include "process/IResourceTableConsumer.h" -#include "process/SymbolTable.h" - -#include <cassert> - -namespace aapt { - -/** - * The ReferenceLinkerVisitor will follow all references and make sure they point - * to resources that actually exist in the given ISymbolTable. - * Once the target resource has been found, the ID of the resource will be assigned - * to the reference object. - */ -class ReferenceLinkerVisitor : public ValueVisitor { - using ValueVisitor::visit; -private: - IAaptContext* mContext; - ISymbolTable* mSymbols; - IPackageDeclStack* mPackageDecls; - bool mError = false; - -public: - ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, IPackageDeclStack* decls) : - mContext(context), mSymbols(symbols), mPackageDecls(decls) { - } - - /** - * Lookup a reference and ensure it exists, either in our local table, or as an external - * symbol. Once found, assign the ID of the target resource to this reference object. - */ - void visit(Reference* reference) override { - assert(reference); - assert(reference->name || reference->id); - - // We prefer to lookup by name if the name is set. Otherwise it could be - // an out-of-date ID. - if (reference->name) { - // Transform the package name if it is an alias. - Maybe<ResourceName> realName = mPackageDecls->transformPackage( - reference->name.value(), mContext->getCompilationPackage()); - - // Mangle the reference name if it should be mangled. - Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName( - realName ? realName.value() : reference->name.value()); - - const ISymbolTable::Symbol* s = nullptr; - if (mangledName) { - s = mSymbols->findByName(mangledName.value()); - } else if (realName) { - s = mSymbols->findByName(realName.value()); - } else { - s = mSymbols->findByName(reference->name.value()); - } - - if (s) { - reference->id = s->id; - return; - } - - DiagMessage errorMsg(reference->getSource()); - errorMsg << "reference to " << reference->name.value(); - if (realName) { - errorMsg << " (aka " << realName.value() << ")"; - } - errorMsg << " was not found"; - mContext->getDiagnostics()->error(errorMsg); - mError = true; - return; - } - - if (!mSymbols->findById(reference->id.value())) { - mContext->getDiagnostics()->error(DiagMessage(reference->getSource()) - << "reference to " << reference->id.value() - << " was not found"); - mError = true; - } - } - - inline bool hasError() { - return mError; - } -}; - -} // namespace aapt - -#endif /* AAPT_LINKER_REFERENCELINKERVISITOR_H */ diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp index 5e7641a4ec4f..8d324fe753a1 100644 --- a/tools/aapt2/link/ReferenceLinker_test.cpp +++ b/tools/aapt2/link/ReferenceLinker_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "link/Linkers.h" +#include "link/ReferenceLinker.h" #include "process/SymbolTable.h" #include "test/Builders.h" @@ -44,7 +44,7 @@ TEST(ReferenceLinkerTest, LinkSimpleReferences) { .setSymbolTable(JoinedSymbolTableBuilder() .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) .addSymbolTable(test::StaticSymbolTableBuilder() - .addSymbol(u"@android:string/ok", ResourceId(0x01040034)) + .addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034)) .build()) .build()) .build(); @@ -92,12 +92,12 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) { .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) .setSymbolTable(test::StaticSymbolTableBuilder() - .addSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000)) - .addSymbol(u"@android:attr/foo", ResourceId(0x01010001), + .addPublicSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000)) + .addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR) .build()) - .addSymbol(u"@android:attr/bar", ResourceId(0x01010002), + .addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_FLAGS) .addItem(u"one", 0x01) @@ -132,7 +132,7 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) { .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } }) .setSymbolTable(test::StaticSymbolTableBuilder() - .addSymbol(u"@com.app.test:attr/com.android.support$foo", + .addPublicSymbol(u"@com.app.test:attr/com.android.support$foo", ResourceId(0x7f010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) .build()) @@ -156,4 +156,78 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) { EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000)); } +TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) { + std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() + .setPackageId(u"com.app.test", 0x7f) + .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000), + u"@android:string/hidden") + .build(); + + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .setCompilationPackage(u"com.app.test") + .setPackageId(0x7f) + .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) + .setSymbolTable(JoinedSymbolTableBuilder() + .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) + .addSymbolTable(test::StaticSymbolTableBuilder() + .addSymbol(u"@android:string/hidden", ResourceId(0x01040034)) + .build()) + .build()) + .build(); + + ReferenceLinker linker; + ASSERT_FALSE(linker.consume(context.get(), table.get())); +} + +TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) { + std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() + .setPackageId(u"com.app.test", 0x7f) + .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000), + u"@com.app.lib:string/hidden") + .build(); + + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .setCompilationPackage(u"com.app.test") + .setPackageId(0x7f) + .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.app.lib" } }) + .setSymbolTable(JoinedSymbolTableBuilder() + .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) + .addSymbolTable(test::StaticSymbolTableBuilder() + .addSymbol(u"@com.app.test:string/com.app.lib$hidden", + ResourceId(0x7f040034)) + .build()) + .build()) + .build(); + + ReferenceLinker linker; + ASSERT_FALSE(linker.consume(context.get(), table.get())); +} + +TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) { + std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() + .setPackageId(u"com.app.test", 0x7f) + .addValue(u"@com.app.test:style/Theme", test::StyleBuilder() + .addItem(u"@android:attr/hidden", ResourceUtils::tryParseColor(u"#ff00ff")) + .build()) + .build(); + + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .setCompilationPackage(u"com.app.test") + .setPackageId(0x7f) + .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) + .setSymbolTable(JoinedSymbolTableBuilder() + .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) + .addSymbolTable(test::StaticSymbolTableBuilder() + .addSymbol(u"@android:attr/hidden", ResourceId(0x01010001), + test::AttributeBuilder() + .setTypeMask(android::ResTable_map::TYPE_COLOR) + .build()) + .build()) + .build()) + .build(); + + ReferenceLinker linker; + ASSERT_FALSE(linker.consume(context.get(), table.get())); +} + } // namespace aapt diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index caab9b8a4684..a26d7637ab3a 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -17,51 +17,91 @@ #include "Diagnostics.h" #include "ResourceUtils.h" #include "SdkConstants.h" -#include "XmlDom.h" - #include "link/Linkers.h" -#include "link/ReferenceLinkerVisitor.h" +#include "link/ReferenceLinker.h" #include "process/IResourceTableConsumer.h" #include "process/SymbolTable.h" #include "util/Util.h" +#include "xml/XmlDom.h" namespace aapt { namespace { -class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor { +/** + * Visits all references (including parents of styles, references in styles, arrays, etc) and + * links their symbolic name to their Resource ID, performing mangling and package aliasing + * as needed. + */ +class ReferenceVisitor : public ValueVisitor { +private: + IAaptContext* mContext; + ISymbolTable* mSymbols; + xml::IPackageDeclStack* mDecls; + CallSite* mCallSite; + bool mError; + +public: + using ValueVisitor::visit; + + ReferenceVisitor(IAaptContext* context, ISymbolTable* symbols, xml::IPackageDeclStack* decls, + CallSite* callSite) : + mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite), + mError(false) { + } + + void visit(Reference* ref) override { + if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) { + mError = true; + } + } + + bool hasError() const { + return mError; + } +}; + +/** + * Visits each xml Element and compiles the attributes within. + */ +class XmlVisitor : public xml::PackageAwareVisitor { private: IAaptContext* mContext; ISymbolTable* mSymbols; Source mSource; std::set<int>* mSdkLevelsFound; - ReferenceLinkerVisitor mReferenceLinkerVisitor; + CallSite* mCallSite; + ReferenceVisitor mReferenceVisitor; bool mError = false; public: using xml::PackageAwareVisitor::visit; - XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source, - std::set<int>* sdkLevelsFound) : + XmlVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source, + std::set<int>* sdkLevelsFound, CallSite* callSite) : mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound), - mReferenceLinkerVisitor(context, symbols, this) { + mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) { } void visit(xml::Element* el) override { const Source source = mSource.withLine(el->lineNumber); for (xml::Attribute& attr : el->attributes) { - Maybe<std::u16string> maybePackage = - util::extractPackageFromNamespace(attr.namespaceUri); + Maybe<xml::ExtractedPackage> maybePackage = + xml::extractPackageFromNamespace(attr.namespaceUri); if (maybePackage) { // There is a valid package name for this attribute. We will look this up. - StringPiece16 package = maybePackage.value(); + StringPiece16 package = maybePackage.value().package; if (package.empty()) { // Empty package means the 'current' or 'local' package. package = mContext->getCompilationPackage(); } - attr.compiledAttribute = compileAttribute( - ResourceName{ package.toString(), ResourceType::kAttr, attr.name }); + Reference attrRef(ResourceNameRef(package, ResourceType::kAttr, attr.name)); + attrRef.privateReference = maybePackage.value().privateNamespace; + + std::string errStr; + attr.compiledAttribute = ReferenceLinker::compileXmlAttribute( + attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr); // Convert the string value into a compiled Value if this is a valid attribute. if (attr.compiledAttribute) { @@ -87,7 +127,7 @@ public: } else { mContext->getDiagnostics()->error(DiagMessage(source) << "attribute '" << package << ":" - << attr.name << "' was not found"); + << attr.name << "' " << errStr); mError = true; } @@ -99,7 +139,7 @@ public: if (attr.compiledValue) { // With a compiledValue, we must resolve the reference and assign it an ID. attr.compiledValue->setSource(source); - attr.compiledValue->accept(&mReferenceLinkerVisitor); + attr.compiledValue->accept(&mReferenceVisitor); } } @@ -107,28 +147,18 @@ public: xml::PackageAwareVisitor::visit(el); } - Maybe<xml::AaptAttribute> compileAttribute(const ResourceName& name) { - Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(name); - if (const ISymbolTable::Symbol* symbol = mSymbols->findByName( - mangledName ? mangledName.value() : name)) { - if (symbol->attribute) { - return xml::AaptAttribute{ symbol->id, *symbol->attribute }; - } - } - return {}; - } - - inline bool hasError() { - return mError || mReferenceLinkerVisitor.hasError(); + bool hasError() { + return mError || mReferenceVisitor.hasError(); } }; } // namespace -bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) { +bool XmlReferenceLinker::consume(IAaptContext* context, xml::XmlResource* resource) { mSdkLevelsFound.clear(); - XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), resource->file.source, - &mSdkLevelsFound); + CallSite callSite = { resource->file.name }; + XmlVisitor visitor(context, context->getExternalSymbols(), resource->file.source, + &mSdkLevelsFound, &callSite); if (resource->root) { resource->root->accept(&visitor); return !visitor.hasError(); diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp index 7f91ec348c9d..3bfaf91854bb 100644 --- a/tools/aapt2/link/XmlReferenceLinker_test.cpp +++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp @@ -31,37 +31,40 @@ public: .setNameManglerPolicy( NameManglerPolicy{ u"com.app.test", { u"com.android.support" } }) .setSymbolTable(test::StaticSymbolTableBuilder() - .addSymbol(u"@android:attr/layout_width", ResourceId(0x01010000), + .addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_DIMENSION) .addItem(u"match_parent", 0xffffffff) .build()) - .addSymbol(u"@android:attr/background", ResourceId(0x01010001), + .addPublicSymbol(u"@android:attr/background", ResourceId(0x01010001), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) - .addSymbol(u"@android:attr/attr", ResourceId(0x01010002), + .addPublicSymbol(u"@android:attr/attr", ResourceId(0x01010002), test::AttributeBuilder().build()) - .addSymbol(u"@android:attr/text", ResourceId(0x01010003), + .addPublicSymbol(u"@android:attr/text", ResourceId(0x01010003), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_STRING) .build()) // Add one real symbol that was introduces in v21 - .addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435), + .addPublicSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435), test::AttributeBuilder().build()) - .addSymbol(u"@android:id/id", ResourceId(0x01030000)) + // Private symbol. + .addSymbol(u"@android:color/hidden", ResourceId(0x01020001)) + + .addPublicSymbol(u"@android:id/id", ResourceId(0x01030000)) .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f030000)) .addSymbol(u"@com.app.test:color/green", ResourceId(0x7f020000)) .addSymbol(u"@com.app.test:color/red", ResourceId(0x7f020001)) .addSymbol(u"@com.app.test:attr/colorAccent", ResourceId(0x7f010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) - .addSymbol(u"@com.app.test:attr/com.android.support$colorAccent", + .addPublicSymbol(u"@com.app.test:attr/com.android.support$colorAccent", ResourceId(0x7f010001), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) - .addSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002), + .addPublicSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002), test::AttributeBuilder().build()) .build()) .build(); @@ -71,23 +74,8 @@ protected: std::unique_ptr<IAaptContext> mContext; }; -static xml::Element* getRootElement(XmlResource* doc) { - xml::Node* node = doc->root.get(); - while (xml::nodeCast<xml::Namespace>(node)) { - if (node->children.empty()) { - return nullptr; - } - node = node->children.front().get(); - } - - if (xml::Element* el = xml::nodeCast<xml::Element>(node)) { - return el; - } - return nullptr; -} - TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( <View xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:background="@color/green" @@ -97,7 +85,7 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { XmlReferenceLinker linker; ASSERT_TRUE(linker.consume(mContext.get(), doc.get())); - xml::Element* viewEl = getRootElement(doc.get()); + xml::Element* viewEl = xml::findRootElement(doc.get()); ASSERT_NE(viewEl, nullptr); xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", @@ -132,8 +120,26 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { ASSERT_EQ(xmlAttr->compiledValue, nullptr); } +TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + android:colorAccent="@android:color/hidden" />)EOF"); + + XmlReferenceLinker linker; + ASSERT_FALSE(linker.consume(mContext.get(), doc.get())); +} + +TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( + <View xmlns:android="http://schemas.android.com/apk/res/android" + android:colorAccent="@*android:color/hidden" />)EOF"); + + XmlReferenceLinker linker; + ASSERT_TRUE(linker.consume(mContext.get(), doc.get())); +} + TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( <View xmlns:android="http://schemas.android.com/apk/res/android" android:colorAccent="#ffffff" />)EOF"); @@ -143,14 +149,14 @@ TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) { } TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( <View xmlns:support="http://schemas.android.com/apk/res/com.android.support" support:colorAccent="#ff0000" />)EOF"); XmlReferenceLinker linker; ASSERT_TRUE(linker.consume(mContext.get(), doc.get())); - xml::Element* viewEl = getRootElement(doc.get()); + xml::Element* viewEl = xml::findRootElement(doc.get()); ASSERT_NE(viewEl, nullptr); xml::Attribute* xmlAttr = viewEl->findAttribute( @@ -162,14 +168,14 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) { } TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( <View xmlns:app="http://schemas.android.com/apk/res-auto" app:colorAccent="@app:color/red" />)EOF"); XmlReferenceLinker linker; ASSERT_TRUE(linker.consume(mContext.get(), doc.get())); - xml::Element* viewEl = getRootElement(doc.get()); + xml::Element* viewEl = xml::findRootElement(doc.get()); ASSERT_NE(viewEl, nullptr); xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res-auto", @@ -185,7 +191,7 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) { } TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( <View xmlns:app="http://schemas.android.com/apk/res/android" app:attr="@app:id/id"> <View xmlns:app="http://schemas.android.com/apk/res/com.app.test" @@ -195,7 +201,7 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) { XmlReferenceLinker linker; ASSERT_TRUE(linker.consume(mContext.get(), doc.get())); - xml::Element* viewEl = getRootElement(doc.get()); + xml::Element* viewEl = xml::findRootElement(doc.get()); ASSERT_NE(viewEl, nullptr); // All attributes and references in this element should be referring to "android" (0x01). @@ -225,14 +231,14 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) { } TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) { - std::unique_ptr<XmlResource> doc = test::buildXmlDom(R"EOF( + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF( <View xmlns:android="http://schemas.android.com/apk/res/com.app.test" android:attr="@id/id"/>)EOF"); XmlReferenceLinker linker; ASSERT_TRUE(linker.consume(mContext.get(), doc.get())); - xml::Element* viewEl = getRootElement(doc.get()); + xml::Element* viewEl = xml::findRootElement(doc.get()); ASSERT_NE(viewEl, nullptr); // All attributes and references in this element should be referring to "com.app.test" (0x7f). diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h index 24ad05d14cec..a2528d2ac195 100644 --- a/tools/aapt2/process/IResourceTableConsumer.h +++ b/tools/aapt2/process/IResourceTableConsumer.h @@ -49,25 +49,13 @@ struct IResourceTableConsumer { }; namespace xml { -struct Node; +struct XmlResource; } -struct XmlResource { - ResourceFile file; - std::unique_ptr<xml::Node> root; -}; - struct IXmlResourceConsumer { virtual ~IXmlResourceConsumer() = default; - virtual bool consume(IAaptContext* context, XmlResource* resource) = 0; -}; - -struct IPackageDeclStack { - virtual ~IPackageDeclStack() = default; - - virtual Maybe<ResourceName> transformPackage(const ResourceName& name, - const StringPiece16& localPackage) const = 0; + virtual bool consume(IAaptContext* context, xml::XmlResource* resource) = 0; }; } // namespace aapt diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index bb33ea78f496..d04181d583bc 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -51,6 +51,7 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>(); symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value()); + symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic); if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) { const ConfigDescription kDefaultConfig; @@ -158,6 +159,7 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa } if (s) { + s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; mCache.put(name, s); return s.get(); } @@ -165,6 +167,44 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa return nullptr; } +static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) { + android::ResTable::resource_name resName; + if (!table.getResourceName(id.id, true, &resName)) { + return {}; + } + + ResourceName name; + if (resName.package) { + name.package = StringPiece16(resName.package, resName.packageLen).toString(); + } + + const ResourceType* type; + if (resName.type) { + type = parseResourceType(StringPiece16(resName.type, resName.typeLen)); + + } else if (resName.type8) { + type = parseResourceType(util::utf8ToUtf16(StringPiece(resName.type8, resName.typeLen))); + } else { + return {}; + } + + if (!type) { + return {}; + } + + name.type = *type; + + if (resName.name) { + name.entry = StringPiece16(resName.name, resName.nameLen).toString(); + } else if (resName.name8) { + name.entry = util::utf8ToUtf16(StringPiece(resName.name8, resName.nameLen)); + } else { + return {}; + } + + return name; +} + const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById( ResourceId id) { if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) { @@ -174,22 +214,16 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa for (const auto& asset : mAssets) { const android::ResTable& table = asset->getResources(false); - android::ResTable::resource_name name; - if (!table.getResourceName(id.id, true, &name)) { + Maybe<ResourceName> maybeName = getResourceName(table, id); + if (!maybeName) { continue; } - bool isAttr = false; - if (name.type) { - if (const ResourceType* t = parseResourceType(StringPiece16(name.type, name.typeLen))) { - isAttr = (*t == ResourceType::kAttr); - } - } else if (name.type8) { - isAttr = (StringPiece(name.type8, name.typeLen) == "attr"); - } + uint32_t typeSpecFlags = 0; + table.getResourceFlags(id.id, &typeSpecFlags); std::shared_ptr<Symbol> s; - if (isAttr) { + if (maybeName.value().type == ResourceType::kAttr) { s = lookupAttributeInTable(table, id); } else { s = std::make_shared<Symbol>(); @@ -197,6 +231,7 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa } if (s) { + s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; mIdCache.put(id, s); return s.get(); } diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index 89cd9725227a..9ca694a7ef61 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -19,10 +19,9 @@ #include "ResourceTable.h" #include "ResourceValues.h" -#include "XmlDom.h" -#include "util/Util.h" - #include "test/Common.h" +#include "util/Util.h" +#include "xml/XmlDom.h" #include <memory> @@ -212,15 +211,22 @@ public: } }; -inline std::unique_ptr<XmlResource> buildXmlDom(const StringPiece& str) { +inline std::unique_ptr<xml::XmlResource> buildXmlDom(const StringPiece& str) { std::stringstream in; in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str; StdErrDiagnostics diag; - std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, {}); + std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, {}); assert(doc); return doc; } +inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context, + const StringPiece& str) { + std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str); + doc->file.name.package = context->getCompilationPackage().toString(); + return doc; +} + } // namespace test } // namespace aapt diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h index 4fa49186a09f..555a53959737 100644 --- a/tools/aapt2/test/Context.h +++ b/tools/aapt2/test/Context.h @@ -135,8 +135,19 @@ private: std::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>(); public: + StaticSymbolTableBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id, + std::unique_ptr<Attribute> attr = {}) { + std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>( + id, std::move(attr)); + symbol->isPublic = true; + mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get(); + mSymbolTable->mIdMap[id] = symbol.get(); + mSymbolTable->mSymbols.push_back(std::move(symbol)); + return *this; + } + StaticSymbolTableBuilder& addSymbol(const StringPiece16& name, ResourceId id, - std::unique_ptr<Attribute> attr = {}) { + std::unique_ptr<Attribute> attr = {}) { std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>( id, std::move(attr)); mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get(); diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp index 304833481be0..49625b56acbb 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.cpp +++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp @@ -97,19 +97,19 @@ bool BinaryResourceParser::parse() { return !error; } -bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) { +Maybe<Reference> BinaryResourceParser::getSymbol(const void* data) { if (!mSymbolEntries || mSymbolEntryCount == 0) { - return false; + return {}; } if ((uintptr_t) data < (uintptr_t) mData) { - return false; + return {}; } // We only support 32 bit offsets right now. const uintptr_t offset = (uintptr_t) data - (uintptr_t) mData; if (offset > std::numeric_limits<uint32_t>::max()) { - return false; + return {}; } for (size_t i = 0; i < mSymbolEntryCount; i++) { @@ -118,24 +118,23 @@ bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbo const StringPiece16 str = util::getString( mSymbolPool, util::deviceToHost32(mSymbolEntries[i].name.index)); - StringPiece16 typeStr; - ResourceUtils::extractResourceName(str, &outSymbol->package, &typeStr, - &outSymbol->entry); - const ResourceType* type = parseResourceType(typeStr); - if (!type) { - return false; + ResourceNameRef nameRef; + bool privateRef = false; + if (!ResourceUtils::parseResourceName(str, &nameRef, &privateRef)) { + return {}; } - outSymbol->type = *type; - // Since we scan the symbol table in order, we can start looking for the // next symbol from this point. mSymbolEntryCount -= i + 1; mSymbolEntries += i + 1; - return true; + + Reference ref(nameRef); + ref.privateReference = privateRef; + return Maybe<Reference>(std::move(ref)); } } - return false; + return {}; } /** @@ -566,7 +565,13 @@ bool BinaryResourceParser::parseType(const ResourceTablePackage* package, resourceValue = parseValue(name, config, value, entry->flags); } - assert(resourceValue && "failed to interpret valid resource"); + if (!resourceValue) { + mContext->getDiagnostics()->error(DiagMessage(mSource) + << "failed to parse value for resource " << name + << " (" << resId << ") with configuration '" + << config << "'"); + return false; + } Source source = mSource; if (sourceBlock) { @@ -657,7 +662,7 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na 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; + Reference::Type::kResource : Reference::Type::kAttribute; if (data != 0) { // This is a normal reference. @@ -665,9 +670,9 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na } // This reference has an invalid ID. Check if it is an unresolved symbol. - ResourceNameRef symbol; - if (getSymbol(&value->data, &symbol)) { - return util::make_unique<Reference>(symbol, type); + if (Maybe<Reference> ref = getSymbol(&value->data)) { + ref.value().referenceType = type; + return util::make_unique<Reference>(std::move(ref.value())); } // This is not an unresolved symbol, so it must be the magic @null reference. @@ -715,10 +720,8 @@ std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& n if (util::deviceToHost32(map->parent.ident) == 0) { // The parent is either not set or it is an unresolved symbol. // Check to see if it is a symbol. - ResourceNameRef symbol; - if (getSymbol(&map->parent.ident, &symbol)) { - style->parent = Reference(symbol.toResourceName()); - } + style->parent = getSymbol(&map->parent.ident); + } else { // The parent is a regular reference to a resource. style->parent = Reference(util::deviceToHost32(map->parent.ident)); @@ -731,10 +734,14 @@ std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& n if (util::deviceToHost32(mapEntry.name.ident) == 0) { // The map entry's key (attribute) is not set. This must be // a symbol reference, so resolve it. - ResourceNameRef symbol; - bool result = getSymbol(&mapEntry.name.ident, &symbol); - assert(result); - styleEntry.key.name = symbol.toResourceName(); + Maybe<Reference> symbol = getSymbol(&mapEntry.name.ident); + if (!symbol) { + mContext->getDiagnostics()->error(DiagMessage(mSource) + << "unresolved style attribute"); + return {}; + } + styleEntry.key = std::move(symbol.value()); + } else { // The map entry's key (attribute) is a regular reference. styleEntry.key.id = ResourceId(util::deviceToHost32(mapEntry.name.ident)); @@ -742,7 +749,9 @@ std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& n // Parse the attribute's value. styleEntry.value = parseValue(name, config, &mapEntry.value, 0); - assert(styleEntry.value); + if (!styleEntry.value) { + return {}; + } } return style; } @@ -773,10 +782,14 @@ std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef if (util::deviceToHost32(mapEntry.name.ident) == 0) { // The map entry's key (id) is not set. This must be // a symbol reference, so resolve it. - ResourceNameRef symbolName; - bool result = getSymbol(&mapEntry.name.ident, &symbolName); - assert(result); - symbol.symbol.name = symbolName.toResourceName(); + Maybe<Reference> ref = getSymbol(&mapEntry.name.ident); + if (!ref) { + mContext->getDiagnostics()->error(DiagMessage(mSource) + << "unresolved attribute symbol"); + return {}; + } + symbol.symbol = std::move(ref.value()); + } else { // The map entry's key (id) is a regular reference. symbol.symbol.id = ResourceId(util::deviceToHost32(mapEntry.name.ident)); @@ -808,10 +821,14 @@ std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNa if (util::deviceToHost32(mapEntry.name.ident) == 0) { // The map entry's key (attribute) is not set. This must be // a symbol reference, so resolve it. - ResourceNameRef symbol; - bool result = getSymbol(&mapEntry.name.ident, &symbol); - assert(result); - styleable->entries.emplace_back(symbol); + Maybe<Reference> ref = getSymbol(&mapEntry.name.ident); + if (!ref) { + mContext->getDiagnostics()->error(DiagMessage(mSource) + << "unresolved styleable symbol"); + return {}; + } + styleable->entries.emplace_back(std::move(ref.value())); + } else { // The map entry's key (attribute) is a regular reference. styleable->entries.emplace_back(util::deviceToHost32(mapEntry.name.ident)); @@ -826,6 +843,9 @@ std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& 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 android::ResTable_map::ATTR_ZERO: diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h index 02c4081cc0e2..73fcf52d0607 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.h +++ b/tools/aapt2/unflatten/BinaryResourceParser.h @@ -57,7 +57,7 @@ public: private: // Helper method to retrieve the symbol name for a given table offset specified // as a pointer. - bool getSymbol(const void* data, ResourceNameRef* outSymbol); + Maybe<Reference> getSymbol(const void* data); bool parseTable(const android::ResChunk_header* chunk); bool parseSymbolTable(const android::ResChunk_header* chunk); diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index 59b838587a6a..9ecc974d3a1c 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -28,9 +28,6 @@ namespace aapt { namespace util { -constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto"; -constexpr const char16_t* kSchemaPrefix = u"http://schemas.android.com/apk/res/"; - static std::vector<std::string> splitAndTransform(const StringPiece& str, char sep, const std::function<char(char)>& f) { std::vector<std::string> parts; @@ -467,18 +464,6 @@ std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer) { return data; } -Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri) { - if (stringStartsWith<char16_t>(namespaceUri, kSchemaPrefix)) { - StringPiece16 schemaPrefix = kSchemaPrefix; - StringPiece16 package = namespaceUri; - return package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size()) - .toString(); - } else if (namespaceUri == kSchemaAuto) { - return std::u16string(); - } - return {}; -} - bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix, StringPiece16* outEntry, StringPiece16* outSuffix) { if (!stringStartsWith<char16_t>(path, u"res/")) { diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 324afb3c7de3..a89861928585 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -331,15 +331,6 @@ inline uint32_t deviceToHost32(uint32_t value) { } /** - * Returns a package name if the namespace URI is of the form: - * http://schemas.android.com/apk/res/<package> - * - * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, - * returns an empty package name. - */ -Maybe<std::u16string> extractPackageFromNamespace(const std::u16string& namespaceUri); - -/** * Given a path like: res/xml-sw600dp/foo.xml * * Extracts "res/xml-sw600dp/" into outPrefix. diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp index b769c7620658..d27b62fd99fb 100644 --- a/tools/aapt2/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ -#include "util/Util.h" #include "XmlDom.h" #include "XmlPullParser.h" +#include "util/Util.h" #include <cassert> +#include <expat.h> #include <memory> #include <stack> #include <string> @@ -317,6 +318,10 @@ std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnost return util::make_unique<XmlResource>(ResourceFile{}, std::move(root)); } +Element* findRootElement(XmlResource* doc) { + return findRootElement(doc->root.get()); +} + Element* findRootElement(Node* node) { if (!node) { return nullptr; @@ -397,5 +402,39 @@ std::vector<Element*> Element::getChildElements() { return elements; } +void PackageAwareVisitor::visit(Namespace* ns) { + bool added = false; + if (Maybe<ExtractedPackage> maybePackage = extractPackageFromNamespace(ns->namespaceUri)) { + ExtractedPackage& package = maybePackage.value(); + mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, std::move(package) }); + added = true; + } + + Visitor::visit(ns); + + if (added) { + mPackageDecls.pop_back(); + } +} + +Maybe<ExtractedPackage> PackageAwareVisitor::transformPackageAlias( + const StringPiece16& alias, const StringPiece16& localPackage) const { + if (alias.empty()) { + return ExtractedPackage{ localPackage.toString(), false /* private */ }; + } + + const auto rend = mPackageDecls.rend(); + for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) { + if (alias == iter->prefix) { + if (iter->package.package.empty()) { + return ExtractedPackage{ localPackage.toString(), + iter->package.privateNamespace }; + } + return iter->package; + } + } + return {}; +} + } // namespace xml } // namespace aapt diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/xml/XmlDom.h index 721bf5bd6320..033b0a4d031c 100644 --- a/tools/aapt2/XmlDom.h +++ b/tools/aapt2/xml/XmlDom.h @@ -22,11 +22,9 @@ #include "ResourceValues.h" #include "util/StringPiece.h" #include "util/Util.h" - -#include "process/IResourceTableConsumer.h" +#include "xml/XmlUtil.h" #include <istream> -#include <expat.h> #include <memory> #include <string> #include <vector> @@ -34,21 +32,9 @@ namespace aapt { namespace xml { -constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android"; - struct RawVisitor; /** - * The type of node. Can be used to downcast to the concrete XML node - * class. - */ -enum class NodeType { - kNamespace, - kElement, - kText, -}; - -/** * Base class for all XML nodes. */ struct Node { @@ -58,9 +44,10 @@ struct Node { std::u16string comment; std::vector<std::unique_ptr<Node>> children; + virtual ~Node() = default; + void addChild(std::unique_ptr<Node> child); virtual void accept(RawVisitor* visitor) = 0; - virtual ~Node() {} }; /** @@ -122,6 +109,14 @@ struct Text : public BaseNode<Text> { }; /** + * An XML resource with a source, name, and XML tree. + */ +struct XmlResource { + ResourceFile file; + std::unique_ptr<xml::Node> root; +}; + +/** * Inflates an XML DOM from a text stream, logging errors to the logger. * Returns the root node on success, or nullptr on failure. */ @@ -134,6 +129,7 @@ std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag, const Source& source); +Element* findRootElement(XmlResource* doc); Element* findRootElement(Node* node); /** @@ -180,7 +176,7 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack { private: struct PackageDecl { std::u16string prefix; - std::u16string package; + ExtractedPackage package; }; std::vector<PackageDecl> mPackageDecls; @@ -188,44 +184,9 @@ private: public: using Visitor::visit; - void visit(Namespace* ns) override { - bool added = false; - { - Maybe<std::u16string> package = util::extractPackageFromNamespace(ns->namespaceUri); - if (package) { - mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, package.value() }); - added = true; - } - } - - Visitor::visit(ns); - - if (added) { - mPackageDecls.pop_back(); - } - } - - Maybe<ResourceName> transformPackage(const ResourceName& name, - const StringPiece16& localPackage) const override { - if (name.package.empty()) { - return ResourceName{ localPackage.toString(), name.type, name.entry }; - } - - const auto rend = mPackageDecls.rend(); - for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) { - if (name.package == iter->prefix) { - if (iter->package.empty()) { - if (localPackage != name.package) { - return ResourceName{ localPackage.toString(), name.type, name.entry }; - } - } else if (iter->package != name.package) { - return ResourceName{ iter->package, name.type, name.entry }; - } - break; - } - } - return {}; - } + void visit(Namespace* ns) override; + Maybe<ExtractedPackage> transformPackageAlias( + const StringPiece16& alias, const StringPiece16& localPackage) const override; }; // Implementations diff --git a/tools/aapt2/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index a1b9ed067aaa..431ee2c8fb46 100644 --- a/tools/aapt2/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "XmlDom.h" +#include "xml/XmlDom.h" #include <gtest/gtest.h> #include <sstream> @@ -38,7 +38,7 @@ TEST(XmlDomTest, Inflate) { const Source source = { "test.xml" }; StdErrDiagnostics diag; - std::unique_ptr<XmlResource> doc = xml::inflate(&in, &diag, source); + std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, source); ASSERT_NE(doc, nullptr); xml::Namespace* ns = xml::nodeCast<xml::Namespace>(doc->root.get()); diff --git a/tools/aapt2/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp index cff935c10aae..323ec05b5f2c 100644 --- a/tools/aapt2/XmlPullParser.cpp +++ b/tools/aapt2/xml/XmlPullParser.cpp @@ -16,12 +16,14 @@ #include "util/Maybe.h" #include "util/Util.h" -#include "XmlPullParser.h" +#include "xml/XmlPullParser.h" +#include "xml/XmlUtil.h" #include <iostream> #include <string> namespace aapt { +namespace xml { constexpr char kXmlNamespaceSep = 1; @@ -72,14 +74,14 @@ XmlPullParser::Event XmlPullParser::next() { // Record namespace prefixes and package names so that we can do our own // handling of references that use namespace aliases. if (event == Event::kStartNamespace || event == Event::kEndNamespace) { - Maybe<std::u16string> result = util::extractPackageFromNamespace(getNamespaceUri()); + Maybe<ExtractedPackage> result = extractPackageFromNamespace(getNamespaceUri()); if (event == Event::kStartNamespace) { if (result) { - mPackageAliases.emplace_back(getNamespacePrefix(), result.value()); + mPackageAliases.emplace_back( + PackageDecl{ getNamespacePrefix(), std::move(result.value()) }); } } else { if (result) { - assert(mPackageAliases.back().second == result.value()); mPackageAliases.pop_back(); } } @@ -131,20 +133,20 @@ const std::u16string& XmlPullParser::getNamespaceUri() const { return mEventQueue.front().data2; } -Maybe<ResourceName> XmlPullParser::transformPackage( - const ResourceName& name, const StringPiece16& localPackage) const { - if (name.package.empty()) { - return ResourceName{ localPackage.toString(), name.type, name.entry }; +Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias( + const StringPiece16& alias, const StringPiece16& localPackage) const { + if (alias.empty()) { + return ExtractedPackage{ localPackage.toString(), false /* private */ }; } const auto endIter = mPackageAliases.rend(); for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) { - if (name.package == iter->first) { - if (iter->second.empty()) { - return ResourceName{ localPackage.toString(), name.type, name.entry }; - } else { - return ResourceName{ iter->second, name.type, name.entry }; + if (alias == iter->prefix) { + if (iter->package.package.empty()) { + return ExtractedPackage{ localPackage.toString(), + iter->package.privateNamespace }; } + return iter->package; } } return {}; @@ -283,4 +285,24 @@ void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comme }); } +Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name) { + auto iter = parser->findAttribute(u"", name); + if (iter != parser->endAttributes()) { + return StringPiece16(util::trimWhitespace(iter->value)); + } + return {}; +} + +Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name) { + auto iter = parser->findAttribute(u"", name); + if (iter != parser->endAttributes()) { + StringPiece16 trimmed = util::trimWhitespace(iter->value); + if (!trimmed.empty()) { + return trimmed; + } + } + return {}; +} + +} // namespace xml } // namespace aapt diff --git a/tools/aapt2/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h index a0ce21dd3218..7e7070e5e5ea 100644 --- a/tools/aapt2/XmlPullParser.h +++ b/tools/aapt2/xml/XmlPullParser.h @@ -17,11 +17,11 @@ #ifndef AAPT_XML_PULL_PARSER_H #define AAPT_XML_PULL_PARSER_H -#include "util/Maybe.h" #include "Resource.h" -#include "util/StringPiece.h" - #include "process/IResourceTableConsumer.h" +#include "util/Maybe.h" +#include "util/StringPiece.h" +#include "xml/XmlUtil.h" #include <algorithm> #include <expat.h> @@ -33,6 +33,7 @@ #include <vector> namespace aapt { +namespace xml { class XmlPullParser : public IPackageDeclStack { public: @@ -60,7 +61,7 @@ public: static bool isGoodEvent(Event event); XmlPullParser(std::istream& in); - virtual ~XmlPullParser(); + ~XmlPullParser(); /** * Returns the current event that is being processed. @@ -95,6 +96,13 @@ public: const std::u16string& getNamespacePrefix() const; const std::u16string& getNamespaceUri() const; + // + // These are available for StartElement and EndElement. + // + + const std::u16string& getElementNamespace() const; + const std::u16string& getElementName() const; + /* * Uses the current stack of namespaces to resolve the package. Eg: * xmlns:app = "http://schemas.android.com/apk/res/com.android.app" @@ -106,17 +114,8 @@ public: * If xmlns:app="http://schemas.android.com/apk/res-auto", then * 'package' will be set to 'defaultPackage'. */ - // - - // - // These are available for StartElement and EndElement. - // - - const std::u16string& getElementNamespace() const; - const std::u16string& getElementName() const; - - Maybe<ResourceName> transformPackage(const ResourceName& name, - const StringPiece16& localPackage) const override; + Maybe<ExtractedPackage> transformPackageAlias( + const StringPiece16& alias, const StringPiece16& localPackage) const override; // // Remaining methods are for retrieving information about attributes @@ -169,9 +168,25 @@ private: const std::u16string mEmpty; size_t mDepth; std::stack<std::u16string> mNamespaceUris; - std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases; + + struct PackageDecl { + std::u16string prefix; + ExtractedPackage package; + }; + std::vector<PackageDecl> mPackageAliases; }; +/** + * Finds the attribute in the current element within the global namespace. + */ +Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name); + +/** + * Finds the attribute in the current element within the global namespace. The attribute's value + * must not be the empty string. + */ +Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name); + // // Implementation // @@ -277,6 +292,7 @@ inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 return endIter; } +} // namespace xml } // namespace aapt #endif // AAPT_XML_PULL_PARSER_H diff --git a/tools/aapt2/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp index 1c99a432f597..8fa2c6d274c8 100644 --- a/tools/aapt2/XmlPullParser_test.cpp +++ b/tools/aapt2/xml/XmlPullParser_test.cpp @@ -15,7 +15,7 @@ */ #include "util/StringPiece.h" -#include "XmlPullParser.h" +#include "xml/XmlPullParser.h" #include <gtest/gtest.h> #include <sstream> @@ -26,30 +26,30 @@ TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) { std::stringstream str; str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>"; - XmlPullParser parser(str); + xml::XmlPullParser parser(str); const size_t depthOuter = parser.getDepth(); - ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthOuter)); + ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthOuter)); - EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent()); + EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); EXPECT_EQ(StringPiece16(u"a"), StringPiece16(parser.getElementName())); const size_t depthA = parser.getDepth(); - ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthA)); - EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent()); + ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthA)); + EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); EXPECT_EQ(StringPiece16(u"b"), StringPiece16(parser.getElementName())); const size_t depthB = parser.getDepth(); - ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB)); - EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent()); + ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB)); + EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); EXPECT_EQ(StringPiece16(u"c"), StringPiece16(parser.getElementName())); - ASSERT_TRUE(XmlPullParser::nextChildNode(&parser, depthB)); - EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.getEvent()); + ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB)); + EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); EXPECT_EQ(StringPiece16(u"e"), StringPiece16(parser.getElementName())); - ASSERT_FALSE(XmlPullParser::nextChildNode(&parser, depthOuter)); - EXPECT_EQ(XmlPullParser::Event::kEndDocument, parser.getEvent()); + ASSERT_FALSE(xml::XmlPullParser::nextChildNode(&parser, depthOuter)); + EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.getEvent()); } } // namespace aapt diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp new file mode 100644 index 000000000000..ab9f544d67ea --- /dev/null +++ b/tools/aapt2/xml/XmlUtil.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util/Maybe.h" +#include "util/Util.h" +#include "xml/XmlUtil.h" + +#include <string> + +namespace aapt { +namespace xml { + +Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri) { + if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPublicPrefix)) { + StringPiece16 schemaPrefix = kSchemaPublicPrefix; + StringPiece16 package = namespaceUri; + package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size()); + if (package.empty()) { + return {}; + } + return ExtractedPackage{ package.toString(), false /* isPrivate */ }; + + } else if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPrivatePrefix)) { + StringPiece16 schemaPrefix = kSchemaPrivatePrefix; + StringPiece16 package = namespaceUri; + package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size()); + if (package.empty()) { + return {}; + } + return ExtractedPackage{ package.toString(), true /* isPrivate */ }; + + } else if (namespaceUri == kSchemaAuto) { + return ExtractedPackage{ std::u16string(), true /* isPrivate */ }; + } + return {}; +} + +void transformReferenceFromNamespace(IPackageDeclStack* declStack, + const StringPiece16& localPackage, Reference* inRef) { + if (inRef->name) { + if (Maybe<ExtractedPackage> transformedPackage = + declStack->transformPackageAlias(inRef->name.value().package, localPackage)) { + ExtractedPackage& extractedPackage = transformedPackage.value(); + inRef->name.value().package = std::move(extractedPackage.package); + + // If the reference was already private (with a * prefix) and the namespace is public, + // we keep the reference private. + inRef->privateReference |= extractedPackage.privateNamespace; + } + } +} + +} // namespace xml +} // namespace aapt diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h new file mode 100644 index 000000000000..98e5520a6ea2 --- /dev/null +++ b/tools/aapt2/xml/XmlUtil.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AAPT_XML_XMLUTIL_H +#define AAPT_XML_XMLUTIL_H + +#include "ResourceValues.h" +#include "util/Maybe.h" + +#include <string> + +namespace aapt { +namespace xml { + +constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto"; +constexpr const char16_t* kSchemaPublicPrefix = u"http://schemas.android.com/apk/res/"; +constexpr const char16_t* kSchemaPrivatePrefix = u"http://schemas.android.com/apk/prv/res/"; +constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android"; + +/** + * Result of extracting a package name from a namespace URI declaration. + */ +struct ExtractedPackage { + /** + * The name of the package. This can be the empty string, which means that the package + * should be assumed to be the package being compiled. + */ + std::u16string package; + + /** + * True if the package's private namespace was declared. This means that private resources + * are made visible. + */ + bool privateNamespace; +}; + +/** + * Returns an ExtractedPackage struct if the namespace URI is of the form: + * http://schemas.android.com/apk/res/<package> or + * http://schemas.android.com/apk/prv/res/<package> + * + * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, + * returns an empty package name. + */ +Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri); + +/** + * Interface representing a stack of XML namespace declarations. When looking up the package + * for a namespace prefix, the stack is checked from top to bottom. + */ +struct IPackageDeclStack { + virtual ~IPackageDeclStack() = default; + + /** + * Returns an ExtractedPackage struct if the alias given corresponds with a package declaration. + */ + virtual Maybe<ExtractedPackage> transformPackageAlias( + const StringPiece16& alias, const StringPiece16& localPackage) const = 0; +}; + +/** + * Helper function for transforming the original Reference inRef to a fully qualified reference + * via the IPackageDeclStack. This will also mark the Reference as private if the namespace of + * the package declaration was private. + */ +void transformReferenceFromNamespace(IPackageDeclStack* declStack, + const StringPiece16& localPackage, Reference* inRef); + +} // namespace xml +} // namespace aapt + +#endif /* AAPT_XML_XMLUTIL_H */ diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp new file mode 100644 index 000000000000..7796b3ea7691 --- /dev/null +++ b/tools/aapt2/xml/XmlUtil_test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test/Common.h" +#include "xml/XmlUtil.h" + +#include <gtest/gtest.h> + +namespace aapt { + +TEST(XmlUtilTest, ExtractPackageFromNamespace) { + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"com.android")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace( + u"http://schemas.android.com/apk/prv/res/")); + + Maybe<xml::ExtractedPackage> p = + xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/a"); + AAPT_ASSERT_TRUE(p); + EXPECT_EQ(std::u16string(u"a"), p.value().package); + EXPECT_EQ(false, p.value().privateNamespace); + + p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/android"); + AAPT_ASSERT_TRUE(p); + EXPECT_EQ(std::u16string(u"android"), p.value().package); + EXPECT_EQ(true, p.value().privateNamespace); + + p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/com.test"); + AAPT_ASSERT_TRUE(p); + EXPECT_EQ(std::u16string(u"com.test"), p.value().package); + EXPECT_EQ(true, p.value().privateNamespace); + + p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res-auto"); + AAPT_ASSERT_TRUE(p); + EXPECT_EQ(std::u16string(), p.value().package); + EXPECT_EQ(true, p.value().privateNamespace); +} + +} // namespace aapt |