Merge "DO NOT MERGE: AAPT2: Allow undefined resources (placeholders)" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2f84c4715d
@@ -159,7 +159,7 @@ cc_library_host_shared {
|
||||
// ==========================================================
|
||||
cc_test_host {
|
||||
name: "aapt2_tests",
|
||||
srcs: ["**/*_test.cpp"],
|
||||
srcs: ["test/Common.cpp", "**/*_test.cpp"],
|
||||
static_libs: ["libaapt2", "libgmock"],
|
||||
defaults: ["aapt_defaults"],
|
||||
}
|
||||
|
||||
@@ -623,6 +623,11 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
|
||||
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>(
|
||||
|
||||
@@ -78,42 +78,32 @@ class ResourceParser {
|
||||
* 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_;
|
||||
|
||||
@@ -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 @@ TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
|
||||
|
||||
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 @@ TEST_F(ResourceParserTest, ParseNull) {
|
||||
// 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 @@ TEST_F(ResourceParserTest, ParseAttr) {
|
||||
// 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 @@ TEST_F(ResourceParserTest, ParseBagElement) {
|
||||
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
|
||||
|
||||
@@ -305,21 +305,25 @@ std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
|
||||
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 @@ uint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
|
||||
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 @@ std::unique_ptr<Item> TryParseItemForAttribute(
|
||||
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(
|
||||
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 @@ std::unique_ptr<Item> TryParseItemForAttribute(
|
||||
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 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config
|
||||
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.
|
||||
|
||||
@@ -133,7 +133,16 @@ std::unique_ptr<Reference> TryParseReference(const android::StringPiece& str,
|
||||
* 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
|
||||
|
||||
@@ -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 @@ TEST(ResourceUtilsTest, ParseEmptyFlag) {
|
||||
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
|
||||
|
||||
@@ -94,8 +94,8 @@ bool Reference::Equals(const Value* value) const {
|
||||
|
||||
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 @@ Reference* Reference::Clone(StringPool* /*new_pool*/) const {
|
||||
}
|
||||
|
||||
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 @@ BinaryPrimitive* BinaryPrimitive::Clone(StringPool* /*new_pool*/) const {
|
||||
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);
|
||||
|
||||
@@ -180,4 +180,14 @@ TEST(ResourceValuesTest, StyleMerges) {
|
||||
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
|
||||
|
||||
@@ -38,4 +38,7 @@
|
||||
<attr name="android:text" />
|
||||
<attr name="layout_width" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- Declare an empty, resource -->
|
||||
<item type="drawable" name="undefined" />
|
||||
</resources>
|
||||
|
||||
@@ -296,7 +296,10 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
|
||||
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);
|
||||
|
||||
@@ -343,26 +343,19 @@ class PackagePbDeserializer {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,8 +190,7 @@ class PbSerializerVisitor : public RawValueVisitor {
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,7 @@ class ResourceTableBuilder {
|
||||
|
||||
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 @@ class ResourceTableBuilder {
|
||||
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 @@ class ResourceTableBuilder {
|
||||
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 @@ class ResourceTableBuilder {
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ResourceTableBuilder);
|
||||
|
||||
DummyDiagnosticsImpl diagnostics_;
|
||||
std::unique_ptr<ResourceTable> table_ = util::make_unique<ResourceTable>();
|
||||
};
|
||||
|
||||
|
||||
60
tools/aapt2/test/Common.cpp
Normal file
60
tools/aapt2/test/Common.cpp
Normal file
@@ -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
|
||||
@@ -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 @@ inline ConfigDescription ParseConfigOrDie(const android::StringPiece& str) {
|
||||
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 @@ T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece&
|
||||
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, {});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user