AAPT2: Fix string escaping

We were processing escaped strings too early, before
parsing of values into types. Now the escaped strings get
processed when they are being flattened.

Bug: 37715376
Test: make aapt2_tests
Change-Id: Ic59aa2e3a20c40756c219752ff74b2a4f8a602ba
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 6055190..98f5f1d 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -42,7 +42,6 @@
   std::stack<xml::Node*> node_stack;
   std::string pending_comment;
   std::unique_ptr<xml::Text> last_text_node;
-  util::StringBuilder pending_text;
 };
 
 /**
@@ -66,14 +65,12 @@
 
 static void FinishPendingText(Stack* stack) {
   if (stack->last_text_node != nullptr) {
-    if (!stack->pending_text.IsEmpty()) {
-      stack->last_text_node->text = stack->pending_text.ToString();
-      stack->pending_text = {};
+    if (!stack->last_text_node->text.empty()) {
       stack->node_stack.top()->AppendChild(std::move(stack->last_text_node));
     } else {
       // Drop an empty text node.
-      stack->last_text_node = nullptr;
     }
+    stack->last_text_node = nullptr;
   }
 }
 
@@ -138,13 +135,11 @@
   while (*attrs) {
     Attribute attribute;
     SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
-    util::StringBuilder builder;
-    builder.Append(*attrs++);
-    attribute.value = builder.ToString();
+    attribute.value = *attrs++;
 
     // Insert in sorted order.
-    auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(),
-                                 attribute, less_attribute);
+    auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
+                                 less_attribute);
     el->attributes.insert(iter, std::move(attribute));
   }
 
@@ -173,14 +168,14 @@
 
   // See if we can just append the text to a previous text node.
   if (stack->last_text_node != nullptr) {
-    stack->pending_text.Append(str);
+    stack->last_text_node->text.append(str.data(), str.size());
     return;
   }
 
   stack->last_text_node = util::make_unique<Text>();
   stack->last_text_node->line_number = XML_GetCurrentLineNumber(parser);
   stack->last_text_node->column_number = XML_GetCurrentColumnNumber(parser);
-  stack->pending_text.Append(str);
+  stack->last_text_node->text = str.to_string();
 }
 
 static void XMLCALL CommentDataHandler(void* user_data, const char* comment) {
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index 0fc3cec6..fb18ea3 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -49,23 +49,26 @@
   EXPECT_EQ(ns->namespace_prefix, "android");
 }
 
-TEST(XmlDomTest, HandleEscapes) {
-  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
-      R"EOF(<shortcode pattern="\\d{5}">\\d{5}</shortcode>)EOF");
+// Escaping is handled after parsing of the values for resource-specific values.
+TEST(XmlDomTest, ForwardEscapes) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
+      <element value="\?hello" pattern="\\d{5}">\\d{5}</element>)EOF");
 
   xml::Element* el = xml::FindRootElement(doc->root.get());
   ASSERT_NE(nullptr, el);
 
   xml::Attribute* attr = el->FindAttribute({}, "pattern");
   ASSERT_NE(nullptr, attr);
+  EXPECT_EQ("\\\\d{5}", attr->value);
 
-  EXPECT_EQ("\\d{5}", attr->value);
+  attr = el->FindAttribute({}, "value");
+  ASSERT_NE(nullptr, attr);
+  EXPECT_EQ("\\?hello", attr->value);
 
   ASSERT_EQ(1u, el->children.size());
-
   xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
   ASSERT_NE(nullptr, text);
-  EXPECT_EQ("\\d{5}", text->text);
+  EXPECT_EQ("\\\\d{5}", text->text);
 }
 
 }  // namespace aapt