summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt2/Android.bp1
-rw-r--r--tools/aapt2/Resource.cpp3
-rw-r--r--tools/aapt2/Resource.h1
-rw-r--r--tools/aapt2/ResourceParser.cpp110
-rw-r--r--tools/aapt2/ResourceParser.h20
-rw-r--r--tools/aapt2/ResourceParser_test.cpp84
-rw-r--r--tools/aapt2/ResourceTable.cpp3
-rw-r--r--tools/aapt2/ResourceUtils.cpp9
-rw-r--r--tools/aapt2/ResourceUtils.h4
-rw-r--r--tools/aapt2/ResourceValues.cpp234
-rw-r--r--tools/aapt2/ResourceValues.h98
-rw-r--r--tools/aapt2/ResourceValues_test.cpp12
-rw-r--r--tools/aapt2/Resources.proto37
-rw-r--r--tools/aapt2/StringPool.h4
-rw-r--r--tools/aapt2/ValueTransformer.cpp51
-rw-r--r--tools/aapt2/ValueTransformer.h130
-rw-r--r--tools/aapt2/ValueTransformer_inline.h47
-rw-r--r--tools/aapt2/ValueVisitor.h6
-rw-r--r--tools/aapt2/cmd/Link.cpp11
-rw-r--r--tools/aapt2/cmd/Link_test.cpp106
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator.cpp3
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp7
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp5
-rw-r--r--tools/aapt2/format/binary/XmlFlattener_test.cpp2
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.cpp41
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp35
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp34
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk5
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/App/Android.mk3
-rw-r--r--tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk3
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp5
-rw-r--r--tools/aapt2/java/JavaClassGenerator_test.cpp27
-rw-r--r--tools/aapt2/java/ProguardRules_test.cpp2
-rw-r--r--tools/aapt2/link/AutoVersioner.cpp3
-rw-r--r--tools/aapt2/link/Linkers.h4
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp366
-rw-r--r--tools/aapt2/link/ReferenceLinker.h60
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp18
-rw-r--r--tools/aapt2/link/TableMerger.cpp11
-rw-r--r--tools/aapt2/link/XmlCompatVersioner.cpp9
-rw-r--r--tools/aapt2/link/XmlCompatVersioner_test.cpp8
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp46
-rw-r--r--tools/aapt2/link/XmlReferenceLinker_test.cpp20
-rw-r--r--tools/aapt2/split/TableSplitter.cpp4
-rw-r--r--tools/aapt2/test/Context.h3
-rw-r--r--tools/aapt2/xml/XmlDom.cpp3
-rw-r--r--tools/aapt2/xml/XmlPullParser.cpp4
-rw-r--r--tools/aapt2/xml/XmlPullParser.h12
-rw-r--r--tools/fonts/Android.bp9
49 files changed, 1319 insertions, 404 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 3937cee99e3b..12dc156f75be 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -162,6 +162,7 @@ cc_library_host_static {
"Configuration.proto",
"Resources.proto",
"ResourcesInternal.proto",
+ "ValueTransformer.cpp",
],
proto: {
export_proto_headers: true,
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index b78f48ce7f17..6364ccdd09e5 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -78,6 +78,8 @@ StringPiece to_string(ResourceType type) {
return "interpolator";
case ResourceType::kLayout:
return "layout";
+ case ResourceType::kMacro:
+ return "macro";
case ResourceType::kMenu:
return "menu";
case ResourceType::kMipmap:
@@ -119,6 +121,7 @@ static const std::map<StringPiece, ResourceType> sResourceTypeMap{
{"integer", ResourceType::kInteger},
{"interpolator", ResourceType::kInterpolator},
{"layout", ResourceType::kLayout},
+ {"macro", ResourceType::kMacro},
{"menu", ResourceType::kMenu},
{"mipmap", ResourceType::kMipmap},
{"navigation", ResourceType::kNavigation},
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cf938703e1e9..307c21d9dc96 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -57,6 +57,7 @@ enum class ResourceType {
kInteger,
kInterpolator,
kLayout,
+ kMacro,
kMenu,
kMipmap,
kNavigation,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 24c60b740bc3..1efabbb46fd5 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -627,6 +627,16 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
}
return true;
+ } else if (resource_type == "macro") {
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name() << "> missing 'name' attribute");
+ return false;
+ }
+
+ out_resource->name.type = ResourceType::kMacro;
+ out_resource->name.entry = maybe_name.value().to_string();
+ return ParseMacro(parser, out_resource);
}
if (can_be_item) {
@@ -726,6 +736,24 @@ bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
return true;
}
+std::optional<FlattenedXmlSubTree> ResourceParser::CreateFlattenSubTree(
+ xml::XmlPullParser* parser) {
+ const size_t begin_xml_line = parser->line_number();
+
+ std::string raw_value;
+ StyleString style_string;
+ std::vector<UntranslatableSection> untranslatable_sections;
+ if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) {
+ return {};
+ }
+
+ return FlattenedXmlSubTree{.raw_value = raw_value,
+ .style_string = style_string,
+ .untranslatable_sections = untranslatable_sections,
+ .namespace_resolver = parser,
+ .source = source_.WithLine(begin_xml_line)};
+}
+
/**
* Reads the entire XML subtree and attempts to parse it as some Item,
* with typeMask denoting which items it can be. If allowRawValue is
@@ -733,42 +761,46 @@ bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
* an Item. If allowRawValue is false, nullptr is returned in this
* case.
*/
-std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
- const uint32_t type_mask,
+std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask,
const bool allow_raw_value) {
- const size_t begin_xml_line = parser->line_number();
-
- std::string raw_value;
- StyleString style_string;
- std::vector<UntranslatableSection> untranslatable_sections;
- if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) {
+ auto sub_tree = CreateFlattenSubTree(parser);
+ if (!sub_tree.has_value()) {
return {};
}
+ return ParseXml(sub_tree.value(), type_mask, allow_raw_value, *table_, config_, *diag_);
+}
- if (!style_string.spans.empty()) {
+std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub_tree,
+ const uint32_t type_mask, const bool allow_raw_value,
+ ResourceTable& table,
+ const android::ConfigDescription& config,
+ IDiagnostics& diag) {
+ if (!xmlsub_tree.style_string.spans.empty()) {
// This can only be a StyledString.
std::unique_ptr<StyledString> styled_string =
- util::make_unique<StyledString>(table_->string_pool.MakeRef(
- style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
- styled_string->untranslatable_sections = std::move(untranslatable_sections);
+ util::make_unique<StyledString>(table.string_pool.MakeRef(
+ xmlsub_tree.style_string,
+ StringPool::Context(StringPool::Context::kNormalPriority, config)));
+ styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
return std::move(styled_string);
}
auto on_create_reference = [&](const ResourceName& name) {
// name.package can be empty here, as it will assume the package name of the
// table.
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->SetSource(source_.WithLine(begin_xml_line));
- table_->AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), diag_);
+ auto id = util::make_unique<Id>();
+ id->SetSource(xmlsub_tree.source);
+ return table.AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), &diag);
};
// Process the raw value.
- std::unique_ptr<Item> processed_item =
- ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
+ std::unique_ptr<Item> processed_item = ResourceUtils::TryParseItemForAttribute(
+ xmlsub_tree.raw_value, type_mask, on_create_reference);
if (processed_item) {
// Fix up the reference.
- if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
- ResolvePackage(parser, ref);
+ if (auto ref = ValueCast<Reference>(processed_item.get())) {
+ ref->allow_raw = allow_raw_value;
+ ResolvePackage(xmlsub_tree.namespace_resolver, ref);
}
return processed_item;
}
@@ -777,17 +809,16 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
if (type_mask & android::ResTable_map::TYPE_STRING) {
// Use the trimmed, escaped string.
std::unique_ptr<String> string = util::make_unique<String>(
- table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
- string->untranslatable_sections = std::move(untranslatable_sections);
+ table.string_pool.MakeRef(xmlsub_tree.style_string.str, StringPool::Context(config)));
+ string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
return std::move(string);
}
if (allow_raw_value) {
// We can't parse this so return a RawString if we are allowed.
- return util::make_unique<RawString>(
- table_->string_pool.MakeRef(util::TrimWhitespace(raw_value),
- StringPool::Context(config_)));
- } else if (util::TrimWhitespace(raw_value).empty()) {
+ return util::make_unique<RawString>(table.string_pool.MakeRef(
+ util::TrimWhitespace(xmlsub_tree.raw_value), StringPool::Context(config)));
+ } else if (util::TrimWhitespace(xmlsub_tree.raw_value).empty()) {
// If the text is empty, and the value is not allowed to be a string, encode it as a @null.
return ResourceUtils::MakeNull();
}
@@ -850,6 +881,35 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser,
return true;
}
+bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ auto sub_tree = CreateFlattenSubTree(parser);
+ if (!sub_tree) {
+ return false;
+ }
+
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<macro> tags cannot be declared in configurations other than the default "
+ "configuration'");
+ return false;
+ }
+
+ auto macro = std::make_unique<Macro>();
+ macro->raw_value = std::move(sub_tree->raw_value);
+ macro->style_string = std::move(sub_tree->style_string);
+ macro->untranslatable_sections = std::move(sub_tree->untranslatable_sections);
+
+ for (const auto& decl : parser->package_decls()) {
+ macro->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = decl.prefix,
+ .package_name = decl.package.package,
+ .is_private = decl.package.private_namespace});
+ }
+
+ out_resource->value = std::move(macro);
+ return true;
+}
+
bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (options_.visibility) {
diag_->Error(DiagMessage(out_resource->source)
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index af0db8c0ba2c..5c92def50616 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -57,6 +57,14 @@ struct ResourceParserOptions {
Maybe<Visibility::Level> visibility;
};
+struct FlattenedXmlSubTree {
+ std::string raw_value;
+ StyleString style_string;
+ std::vector<UntranslatableSection> untranslatable_sections;
+ xml::IPackageDeclStack* namespace_resolver;
+ Source source;
+};
+
/*
* Parses an XML file for resources and adds them to a ResourceTable.
*/
@@ -67,9 +75,16 @@ class ResourceParser {
const ResourceParserOptions& options = {});
bool Parse(xml::XmlPullParser* parser);
+ static std::unique_ptr<Item> ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask,
+ bool allow_raw_value, ResourceTable& table,
+ const android::ConfigDescription& config,
+ IDiagnostics& diag);
+
private:
DISALLOW_COPY_AND_ASSIGN(ResourceParser);
+ std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
+
// Parses the XML subtree as a StyleString (flattened XML representation for strings with
// formatting). If parsing fails, false is returned and the out parameters are left in an
// unspecified state. Otherwise,
@@ -96,7 +111,7 @@ class ResourceParser {
bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format);
bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
-
+ bool ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
@@ -108,8 +123,7 @@ class ResourceParser {
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 ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 4a509be56776..279ebcba2f71 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -336,6 +336,90 @@ TEST_F(ResourceParserTest, ParseAttr) {
EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY));
}
+TEST_F(ResourceParserTest, ParseMacro) {
+ std::string input = R"(<macro name="foo">12345</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("12345"));
+ EXPECT_THAT(macro->style_string.str, Eq("12345"));
+ EXPECT_THAT(macro->style_string.spans, IsEmpty());
+ EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+ EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroUntranslatableSection) {
+ std::string input = R"(<macro name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+This being <b><xliff:g>human</xliff:g></b> is a guest house.</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("\nThis being human is a guest house."));
+ EXPECT_THAT(macro->style_string.str, Eq(" This being human is a guest house."));
+ EXPECT_THAT(macro->style_string.spans.size(), Eq(1));
+ EXPECT_THAT(macro->style_string.spans[0].name, Eq("b"));
+ EXPECT_THAT(macro->style_string.spans[0].first_char, Eq(12));
+ EXPECT_THAT(macro->style_string.spans[0].last_char, Eq(16));
+ ASSERT_THAT(macro->untranslatable_sections.size(), Eq(1));
+ EXPECT_THAT(macro->untranslatable_sections[0].start, Eq(12));
+ EXPECT_THAT(macro->untranslatable_sections[0].end, Eq(17));
+ EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroNamespaces) {
+ std::string input = R"(<macro name="foo" xmlns:app="http://schemas.android.com/apk/res/android">
+@app:string/foo</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("\n@app:string/foo"));
+ EXPECT_THAT(macro->style_string.str, Eq("@app:string/foo"));
+ EXPECT_THAT(macro->style_string.spans, IsEmpty());
+ EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+ EXPECT_THAT(macro->alias_namespaces.size(), Eq(1));
+ EXPECT_THAT(macro->alias_namespaces[0].alias, Eq("app"));
+ EXPECT_THAT(macro->alias_namespaces[0].package_name, Eq("android"));
+ EXPECT_THAT(macro->alias_namespaces[0].is_private, Eq(false));
+}
+
+TEST_F(ResourceParserTest, ParseMacroReference) {
+ std::string input = R"(<string name="res_string">@macro/foo</string>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Reference* macro = test::GetValue<Reference>(&table_, "string/res_string");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->type_flags, Eq(ResTable_map::TYPE_STRING));
+ EXPECT_THAT(macro->allow_raw, Eq(false));
+
+ input = R"(<style name="foo">
+ <item name="bar">@macro/foo</item>
+ </style>)";
+
+ ASSERT_TRUE(TestParse(input));
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_THAT(style, NotNull());
+ EXPECT_THAT(style->entries.size(), Eq(1));
+
+ macro = ValueCast<Reference>(style->entries[0].value.get());
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->type_flags, Eq(0U));
+ EXPECT_THAT(macro->allow_raw, Eq(true));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNoNameFail) {
+ std::string input = R"(<macro>12345</macro>)";
+ ASSERT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNonDefaultConfigurationFail) {
+ const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
+ std::string input = R"(<macro name="foo">12345</macro>)";
+ ASSERT_FALSE(TestParse(input, watch_config));
+}
+
// Old AAPT allowed attributes to be defined under different configurations, but ultimately
// stored them with the default configuration. Check that we have the same behavior.
TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 27f7bdd83c9e..45ea65430bb6 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -577,6 +577,7 @@ Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNam
std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
+ CloningValueTransformer cloner(&new_table->string_pool);
for (const auto& pkg : packages) {
ResourceTablePackage* new_pkg = new_table->FindOrCreatePackage(pkg->name);
for (const auto& type : pkg->types) {
@@ -593,7 +594,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
for (const auto& config_value : entry->values) {
ResourceConfigValue* new_value =
new_entry->FindOrCreateValue(config_value->config, config_value->product);
- new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
+ new_value->value = config_value->value->Transform(cloner);
}
}
}
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 5b43df6f0935..e0e80ac02dea 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -628,7 +628,7 @@ 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) {
+ const std::function<bool(const ResourceName&)>& on_create_reference) {
using android::ResTable_map;
auto null_or_empty = TryParseNullOrEmpty(value);
@@ -639,8 +639,11 @@ std::unique_ptr<Item> TryParseItemForAttribute(
bool create = false;
auto reference = TryParseReference(value, &create);
if (reference) {
+ reference->type_flags = type_mask;
if (create && on_create_reference) {
- on_create_reference(reference->name.value());
+ if (!on_create_reference(reference->name.value())) {
+ return {};
+ }
}
return std::move(reference);
}
@@ -689,7 +692,7 @@ 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) {
+ const std::function<bool(const ResourceName&)>& on_create_reference) {
using android::ResTable_map;
const uint32_t type_mask = attr->type_mask;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f77766ee9061..be493db8cee0 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -204,11 +204,11 @@ std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* enum_attr,
*/
std::unique_ptr<Item> TryParseItemForAttribute(
const android::StringPiece& value, const Attribute* attr,
- const std::function<void(const ResourceName&)>& on_create_reference = {});
+ const std::function<bool(const ResourceName&)>& on_create_reference = {});
std::unique_ptr<Item> TryParseItemForAttribute(
const android::StringPiece& value, uint32_t type_mask,
- const std::function<void(const ResourceName&)>& on_create_reference = {});
+ const std::function<bool(const ResourceName&)>& on_create_reference = {});
uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 4f0fa8ae29ba..2a90f267f185 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -47,6 +47,14 @@ std::ostream& operator<<(std::ostream& out, const Value& value) {
return out;
}
+std::unique_ptr<Value> Value::Transform(ValueTransformer& transformer) const {
+ return std::unique_ptr<Value>(this->TransformValueImpl(transformer));
+}
+
+std::unique_ptr<Item> Item::Transform(ValueTransformer& transformer) const {
+ return std::unique_ptr<Item>(this->TransformItemImpl(transformer));
+}
+
template <typename Derived>
void BaseValue<Derived>::Accept(ValueVisitor* visitor) {
visitor->Visit(static_cast<Derived*>(this));
@@ -77,13 +85,6 @@ bool RawString::Equals(const Value* value) const {
return *this->value == *other->value;
}
-RawString* RawString::Clone(StringPool* new_pool) const {
- RawString* rs = new RawString(new_pool->MakeRef(value));
- rs->comment_ = comment_;
- rs->source_ = source_;
- return rs;
-}
-
bool RawString::Flatten(android::Res_value* out_value) const {
out_value->dataType = android::Res_value::TYPE_STRING;
out_value->data = util::HostToDevice32(static_cast<uint32_t>(value.index()));
@@ -110,12 +111,15 @@ bool Reference::Equals(const Value* value) const {
if (!other) {
return false;
}
- return reference_type == other->reference_type &&
- private_reference == other->private_reference && id == other->id &&
- name == other->name;
+ return reference_type == other->reference_type && private_reference == other->private_reference &&
+ id == other->id && name == other->name && type_flags == other->type_flags;
}
bool Reference::Flatten(android::Res_value* out_value) const {
+ if (name && name.value().type == ResourceType::kMacro) {
+ return false;
+ }
+
const ResourceId resid = id.value_or_default(ResourceId(0));
const bool dynamic = resid.is_valid() && is_dynamic;
@@ -136,10 +140,6 @@ bool Reference::Flatten(android::Res_value* out_value) const {
return true;
}
-Reference* Reference::Clone(StringPool* /*new_pool*/) const {
- return new Reference(*this);
-}
-
void Reference::Print(std::ostream* out) const {
if (reference_type == Type::kResource) {
*out << "(reference) @";
@@ -220,10 +220,6 @@ bool Id::Flatten(android::Res_value* out) const {
return true;
}
-Id* Id::Clone(StringPool* /*new_pool*/) const {
- return new Id(*this);
-}
-
void Id::Print(std::ostream* out) const {
*out << "(id)";
}
@@ -266,14 +262,6 @@ bool String::Flatten(android::Res_value* out_value) const {
return true;
}
-String* String::Clone(StringPool* new_pool) const {
- String* str = new String(new_pool->MakeRef(value));
- str->comment_ = comment_;
- str->source_ = source_;
- str->untranslatable_sections = untranslatable_sections;
- return str;
-}
-
void String::Print(std::ostream* out) const {
*out << "(string) \"" << *value << "\"";
}
@@ -321,14 +309,6 @@ bool StyledString::Flatten(android::Res_value* out_value) const {
return true;
}
-StyledString* StyledString::Clone(StringPool* new_pool) const {
- StyledString* str = new StyledString(new_pool->MakeRef(value));
- str->comment_ = comment_;
- str->source_ = source_;
- str->untranslatable_sections = untranslatable_sections;
- return str;
-}
-
void StyledString::Print(std::ostream* out) const {
*out << "(styled string) \"" << value->value << "\"";
for (const StringPool::Span& span : value->spans) {
@@ -357,15 +337,6 @@ bool FileReference::Flatten(android::Res_value* out_value) const {
return true;
}
-FileReference* FileReference::Clone(StringPool* new_pool) const {
- FileReference* fr = new FileReference(new_pool->MakeRef(path));
- fr->file = file;
- fr->type = type;
- fr->comment_ = comment_;
- fr->source_ = source_;
- return fr;
-}
-
void FileReference::Print(std::ostream* out) const {
*out << "(file) " << *path;
switch (type) {
@@ -406,10 +377,6 @@ bool BinaryPrimitive::Flatten(::android::Res_value* out_value) const {
return true;
}
-BinaryPrimitive* BinaryPrimitive::Clone(StringPool* /*new_pool*/) const {
- return new BinaryPrimitive(*this);
-}
-
void BinaryPrimitive::Print(std::ostream* out) const {
*out << StringPrintf("(primitive) type=0x%02x data=0x%08x", value.dataType, value.data);
}
@@ -587,11 +554,7 @@ bool Attribute::IsCompatibleWith(const Attribute& attr) const {
return this_type_mask == that_type_mask;
}
-Attribute* Attribute::Clone(StringPool* /*new_pool*/) const {
- return new Attribute(*this);
-}
-
-std::string Attribute::MaskString() const {
+std::string Attribute::MaskString(uint32_t type_mask) {
if (type_mask == android::ResTable_map::TYPE_ANY) {
return "any";
}
@@ -690,6 +653,10 @@ std::string Attribute::MaskString() const {
return out.str();
}
+std::string Attribute::MaskString() const {
+ return MaskString(type_mask);
+}
+
void Attribute::Print(std::ostream* out) const {
*out << "(attr) " << MaskString();
@@ -893,18 +860,6 @@ bool Style::Equals(const Value* value) const {
});
}
-Style* Style::Clone(StringPool* new_pool) const {
- Style* style = new Style();
- style->parent = parent;
- style->parent_inferred = parent_inferred;
- style->comment_ = comment_;
- style->source_ = source_;
- for (auto& entry : entries) {
- style->entries.push_back(Entry{entry.key, std::unique_ptr<Item>(entry.value->Clone(new_pool))});
- }
- return style;
-}
-
void Style::Print(std::ostream* out) const {
*out << "(style) ";
if (parent && parent.value().name) {
@@ -920,7 +875,8 @@ void Style::Print(std::ostream* out) const {
Style::Entry CloneEntry(const Style::Entry& entry, StringPool* pool) {
Style::Entry cloned_entry{entry.key};
if (entry.value != nullptr) {
- cloned_entry.value.reset(entry.value->Clone(pool));
+ CloningValueTransformer cloner(pool);
+ cloned_entry.value = entry.value->Transform(cloner);
}
return cloned_entry;
}
@@ -993,16 +949,6 @@ bool Array::Equals(const Value* value) const {
});
}
-Array* Array::Clone(StringPool* new_pool) const {
- Array* array = new Array();
- array->comment_ = comment_;
- array->source_ = source_;
- for (auto& item : elements) {
- array->elements.emplace_back(std::unique_ptr<Item>(item->Clone(new_pool)));
- }
- return array;
-}
-
void Array::Print(std::ostream* out) const {
*out << "(array) [" << util::Joiner(elements, ", ") << "]";
}
@@ -1030,19 +976,6 @@ bool Plural::Equals(const Value* value) const {
return true;
}
-Plural* Plural::Clone(StringPool* new_pool) const {
- Plural* p = new Plural();
- p->comment_ = comment_;
- p->source_ = source_;
- const size_t count = values.size();
- for (size_t i = 0; i < count; i++) {
- if (values[i]) {
- p->values[i] = std::unique_ptr<Item>(values[i]->Clone(new_pool));
- }
- }
- return p;
-}
-
void Plural::Print(std::ostream* out) const {
*out << "(plural)";
if (values[Zero]) {
@@ -1086,15 +1019,26 @@ bool Styleable::Equals(const Value* value) const {
});
}
-Styleable* Styleable::Clone(StringPool* /*new_pool*/) const {
- return new Styleable(*this);
-}
-
void Styleable::Print(std::ostream* out) const {
*out << "(styleable) "
<< " [" << util::Joiner(entries, ", ") << "]";
}
+bool Macro::Equals(const Value* value) const {
+ const Macro* other = ValueCast<Macro>(value);
+ if (!other) {
+ return false;
+ }
+ return other->raw_value == raw_value && other->style_string.spans == style_string.spans &&
+ other->style_string.str == style_string.str &&
+ other->untranslatable_sections == untranslatable_sections &&
+ other->alias_namespaces == alias_namespaces;
+}
+
+void Macro::Print(std::ostream* out) const {
+ *out << "(macro) ";
+}
+
bool operator<(const Reference& a, const Reference& b) {
int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
if (cmp != 0) return cmp < 0;
@@ -1126,4 +1070,110 @@ void Styleable::MergeWith(Styleable* other) {
entries.insert(entries.end(), references.begin(), references.end());
}
+template <typename T>
+std::unique_ptr<T> CopyValueFields(std::unique_ptr<T> new_value, const T* value) {
+ new_value->SetSource(value->GetSource());
+ new_value->SetComment(value->GetComment());
+ return new_value;
+}
+
+CloningValueTransformer::CloningValueTransformer(StringPool* new_pool)
+ : ValueTransformer(new_pool) {
+}
+
+std::unique_ptr<Reference> CloningValueTransformer::TransformDerived(const Reference* value) {
+ return std::make_unique<Reference>(*value);
+}
+
+std::unique_ptr<Id> CloningValueTransformer::TransformDerived(const Id* value) {
+ return std::make_unique<Id>(*value);
+}
+
+std::unique_ptr<RawString> CloningValueTransformer::TransformDerived(const RawString* value) {
+ auto new_value = std::make_unique<RawString>(pool_->MakeRef(value->value));
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<String> CloningValueTransformer::TransformDerived(const String* value) {
+ auto new_value = std::make_unique<String>(pool_->MakeRef(value->value));
+ new_value->untranslatable_sections = value->untranslatable_sections;
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<StyledString> CloningValueTransformer::TransformDerived(const StyledString* value) {
+ auto new_value = std::make_unique<StyledString>(pool_->MakeRef(value->value));
+ new_value->untranslatable_sections = value->untranslatable_sections;
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<FileReference> CloningValueTransformer::TransformDerived(
+ const FileReference* value) {
+ auto new_value = std::make_unique<FileReference>(pool_->MakeRef(value->path));
+ new_value->file = value->file;
+ new_value->type = value->type;
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<BinaryPrimitive> CloningValueTransformer::TransformDerived(
+ const BinaryPrimitive* value) {
+ return std::make_unique<BinaryPrimitive>(*value);
+}
+
+std::unique_ptr<Attribute> CloningValueTransformer::TransformDerived(const Attribute* value) {
+ auto new_value = std::make_unique<Attribute>();
+ new_value->type_mask = value->type_mask;
+ new_value->min_int = value->min_int;
+ new_value->max_int = value->max_int;
+ for (const Attribute::Symbol& s : value->symbols) {
+ new_value->symbols.emplace_back(Attribute::Symbol{
+ .symbol = *s.symbol.Transform(*this),
+ .value = s.value,
+ .type = s.type,
+ });
+ }
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<Style> CloningValueTransformer::TransformDerived(const Style* value) {
+ auto new_value = std::make_unique<Style>();
+ new_value->parent = value->parent;
+ new_value->parent_inferred = value->parent_inferred;
+ for (auto& entry : value->entries) {
+ new_value->entries.push_back(Style::Entry{entry.key, entry.value->Transform(*this)});
+ }
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<Array> CloningValueTransformer::TransformDerived(const Array* value) {
+ auto new_value = std::make_unique<Array>();
+ for (auto& item : value->elements) {
+ new_value->elements.emplace_back(item->Transform(*this));
+ }
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<Plural> CloningValueTransformer::TransformDerived(const Plural* value) {
+ auto new_value = std::make_unique<Plural>();
+ const size_t count = value->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (value->values[i]) {
+ new_value->values[i] = value->values[i]->Transform(*this);
+ }
+ }
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<Styleable> CloningValueTransformer::TransformDerived(const Styleable* value) {
+ auto new_value = std::make_unique<Styleable>();
+ for (const Reference& s : value->entries) {
+ new_value->entries.emplace_back(*s.Transform(*this));
+ }
+ return CopyValueFields(std::move(new_value), value);
+}
+
+std::unique_ptr<Macro> CloningValueTransformer::TransformDerived(const Macro* value) {
+ auto new_value = std::make_unique<Macro>(*value);
+ return CopyValueFields(std::move(new_value), value);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index fe0883be50aa..d11b013f14d5 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -28,6 +28,7 @@
#include "Diagnostics.h"
#include "Resource.h"
#include "StringPool.h"
+#include "ValueTransformer.h"
#include "io/File.h"
#include "text/Printer.h"
#include "util/Maybe.h"
@@ -100,9 +101,8 @@ class Value {
// Calls the appropriate overload of ConstValueVisitor.
virtual void Accept(ConstValueVisitor* visitor) const = 0;
- // Clone the value. `new_pool` is the new StringPool that
- // any resources with strings should use when copying their string.
- virtual Value* Clone(StringPool* new_pool) const = 0;
+ // Transform this Value into another Value using the transformer.
+ std::unique_ptr<Value> Transform(ValueTransformer& transformer) const;
// Human readable printout of this value.
virtual void Print(std::ostream* out) const = 0;
@@ -118,6 +118,9 @@ class Value {
std::string comment_;
bool weak_ = false;
bool translatable_ = true;
+
+ private:
+ virtual Value* TransformValueImpl(ValueTransformer& transformer) const = 0;
};
// Inherit from this to get visitor accepting implementations for free.
@@ -129,12 +132,15 @@ struct BaseValue : public Value {
// A resource item with a single value. This maps to android::ResTable_entry.
struct Item : public Value {
- // Clone the Item.
- virtual Item* Clone(StringPool* new_pool) const override = 0;
-
// Fills in an android::Res_value structure with this Item's binary representation.
// Returns false if an error occurred.
virtual bool Flatten(android::Res_value* out_value) const = 0;
+
+ // Transform this Item into another Item using the transformer.
+ std::unique_ptr<Item> Transform(ValueTransformer& transformer) const;
+
+ private:
+ virtual Item* TransformItemImpl(ValueTransformer& transformer) const = 0;
};
// Inherit from this to get visitor accepting implementations for free.
@@ -147,7 +153,7 @@ struct BaseItem : public Item {
// A reference to another resource. This maps to android::Res_value::TYPE_REFERENCE.
// A reference can be symbolic (with the name set to a valid resource name) or be
// numeric (the id is set to a valid resource ID).
-struct Reference : public BaseItem<Reference> {
+struct Reference : public TransformableItem<Reference, BaseItem<Reference>> {
enum class Type {
kResource,
kAttribute,
@@ -158,6 +164,8 @@ struct Reference : public BaseItem<Reference> {
Reference::Type reference_type;
bool private_reference = false;
bool is_dynamic = false;
+ std::optional<uint32_t> type_flags;
+ bool allow_raw;
Reference();
explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
@@ -166,7 +174,6 @@ struct Reference : public BaseItem<Reference> {
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
- Reference* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
void PrettyPrint(text::Printer* printer) const override;
@@ -178,27 +185,25 @@ bool operator<(const Reference&, const Reference&);
bool operator==(const Reference&, const Reference&);
// An ID resource. Has no real value, just a place holder.
-struct Id : public BaseItem<Id> {
+struct Id : public TransformableItem<Id, BaseItem<Id>> {
Id() {
weak_ = true;
}
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out) const override;
- Id* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
// A raw, unprocessed string. This may contain quotations, escape sequences, and whitespace.
// This shall *NOT* end up in the final resource table.
-struct RawString : public BaseItem<RawString> {
+struct RawString : public TransformableItem<RawString, BaseItem<RawString>> {
StringPool::Ref value;
explicit RawString(const StringPool::Ref& ref);
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
- RawString* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
@@ -220,7 +225,7 @@ inline bool operator!=(const UntranslatableSection& a, const UntranslatableSecti
return a.start != b.start || a.end != b.end;
}
-struct String : public BaseItem<String> {
+struct String : public TransformableItem<String, BaseItem<String>> {
StringPool::Ref value;
// Sections of the string to NOT translate. Mainly used
@@ -232,12 +237,11 @@ struct String : public BaseItem<String> {
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
- String* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
void PrettyPrint(text::Printer* printer) const override;
};
-struct StyledString : public BaseItem<StyledString> {
+struct StyledString : public TransformableItem<StyledString, BaseItem<StyledString>> {
StringPool::StyleRef value;
// Sections of the string to NOT translate. Mainly used
@@ -249,11 +253,10 @@ struct StyledString : public BaseItem<StyledString> {
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
- StyledString* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
-struct FileReference : public BaseItem<FileReference> {
+struct FileReference : public TransformableItem<FileReference, BaseItem<FileReference>> {
StringPool::Ref path;
// A handle to the file object from which this file can be read.
@@ -269,12 +272,11 @@ struct FileReference : public BaseItem<FileReference> {
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
- FileReference* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
// Represents any other android::Res_value.
-struct BinaryPrimitive : public BaseItem<BinaryPrimitive> {
+struct BinaryPrimitive : public TransformableItem<BinaryPrimitive, BaseItem<BinaryPrimitive>> {
android::Res_value value;
BinaryPrimitive() = default;
@@ -283,12 +285,11 @@ struct BinaryPrimitive : public BaseItem<BinaryPrimitive> {
bool Equals(const Value* value) const override;
bool Flatten(android::Res_value* out_value) const override;
- BinaryPrimitive* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
void PrettyPrint(text::Printer* printer) const override;
};
-struct Attribute : public BaseValue<Attribute> {
+struct Attribute : public TransformableValue<Attribute, BaseValue<Attribute>> {
struct Symbol {
Reference symbol;
uint32_t value;
@@ -311,13 +312,14 @@ struct Attribute : public BaseValue<Attribute> {
// TYPE_ENUMS are never compatible.
bool IsCompatibleWith(const Attribute& attr) const;
- Attribute* Clone(StringPool* new_pool) const override;
std::string MaskString() const;
+ static std::string MaskString(uint32_t type_mask);
+
void Print(std::ostream* out) const override;
bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const;
};
-struct Style : public BaseValue<Style> {
+struct Style : public TransformableValue<Style, BaseValue<Style>> {
struct Entry {
Reference key;
std::unique_ptr<Item> value;
@@ -333,7 +335,6 @@ struct Style : public BaseValue<Style> {
std::vector<Entry> entries;
bool Equals(const Value* value) const override;
- Style* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
// Merges `style` into this Style. All identical attributes of `style` take precedence, including
@@ -341,33 +342,52 @@ struct Style : public BaseValue<Style> {
void MergeWith(Style* style, StringPool* pool);
};
-struct Array : public BaseValue<Array> {
+struct Array : public TransformableValue<Array, BaseValue<Array>> {
std::vector<std::unique_ptr<Item>> elements;
bool Equals(const Value* value) const override;
- Array* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
-struct Plural : public BaseValue<Plural> {
+struct Plural : public TransformableValue<Plural, BaseValue<Plural>> {
enum { Zero = 0, One, Two, Few, Many, Other, Count };
std::array<std::unique_ptr<Item>, Count> values;
bool Equals(const Value* value) const override;
- Plural* Clone(StringPool* new_pool) const override;
void Print(std::ostream* out) const override;
};
-struct Styleable : public BaseValue<Styleable> {
+struct Styleable : public TransformableValue<Styleable, BaseValue<Styleable>> {
std::vector<Reference> entries;
bool Equals(const Value* value) const override;
- Styleable* Clone(StringPool* newPool) const override;
void Print(std::ostream* out) const override;
void MergeWith(Styleable* styleable);
};
+struct Macro : public TransformableValue<Macro, BaseValue<Macro>> {
+ std::string raw_value;
+ StyleString style_string;
+ std::vector<UntranslatableSection> untranslatable_sections;
+
+ struct Namespace {
+ std::string alias;
+ std::string package_name;
+ bool is_private;
+
+ bool operator==(const Namespace& right) const {
+ return alias == right.alias && package_name == right.package_name &&
+ is_private == right.is_private;
+ }
+ };
+
+ std::vector<Namespace> alias_namespaces;
+
+ bool Equals(const Value* value) const override;
+ void Print(std::ostream* out) const override;
+};
+
template <typename T>
typename std::enable_if<std::is_base_of<Value, T>::value, std::ostream&>::type operator<<(
std::ostream& out, const std::unique_ptr<T>& value) {
@@ -379,6 +399,24 @@ typename std::enable_if<std::is_base_of<Value, T>::value, std::ostream&>::type o
return out;
}
+struct CloningValueTransformer : public ValueTransformer {
+ explicit CloningValueTransformer(StringPool* new_pool);
+
+ std::unique_ptr<Reference> TransformDerived(const Reference* value) override;
+ std::unique_ptr<Id> TransformDerived(const Id* value) override;
+ std::unique_ptr<RawString> TransformDerived(const RawString* value) override;
+ std::unique_ptr<String> TransformDerived(const String* value) override;
+ std::unique_ptr<StyledString> TransformDerived(const StyledString* value) override;
+ std::unique_ptr<FileReference> TransformDerived(const FileReference* value) override;
+ std::unique_ptr<BinaryPrimitive> TransformDerived(const BinaryPrimitive* value) override;
+ std::unique_ptr<Attribute> TransformDerived(const Attribute* value) override;
+ std::unique_ptr<Style> TransformDerived(const Style* value) override;
+ std::unique_ptr<Array> TransformDerived(const Array* value) override;
+ std::unique_ptr<Plural> TransformDerived(const Plural* value) override;
+ std::unique_ptr<Styleable> TransformDerived(const Styleable* value) override;
+ std::unique_ptr<Macro> TransformDerived(const Macro* value) override;
+};
+
} // namespace aapt
#endif // AAPT_RESOURCE_VALUES_H
diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp
index c4a1108ac62a..c75a4b99e138 100644
--- a/tools/aapt2/ResourceValues_test.cpp
+++ b/tools/aapt2/ResourceValues_test.cpp
@@ -62,7 +62,8 @@ TEST(ResourceValuesTest, PluralClone) {
a.values[Plural::One] = util::make_unique<String>(pool.MakeRef("one"));
a.values[Plural::Other] = util::make_unique<String>(pool.MakeRef("other"));
- std::unique_ptr<Plural> b(a.Clone(&pool));
+ CloningValueTransformer cloner(&pool);
+ std::unique_ptr<Plural> b(a.Transform(cloner));
EXPECT_TRUE(a.Equals(b.get()));
}
@@ -97,7 +98,8 @@ TEST(ResourceValuesTest, ArrayClone) {
a.elements.push_back(util::make_unique<String>(pool.MakeRef("one")));
a.elements.push_back(util::make_unique<String>(pool.MakeRef("two")));
- std::unique_ptr<Array> b(a.Clone(&pool));
+ CloningValueTransformer cloner(&pool);
+ std::unique_ptr<Array> b(a.Transform(cloner));
EXPECT_TRUE(a.Equals(b.get()));
}
@@ -160,7 +162,8 @@ TEST(ResourceValuesTest, StyleClone) {
.AddItem("android:attr/bar", ResourceUtils::TryParseInt("2"))
.Build();
- std::unique_ptr<Style> b(a->Clone(nullptr));
+ CloningValueTransformer cloner(nullptr);
+ std::unique_ptr<Style> b(a->Transform(cloner));
EXPECT_TRUE(a->Equals(b.get()));
}
@@ -174,7 +177,8 @@ TEST(ResourcesValuesTest, StringClones) {
EXPECT_THAT(pool_a.strings()[0]->context.config, Eq(test::ParseConfigOrDie("en")));
EXPECT_THAT(pool_a.strings()[0]->value, StrEq("hello"));
- std::unique_ptr<String> str_b(str_a.Clone(&pool_b));
+ CloningValueTransformer cloner(&pool_b);
+ str_a.Transform(cloner);
ASSERT_THAT(pool_b, SizeIs(1u));
EXPECT_THAT(pool_b.strings()[0]->context.config, Eq(test::ParseConfigOrDie("en")));
EXPECT_THAT(pool_b.strings()[0]->value, StrEq("hello"));
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 4247ec5a3495..b45c0401d19a 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -273,6 +273,7 @@ message CompoundValue {
Styleable styleable = 3;
Array array = 4;
Plural plural = 5;
+ MacroBody macro = 6;
}
}
@@ -304,6 +305,13 @@ message Reference {
// Whether this reference is dynamic.
Boolean is_dynamic = 5;
+
+ // The type flags used when compiling the reference. Used for substituting the contents of macros.
+ uint32 type_flags = 6;
+
+ // Whether raw string values would have been accepted in place of this reference definition. Used
+ // for substituting the contents of macros.
+ bool allow_raw = 7;
}
// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -591,3 +599,32 @@ message XmlAttribute {
// The optional interpreted/compiled version of the `value` string.
Item compiled_item = 6;
}
+
+message MacroBody {
+ string raw_string = 1;
+ StyleString style_string = 2;
+ repeated UntranslatableSection untranslatable_sections = 3;
+ repeated NamespaceAlias namespace_stack = 4;
+ SourcePosition source = 5;
+}
+
+message NamespaceAlias {
+ string prefix = 1;
+ string package_name = 2;
+ bool is_private = 3;
+}
+
+message StyleString {
+ message Span {
+ string name = 1;
+ uint32 start_index = 2;
+ uint32 end_index = 3;
+ }
+ string str = 1;
+ repeated Span spans = 2;
+}
+
+message UntranslatableSection {
+ uint64 start_index = 1;
+ uint64 end_index = 2;
+} \ No newline at end of file
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 1006ca970dc5..3457e0b41859 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -36,6 +36,10 @@ struct Span {
std::string name;
uint32_t first_char;
uint32_t last_char;
+
+ bool operator==(const Span& right) const {
+ return name == right.name && first_char == right.first_char && last_char == right.last_char;
+ }
};
struct StyleString {
diff --git a/tools/aapt2/ValueTransformer.cpp b/tools/aapt2/ValueTransformer.cpp
new file mode 100644
index 000000000000..2d7996b9d880
--- /dev/null
+++ b/tools/aapt2/ValueTransformer.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 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 "ValueTransformer.h"
+
+#include "ResourceValues.h"
+
+namespace aapt {
+
+#define VALUE_CREATE_VALUE_DECL(T) \
+ std::unique_ptr<Value> ValueTransformer::TransformValue(const T* value) { \
+ return TransformDerived(value); \
+ }
+
+#define VALUE_CREATE_ITEM_DECL(T) \
+ std::unique_ptr<Item> ValueTransformer::TransformItem(const T* value) { \
+ return TransformDerived(value); \
+ } \
+ std::unique_ptr<Value> ValueTransformer::TransformValue(const T* value) { \
+ return TransformItem(value); \
+ }
+
+VALUE_CREATE_ITEM_DECL(Id);
+VALUE_CREATE_ITEM_DECL(Reference);
+VALUE_CREATE_ITEM_DECL(RawString);
+VALUE_CREATE_ITEM_DECL(String);
+VALUE_CREATE_ITEM_DECL(StyledString);
+VALUE_CREATE_ITEM_DECL(FileReference);
+VALUE_CREATE_ITEM_DECL(BinaryPrimitive);
+
+VALUE_CREATE_VALUE_DECL(Attribute);
+VALUE_CREATE_VALUE_DECL(Style);
+VALUE_CREATE_VALUE_DECL(Array);
+VALUE_CREATE_VALUE_DECL(Plural);
+VALUE_CREATE_VALUE_DECL(Styleable);
+VALUE_CREATE_VALUE_DECL(Macro);
+
+} // namespace aapt \ No newline at end of file
diff --git a/tools/aapt2/ValueTransformer.h b/tools/aapt2/ValueTransformer.h
new file mode 100644
index 000000000000..6fc4a191b04b
--- /dev/null
+++ b/tools/aapt2/ValueTransformer.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 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_VALUE_TRANSFORMER_H
+#define AAPT_VALUE_TRANSFORMER_H
+
+#include <memory>
+
+#include "StringPool.h"
+
+namespace aapt {
+
+class Value;
+struct Item;
+struct Reference;
+struct Id;
+struct RawString;
+struct String;
+struct StyledString;
+struct FileReference;
+struct BinaryPrimitive;
+struct Attribute;
+struct Style;
+struct Array;
+struct Plural;
+struct Styleable;
+struct Macro;
+
+#define AAPT_TRANSFORM_VALUE(T) \
+ virtual std::unique_ptr<T> TransformDerived(const T* value) = 0; \
+ virtual std::unique_ptr<Value> TransformValue(const T* value);
+
+#define AAPT_TRANSFORM_ITEM(T) \
+ virtual std::unique_ptr<Item> TransformItem(const T* value); \
+ AAPT_TRANSFORM_VALUE(T)
+
+/**
+ * An interface for consuming a Value type and transforming it into another Value.
+ *
+ * The interface defines 2 methods for each type (T) that inherits from TransformableValue:
+ * std::unique_ptr<T> TransformDerived(const T*)
+ * std::unique_ptr<Value> TransformValue(const T*)
+ *
+ * The interface defines 3 method for each type (T) that inherits from TransformableItem:
+ * std::unique_ptr<T> TransformDerived(const T*)
+ * std::unique_ptr<Item> TransformItem(const T*)
+ * std::unique_ptr<Value> TransformValue(const T*)
+ *
+ * TransformDerived is invoked when Transform is invoked on the derived type T.
+ * TransformItem is invoked when Transform is invoked on an Item type.
+ * TransformValue is invoked when Transform is invoked on a Value type.
+ *
+ * ValueTransformerImpl transformer(&string_pool);
+ * T* derived = ...;
+ * std::unique_ptr<T> new_type = derived->Transform(transformer); // Invokes TransformDerived
+ *
+ * Item* item = derived;
+ * std::unique_ptr<Item> new_item = item->TransformItem(transformer); // Invokes TransformItem
+ *
+ * Value* value = item;
+ * std::unique_ptr<Value> new_value = value->TransformValue(transformer); // Invokes TransformValue
+ *
+ * For types that inherit from AbstractTransformableItem, the default implementation of
+ * TransformValue invokes TransformItem which invokes TransformDerived.
+ *
+ * For types that inherit from AbstractTransformableValue, the default implementation of
+ * TransformValue invokes TransformDerived.
+ */
+struct ValueTransformer {
+ // `new_pool` is the new StringPool that newly created Values should use for string storing string
+ // values.
+ explicit ValueTransformer(StringPool* new_pool);
+ virtual ~ValueTransformer() = default;
+
+ AAPT_TRANSFORM_ITEM(Id);
+ AAPT_TRANSFORM_ITEM(Reference);
+ AAPT_TRANSFORM_ITEM(RawString);
+ AAPT_TRANSFORM_ITEM(String);
+ AAPT_TRANSFORM_ITEM(StyledString);
+ AAPT_TRANSFORM_ITEM(FileReference);
+ AAPT_TRANSFORM_ITEM(BinaryPrimitive);
+
+ AAPT_TRANSFORM_VALUE(Attribute);
+ AAPT_TRANSFORM_VALUE(Style);
+ AAPT_TRANSFORM_VALUE(Array);
+ AAPT_TRANSFORM_VALUE(Plural);
+ AAPT_TRANSFORM_VALUE(Styleable);
+ AAPT_TRANSFORM_VALUE(Macro);
+
+ protected:
+ StringPool* const pool_;
+};
+
+#undef AAPT_TRANSFORM_VALUE
+#undef AAPT_TRANSFORM_ITEM
+
+template <typename Derived, typename Base>
+struct TransformableValue : public Base {
+ // Transform this Derived into another Derived using the transformer.
+ std::unique_ptr<Derived> Transform(ValueTransformer& transformer) const;
+
+ private:
+ Value* TransformValueImpl(ValueTransformer& transformer) const override;
+};
+
+template <typename Derived, typename Base>
+struct TransformableItem : public TransformableValue<Derived, Base> {
+ private:
+ Item* TransformItemImpl(ValueTransformer& transformer) const override;
+};
+
+} // namespace aapt
+
+// Implementation
+#include "ValueTransformer_inline.h"
+
+#endif // AAPT_VALUE_TRANSFORMER_H \ No newline at end of file
diff --git a/tools/aapt2/ValueTransformer_inline.h b/tools/aapt2/ValueTransformer_inline.h
new file mode 100644
index 000000000000..c6c07c0fd6f9
--- /dev/null
+++ b/tools/aapt2/ValueTransformer_inline.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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_VALUE_TRANSFORMER_IMPL_H
+#define AAPT_VALUE_TRANSFORMER_IMPL_H
+
+namespace aapt {
+
+inline ValueTransformer::ValueTransformer(StringPool* new_pool) : pool_(new_pool) {
+}
+
+template <typename Derived, typename Base>
+inline std::unique_ptr<Derived> TransformableValue<Derived, Base>::Transform(
+ ValueTransformer& transformer) const {
+ return transformer.TransformDerived(static_cast<const Derived*>(this));
+}
+
+template <typename Derived, typename Base>
+Value* TransformableValue<Derived, Base>::TransformValueImpl(ValueTransformer& transformer) const {
+ auto self = static_cast<const Derived*>(this);
+ auto transformed = transformer.TransformValue(self);
+ return transformed.release();
+}
+
+template <typename Derived, typename Base>
+Item* TransformableItem<Derived, Base>::TransformItemImpl(ValueTransformer& transformer) const {
+ auto self = static_cast<const Derived*>(this);
+ auto transformed = transformer.TransformItem(self);
+ return transformed.release();
+}
+
+} // namespace aapt
+
+#endif // AAPT_VALUE_TRANSFORMER_IMPL_H \ No newline at end of file
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 4e74ec366dab..d0c9d89b6f0c 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -43,6 +43,9 @@ class ValueVisitor {
virtual void Visit(Array* value) { VisitAny(value); }
virtual void Visit(Plural* value) { VisitAny(value); }
virtual void Visit(Styleable* value) { VisitAny(value); }
+ virtual void Visit(Macro* value) {
+ VisitAny(value);
+ }
};
// Const version of ValueVisitor.
@@ -92,6 +95,9 @@ class ConstValueVisitor {
virtual void Visit(const Styleable* value) {
VisitAny(value);
}
+ virtual void Visit(const Macro* value) {
+ VisitAny(value);
+ }
};
// NOLINT, do not add parentheses around T.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 2a8923d927f6..e4d0f3b6bd23 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -462,7 +462,7 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
// that existing projects have out-of-date references which pass compilation.
xml::StripAndroidStudioAttributes(doc->root.get());
- XmlReferenceLinker xml_linker;
+ XmlReferenceLinker xml_linker(table);
if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
return {};
}
@@ -1656,10 +1656,11 @@ class Linker {
<< " with config \"" << config_value->config
<< "\" for round icon compatibility");
- auto value = icon_reference->Clone(&table->string_pool);
+ CloningValueTransformer cloner(&table->string_pool);
+ auto value = icon_reference->Transform(cloner);
auto round_config_value =
round_icon_entry->FindOrCreateValue(config_value->config, config_value->product);
- round_config_value->value.reset(value);
+ round_config_value->value = std::move(value);
}
}
@@ -2111,7 +2112,7 @@ class Linker {
std::unique_ptr<xml::XmlResource> split_manifest =
GenerateSplitManifest(app_info_, *split_constraints_iter);
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(&final_table_);
if (!linker.Consume(context_, split_manifest.get())) {
context_->GetDiagnostics()->Error(DiagMessage()
<< "failed to create Split AndroidManifest.xml");
@@ -2142,7 +2143,7 @@ class Linker {
// So we give it a package name so it can see local resources.
manifest_xml->file.name.package = context_->GetCompilationPackage();
- XmlReferenceLinker manifest_linker;
+ XmlReferenceLinker manifest_linker(&final_table_);
if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
if (options_.generate_proguard_rules_path &&
!proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index d1e6d3922f3f..3118eb8f7731 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -24,6 +24,7 @@
using testing::Eq;
using testing::HasSubstr;
+using testing::IsNull;
using testing::Ne;
using testing::NotNull;
@@ -532,4 +533,109 @@ TEST_F(LinkTest, StagedAndroidApi) {
EXPECT_THAT(*result, Eq(0x01fd0072));
}
+TEST_F(LinkTest, MacroSubstitution) {
+ StdErrDiagnostics diag;
+ const std::string values =
+ R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
+ <macro name="is_enabled">true</macro>
+ <macro name="deep_is_enabled">@macro/is_enabled</macro>
+ <macro name="attr_ref">?is_enabled_attr</macro>
+ <macro name="raw_string">Hello World!</macro>
+ <macro name="android_ref">@an:color/primary_text_dark</macro>
+
+ <attr name="is_enabled_attr" />
+ <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
+
+ <string name="is_enabled_str">@macro/is_enabled</string>
+ <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
+
+ <array name="my_array">
+ <item>@macro/is_enabled</item>
+ </array>
+
+ <style name="MyStyle">
+ <item name="android:background">@macro/attr_ref</item>
+ <item name="android:fontFamily">@macro/raw_string</item>
+ </style>
+ </resources>)";
+
+ const std::string xml_values =
+ R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@macro/android_ref"
+ android:fontFamily="@macro/raw_string">
+ </SomeLayout>)";
+
+ // Build a library with a public attribute
+ const std::string lib_res = GetTestPath("test-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
+
+ const std::string lib_apk = GetTestPath("test.apk");
+ // clang-format off
+ auto lib_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
+ .AddCompiledResDir(lib_res, &diag)
+ .AddFlag("--no-auto-version")
+ .Build(lib_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(lib_link_args, &diag));
+
+ auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
+ ASSERT_THAT(apk, NotNull());
+
+ // Test that the type flags determines the value type
+ auto actual_bool =
+ test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
+ ASSERT_THAT(actual_bool, NotNull());
+ EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
+ EXPECT_EQ(0xffffffffu, actual_bool->value.data);
+
+ auto actual_str =
+ test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
+ ASSERT_THAT(actual_str, NotNull());
+ EXPECT_EQ(*actual_str->value, "true");
+
+ // Test nested data structures
+ auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
+ ASSERT_THAT(actual_array, NotNull());
+ EXPECT_THAT(actual_array->elements.size(), Eq(1));
+
+ auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
+ ASSERT_THAT(array_el_ref, NotNull());
+ EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
+ EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
+
+ auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
+ ASSERT_THAT(actual_style, NotNull());
+ EXPECT_THAT(actual_style->entries.size(), Eq(2));
+
+ {
+ auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
+ ASSERT_THAT(style_el, NotNull());
+ EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
+ EXPECT_THAT(style_el->id, Eq(0x7f010000));
+ }
+
+ {
+ auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
+ ASSERT_THAT(style_el, NotNull());
+ EXPECT_THAT(*style_el->value, Eq("Hello World!"));
+ }
+
+ // Test substitution in compiled xml files
+ auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
+ ASSERT_THAT(xml, NotNull());
+
+ auto& xml_attrs = xml->root->attributes;
+ ASSERT_THAT(xml_attrs.size(), Eq(2));
+
+ auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
+ ASSERT_THAT(attr_value, NotNull());
+ EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
+ EXPECT_THAT(attr_value->id, Eq(0x01060001));
+
+ EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
+ EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 5e0300b3071b..3f574ee8e897 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -226,6 +226,7 @@ class Visitor : public ValueVisitor {
: pool_(pool), method_(method), localizer_(method) {}
void Visit(Plural* plural) override {
+ CloningValueTransformer cloner(pool_);
std::unique_ptr<Plural> localized = util::make_unique<Plural>();
for (size_t i = 0; i < plural->values.size(); i++) {
Visitor sub_visitor(pool_, method_);
@@ -234,7 +235,7 @@ class Visitor : public ValueVisitor {
if (sub_visitor.item) {
localized->values[i] = std::move(sub_visitor.item);
} else {
- localized->values[i] = std::unique_ptr<Item>(plural->values[i]->Clone(pool_));
+ localized->values[i] = plural->values[i]->Transform(cloner);
}
}
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 17d11a63553a..74ecf47cae4c 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -567,13 +567,10 @@ class PackageFlattener {
}
bool FlattenTypes(BigBuffer* buffer) {
- // Sort the types by their IDs. They will be inserted into the StringPool in
- // this order.
-
size_t expected_type_id = 1;
for (const ResourceTableTypeView& type : package_.types) {
- if (type.type == ResourceType::kStyleable) {
- // Styleables aren't real Resource Types, they are represented in the R.java file.
+ if (type.type == ResourceType::kStyleable || type.type == ResourceType::kMacro) {
+ // Styleables and macros are not real resource types.
continue;
}
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index f5fe5e30bf0f..8139d7385092 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -299,6 +299,7 @@ static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries(
.Build();
// Add regular entries.
+ CloningValueTransformer cloner(&table->string_pool);
int stride = static_cast<int>(1.0f / load);
for (int i = 0; i < 100; i++) {
const ResourceName name = test::ParseNameOrDie(
@@ -308,7 +309,7 @@ static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries(
util::make_unique<BinaryPrimitive>(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(i));
CHECK(table->AddResource(NewResourceBuilder(name)
.SetId(resid)
- .SetValue(std::unique_ptr<Value>(value->Clone(nullptr)))
+ .SetValue(std::unique_ptr<Value>(value->Transform(cloner)))
.Build(),
context->GetDiagnostics()));
@@ -317,7 +318,7 @@ static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries(
CHECK(table->AddResource(
NewResourceBuilder(name)
.SetId(resid)
- .SetValue(std::unique_ptr<Value>(value->Clone(nullptr)), sparse_config)
+ .SetValue(std::unique_ptr<Value>(value->Transform(cloner)), sparse_config)
.Build(),
context->GetDiagnostics()));
}
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index c24488b16153..d97e8882e5a2 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -222,7 +222,7 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
android:id="@id/foo"
app:foo="@id/foo" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
// The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 498d5a27d69d..ec331df480cd 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -656,6 +656,38 @@ static bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* o
}
out_ref->name = name_ref.ToResourceName();
}
+ if (pb_ref.type_flags() != 0) {
+ out_ref->type_flags = pb_ref.type_flags();
+ }
+ out_ref->allow_raw = pb_ref.allow_raw();
+ return true;
+}
+
+static bool DeserializeMacroFromPb(const pb::MacroBody& pb_ref, Macro* out_ref,
+ std::string* out_error) {
+ out_ref->raw_value = pb_ref.raw_string();
+
+ if (pb_ref.has_style_string()) {
+ out_ref->style_string.str = pb_ref.style_string().str();
+ for (const auto& span : pb_ref.style_string().spans()) {
+ out_ref->style_string.spans.emplace_back(Span{
+ .name = span.name(), .first_char = span.start_index(), .last_char = span.end_index()});
+ }
+ }
+
+ for (const auto& untranslatable_section : pb_ref.untranslatable_sections()) {
+ out_ref->untranslatable_sections.emplace_back(
+ UntranslatableSection{.start = static_cast<size_t>(untranslatable_section.start_index()),
+ .end = static_cast<size_t>(untranslatable_section.end_index())});
+ }
+
+ for (const auto& namespace_decls : pb_ref.namespace_stack()) {
+ out_ref->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = namespace_decls.prefix(),
+ .package_name = namespace_decls.package_name(),
+ .is_private = namespace_decls.is_private()});
+ }
+
return true;
}
@@ -801,6 +833,15 @@ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
value = std::move(plural);
} break;
+ case pb::CompoundValue::kMacro: {
+ const pb::MacroBody& pb_macro = pb_compound_value.macro();
+ auto macro = std::make_unique<Macro>();
+ if (!DeserializeMacroFromPb(pb_macro, macro.get(), out_error)) {
+ return {};
+ }
+ value = std::move(macro);
+ } break;
+
default:
LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
break;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index f13f82da3102..d2f033683cc5 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -440,6 +440,36 @@ static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref)
if (ref.is_dynamic) {
pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
}
+ if (ref.type_flags) {
+ pb_ref->set_type_flags(*ref.type_flags);
+ }
+ pb_ref->set_allow_raw(ref.allow_raw);
+}
+
+static void SerializeMacroToPb(const Macro& ref, pb::MacroBody* pb_macro) {
+ pb_macro->set_raw_string(ref.raw_value);
+
+ auto pb_style_str = pb_macro->mutable_style_string();
+ pb_style_str->set_str(ref.style_string.str);
+ for (const auto& span : ref.style_string.spans) {
+ auto pb_span = pb_style_str->add_spans();
+ pb_span->set_name(span.name);
+ pb_span->set_start_index(span.first_char);
+ pb_span->set_end_index(span.last_char);
+ }
+
+ for (const auto& untranslatable_section : ref.untranslatable_sections) {
+ auto pb_section = pb_macro->add_untranslatable_sections();
+ pb_section->set_start_index(untranslatable_section.start);
+ pb_section->set_end_index(untranslatable_section.end);
+ }
+
+ for (const auto& namespace_decls : ref.alias_namespaces) {
+ auto pb_namespace = pb_macro->add_namespace_stack();
+ pb_namespace->set_prefix(namespace_decls.alias);
+ pb_namespace->set_package_name(namespace_decls.package_name);
+ pb_namespace->set_is_private(namespace_decls.is_private);
+ }
}
template <typename T>
@@ -643,6 +673,11 @@ class ValueSerializer : public ConstValueVisitor {
}
}
+ void Visit(const Macro* macro) override {
+ pb::MacroBody* pb_macro = out_value_->mutable_compound_value()->mutable_macro();
+ SerializeMacroToPb(*macro, pb_macro);
+ }
+
void VisitAny(const Value* unknown) override {
LOG(FATAL) << "unimplemented value: " << *unknown;
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 591ba14942cd..e563eda93e20 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -894,4 +894,38 @@ TEST(ProtoSerializeTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucce
EXPECT_THAT(*(s->value), Eq("foo"));
}
+TEST(ProtoSerializeTest, SerializeMacro) {
+ auto original = std::make_unique<Macro>();
+ original->raw_value = "\nThis being human is a guest house.";
+ original->style_string.str = " This being human is a guest house.";
+ original->style_string.spans.emplace_back(Span{.name = "b", .first_char = 12, .last_char = 16});
+ original->untranslatable_sections.emplace_back(UntranslatableSection{.start = 12, .end = 17});
+ original->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = "prefix", .package_name = "package.name", .is_private = true});
+
+ CloningValueTransformer cloner(nullptr);
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:macro/foo")
+ .SetValue(original->Transform(cloner))
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ Macro* deserialized = test::GetValue<Macro>(&new_table, "com.app.a:macro/foo");
+ ASSERT_THAT(deserialized, NotNull());
+ EXPECT_THAT(deserialized->raw_value, Eq(original->raw_value));
+ EXPECT_THAT(deserialized->style_string.str, Eq(original->style_string.str));
+ EXPECT_THAT(deserialized->style_string.spans, Eq(original->style_string.spans));
+ EXPECT_THAT(deserialized->untranslatable_sections, Eq(original->untranslatable_sections));
+ EXPECT_THAT(deserialized->alias_namespaces, Eq(original->alias_namespaces));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
index 6bc2064c6e63..27b6068632f3 100644
--- a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
@@ -20,10 +20,13 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_PACKAGE_NAME := AaptTestMergeOnly_App
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
LOCAL_SDK_VERSION := current
LOCAL_EXPORT_PACKAGE_RESOURCES := true
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_ANDROID_LIBRARIES := \
AaptTestMergeOnly_LeafLib \
AaptTestMergeOnly_LocalLib
-include $(BUILD_PACKAGE) \ No newline at end of file
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
index 446237412370..98b74403a7ff 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -20,6 +20,9 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_PACKAGE_NAME := AaptTestNamespace_App
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
LOCAL_SDK_VERSION := current
LOCAL_EXPORT_PACKAGE_RESOURCES := true
LOCAL_MODULE_TAGS := tests
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
index 83e2289430f7..30375728c9e0 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
@@ -20,6 +20,9 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_PACKAGE_NAME := AaptTestNamespace_Split
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
LOCAL_SDK_VERSION := current
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index e1e2e0135cf7..de6524dc7027 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -616,8 +616,9 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
for (const auto& package : table_->packages) {
for (const auto& type : package->types) {
- if (type->type == ResourceType::kAttrPrivate) {
- // We generate these as part of the kAttr type, so skip them here.
+ if (type->type == ResourceType::kAttrPrivate || type->type == ResourceType::kMacro) {
+ // We generate kAttrPrivate as part of the kAttr type, so skip them here.
+ // Macros are not actual resources, so skip them as well.
continue;
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 8bb3ee93d312..40395ed64fe3 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -334,11 +334,12 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
styleable.SetComment(StringPiece("This is a styleable"));
+ CloningValueTransformer cloner(nullptr);
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.AddValue("android:attr/one", util::make_unique<Attribute>(attr))
.AddValue("android:styleable/Container",
- std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
+ std::unique_ptr<Styleable>(styleable.Transform(cloner)))
.Build();
std::unique_ptr<IAaptContext> context =
@@ -371,11 +372,12 @@ TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent)
styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
styleable.SetComment(StringPiece("This is a styleable"));
+ CloningValueTransformer cloner(nullptr);
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.AddValue("android:attr/one", util::make_unique<Attribute>(attr))
.AddValue("android:styleable/Container",
- std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
+ std::unique_ptr<Styleable>(styleable.Transform(cloner)))
.Build();
std::unique_ptr<IAaptContext> context =
@@ -568,4 +570,25 @@ TEST(JavaClassGeneratorTest, SortsDynamicAttributesAfterFrameworkAttributes) {
EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
}
+TEST(JavaClassGeneratorTest, SkipMacros) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("android:macro/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+
+ std::string output;
+ StringOutputStream out(&output);
+ EXPECT_TRUE(generator.Generate("android", &out));
+ out.Flush();
+
+ EXPECT_THAT(output, Not(HasSubstr("bar")));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index b7dfec3a6b28..e1040666e410 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -264,7 +264,7 @@ TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
</View>)");
foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
- XmlReferenceLinker xml_linker;
+ XmlReferenceLinker xml_linker(nullptr);
ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 73b92542a755..876494e617a6 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -72,6 +72,7 @@ ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry,
bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
TRACE_NAME("AutoVersioner::Consume");
+ CloningValueTransformer cloner(&table->string_pool);
for (auto& package : table->packages) {
for (auto& type : package->types) {
if (type->type != ResourceType::kStyle) {
@@ -128,7 +129,7 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
ConfigDescription new_config(config_value->config);
new_config.sdkVersion = static_cast<uint16_t>(min_sdk_stripped.value());
- std::unique_ptr<Style> new_style(style->Clone(&table->string_pool));
+ std::unique_ptr<Style> new_style(style->Transform(cloner));
new_style->SetComment(style->GetComment());
new_style->SetSource(style->GetSource());
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index c9b8d3993959..be6c930b9284 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -133,12 +133,14 @@ class XmlNamespaceRemover : public IXmlResourceConsumer {
// Once an XmlResource is processed by this linker, it is ready to be flattened.
class XmlReferenceLinker : public IXmlResourceConsumer {
public:
- XmlReferenceLinker() = default;
+ explicit XmlReferenceLinker(ResourceTable* table) : table_(table) {
+ }
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
private:
DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
+ ResourceTable* table_;
};
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8e49fabe6a5c..4ac25bd6a0e0 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -21,6 +21,7 @@
#include "androidfw/ResourceTypes.h"
#include "Diagnostics.h"
+#include "ResourceParser.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -37,128 +38,153 @@ using ::android::StringPiece;
using ::android::base::StringPrintf;
namespace aapt {
-
namespace {
+struct LoggingResourceName {
+ LoggingResourceName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls)
+ : ref_(ref), callsite_(callsite), decls_(decls) {
+ }
-// The ReferenceLinkerVisitor will follow all references and make sure they point
-// to resources that actually exist, either in the local resource table, or as external
-// symbols. Once the target resource has been found, the ID of the resource will be assigned
-// to the reference object.
-//
-// NOTE: All of the entries in the ResourceTable must be assigned IDs.
-class ReferenceLinkerVisitor : public DescendingValueVisitor {
- public:
- using DescendingValueVisitor::Visit;
-
- ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
- StringPool* string_pool, xml::IPackageDeclStack* decl)
- : callsite_(callsite),
- context_(context),
- symbols_(symbols),
- package_decls_(decl),
- string_pool_(string_pool) {}
-
- void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
- error_ = true;
- }
+ const Reference& ref_;
+ const CallSite& callsite_;
+ const xml::IPackageDeclStack* decls_;
+};
+
+inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) {
+ if (!name.ref_.name) {
+ out << name.ref_.id.value();
+ return out;
}
- // We visit the Style specially because during this phase, values of attributes are
- // all RawString values. Now that we are expected to resolve all symbols, we can
- // lookup the attributes to find out which types are allowed for the attributes' values.
- void Visit(Style* style) override {
- if (style->parent) {
- Visit(&style->parent.value());
- }
+ out << name.ref_.name.value();
+
+ Reference fully_qualified = name.ref_;
+ xml::ResolvePackage(name.decls_, &fully_qualified);
+
+ ResourceName& full_name = fully_qualified.name.value();
+ if (full_name.package.empty()) {
+ full_name.package = name.callsite_.package;
+ }
- for (Style::Entry& entry : style->entries) {
- std::string err_str;
+ if (full_name != name.ref_.name.value()) {
+ out << " (aka " << full_name << ")";
+ }
+ return out;
+}
- // 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 transformed_reference = entry.key;
- ResolvePackage(package_decls_, &transformed_reference);
+} // namespace
- // Find the attribute in the symbol table and check if it is visible from this callsite.
- const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
- transformed_reference, callsite_, context_, symbols_, &err_str);
- if (symbol) {
- // Assign our style key the correct ID. The ID may not exist.
- 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), symbol->attribute.get());
-
- // Link/resolve the final value (mostly if it's a reference).
- entry.value->Accept(this);
-
- // Now verify that the type of this item is compatible with the
- // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
- // check is fast and we avoid creating a DiagMessage when the match is successful.
- if (!symbol->attribute->Matches(*entry.value, nullptr)) {
- // The actual type of this item is incompatible with the attribute.
- DiagMessage msg(entry.key.GetSource());
-
- // Call the matches method again, this time with a DiagMessage so we fill in the actual
- // error message.
- symbol->attribute->Matches(*entry.value, &msg);
- context_->GetDiagnostics()->Error(msg);
- error_ = true;
- }
+std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) {
+ auto linked_item =
+ ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+ if (linked_item) {
+ auto linked_item_ptr = linked_item.release();
+ if (auto ref = ValueCast<Reference>(linked_item_ptr)) {
+ return std::unique_ptr<Reference>(ref);
+ }
+ context_->GetDiagnostics()->Error(DiagMessage(value->GetSource())
+ << "value of '"
+ << LoggingResourceName(*value, callsite_, package_decls_)
+ << "' must be a resource reference");
+ delete linked_item_ptr;
+ }
+
+ error_ = true;
+ return CloningValueTransformer::TransformDerived(value);
+}
+
+std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) {
+ // We visit the Style specially because during this phase, values of attributes are either
+ // RawString or Reference values. Now that we are expected to resolve all symbols, we can lookup
+ // the attributes to find out which types are allowed for the attributes' values.
+ auto new_style = CloningValueTransformer::TransformDerived(style);
+ if (new_style->parent) {
+ new_style->parent = *TransformDerived(&style->parent.value());
+ }
- } else {
+ for (Style::Entry& entry : new_style->entries) {
+ std::string err_str;
+
+ // 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 transformed_reference = entry.key;
+ ResolvePackage(package_decls_, &transformed_reference);
+
+ // Find the attribute in the symbol table and check if it is visible from this callsite.
+ const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
+ transformed_reference, callsite_, context_, symbols_, &err_str);
+ if (symbol) {
+ // Assign our style key the correct ID. The ID may not exist.
+ entry.key.id = symbol->id;
+
+ // Link/resolve the final value if it's a reference.
+ entry.value = entry.value->Transform(*this);
+
+ // 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), symbol->attribute.get());
+
+ // Now verify that the type of this item is compatible with the
+ // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
+ // check is fast and we avoid creating a DiagMessage when the match is successful.
+ if (!symbol->attribute->Matches(*entry.value, nullptr)) {
+ // The actual type of this item is incompatible with the attribute.
DiagMessage msg(entry.key.GetSource());
- msg << "style attribute '";
- ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
- msg << "' " << err_str;
+
+ // Call the matches method again, this time with a DiagMessage so we fill in the actual
+ // error message.
+ symbol->attribute->Matches(*entry.value, &msg);
context_->GetDiagnostics()->Error(msg);
error_ = true;
}
+ } else {
+ context_->GetDiagnostics()->Error(DiagMessage(entry.key.GetSource())
+ << "style attribute '"
+ << LoggingResourceName(entry.key, callsite_, package_decls_)
+ << "' " << err_str);
+
+ error_ = true;
}
}
+ return new_style;
+}
- bool HasError() {
- return error_;
+std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) {
+ auto linked_value =
+ ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+ if (linked_value) {
+ return linked_value;
}
+ error_ = true;
+ return CloningValueTransformer::TransformDerived(value);
+}
- private:
- DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
-
- // 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.
- std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
- const Attribute* attr) {
- if (RawString* raw_string = ValueCast<RawString>(value.get())) {
- std::unique_ptr<Item> transformed =
- ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
-
- // If we could not parse as any specific type, try a basic STRING.
- if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
- StringBuilder string_builder;
- string_builder.AppendText(*raw_string->value);
- if (string_builder) {
- transformed =
- util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
- }
+// 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.
+std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute(
+ std::unique_ptr<Item> value, const Attribute* attr) {
+ if (RawString* raw_string = ValueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
+
+ // If we could not parse as any specific type, try a basic STRING.
+ if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
+ StringBuilder string_builder;
+ string_builder.AppendText(*raw_string->value);
+ if (string_builder) {
+ transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string()));
}
+ }
- if (transformed) {
- return transformed;
- }
+ if (transformed) {
+ return transformed;
}
- return value;
}
+ return value;
+}
- const CallSite& callsite_;
- IAaptContext* context_;
- SymbolTable* symbols_;
- xml::IPackageDeclStack* package_decls_;
- StringPool* string_pool_;
- bool error_ = false;
-};
+namespace {
class EmptyDeclStack : public xml::IPackageDeclStack {
public:
@@ -175,6 +201,27 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
};
+struct MacroDeclStack : public xml::IPackageDeclStack {
+ explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces)
+ : alias_namespaces_(std::move(namespaces)) {
+ }
+
+ Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+ if (alias.empty()) {
+ return xml::ExtractedPackage{{}, true /*private*/};
+ }
+ for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) {
+ if (alias == StringPiece(it->alias)) {
+ return xml::ExtractedPackage{it->package_name, it->is_private};
+ }
+ }
+ return {};
+ }
+
+ private:
+ std::vector<Macro::Namespace> alias_namespaces_;
+};
+
// 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 IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
@@ -220,8 +267,6 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& refer
// If the callsite package is the same as the current compilation package,
// check the feature split dependencies as well. Feature split resources
// can be referenced without a namespace, just like the base package.
- // TODO: modify the package name of included splits instead of having the
- // symbol table look up the resource in in every package. b/136105066
if (callsite.package == context->GetCompilationPackage()) {
const auto& split_name_dependencies = context->GetSplitNameDependencies();
for (const std::string& split_name : split_name_dependencies) {
@@ -295,29 +340,6 @@ Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference&
return xml::AaptAttribute(*symbol->attribute, symbol->id);
}
-void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
- const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
- CHECK(out_msg != nullptr);
- if (!ref.name) {
- *out_msg << ref.id.value();
- return;
- }
-
- *out_msg << ref.name.value();
-
- Reference fully_qualified = ref;
- xml::ResolvePackage(decls, &fully_qualified);
-
- ResourceName& full_name = fully_qualified.name.value();
- if (full_name.package.empty()) {
- full_name.package = callsite.package;
- }
-
- if (full_name != ref.name.value()) {
- *out_msg << " (aka " << full_name << ")";
- }
-}
-
void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
const xml::IPackageDeclStack* decls,
DiagMessage* out_msg) {
@@ -348,18 +370,71 @@ void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& c
}
}
-bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
- IAaptContext* context, SymbolTable* symbols,
- const xml::IPackageDeclStack* decls) {
- CHECK(reference != nullptr);
- if (!reference->name && !reference->id) {
+std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
+ const Reference& reference,
+ IAaptContext* context, SymbolTable* symbols,
+ ResourceTable* table,
+ const xml::IPackageDeclStack* decls) {
+ if (!reference.name && !reference.id) {
// This is @null.
- return true;
+ return std::make_unique<Reference>(reference);
}
- Reference transformed_reference = *reference;
+ Reference transformed_reference = reference;
xml::ResolvePackage(decls, &transformed_reference);
+ if (transformed_reference.name.value().type == ResourceType::kMacro) {
+ if (transformed_reference.name.value().package.empty()) {
+ transformed_reference.name.value().package = callsite.package;
+ }
+
+ auto result = table->FindResource(transformed_reference.name.value());
+ if (!result || result.value().entry->values.empty()) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(reference.GetSource())
+ << "failed to find definition for "
+ << LoggingResourceName(transformed_reference, callsite, decls));
+ return {};
+ }
+
+ auto& macro_values = result.value().entry->values;
+ CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration.";
+
+ auto macro = ValueCast<Macro>(macro_values[0]->value.get());
+ CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual "
+ << *macro_values[0]->value << ")";
+
+ // Re-create the state used to parse the macro tag to compile the macro contents as if it was
+ // defined inline
+ uint32_t type_flags = 0;
+ if (reference.type_flags.has_value()) {
+ type_flags = reference.type_flags.value();
+ }
+
+ MacroDeclStack namespace_stack(macro->alias_namespaces);
+ FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value,
+ .style_string = macro->style_string,
+ .untranslatable_sections = macro->untranslatable_sections,
+ .namespace_resolver = &namespace_stack,
+ .source = macro->GetSource()};
+
+ auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table,
+ macro_values[0]->config, *context->GetDiagnostics());
+ if (new_value == nullptr) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(reference.GetSource())
+ << "failed to substitute macro "
+ << LoggingResourceName(transformed_reference, callsite, decls)
+ << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags));
+ return {};
+ }
+
+ if (auto ref = ValueCast<Reference>(new_value.get())) {
+ return LinkReference(callsite, *ref, context, symbols, table, decls);
+ }
+ return new_value;
+ }
+
std::string err_str;
const SymbolTable::Symbol* s =
ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
@@ -367,17 +442,17 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
// Ex: Linking against own resources when building a static library.
- reference->id = s->id;
- reference->is_dynamic = s->is_dynamic;
- return true;
+ auto new_ref = std::make_unique<Reference>(reference);
+ new_ref->id = s->id;
+ new_ref->is_dynamic = s->is_dynamic;
+ return std::move(new_ref);
}
- DiagMessage error_msg(reference->GetSource());
- error_msg << "resource ";
- WriteResourceName(*reference, callsite, decls, &error_msg);
- error_msg << " " << err_str;
- context->GetDiagnostics()->Error(error_msg);
- return false;
+ context->GetDiagnostics()->Error(DiagMessage(reference.GetSource())
+ << "resource "
+ << LoggingResourceName(transformed_reference, callsite, decls)
+ << " " << err_str);
+ return {};
}
bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
@@ -412,14 +487,15 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
// The context of this resource is the package in which it is defined.
const CallSite callsite{name.package};
- ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
- &table->string_pool, &decl_stack);
+ ReferenceLinkerTransformer reference_transformer(callsite, context,
+ context->GetExternalSymbols(),
+ &table->string_pool, table, &decl_stack);
for (auto& config_value : entry->values) {
- config_value->value->Accept(&visitor);
+ config_value->value = config_value->value->Transform(reference_transformer);
}
- if (visitor.HasError()) {
+ if (reference_transformer.HasError()) {
error = true;
}
}
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 1256709edbf4..770f1e500ac0 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -28,6 +28,41 @@
namespace aapt {
+// A ValueTransformer that returns fully linked versions of resource and macro references.
+class ReferenceLinkerTransformer : public CloningValueTransformer {
+ public:
+ ReferenceLinkerTransformer(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
+ StringPool* string_pool, ResourceTable* table,
+ xml::IPackageDeclStack* decl)
+ : CloningValueTransformer(string_pool),
+ callsite_(callsite),
+ context_(context),
+ symbols_(symbols),
+ table_(table),
+ package_decls_(decl) {
+ }
+
+ std::unique_ptr<Reference> TransformDerived(const Reference* value) override;
+ std::unique_ptr<Item> TransformItem(const Reference* value) override;
+ std::unique_ptr<Style> TransformDerived(const Style* value) override;
+
+ bool HasError() {
+ return error_;
+ }
+
+ private:
+ // 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.
+ std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value, const Attribute* attr);
+
+ const CallSite& callsite_;
+ IAaptContext* context_;
+ SymbolTable* symbols_;
+ ResourceTable* table_;
+ xml::IPackageDeclStack* package_decls_;
+ bool error_ = false;
+};
+
// 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.
@@ -70,19 +105,28 @@ class ReferenceLinker : public IResourceTableConsumer {
// Writes the resource name to the DiagMessage, using the
// "orig_name (aka <transformed_name>)" syntax.
- static void WriteResourceName(const Reference& orig, const CallSite& callsite,
- const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+ /*static void WriteResourceName(const Reference& orig, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg);*/
// Same as WriteResourceName but omits the 'attr' part.
static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
- // 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(const CallSite& callsite, Reference* reference, IAaptContext* context,
- SymbolTable* symbols, const xml::IPackageDeclStack* decls);
+ // Returns a fully linked version a resource reference.
+ //
+ // If the reference points to a non-macro resource, the xml::IPackageDeclStack is used to
+ // determine the fully qualified name of the referenced resource. If the symbol is visible
+ // to the reference at the callsite, a copy of the reference with an updated updated ID is
+ // returned.
+ //
+ // If the reference points to a macro, the ResourceTable is used to find the macro definition and
+ // substitute its contents in place of the reference.
+ //
+ // Returns nullptr on failure, and an error message is logged to the IDiagnostics in the context.
+ static std::unique_ptr<Item> LinkReference(const CallSite& callsite, const Reference& reference,
+ IAaptContext* context, SymbolTable* symbols,
+ ResourceTable* table,
+ const xml::IPackageDeclStack* decls);
// Links all references in the ResourceTable.
bool Consume(IAaptContext* context, ResourceTable* table) override;
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 228c5bd743a0..2d8f0d39053f 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -365,4 +365,22 @@ TEST(ReferenceLinkerTest, ReferenceSymbolFromOtherSplit) {
EXPECT_THAT(s, IsNull());
}
+TEST(ReferenceLinkerTest, MacroFailToFindDefinition) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddReference("com.app.test:string/foo", ResourceId(0x7f020000), "com.app.test:macro/bar")
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .Build();
+
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.Consume(context.get(), table.get()));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 4ef2882ce347..bc93ec6908e7 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -280,13 +280,13 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
}
// Continue if we're taking the new resource.
-
+ CloningValueTransformer cloner(&main_table_->string_pool);
if (FileReference* f = ValueCast<FileReference>(src_config_value->value.get())) {
std::unique_ptr<FileReference> new_file_ref;
if (mangle_package) {
new_file_ref = CloneAndMangleFile(src_package->name, *f);
} else {
- new_file_ref = std::unique_ptr<FileReference>(f->Clone(&main_table_->string_pool));
+ new_file_ref = std::unique_ptr<FileReference>(f->Transform(cloner));
}
dst_config_value->value = std::move(new_file_ref);
@@ -294,8 +294,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
Maybe<std::string> original_comment = (dst_config_value->value)
? dst_config_value->value->GetComment() : Maybe<std::string>();
- dst_config_value->value = std::unique_ptr<Value>(
- src_config_value->value->Clone(&main_table_->string_pool));
+ dst_config_value->value = src_config_value->value->Transform(cloner);
// Keep the comment from the original resource and ignore all comments from overlaying
// resources
@@ -323,7 +322,9 @@ std::unique_ptr<FileReference> TableMerger::CloneAndMangleFile(
new_file_ref->file = file_ref.file;
return new_file_ref;
}
- return std::unique_ptr<FileReference>(file_ref.Clone(&main_table_->string_pool));
+
+ CloningValueTransformer cloner(&main_table_->string_pool);
+ return std::unique_ptr<FileReference>(file_ref.Transform(cloner));
}
bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFile* file) {
diff --git a/tools/aapt2/link/XmlCompatVersioner.cpp b/tools/aapt2/link/XmlCompatVersioner.cpp
index 6937ca961f06..957b64cd8dbb 100644
--- a/tools/aapt2/link/XmlCompatVersioner.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner.cpp
@@ -23,9 +23,10 @@
namespace aapt {
static xml::Attribute CopyAttr(const xml::Attribute& src, StringPool* out_string_pool) {
+ CloningValueTransformer cloner(out_string_pool);
xml::Attribute dst{src.namespace_uri, src.name, src.value, src.compiled_attribute};
if (src.compiled_value != nullptr) {
- dst.compiled_value.reset(src.compiled_value->Clone(out_string_pool));
+ dst.compiled_value = src.compiled_value->Transform(cloner);
}
return dst;
}
@@ -34,6 +35,7 @@ static xml::Attribute CopyAttr(const xml::Attribute& src, StringPool* out_string
// (came from a rule).
static bool CopyAttribute(const xml::Attribute& src_attr, bool generated, xml::Element* dst_el,
StringPool* out_string_pool) {
+ CloningValueTransformer cloner(out_string_pool);
xml::Attribute* dst_attr = dst_el->FindAttribute(src_attr.namespace_uri, src_attr.name);
if (dst_attr != nullptr) {
if (generated) {
@@ -41,7 +43,7 @@ static bool CopyAttribute(const xml::Attribute& src_attr, bool generated, xml::E
dst_attr->value = src_attr.value;
dst_attr->compiled_attribute = src_attr.compiled_attribute;
if (src_attr.compiled_value != nullptr) {
- dst_attr->compiled_value.reset(src_attr.compiled_value->Clone(out_string_pool));
+ dst_attr->compiled_value = src_attr.compiled_value->Transform(cloner);
}
return true;
}
@@ -158,7 +160,8 @@ static inline std::unique_ptr<Item> CloneIfNotNull(const std::unique_ptr<Item>&
if (src == nullptr) {
return {};
}
- return std::unique_ptr<Item>(src->Clone(out_string_pool));
+ CloningValueTransformer cloner(out_string_pool);
+ return src->Transform(cloner);
}
std::vector<DegradeResult> DegradeToManyRule::Degrade(const xml::Element& src_el,
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index a98ab0f76de4..d63809615a5b 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -82,7 +82,7 @@ TEST_F(XmlCompatVersionerTest, NoRulesOnlyStripsAndCopies) {
app:foo="16dp"
foo="bar"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -121,7 +121,7 @@ TEST_F(XmlCompatVersionerTest, SingleRule) {
app:foo="16dp"
foo="bar"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -181,7 +181,7 @@ TEST_F(XmlCompatVersionerTest, ChainedRule) {
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingHorizontal="24dp" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -256,7 +256,7 @@ TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) {
android:paddingLeft="16dp"
android:paddingRight="16dp"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
Item* padding_horizontal_value =
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index c3c16b92f712..aaa085e2eb15 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -33,49 +33,18 @@ namespace aapt {
namespace {
-// 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 DescendingValueVisitor {
- public:
- using DescendingValueVisitor::Visit;
-
- ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
- xml::IPackageDeclStack* decls)
- : callsite_(callsite), context_(context), symbols_(symbols), decls_(decls), error_(false) {}
-
- void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, decls_)) {
- error_ = true;
- }
- }
-
- bool HasError() const {
- return error_;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
-
- const CallSite& callsite_;
- IAaptContext* context_;
- SymbolTable* symbols_;
- xml::IPackageDeclStack* decls_;
- bool error_;
-};
-
// Visits each xml Element and compiles the attributes within.
class XmlVisitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
- XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
- SymbolTable* symbols)
+ XmlVisitor(const Source& source, StringPool* pool, const CallSite& callsite,
+ IAaptContext* context, ResourceTable* table, SymbolTable* symbols)
: source_(source),
callsite_(callsite),
context_(context),
symbols_(symbols),
- reference_visitor_(callsite, context, symbols, this) {
+ reference_transformer_(callsite, context, symbols, pool, table, this) {
}
void Visit(xml::Element* el) override {
@@ -127,7 +96,7 @@ class XmlVisitor : public xml::PackageAwareVisitor {
if (attr.compiled_value) {
// With a compiledValue, we must resolve the reference and assign it an ID.
attr.compiled_value->SetSource(source);
- attr.compiled_value->Accept(&reference_visitor_);
+ attr.compiled_value = attr.compiled_value->Transform(reference_transformer_);
} else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
// We won't be able to encode this as a string.
DiagMessage msg(source);
@@ -143,7 +112,7 @@ class XmlVisitor : public xml::PackageAwareVisitor {
}
bool HasError() {
- return error_ || reference_visitor_.HasError();
+ return error_ || reference_transformer_.HasError();
}
private:
@@ -154,7 +123,7 @@ class XmlVisitor : public xml::PackageAwareVisitor {
IAaptContext* context_;
SymbolTable* symbols_;
- ReferenceVisitor reference_visitor_;
+ ReferenceLinkerTransformer reference_transformer_;
bool error_ = false;
};
@@ -173,7 +142,8 @@ bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resour
callsite.package = context->GetCompilationPackage();
}
- XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
+ XmlVisitor visitor(resource->file.source, &resource->string_pool, callsite, context, table_,
+ context->GetExternalSymbols());
if (resource->root) {
resource->root->Accept(&visitor);
return !visitor.HasError();
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 0ce2e50d6e44..ddf5b9a22c2f 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -91,7 +91,7 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
nonAaptAttrRef="@id/id"
class="hello" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -144,7 +144,7 @@ TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@android:color/hidden" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
}
@@ -153,7 +153,7 @@ TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@*android:color/hidden" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
}
@@ -162,7 +162,7 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
support:colorAccent="#ff0000" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -181,7 +181,7 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:colorAccent="@app:color/red" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -203,7 +203,7 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
</View>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -239,7 +239,7 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
android:attr="@id/id"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -261,7 +261,7 @@ TEST_F(XmlReferenceLinkerTest, AddAngleOnGradientForAndroidQ) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<gradient />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
@@ -283,7 +283,7 @@ TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForAndroidQ) {
<gradient xmlns:android="http://schemas.android.com/apk/res/android"
android:angle="90"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
@@ -305,7 +305,7 @@ TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForPostAndroidQ) {
<gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
context_->SetMinSdkVersion(30);
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 2f319b11e3b2..116b2ab9aa98 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -229,6 +229,7 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
for (size_t idx = 0; idx < split_count; idx++) {
const SplitConstraints& split_constraint = split_constraints_[idx];
ResourceTable* split_table = splits_[idx].get();
+ CloningValueTransformer cloner(&split_table->string_pool);
// Select the values we want from this entry for this split.
SplitValueSelector selector(split_constraint);
@@ -254,8 +255,7 @@ void TableSplitter::SplitTable(ResourceTable* original_table) {
for (ResourceConfigValue* config_value : selected_values) {
ResourceConfigValue* new_config_value =
split_entry->FindOrCreateValue(config_value->config, config_value->product);
- new_config_value->value = std::unique_ptr<Value>(
- config_value->value->Clone(&split_table->string_pool));
+ new_config_value->value = config_value->value->Transform(cloner);
}
}
}
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 553c43e6c469..5d8ded39e654 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -200,10 +200,11 @@ class StaticSymbolSourceBuilder {
private:
std::unique_ptr<SymbolTable::Symbol> CloneSymbol(SymbolTable::Symbol* sym) {
+ CloningValueTransformer cloner(nullptr);
std::unique_ptr<SymbolTable::Symbol> clone = util::make_unique<SymbolTable::Symbol>();
clone->id = sym->id;
if (sym->attribute) {
- clone->attribute = std::unique_ptr<Attribute>(sym->attribute->Clone(nullptr));
+ clone->attribute = std::unique_ptr<Attribute>(sym->attribute->Transform(cloner));
}
clone->is_public = sym->is_public;
return clone;
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 005eeb936612..2cdcfe45b50e 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -374,6 +374,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t len, std::string*
std::unique_ptr<XmlResource> XmlResource::Clone() const {
std::unique_ptr<XmlResource> cloned = util::make_unique<XmlResource>(file);
+ CloningValueTransformer cloner(&cloned->string_pool);
if (root != nullptr) {
cloned->root = root->CloneElement([&](const xml::Element& src, xml::Element* dst) {
dst->attributes.reserve(src.attributes.size());
@@ -384,7 +385,7 @@ std::unique_ptr<XmlResource> XmlResource::Clone() const {
cloned_attr.value = attr.value;
cloned_attr.compiled_attribute = attr.compiled_attribute;
if (attr.compiled_value != nullptr) {
- cloned_attr.compiled_value.reset(attr.compiled_value->Clone(&cloned->string_pool));
+ cloned_attr.compiled_value = attr.compiled_value->Transform(cloner);
}
dst->attributes.push_back(std::move(cloned_attr));
}
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index a023494ad8f7..182203d397c3 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -177,6 +177,10 @@ const std::string& XmlPullParser::element_name() const {
return event_queue_.front().data2;
}
+const std::vector<XmlPullParser::PackageDecl>& XmlPullParser::package_decls() const {
+ return package_aliases_;
+}
+
XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
return event_queue_.front().attributes.begin();
}
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 6ebaa285745b..5da2d4b10a4b 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -123,6 +123,13 @@ class XmlPullParser : public IPackageDeclStack {
*/
Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
+
+ const std::vector<PackageDecl>& package_decls() const;
+
//
// Remaining methods are for retrieving information about attributes
// associated with a StartElement.
@@ -180,11 +187,6 @@ class XmlPullParser : public IPackageDeclStack {
const std::string empty_;
size_t depth_;
std::stack<std::string> namespace_uris_;
-
- struct PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
std::vector<PackageDecl> package_aliases_;
};
diff --git a/tools/fonts/Android.bp b/tools/fonts/Android.bp
index 8ea114f1efc2..eeb9e3ceda1e 100644
--- a/tools/fonts/Android.bp
+++ b/tools/fonts/Android.bp
@@ -12,6 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
python_defaults {
name: "fonts_python_defaults",
version: {