ART: Add cutout for Character.toLower/UpperCase

Add support for ASCII codepoint toLowerCase/toUpperCase. Those
inputs are easy, and generally sufficient.

Allows to compile-time initialize:
* android.text.Html$HtmlParser
* java.util.UUID$Holder
* sun.security.ec.ECKeyFactory

Bug: 27265238

(cherry picked from commit 0bdce99fcecdadcbafc8e7a9bb92f491a4f37b2a)

Change-Id: I0f164a7df4f26c0b266cef230e36f6ca3af20bde
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 9531557..a2a190e 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -16,11 +16,13 @@
 
 #include "unstarted_runtime.h"
 
+#include <ctype.h>
 #include <errno.h>
 #include <stdlib.h>
 
 #include <cmath>
 #include <limits>
+#include <locale>
 #include <unordered_map>
 
 #include "ScopedLocalRef.h"
@@ -70,6 +72,43 @@
   }
 }
 
+// Restricted support for character upper case / lower case. Only support ASCII, where
+// it's easy. Abort the transaction otherwise.
+static void CharacterLowerUpper(Thread* self,
+                                ShadowFrame* shadow_frame,
+                                JValue* result,
+                                size_t arg_offset,
+                                bool to_lower_case) SHARED_REQUIRES(Locks::mutator_lock_) {
+  uint32_t int_value = static_cast<uint32_t>(shadow_frame->GetVReg(arg_offset));
+
+  // Only ASCII (7-bit).
+  if (!isascii(int_value)) {
+    AbortTransactionOrFail(self,
+                           "Only support ASCII characters for toLowerCase/toUpperCase: %u",
+                           int_value);
+    return;
+  }
+
+  std::locale c_locale("C");
+  char char_value = static_cast<char>(int_value);
+
+  if (to_lower_case) {
+    result->SetI(std::tolower(char_value, c_locale));
+  } else {
+    result->SetI(std::toupper(char_value, c_locale));
+  }
+}
+
+void UnstartedRuntime::UnstartedCharacterToLowerCase(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+  CharacterLowerUpper(self, shadow_frame, result, arg_offset, true);
+}
+
+void UnstartedRuntime::UnstartedCharacterToUpperCase(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+  CharacterLowerUpper(self, shadow_frame, result, arg_offset, false);
+}
+
 // Helper function to deal with class loading in an unstarted runtime.
 static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> className,
                                       Handle<mirror::ClassLoader> class_loader, JValue* result,