OSDN Git Service

ART: Add cutout for Character.toLower/UpperCase
authorAndreas Gampe <agampe@google.com>
Sat, 16 Apr 2016 04:24:28 +0000 (21:24 -0700)
committerAndreas Gampe <agampe@google.com>
Tue, 19 Apr 2016 00:19:58 +0000 (17:19 -0700)
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

runtime/interpreter/unstarted_runtime.cc
runtime/interpreter/unstarted_runtime_list.h
runtime/interpreter/unstarted_runtime_test.cc

index 9531557..a2a190e 100644 (file)
 
 #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 @@ static void AbortTransactionOrFail(Thread* self, const char* fmt, ...) {
   }
 }
 
+// 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,
index de62ed8..be881cd 100644 (file)
@@ -19,6 +19,8 @@
 
 // Methods that intercept available libcore implementations.
 #define UNSTARTED_RUNTIME_DIRECT_LIST(V)    \
+  V(CharacterToLowerCase, "int java.lang.Character.toLowerCase(int)") \
+  V(CharacterToUpperCase, "int java.lang.Character.toUpperCase(int)") \
   V(ClassForName, "java.lang.Class java.lang.Class.forName(java.lang.String)") \
   V(ClassForNameLong, "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)") \
   V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \
index 1b5b665..100a446 100644 (file)
@@ -17,6 +17,7 @@
 #include "unstarted_runtime.h"
 
 #include <limits>
+#include <locale>
 
 #include "base/casts.h"
 #include "class_linker.h"
@@ -30,6 +31,7 @@
 #include "runtime.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
+#include "transaction.h"
 
 namespace art {
 namespace interpreter {
@@ -182,6 +184,16 @@ class UnstartedRuntimeTest : public CommonRuntimeTest {
       EXPECT_EQ(expect_int64t, result_int64t) << result.GetD() << " vs " << test_pairs[i][1];
     }
   }
+
+  // Prepare for aborts. Aborts assume that the exception class is already resolved, as the
+  // loading code doesn't work under transactions.
+  void PrepareForAborts() SHARED_REQUIRES(Locks::mutator_lock_) {
+    mirror::Object* result = Runtime::Current()->GetClassLinker()->FindClass(
+        Thread::Current(),
+        Transaction::kAbortExceptionSignature,
+        ScopedNullHandle<mirror::ClassLoader>());
+    CHECK(result != nullptr);
+  }
 };
 
 TEST_F(UnstartedRuntimeTest, MemoryPeekByte) {
@@ -689,5 +701,106 @@ TEST_F(UnstartedRuntimeTest, Floor) {
   ShadowFrame::DeleteDeoptimizedFrame(tmp);
 }
 
+TEST_F(UnstartedRuntimeTest, ToLowerUpper) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  std::locale c_locale("C");
+
+  // Check ASCII.
+  for (uint32_t i = 0; i < 128; ++i) {
+    bool c_upper = std::isupper(static_cast<char>(i), c_locale);
+    bool c_lower = std::islower(static_cast<char>(i), c_locale);
+    EXPECT_FALSE(c_upper && c_lower) << i;
+
+    // Check toLowerCase.
+    {
+      JValue result;
+      tmp->SetVReg(0, static_cast<int32_t>(i));
+      UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+      ASSERT_FALSE(self->IsExceptionPending());
+      uint32_t lower_result = static_cast<uint32_t>(result.GetI());
+      if (c_lower) {
+        EXPECT_EQ(i, lower_result);
+      } else if (c_upper) {
+        EXPECT_EQ(static_cast<uint32_t>(std::tolower(static_cast<char>(i), c_locale)),
+                  lower_result);
+      } else {
+        EXPECT_EQ(i, lower_result);
+      }
+    }
+
+    // Check toUpperCase.
+    {
+      JValue result2;
+      tmp->SetVReg(0, static_cast<int32_t>(i));
+      UnstartedCharacterToUpperCase(self, tmp, &result2, 0);
+      ASSERT_FALSE(self->IsExceptionPending());
+      uint32_t upper_result = static_cast<uint32_t>(result2.GetI());
+      if (c_upper) {
+        EXPECT_EQ(i, upper_result);
+      } else if (c_lower) {
+        EXPECT_EQ(static_cast<uint32_t>(std::toupper(static_cast<char>(i), c_locale)),
+                  upper_result);
+      } else {
+        EXPECT_EQ(i, upper_result);
+      }
+    }
+  }
+
+  // Check abort for other things. Can't test all.
+
+  PrepareForAborts();
+
+  for (uint32_t i = 128; i < 256; ++i) {
+    {
+      JValue result;
+      tmp->SetVReg(0, static_cast<int32_t>(i));
+      Transaction transaction;
+      Runtime::Current()->EnterTransactionMode(&transaction);
+      UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+      Runtime::Current()->ExitTransactionMode();
+      ASSERT_TRUE(self->IsExceptionPending());
+      ASSERT_TRUE(transaction.IsAborted());
+    }
+    {
+      JValue result;
+      tmp->SetVReg(0, static_cast<int32_t>(i));
+      Transaction transaction;
+      Runtime::Current()->EnterTransactionMode(&transaction);
+      UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+      Runtime::Current()->ExitTransactionMode();
+      ASSERT_TRUE(self->IsExceptionPending());
+      ASSERT_TRUE(transaction.IsAborted());
+    }
+  }
+  for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) {
+    {
+      JValue result;
+      tmp->SetVReg(0, static_cast<int32_t>(i));
+      Transaction transaction;
+      Runtime::Current()->EnterTransactionMode(&transaction);
+      UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+      Runtime::Current()->ExitTransactionMode();
+      ASSERT_TRUE(self->IsExceptionPending());
+      ASSERT_TRUE(transaction.IsAborted());
+    }
+    {
+      JValue result;
+      tmp->SetVReg(0, static_cast<int32_t>(i));
+      Transaction transaction;
+      Runtime::Current()->EnterTransactionMode(&transaction);
+      UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+      Runtime::Current()->ExitTransactionMode();
+      ASSERT_TRUE(self->IsExceptionPending());
+      ASSERT_TRUE(transaction.IsAborted());
+    }
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
 }  // namespace interpreter
 }  // namespace art