AAPT2: Allow undefined resources (placeholders)
A resource defined like so:
<item type="drawable" name="foo" />
should be assigned the value @null.
The only exception is for <string> resources, which are given the
empty string value (since <string></string> is ambiguous). The decision
to use "" is based off the fact that old AAPT used to assign "" to all
undefined resources, even non-string ones.
Bug: 38425050
Test: make aapt2_tests
Change-Id: Ib3e0f6f83d16ddd8b279c9fd44a07a37867b85e9
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 064d864..1dc47f8 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -160,7 +160,10 @@
// ==========================================================
cc_test_host {
name: "aapt2_tests",
- srcs: ["**/*_test.cpp"],
+ srcs: [
+ "test/Common.cpp",
+ "**/*_test.cpp",
+ ],
static_libs: [
"libaapt2",
"libgmock",
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 084325a..9a37913 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -623,6 +623,11 @@
return std::move(string);
}
+ // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
+ if (util::TrimWhitespace(raw_value).empty()) {
+ return ResourceUtils::MakeNull();
+ }
+
if (allow_raw_value) {
// We can't parse this so return a RawString if we are allowed.
return util::make_unique<RawString>(
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 8258019..5631dc2 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -78,42 +78,32 @@
* Item, then a
* RawString is returned. Otherwise this returns false;
*/
- std::unique_ptr<Item> ParseXml(xml::XmlPullParser* parser,
- const uint32_t type_mask,
+ std::unique_ptr<Item> ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask,
const bool allow_raw_value);
bool ParseResources(xml::XmlPullParser* parser);
bool ParseResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource,
- uint32_t format);
+ bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format);
bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool ParsePublicGroup(xml::XmlPullParser* parser,
- ParsedResource* out_resource);
- bool ParseSymbolImpl(xml::XmlPullParser* parser,
- ParsedResource* out_resource);
+ bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool ParseAddResource(xml::XmlPullParser* parser,
- ParsedResource* out_resource);
+ bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
- bool weak);
+ bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
const android::StringPiece& tag);
bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
ParsedResource* out_resource);
bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
- bool ParseDeclareStyleable(xml::XmlPullParser* parser,
- ParsedResource* out_resource);
+ bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
- bool ParseIntegerArray(xml::XmlPullParser* parser,
- ParsedResource* out_resource);
- bool ParseStringArray(xml::XmlPullParser* parser,
- ParsedResource* out_resource);
- bool ParseArrayImpl(xml::XmlPullParser* parser, ParsedResource* out_resource,
- uint32_t typeMask);
+ bool ParseIntegerArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseStringArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseArrayImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t typeMask);
bool ParsePlural(xml::XmlPullParser* parser, ParsedResource* out_resource);
IDiagnostics* diag_;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index e60ef66..5352ca8 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -25,19 +25,20 @@
#include "test/Test.h"
#include "xml/XmlPullParser.h"
+using ::aapt::test::ValueEq;
using ::android::StringPiece;
using ::testing::Eq;
using ::testing::NotNull;
+using ::testing::Pointee;
namespace aapt {
-constexpr const char* kXmlPreamble =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::stringstream input(kXmlPreamble);
- input << "<attr name=\"foo\"/>" << std::endl;
+ input << R"(<attr name="foo"/>)" << std::endl;
ResourceTable table;
ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {});
xml::XmlPullParser xml_parser(input);
@@ -46,19 +47,20 @@
class ResourceParserTest : public ::testing::Test {
public:
- void SetUp() override { context_ = test::ContextBuilder().Build(); }
+ void SetUp() override {
+ context_ = test::ContextBuilder().Build();
+ }
::testing::AssertionResult TestParse(const StringPiece& str) {
return TestParse(str, ConfigDescription{});
}
- ::testing::AssertionResult TestParse(const StringPiece& str,
- const ConfigDescription& config) {
+ ::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) {
std::stringstream input(kXmlPreamble);
input << "<resources>\n" << str << "\n</resources>" << std::endl;
ResourceParserOptions parserOptions;
- ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"},
- config, parserOptions);
+ ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config,
+ parserOptions);
xml::XmlPullParser xmlParser(input);
if (parser.Parse(&xmlParser)) {
return ::testing::AssertionSuccess();
@@ -205,18 +207,18 @@
// a non-existing value, and this causes problems in styles when trying to
// resolve an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE
// with a data value of 0.
- BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
- ASSERT_NE(nullptr, integer);
- EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
- EXPECT_EQ(0u, integer->value.data);
+ Reference* null_ref = test::GetValue<Reference>(&table_, "integer/foo");
+ ASSERT_THAT(null_ref, NotNull());
+ EXPECT_FALSE(null_ref->name);
+ EXPECT_FALSE(null_ref->id);
+ EXPECT_EQ(Reference::Type::kResource, null_ref->reference_type);
}
TEST_F(ResourceParserTest, ParseEmpty) {
std::string input = "<integer name=\"foo\">@empty</integer>";
ASSERT_TRUE(TestParse(input));
- BinaryPrimitive* integer =
- test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
+ BinaryPrimitive* integer = test::GetValue<BinaryPrimitive>(&table_, "integer/foo");
ASSERT_NE(nullptr, integer);
EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType);
EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data);
@@ -241,22 +243,18 @@
// ultimately
// stored them with the default configuration. Check that we have the same
// behavior.
-TEST_F(ResourceParserTest,
- ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
+TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
- std::string input = R"EOF(
- <attr name="foo" />
- <declare-styleable name="bar">
- <attr name="baz" />
- </declare-styleable>)EOF";
+ std::string input = R"(
+ <attr name="foo" />
+ <declare-styleable name="bar">
+ <attr name="baz" />
+ </declare-styleable>)";
ASSERT_TRUE(TestParse(input, watch_config));
- EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo",
- watch_config));
- EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz",
- watch_config));
- EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>(
- &table_, "styleable/bar", watch_config));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/foo", watch_config));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<Attribute>(&table_, "attr/baz", watch_config));
+ EXPECT_EQ(nullptr, test::GetValueForConfig<Styleable>(&table_, "styleable/bar", watch_config));
EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/foo"));
EXPECT_NE(nullptr, test::GetValue<Attribute>(&table_, "attr/baz"));
@@ -833,4 +831,16 @@
EXPECT_NE(nullptr, ValueCast<RawString>(val->entries[0].value.get()));
}
+TEST_F(ResourceParserTest, ParseElementWithNoValue) {
+ std::string input = R"(
+ <item type="drawable" format="reference" name="foo" />
+ <string name="foo" />)";
+ ASSERT_TRUE(TestParse(input));
+ ASSERT_THAT(test::GetValue(&table_, "drawable/foo"), Pointee(ValueEq(Reference())));
+
+ String* str = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_THAT(str, NotNull());
+ EXPECT_THAT(*str->value, Eq(""));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 818c8ce..deeef6e 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -305,21 +305,25 @@
return {};
}
-std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const StringPiece& str) {
- StringPiece trimmed_str(util::TrimWhitespace(str));
- android::Res_value value = {};
+std::unique_ptr<Item> TryParseNullOrEmpty(const StringPiece& str) {
+ const StringPiece trimmed_str(util::TrimWhitespace(str));
if (trimmed_str == "@null") {
- // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
- // Instead we set the data type to TYPE_REFERENCE with a value of 0.
- value.dataType = android::Res_value::TYPE_REFERENCE;
+ return MakeNull();
} else if (trimmed_str == "@empty") {
- // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime.
- value.dataType = android::Res_value::TYPE_NULL;
- value.data = android::Res_value::DATA_NULL_EMPTY;
- } else {
- return {};
+ return MakeEmpty();
}
- return util::make_unique<BinaryPrimitive>(value);
+ return {};
+}
+
+std::unique_ptr<Reference> MakeNull() {
+ // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
+ // Instead we set the data type to TYPE_REFERENCE with a value of 0.
+ return util::make_unique<Reference>();
+}
+
+std::unique_ptr<BinaryPrimitive> MakeEmpty() {
+ return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_NULL,
+ android::Res_value::DATA_NULL_EMPTY);
}
std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
@@ -569,13 +573,15 @@
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& value, uint32_t type_mask,
const std::function<void(const ResourceName&)>& on_create_reference) {
- std::unique_ptr<BinaryPrimitive> null_or_empty = TryParseNullOrEmpty(value);
+ using android::ResTable_map;
+
+ auto null_or_empty = TryParseNullOrEmpty(value);
if (null_or_empty) {
- return std::move(null_or_empty);
+ return null_or_empty;
}
bool create = false;
- std::unique_ptr<Reference> reference = TryParseReference(value, &create);
+ auto reference = TryParseReference(value, &create);
if (reference) {
if (create && on_create_reference) {
on_create_reference(reference->name.value());
@@ -583,39 +589,37 @@
return std::move(reference);
}
- if (type_mask & android::ResTable_map::TYPE_COLOR) {
+ if (type_mask & ResTable_map::TYPE_COLOR) {
// Try parsing this as a color.
- std::unique_ptr<BinaryPrimitive> color = TryParseColor(value);
+ auto color = TryParseColor(value);
if (color) {
return std::move(color);
}
}
- if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
+ if (type_mask & ResTable_map::TYPE_BOOLEAN) {
// Try parsing this as a boolean.
- std::unique_ptr<BinaryPrimitive> boolean = TryParseBool(value);
+ auto boolean = TryParseBool(value);
if (boolean) {
return std::move(boolean);
}
}
- if (type_mask & android::ResTable_map::TYPE_INTEGER) {
+ if (type_mask & ResTable_map::TYPE_INTEGER) {
// Try parsing this as an integer.
- std::unique_ptr<BinaryPrimitive> integer = TryParseInt(value);
+ auto integer = TryParseInt(value);
if (integer) {
return std::move(integer);
}
}
- const uint32_t float_mask = android::ResTable_map::TYPE_FLOAT |
- android::ResTable_map::TYPE_DIMENSION |
- android::ResTable_map::TYPE_FRACTION;
+ const uint32_t float_mask =
+ ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION;
if (type_mask & float_mask) {
// Try parsing this as a float.
- std::unique_ptr<BinaryPrimitive> floating_point = TryParseFloat(value);
+ auto floating_point = TryParseFloat(value);
if (floating_point) {
- if (type_mask &
- AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
+ if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
return std::move(floating_point);
}
}
@@ -630,24 +634,25 @@
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& str, const Attribute* attr,
const std::function<void(const ResourceName&)>& on_create_reference) {
+ using android::ResTable_map;
+
const uint32_t type_mask = attr->type_mask;
- std::unique_ptr<Item> value =
- TryParseItemForAttribute(str, type_mask, on_create_reference);
+ auto value = TryParseItemForAttribute(str, type_mask, on_create_reference);
if (value) {
return value;
}
- if (type_mask & android::ResTable_map::TYPE_ENUM) {
+ if (type_mask & ResTable_map::TYPE_ENUM) {
// Try parsing this as an enum.
- std::unique_ptr<BinaryPrimitive> enum_value = TryParseEnumSymbol(attr, str);
+ auto enum_value = TryParseEnumSymbol(attr, str);
if (enum_value) {
return std::move(enum_value);
}
}
- if (type_mask & android::ResTable_map::TYPE_FLAGS) {
+ if (type_mask & ResTable_map::TYPE_FLAGS) {
// Try parsing this as a flag.
- std::unique_ptr<BinaryPrimitive> flag_value = TryParseFlagSymbol(attr, str);
+ auto flag_value = TryParseFlagSymbol(attr, str);
if (flag_value) {
return std::move(flag_value);
}
@@ -655,8 +660,7 @@
return {};
}
-std::string BuildResourceFileName(const ResourceFile& res_file,
- const NameMangler* mangler) {
+std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) {
std::stringstream out;
out << "res/" << res_file.name.type;
if (res_file.config != ConfigDescription{}) {
@@ -719,9 +723,9 @@
ref_type = Reference::Type::kAttribute;
}
- if (data == 0) {
+ if (data == 0u) {
// A reference of 0, must be the magic @null reference.
- return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_REFERENCE, 0u);
+ return util::make_unique<Reference>();
}
// This is a normal reference.
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index da0fc8e..36f6c2b 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -133,7 +133,16 @@
* Returns a BinaryPrimitve object representing @null or @empty if the string
* was parsed as one.
*/
-std::unique_ptr<BinaryPrimitive> TryParseNullOrEmpty(const android::StringPiece& str);
+std::unique_ptr<Item> TryParseNullOrEmpty(const android::StringPiece& str);
+
+// Returns a Reference representing @null.
+// Due to runtime compatibility issues, this is encoded as a reference with ID 0.
+// The runtime will convert this to TYPE_NULL.
+std::unique_ptr<Reference> MakeNull();
+
+// Returns a BinaryPrimitive representing @empty. This is encoded as a Res_value with
+// type Res_value::TYPE_NULL and data Res_value::DATA_NULL_EMPTY.
+std::unique_ptr<BinaryPrimitive> MakeEmpty();
/*
* Returns a BinaryPrimitve object representing a color if the string was parsed
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 048c692..cdc34f1 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -19,6 +19,9 @@
#include "Resource.h"
#include "test/Test.h"
+using ::aapt::test::ValueEq;
+using ::testing::Pointee;
+
namespace aapt {
TEST(ResourceUtilsTest, ParseBool) {
@@ -200,4 +203,22 @@
EXPECT_EQ(0u, result->value.data);
}
+TEST(ResourceUtilsTest, NullIsEmptyReference) {
+ auto null_value = ResourceUtils::MakeNull();
+ ASSERT_THAT(null_value, Pointee(ValueEq(Reference())));
+
+ auto value = ResourceUtils::TryParseNullOrEmpty("@null");
+ ASSERT_THAT(value, Pointee(ValueEq(Reference())));
+}
+
+TEST(ResourceUtilsTest, EmptyIsBinaryPrimitive) {
+ auto empty_value = ResourceUtils::MakeEmpty();
+ ASSERT_THAT(empty_value, Pointee(ValueEq(BinaryPrimitive(android::Res_value::TYPE_NULL,
+ android::Res_value::DATA_NULL_EMPTY))));
+
+ auto value = ResourceUtils::TryParseNullOrEmpty("@empty");
+ ASSERT_THAT(value, Pointee(ValueEq(BinaryPrimitive(android::Res_value::TYPE_NULL,
+ android::Res_value::DATA_NULL_EMPTY))));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index abfdec4..e808984 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -94,8 +94,8 @@
bool Reference::Flatten(android::Res_value* out_value) const {
const ResourceId resid = id.value_or_default(ResourceId(0));
- const bool dynamic =
- (resid.package_id() != kFrameworkPackageId && resid.package_id() != kAppPackageId);
+ const bool dynamic = resid.is_valid_dynamic() && resid.package_id() != kFrameworkPackageId &&
+ resid.package_id() != kAppPackageId;
if (reference_type == Reference::Type::kResource) {
if (dynamic) {
@@ -119,22 +119,29 @@
}
void Reference::Print(std::ostream* out) const {
- *out << "(reference) ";
- if (reference_type == Reference::Type::kResource) {
- *out << "@";
- if (private_reference) {
- *out << "*";
+ if (reference_type == Type::kResource) {
+ *out << "(reference) @";
+ if (!name && !id) {
+ *out << "null";
+ return;
}
} else {
- *out << "?";
+ *out << "(attr-reference) ?";
+ }
+
+ if (private_reference) {
+ *out << "*";
}
if (name) {
*out << name.value();
}
- if (id && !Res_INTERNALID(id.value().id)) {
- *out << " " << id.value();
+ if (id && id.value().is_valid_dynamic()) {
+ if (name) {
+ *out << " ";
+ }
+ *out << id.value();
}
}
@@ -314,7 +321,11 @@
void BinaryPrimitive::Print(std::ostream* out) const {
switch (value.dataType) {
case android::Res_value::TYPE_NULL:
- *out << "(null)";
+ if (value.data == android::Res_value::DATA_NULL_EMPTY) {
+ *out << "(empty)";
+ } else {
+ *out << "(null)";
+ }
break;
case android::Res_value::TYPE_INT_DEC:
*out << "(integer) " << static_cast<int32_t>(value.data);
diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp
index e154d93..69f8e67 100644
--- a/tools/aapt2/ResourceValues_test.cpp
+++ b/tools/aapt2/ResourceValues_test.cpp
@@ -180,4 +180,14 @@
EXPECT_TRUE(a->Equals(expected.get()));
}
+// TYPE_NULL is encoded as TYPE_REFERENCE with a value of 0. This is represented in AAPT2
+// by a default constructed Reference value.
+TEST(ResourcesValuesTest, EmptyReferenceFlattens) {
+ android::Res_value value = {};
+ ASSERT_TRUE(Reference().Flatten(&value));
+
+ EXPECT_EQ(android::Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(0x0u, value.data);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/test.xml b/tools/aapt2/integration-tests/AppOne/res/values/test.xml
index 5104f82..2c9e8b8 100644
--- a/tools/aapt2/integration-tests/AppOne/res/values/test.xml
+++ b/tools/aapt2/integration-tests/AppOne/res/values/test.xml
@@ -38,4 +38,7 @@
<attr name="android:text" />
<attr name="layout_width" />
</declare-styleable>
+
+ <!-- Declare an empty, resource -->
+ <item type="drawable" name="undefined" />
</resources>
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 18498e3..a8e510c 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -296,7 +296,10 @@
IAaptContext* context, SymbolTable* symbols,
xml::IPackageDeclStack* decls) {
CHECK(reference != nullptr);
- CHECK(reference->name || reference->id);
+ if (!reference->name && !reference->id) {
+ // This is @null.
+ return true;
+ }
Reference transformed_reference = *reference;
TransformReferenceFromNamespace(decls, context->GetCompilationPackage(), &transformed_reference);
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 1d0041b..4b56192 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -343,26 +343,19 @@
return value;
}
- bool DeserializeReferenceFromPb(const pb::Reference& pb_ref,
- Reference* out_ref) {
+ bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* out_ref) {
out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
out_ref->private_reference = pb_ref.private_();
- if (!pb_ref.has_id() && !pb_ref.has_symbol_idx()) {
- return false;
- }
-
if (pb_ref.has_id()) {
out_ref->id = ResourceId(pb_ref.id());
}
if (pb_ref.has_symbol_idx()) {
- const std::string str_symbol =
- util::GetString(*symbol_pool_, pb_ref.symbol_idx());
+ const std::string str_symbol = util::GetString(*symbol_pool_, pb_ref.symbol_idx());
ResourceNameRef name_ref;
if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) {
- diag_->Error(DiagMessage(source_) << "invalid reference name '"
- << str_symbol << "'");
+ diag_->Error(DiagMessage(source_) << "invalid reference name '" << str_symbol << "'");
return false;
}
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index de10bb8..d87d64e 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -190,8 +190,7 @@
}
if (ref.name) {
- StringPool::Ref symbol_ref =
- symbol_pool_->MakeRef(ref.name.value().ToString());
+ StringPool::Ref symbol_ref = symbol_pool_->MakeRef(ref.name.value().ToString());
pb_ref->set_symbol_idx(static_cast<uint32_t>(symbol_ref.index()));
}
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 02acedb..f3da780 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -59,8 +59,7 @@
ResourceTableBuilder& AddReference(const android::StringPiece& name, const ResourceId& id,
const android::StringPiece& ref) {
- return AddValue(name, id,
- util::make_unique<Reference>(ParseNameOrDie(ref)));
+ return AddValue(name, id, util::make_unique<Reference>(ParseNameOrDie(ref)));
}
ResourceTableBuilder& AddString(const android::StringPiece& name,
@@ -111,8 +110,8 @@
ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config,
const ResourceId& id, std::unique_ptr<Value> value) {
ResourceName res_name = ParseNameOrDie(name);
- CHECK(table_->AddResourceAllowMangled(res_name, id, config, {},
- std::move(value), &diagnostics_));
+ CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value),
+ GetDiagnostics()));
return *this;
}
@@ -122,7 +121,7 @@
Symbol symbol;
symbol.state = state;
symbol.allow_new = allow_new;
- CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, &diagnostics_));
+ CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics()));
return *this;
}
@@ -131,7 +130,6 @@
private:
DISALLOW_COPY_AND_ASSIGN(ResourceTableBuilder);
- DummyDiagnosticsImpl diagnostics_;
std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>();
};
diff --git a/tools/aapt2/test/Common.cpp b/tools/aapt2/test/Common.cpp
new file mode 100644
index 0000000..0fabbc4
--- /dev/null
+++ b/tools/aapt2/test/Common.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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"
+
+namespace aapt {
+namespace test {
+
+struct DummyDiagnosticsImpl : public IDiagnostics {
+ void Log(Level level, DiagMessageActual& actual_msg) override {
+ switch (level) {
+ case Level::Note:
+ return;
+
+ case Level::Warn:
+ std::cerr << actual_msg.source << ": warn: " << actual_msg.message << "." << std::endl;
+ break;
+
+ case Level::Error:
+ std::cerr << actual_msg.source << ": error: " << actual_msg.message << "." << std::endl;
+ break;
+ }
+ }
+};
+
+IDiagnostics* GetDiagnostics() {
+ static DummyDiagnosticsImpl diag;
+ return &diag;
+}
+
+template <>
+Value* GetValueForConfigAndProduct<Value>(ResourceTable* table,
+ const android::StringPiece& res_name,
+ const ConfigDescription& config,
+ const android::StringPiece& product) {
+ Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
+ if (result) {
+ ResourceConfigValue* config_value = result.value().entry->FindValue(config, product);
+ if (config_value) {
+ return config_value->value.get();
+ }
+ }
+ return nullptr;
+}
+
+} // namespace test
+} // namespace aapt
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index a937de8..0585148 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -46,27 +46,7 @@
namespace aapt {
namespace test {
-struct DummyDiagnosticsImpl : public IDiagnostics {
- void Log(Level level, DiagMessageActual& actual_msg) override {
- switch (level) {
- case Level::Note:
- return;
-
- case Level::Warn:
- std::cerr << actual_msg.source << ": warn: " << actual_msg.message << "." << std::endl;
- break;
-
- case Level::Error:
- std::cerr << actual_msg.source << ": error: " << actual_msg.message << "." << std::endl;
- break;
- }
- }
-};
-
-inline IDiagnostics* GetDiagnostics() {
- static DummyDiagnosticsImpl diag;
- return &diag;
-}
+IDiagnostics* GetDiagnostics();
inline ResourceName ParseNameOrDie(const android::StringPiece& str) {
ResourceNameRef ref;
@@ -80,7 +60,7 @@
return config;
}
-template <typename T>
+template <typename T = Value>
T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece& res_name,
const ConfigDescription& config,
const android::StringPiece& product) {
@@ -94,13 +74,19 @@
return nullptr;
}
-template <typename T>
+template <>
+Value* GetValueForConfigAndProduct<Value>(ResourceTable* table,
+ const android::StringPiece& res_name,
+ const ConfigDescription& config,
+ const android::StringPiece& product);
+
+template <typename T = Value>
T* GetValueForConfig(ResourceTable* table, const android::StringPiece& res_name,
const ConfigDescription& config) {
return GetValueForConfigAndProduct<T>(table, res_name, config, {});
}
-template <typename T>
+template <typename T = Value>
T* GetValue(ResourceTable* table, const android::StringPiece& res_name) {
return GetValueForConfig<T>(table, res_name, {});
}