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
This commit is contained in:
Adam Lesinski
2015-11-16 17:35:44 -08:00
parent abf83cbe4f
commit 467f171315
54 changed files with 1306 additions and 765 deletions

View File

@@ -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 \

View File

@@ -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));

View File

@@ -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;

View File

@@ -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"

View File

@@ -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)) {
ResourceNameRef name;
if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
&name, &priv)) {
return false;
}
const ResourceType* parsedType = parseResourceType(type);
if (!parsedType) {
if (create && priv) {
return false;
}
if (entry.empty()) {
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) {

View File

@@ -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.

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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()));

View File

@@ -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,
};
};

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -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();
}

View File

@@ -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"

View File

@@ -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;

View File

@@ -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);
};

View File

@@ -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. -->

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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>
@@ -27,6 +29,14 @@ class ResourceTable;
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;
};
/**
@@ -68,15 +78,6 @@ struct PrivateAttributeMover : public IResourceTableConsumer {
bool consume(IAaptContext* context, ResourceTable* table) override;
};
/**
* 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

View File

@@ -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)

View File

@@ -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

View File

@@ -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" />

View File

@@ -15,7 +15,6 @@
*/
#include "ResourceTable.h"
#include "link/Linkers.h"
#include <algorithm>

View File

@@ -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;
}
}
}

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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();

View File

@@ -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).

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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:

View File

@@ -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);

View File

@@ -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/")) {

View File

@@ -330,15 +330,6 @@ inline uint32_t deviceToHost32(uint32_t value) {
return dtohl(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
*

View File

@@ -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

View File

@@ -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,20 +32,8 @@
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.
*/
@@ -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() {}
};
/**
@@ -121,6 +108,14 @@ struct Text : public BaseNode<Text> {
std::u16string 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

View File

@@ -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());

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

85
tools/aapt2/xml/XmlUtil.h Normal file
View File

@@ -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 */

View File

@@ -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