Loading tools/aapt2/Debug.cpp +23 −2 Original line number Diff line number Diff line Loading @@ -306,8 +306,29 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& break; } if (entry->overlayable) { for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) { printer->Print((i == 0) ? " " : "|"); printer->Print("OVERLAYABLE"); if (entry->overlayable_declarations[i].policy) { switch (entry->overlayable_declarations[i].policy.value()) { case Overlayable::Policy::kProduct: printer->Print("_PRODUCT"); break; case Overlayable::Policy::kProductServices: printer->Print("_PRODUCT_SERVICES"); break; case Overlayable::Policy::kSystem: printer->Print("_SYSTEM"); break; case Overlayable::Policy::kVendor: printer->Print("_VENDOR"); break; case Overlayable::Policy::kPublic: printer->Print("_PUBLIC"); break; } } } printer->Println(); Loading tools/aapt2/ResourceParser.cpp +112 −52 Original line number Diff line number Diff line Loading @@ -99,7 +99,7 @@ struct ParsedResource { ResourceId id; Visibility::Level visibility_level = Visibility::Level::kUndefined; bool allow_new = false; bool overlayable = false; std::vector<Overlayable> overlayable_declarations; std::string comment; std::unique_ptr<Value> value; Loading Loading @@ -133,11 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed } } if (res->overlayable) { Overlayable overlayable; overlayable.source = res->source; overlayable.comment = res->comment; if (!table->SetOverlayable(res->name, overlayable, diag)) { for (auto& overlayable : res->overlayable_declarations) { if (!table->AddOverlayable(res->name, overlayable, diag)) { return false; } } Loading Loading @@ -673,7 +670,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, if (can_be_bag) { const auto bag_iter = elToBagMap.find(resource_type); if (bag_iter != elToBagMap.end()) { // Ensure we have a name (unless this is a <public-group>). // Ensure we have a name (unless this is a <public-group> or <overlayable>). if (resource_type != "public-group" && resource_type != "overlayable") { if (!maybe_name) { diag_->Error(DiagMessage(out_resource->source) Loading Loading @@ -1062,44 +1059,100 @@ bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (out_resource->config != ConfigDescription::DefaultConfig()) { diag_->Warn(DiagMessage(out_resource->source) << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag"); << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag"); } if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) { const StringPiece& policy = maybe_policy.value(); if (policy != "system") { diag_->Error(DiagMessage(out_resource->source) << "<overlayable> has invalid policy '" << policy << "'"); return false; } } std::string comment; std::vector<Overlayable::Policy> policies; bool error = false; const size_t depth = parser->depth(); while (xml::XmlPullParser::NextChildNode(parser, depth)) { if (parser->event() != xml::XmlPullParser::Event::kStartElement) { // Skip text/comments. const size_t start_depth = parser->depth(); while (xml::XmlPullParser::IsGoodEvent(parser->Next())) { xml::XmlPullParser::Event event = parser->event(); if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) { // Break the loop when exiting the overyabale element break; } else if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth + 1) { // Clear the current policies when exiting the policy element policies.clear(); continue; } else if (event == xml::XmlPullParser::Event::kComment) { // Get the comment of individual item elements comment = parser->comment(); continue; } else if (event != xml::XmlPullParser::Event::kStartElement) { // Skip to the next element continue; } const Source item_source = source_.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); const std::string& element_namespace = parser->element_namespace(); if (element_namespace.empty() && element_name == "item") { if (!ParseOverlayableItem(parser, policies, comment, out_resource)) { error = true; } } else if (element_namespace.empty() && element_name == "policy") { if (!policies.empty()) { // If the policy list is not empty, then we are currently inside a policy element diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested"); error = true; break; } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { // Parse the polices separated by vertical bar characters to allow for specifying multiple // policies at once for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); if (trimmed_part == "public") { policies.push_back(Overlayable::Policy::kPublic); } else if (trimmed_part == "product") { policies.push_back(Overlayable::Policy::kProduct); } else if (trimmed_part == "product_services") { policies.push_back(Overlayable::Policy::kProductServices); } else if (trimmed_part == "system") { policies.push_back(Overlayable::Policy::kSystem); } else if (trimmed_part == "vendor") { policies.push_back(Overlayable::Policy::kVendor); } else { diag_->Error(DiagMessage(out_resource->source) << "<policy> has unsupported type '" << trimmed_part << "'"); error = true; continue; } } } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in " << " <overlayable>"); error = true; break; } } return !error; } bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser, const std::vector<Overlayable::Policy>& policies, const std::string& comment, ParsedResource* out_resource) { const Source item_source = source_.WithLine(parser->line_number()); Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name"); if (!maybe_name) { diag_->Error(DiagMessage(item_source) << "<item> within an <overlayable> tag must have a 'name' attribute"); error = true; continue; return false; } Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type"); if (!maybe_type) { diag_->Error(DiagMessage(item_source) << "<item> within an <overlayable> tag must have a 'type' attribute"); error = true; continue; return false; } const ResourceType* type = ParseResourceType(maybe_type.value()); Loading @@ -1107,27 +1160,34 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '" << maybe_type.value() << "' in <item> within an <overlayable>"); error = true; continue; return false; } ParsedResource child_resource; child_resource.name.type = *type; child_resource.name.entry = maybe_name.value().to_string(); child_resource.source = item_source; child_resource.overlayable = true; if (policies.empty()) { Overlayable overlayable; overlayable.source = item_source; overlayable.comment = comment; child_resource.overlayable_declarations.push_back(overlayable); } else { for (Overlayable::Policy policy : policies) { Overlayable overlayable; overlayable.policy = policy; overlayable.source = item_source; overlayable.comment = comment; child_resource.overlayable_declarations.push_back(overlayable); } } if (options_.visibility) { child_resource.visibility_level = options_.visibility.value(); } out_resource->child_resources.push_back(std::move(child_resource)); xml::XmlPullParser::SkipCurrentElement(parser); } else if (!ShouldIgnoreElement(element_namespace, element_name)) { diag_->Error(DiagMessage(item_source) << ":" << element_name << ">"); error = true; } } return !error; return true; } bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) { Loading tools/aapt2/ResourceParser.h +4 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,10 @@ class ResourceParser { bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseOverlayableItem(xml::XmlPullParser* parser, const std::vector<Overlayable::Policy>& policies, const std::string& comment, ParsedResource* out_resource); bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak); Loading tools/aapt2/ResourceParser_test.cpp +212 −27 Original line number Diff line number Diff line Loading @@ -891,63 +891,248 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)")); } TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) { TEST_F(ResourceParserTest, ParseOverlayable) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); input = R"( <overlayable> <item type="string" name="foo" /> <item type="drawable" name="bar" /> </overlayable>)"; ASSERT_TRUE(TestParse(input)); auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); } TEST_F(ResourceParserTest, ParseOverlayablePolicy) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); input = R"( <overlayable> <item type="string" name="foo" /> <policy type="product"> <item type="string" name="bar" /> </policy> <policy type="product_services"> <item type="string" name="baz" /> </policy> <policy type="system"> <item type="string" name="fiz" /> </policy> <policy type="vendor"> <item type="string" name="fuz" /> </policy> <policy type="public"> <item type="string" name="faz" /> </policy> </overlayable>)"; ASSERT_TRUE(TestParse(input)); auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kProduct)); search_result = table_.FindResource(test::ParseNameOrDie("string/baz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kProductServices)); search_result = table_.FindResource(test::ParseNameOrDie("string/fiz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kSystem)); search_result = table_.FindResource(test::ParseNameOrDie("string/fuz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kVendor)); search_result = table_.FindResource(test::ParseNameOrDie("string/faz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kPublic)); } TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { std::string input = R"( <overlayable policy="illegal_policy"> <overlayable> <policy type="illegal_policy"> <item type="string" name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy="system"> <overlayable> <policy type="product"> <item name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy="system"> <item type="attr" /> <overlayable> <policy type="vendor"> <item type="string" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } input = R"( <overlayable policy="system"> <item type="bad_type" name="foo" /> TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { std::string input = R"( <overlayable> <policy type="vendor|product_services"> <item type="string" name="foo" /> </policy> <policy type="product|system"> <item type="string" name="bar" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); ASSERT_TRUE(TestParse(input)); input = R"(<overlayable policy="system" />)"; EXPECT_TRUE(TestParse(input)); auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kVendor)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy, Eq(Overlayable::Policy::kProductServices)); input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kProduct)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy, Eq(Overlayable::Policy::kSystem)); } TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { std::string input = R"( <overlayable> <item type="string" name="foo" /> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy="system"> <overlayable> <item type="string" name="foo" /> </overlayable> <overlayable> <item type="string" name="foo" /> <item type="dimen" name="foo" /> </overlayable>)"; ASSERT_TRUE(TestParse(input)); EXPECT_FALSE(TestParse(input)); input = R"( <overlayable"> <policy type="product"> <item type="string" name="foo" /> <item type="string" name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <item type="string" name="bar" /> <policy type="product"> <item type="string" name="foo" /> </policy> </overlayable> <overlayable> <policy type="product"> <item type="string" name="foo" /> </policy> </overlayable>)"; ASSERT_TRUE(TestParse(input)); EXPECT_FALSE(TestParse(input)); } Maybe<ResourceTable::SearchResult> search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_TRUE(search_result.value().entry->overlayable); TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) { std::string input = R"( <overlayable policy="product"> <item type="string" name="foo" /> </overlayable> <overlayable policy=""> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy=""> <item type="string" name="foo" /> </overlayable> <overlayable policy="product"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) { std::string input = R"( <overlayable> <policy type="vendor|product"> <item type="string" name="foo" /> </policy> </overlayable> <overlayable> <policy type="product_services|vendor"> <item type="string" name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, NestPolicyInOverlayableError) { std::string input = R"( <overlayable> <policy type="vendor|product"> <policy type="product_services"> <item type="string" name="foo" /> </policy> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } Loading tools/aapt2/ResourceTable.cpp +27 −12 Original line number Diff line number Diff line Loading @@ -625,17 +625,17 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& return true; } bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag); return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag); } bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name, bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag); return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag); } bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, NameValidator name_validator, IDiagnostics* diag) { CHECK(diag != nullptr); Loading @@ -646,13 +646,28 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla ResourceTablePackage* package = FindOrCreatePackage(name.package); ResourceTableType* type = package->FindOrCreateType(name.type); ResourceEntry* entry = type->FindOrCreateEntry(name.entry); if (entry->overlayable) { for (auto& overlayable_declaration : entry->overlayable_declarations) { // An overlayable resource cannot be declared twice with the same policy if (overlayable.policy == overlayable_declaration.policy) { diag->Error(DiagMessage(overlayable.source) << "duplicate overlayable declaration for resource '" << name << "'"); diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here"); diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here"); return false; } entry->overlayable = overlayable; // An overlayable resource cannot be declared once with a policy and without a policy because // the policy becomes unused if (!overlayable.policy || !overlayable_declaration.policy) { diag->Error(DiagMessage(overlayable.source) << "overlayable resource '" << name << "'" << " declared once with a policy and once with no policy"); diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here"); return false; } } entry->overlayable_declarations.push_back(overlayable); return true; } Loading Loading @@ -688,7 +703,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const { new_entry->id = entry->id; new_entry->visibility = entry->visibility; new_entry->allow_new = entry->allow_new; new_entry->overlayable = entry->overlayable; new_entry->overlayable_declarations = entry->overlayable_declarations; for (const auto& config_value : entry->values) { ResourceConfigValue* new_value = Loading Loading
tools/aapt2/Debug.cpp +23 −2 Original line number Diff line number Diff line Loading @@ -306,8 +306,29 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& break; } if (entry->overlayable) { for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) { printer->Print((i == 0) ? " " : "|"); printer->Print("OVERLAYABLE"); if (entry->overlayable_declarations[i].policy) { switch (entry->overlayable_declarations[i].policy.value()) { case Overlayable::Policy::kProduct: printer->Print("_PRODUCT"); break; case Overlayable::Policy::kProductServices: printer->Print("_PRODUCT_SERVICES"); break; case Overlayable::Policy::kSystem: printer->Print("_SYSTEM"); break; case Overlayable::Policy::kVendor: printer->Print("_VENDOR"); break; case Overlayable::Policy::kPublic: printer->Print("_PUBLIC"); break; } } } printer->Println(); Loading
tools/aapt2/ResourceParser.cpp +112 −52 Original line number Diff line number Diff line Loading @@ -99,7 +99,7 @@ struct ParsedResource { ResourceId id; Visibility::Level visibility_level = Visibility::Level::kUndefined; bool allow_new = false; bool overlayable = false; std::vector<Overlayable> overlayable_declarations; std::string comment; std::unique_ptr<Value> value; Loading Loading @@ -133,11 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed } } if (res->overlayable) { Overlayable overlayable; overlayable.source = res->source; overlayable.comment = res->comment; if (!table->SetOverlayable(res->name, overlayable, diag)) { for (auto& overlayable : res->overlayable_declarations) { if (!table->AddOverlayable(res->name, overlayable, diag)) { return false; } } Loading Loading @@ -673,7 +670,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, if (can_be_bag) { const auto bag_iter = elToBagMap.find(resource_type); if (bag_iter != elToBagMap.end()) { // Ensure we have a name (unless this is a <public-group>). // Ensure we have a name (unless this is a <public-group> or <overlayable>). if (resource_type != "public-group" && resource_type != "overlayable") { if (!maybe_name) { diag_->Error(DiagMessage(out_resource->source) Loading Loading @@ -1062,44 +1059,100 @@ bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (out_resource->config != ConfigDescription::DefaultConfig()) { diag_->Warn(DiagMessage(out_resource->source) << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag"); << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag"); } if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) { const StringPiece& policy = maybe_policy.value(); if (policy != "system") { diag_->Error(DiagMessage(out_resource->source) << "<overlayable> has invalid policy '" << policy << "'"); return false; } } std::string comment; std::vector<Overlayable::Policy> policies; bool error = false; const size_t depth = parser->depth(); while (xml::XmlPullParser::NextChildNode(parser, depth)) { if (parser->event() != xml::XmlPullParser::Event::kStartElement) { // Skip text/comments. const size_t start_depth = parser->depth(); while (xml::XmlPullParser::IsGoodEvent(parser->Next())) { xml::XmlPullParser::Event event = parser->event(); if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) { // Break the loop when exiting the overyabale element break; } else if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth + 1) { // Clear the current policies when exiting the policy element policies.clear(); continue; } else if (event == xml::XmlPullParser::Event::kComment) { // Get the comment of individual item elements comment = parser->comment(); continue; } else if (event != xml::XmlPullParser::Event::kStartElement) { // Skip to the next element continue; } const Source item_source = source_.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); const std::string& element_namespace = parser->element_namespace(); if (element_namespace.empty() && element_name == "item") { if (!ParseOverlayableItem(parser, policies, comment, out_resource)) { error = true; } } else if (element_namespace.empty() && element_name == "policy") { if (!policies.empty()) { // If the policy list is not empty, then we are currently inside a policy element diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested"); error = true; break; } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { // Parse the polices separated by vertical bar characters to allow for specifying multiple // policies at once for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); if (trimmed_part == "public") { policies.push_back(Overlayable::Policy::kPublic); } else if (trimmed_part == "product") { policies.push_back(Overlayable::Policy::kProduct); } else if (trimmed_part == "product_services") { policies.push_back(Overlayable::Policy::kProductServices); } else if (trimmed_part == "system") { policies.push_back(Overlayable::Policy::kSystem); } else if (trimmed_part == "vendor") { policies.push_back(Overlayable::Policy::kVendor); } else { diag_->Error(DiagMessage(out_resource->source) << "<policy> has unsupported type '" << trimmed_part << "'"); error = true; continue; } } } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in " << " <overlayable>"); error = true; break; } } return !error; } bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser, const std::vector<Overlayable::Policy>& policies, const std::string& comment, ParsedResource* out_resource) { const Source item_source = source_.WithLine(parser->line_number()); Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name"); if (!maybe_name) { diag_->Error(DiagMessage(item_source) << "<item> within an <overlayable> tag must have a 'name' attribute"); error = true; continue; return false; } Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type"); if (!maybe_type) { diag_->Error(DiagMessage(item_source) << "<item> within an <overlayable> tag must have a 'type' attribute"); error = true; continue; return false; } const ResourceType* type = ParseResourceType(maybe_type.value()); Loading @@ -1107,27 +1160,34 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '" << maybe_type.value() << "' in <item> within an <overlayable>"); error = true; continue; return false; } ParsedResource child_resource; child_resource.name.type = *type; child_resource.name.entry = maybe_name.value().to_string(); child_resource.source = item_source; child_resource.overlayable = true; if (policies.empty()) { Overlayable overlayable; overlayable.source = item_source; overlayable.comment = comment; child_resource.overlayable_declarations.push_back(overlayable); } else { for (Overlayable::Policy policy : policies) { Overlayable overlayable; overlayable.policy = policy; overlayable.source = item_source; overlayable.comment = comment; child_resource.overlayable_declarations.push_back(overlayable); } } if (options_.visibility) { child_resource.visibility_level = options_.visibility.value(); } out_resource->child_resources.push_back(std::move(child_resource)); xml::XmlPullParser::SkipCurrentElement(parser); } else if (!ShouldIgnoreElement(element_namespace, element_name)) { diag_->Error(DiagMessage(item_source) << ":" << element_name << ">"); error = true; } } return !error; return true; } bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) { Loading
tools/aapt2/ResourceParser.h +4 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,10 @@ class ResourceParser { bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseOverlayableItem(xml::XmlPullParser* parser, const std::vector<Overlayable::Policy>& policies, const std::string& comment, ParsedResource* out_resource); bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource); bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak); Loading
tools/aapt2/ResourceParser_test.cpp +212 −27 Original line number Diff line number Diff line Loading @@ -891,63 +891,248 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) { ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)")); } TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) { TEST_F(ResourceParserTest, ParseOverlayable) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); input = R"( <overlayable> <item type="string" name="foo" /> <item type="drawable" name="bar" /> </overlayable>)"; ASSERT_TRUE(TestParse(input)); auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); } TEST_F(ResourceParserTest, ParseOverlayablePolicy) { std::string input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); input = R"( <overlayable> <item type="string" name="foo" /> <policy type="product"> <item type="string" name="bar" /> </policy> <policy type="product_services"> <item type="string" name="baz" /> </policy> <policy type="system"> <item type="string" name="fiz" /> </policy> <policy type="vendor"> <item type="string" name="fuz" /> </policy> <policy type="public"> <item type="string" name="faz" /> </policy> </overlayable>)"; ASSERT_TRUE(TestParse(input)); auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kProduct)); search_result = table_.FindResource(test::ParseNameOrDie("string/baz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kProductServices)); search_result = table_.FindResource(test::ParseNameOrDie("string/fiz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kSystem)); search_result = table_.FindResource(test::ParseNameOrDie("string/fuz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kVendor)); search_result = table_.FindResource(test::ParseNameOrDie("string/faz")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kPublic)); } TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) { std::string input = R"( <overlayable policy="illegal_policy"> <overlayable> <policy type="illegal_policy"> <item type="string" name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy="system"> <overlayable> <policy type="product"> <item name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy="system"> <item type="attr" /> <overlayable> <policy type="vendor"> <item type="string" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } input = R"( <overlayable policy="system"> <item type="bad_type" name="foo" /> TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) { std::string input = R"( <overlayable> <policy type="vendor|product_services"> <item type="string" name="foo" /> </policy> <policy type="product|system"> <item type="string" name="bar" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); ASSERT_TRUE(TestParse(input)); input = R"(<overlayable policy="system" />)"; EXPECT_TRUE(TestParse(input)); auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kVendor)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy, Eq(Overlayable::Policy::kProductServices)); input = R"(<overlayable />)"; EXPECT_TRUE(TestParse(input)); search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy, Eq(Overlayable::Policy::kProduct)); EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy, Eq(Overlayable::Policy::kSystem)); } TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { std::string input = R"( <overlayable> <item type="string" name="foo" /> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy="system"> <overlayable> <item type="string" name="foo" /> </overlayable> <overlayable> <item type="string" name="foo" /> <item type="dimen" name="foo" /> </overlayable>)"; ASSERT_TRUE(TestParse(input)); EXPECT_FALSE(TestParse(input)); input = R"( <overlayable"> <policy type="product"> <item type="string" name="foo" /> <item type="string" name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable> <item type="string" name="bar" /> <policy type="product"> <item type="string" name="foo" /> </policy> </overlayable> <overlayable> <policy type="product"> <item type="string" name="foo" /> </policy> </overlayable>)"; ASSERT_TRUE(TestParse(input)); EXPECT_FALSE(TestParse(input)); } Maybe<ResourceTable::SearchResult> search_result = table_.FindResource(test::ParseNameOrDie("string/bar")); ASSERT_TRUE(search_result); ASSERT_THAT(search_result.value().entry, NotNull()); EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined)); EXPECT_TRUE(search_result.value().entry->overlayable); TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) { std::string input = R"( <overlayable policy="product"> <item type="string" name="foo" /> </overlayable> <overlayable policy=""> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); input = R"( <overlayable policy=""> <item type="string" name="foo" /> </overlayable> <overlayable policy="product"> <item type="string" name="foo" /> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, DuplicateOverlayableIsError) { TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) { std::string input = R"( <overlayable> <policy type="vendor|product"> <item type="string" name="foo" /> </policy> </overlayable> <overlayable> <policy type="product_services|vendor"> <item type="string" name="foo" /> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } TEST_F(ResourceParserTest, NestPolicyInOverlayableError) { std::string input = R"( <overlayable> <policy type="vendor|product"> <policy type="product_services"> <item type="string" name="foo" /> </policy> </policy> </overlayable>)"; EXPECT_FALSE(TestParse(input)); } Loading
tools/aapt2/ResourceTable.cpp +27 −12 Original line number Diff line number Diff line Loading @@ -625,17 +625,17 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& return true; } bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag); return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag); } bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name, bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable, IDiagnostics* diag) { return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag); return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag); } bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable, NameValidator name_validator, IDiagnostics* diag) { CHECK(diag != nullptr); Loading @@ -646,13 +646,28 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla ResourceTablePackage* package = FindOrCreatePackage(name.package); ResourceTableType* type = package->FindOrCreateType(name.type); ResourceEntry* entry = type->FindOrCreateEntry(name.entry); if (entry->overlayable) { for (auto& overlayable_declaration : entry->overlayable_declarations) { // An overlayable resource cannot be declared twice with the same policy if (overlayable.policy == overlayable_declaration.policy) { diag->Error(DiagMessage(overlayable.source) << "duplicate overlayable declaration for resource '" << name << "'"); diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here"); diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here"); return false; } entry->overlayable = overlayable; // An overlayable resource cannot be declared once with a policy and without a policy because // the policy becomes unused if (!overlayable.policy || !overlayable_declaration.policy) { diag->Error(DiagMessage(overlayable.source) << "overlayable resource '" << name << "'" << " declared once with a policy and once with no policy"); diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here"); return false; } } entry->overlayable_declarations.push_back(overlayable); return true; } Loading Loading @@ -688,7 +703,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const { new_entry->id = entry->id; new_entry->visibility = entry->visibility; new_entry->allow_new = entry->allow_new; new_entry->overlayable = entry->overlayable; new_entry->overlayable_declarations = entry->overlayable_declarations; for (const auto& config_value : entry->values) { ResourceConfigValue* new_value = Loading