diff options
Diffstat (limited to 'tools')
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: { |