ART: Update unstarted runtime for changed libcore

Update float conversion to current libcore implementation. Add
tests.

Bug: 34956610
Test: m test-art-host-gtest-unstarted_runtime_test
Change-Id: Ib62c64011e2d5b980fb8fab81f6c343065ce946c
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 371e2f1..545cc1a 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 
 #include <cmath>
+#include <initializer_list>
 #include <limits>
 #include <locale>
 #include <unordered_map>
@@ -883,43 +884,74 @@
   GetSystemProperty(self, shadow_frame, result, arg_offset, true);
 }
 
-void UnstartedRuntime::UnstartedThreadLocalGet(
-    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
-  std::string caller(ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod()));
-  bool ok = false;
-  if (caller == "void java.lang.FloatingDecimal.developLongDigits(int, long, long)" ||
-      caller == "java.lang.String java.lang.FloatingDecimal.toJavaFormatString()") {
-    // Allocate non-threadlocal buffer.
-    result->SetL(mirror::CharArray::Alloc(self, 26));
-    ok = true;
-  } else if (caller ==
-             "java.lang.FloatingDecimal java.lang.FloatingDecimal.getThreadLocalInstance()") {
-    // Allocate new object.
-    StackHandleScope<2> hs(self);
-    Handle<mirror::Class> h_real_to_string_class(hs.NewHandle(
-        shadow_frame->GetLink()->GetMethod()->GetDeclaringClass()));
-    Handle<mirror::Object> h_real_to_string_obj(hs.NewHandle(
-        h_real_to_string_class->AllocObject(self)));
-    if (h_real_to_string_obj.Get() != nullptr) {
-      auto* cl = Runtime::Current()->GetClassLinker();
-      ArtMethod* init_method = h_real_to_string_class->FindDirectMethod(
-          "<init>", "()V", cl->GetImagePointerSize());
-      if (init_method == nullptr) {
-        h_real_to_string_class->DumpClass(LOG_STREAM(FATAL), mirror::Class::kDumpClassFullDetail);
-      } else {
-        JValue invoke_result;
-        EnterInterpreterFromInvoke(self, init_method, h_real_to_string_obj.Get(), nullptr,
-                                   nullptr);
-        if (!self->IsExceptionPending()) {
-          result->SetL(h_real_to_string_obj.Get());
-          ok = true;
-        }
-      }
+static std::string GetImmediateCaller(ShadowFrame* shadow_frame)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (shadow_frame->GetLink() == nullptr) {
+    return "<no caller>";
+  }
+  return ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod());
+}
+
+static bool CheckCallers(ShadowFrame* shadow_frame,
+                         std::initializer_list<std::string> allowed_call_stack)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  for (const std::string& allowed_caller : allowed_call_stack) {
+    if (shadow_frame->GetLink() == nullptr) {
+      return false;
     }
+
+    std::string found_caller = ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod());
+    if (allowed_caller != found_caller) {
+      return false;
+    }
+
+    shadow_frame = shadow_frame->GetLink();
+  }
+  return true;
+}
+
+static ObjPtr<mirror::Object> CreateInstanceOf(Thread* self, const char* class_descriptor)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Find the requested class.
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  ObjPtr<mirror::Class> klass =
+      class_linker->FindClass(self, class_descriptor, ScopedNullHandle<mirror::ClassLoader>());
+  if (klass == nullptr) {
+    AbortTransactionOrFail(self, "Could not load class %s", class_descriptor);
+    return nullptr;
   }
 
-  if (!ok) {
-    AbortTransactionOrFail(self, "Could not create RealToString object");
+  StackHandleScope<2> hs(self);
+  Handle<mirror::Class> h_class(hs.NewHandle(klass));
+  Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
+  if (h_obj.Get() != nullptr) {
+    ArtMethod* init_method = h_class->FindDirectMethod(
+        "<init>", "()V", class_linker->GetImagePointerSize());
+    if (init_method == nullptr) {
+      AbortTransactionOrFail(self, "Could not find <init> for %s", class_descriptor);
+      return nullptr;
+    } else {
+      JValue invoke_result;
+      EnterInterpreterFromInvoke(self, init_method, h_obj.Get(), nullptr, nullptr);
+      if (!self->IsExceptionPending()) {
+        return h_obj.Get();
+      }
+      AbortTransactionOrFail(self, "Could not run <init> for %s", class_descriptor);
+    }
+  }
+  AbortTransactionOrFail(self, "Could not allocate instance of %s", class_descriptor);
+  return nullptr;
+}
+
+void UnstartedRuntime::UnstartedThreadLocalGet(
+    Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
+  if (CheckCallers(shadow_frame, { "sun.misc.FloatingDecimal$BinaryToASCIIBuffer "
+                                       "sun.misc.FloatingDecimal.getBinaryToASCIIBuffer()" })) {
+    result->SetL(CreateInstanceOf(self, "Lsun/misc/FloatingDecimal$BinaryToASCIIBuffer;"));
+  } else {
+    AbortTransactionOrFail(self,
+                           "ThreadLocal.get() does not support %s",
+                           GetImmediateCaller(shadow_frame).c_str());
   }
 }
 
@@ -1252,12 +1284,12 @@
 //       initialization of other classes, so will *use* the value.
 void UnstartedRuntime::UnstartedRuntimeAvailableProcessors(
     Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
-  std::string caller(ArtMethod::PrettyMethod(shadow_frame->GetLink()->GetMethod()));
-  if (caller == "void java.util.concurrent.SynchronousQueue.<clinit>()") {
+  if (CheckCallers(shadow_frame, { "void java.util.concurrent.SynchronousQueue.<clinit>()" })) {
     // SynchronousQueue really only separates between single- and multiprocessor case. Return
     // 8 as a conservative upper approximation.
     result->SetI(8);
-  } else if (caller == "void java.util.concurrent.ConcurrentHashMap.<clinit>()") {
+  } else if (CheckCallers(shadow_frame,
+                          { "void java.util.concurrent.ConcurrentHashMap.<clinit>()" })) {
     // ConcurrentHashMap uses it for striding. 8 still seems an OK general value, as it's likely
     // a good upper bound.
     // TODO: Consider resetting in the zygote?
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index ae55f4c..31be587 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -944,5 +944,100 @@
   ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
 }
 
+TEST_F(UnstartedRuntimeTest, ThreadLocalGet) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  JValue result;
+  ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  StackHandleScope<1> hs(self);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+  // Positive test. See that We get something for float conversion.
+  {
+    Handle<mirror::Class> floating_decimal = hs.NewHandle(
+        class_linker->FindClass(self,
+                                "Lsun/misc/FloatingDecimal;",
+                                ScopedNullHandle<mirror::ClassLoader>()));
+    ASSERT_TRUE(floating_decimal.Get() != nullptr);
+    ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true));
+
+    ArtMethod* caller_method = floating_decimal->FindDeclaredDirectMethod(
+        "getBinaryToASCIIBuffer",
+        "()Lsun/misc/FloatingDecimal$BinaryToASCIIBuffer;",
+        class_linker->GetImagePointerSize());
+    // floating_decimal->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
+    ASSERT_TRUE(caller_method != nullptr);
+    ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0);
+    shadow_frame->SetLink(caller_frame);
+
+    UnstartedThreadLocalGet(self, shadow_frame, &result, 0);
+    EXPECT_TRUE(result.GetL() != nullptr);
+    EXPECT_FALSE(self->IsExceptionPending());
+
+    ShadowFrame::DeleteDeoptimizedFrame(caller_frame);
+  }
+
+  // Negative test.
+  PrepareForAborts();
+
+  {
+    // Just use a method in Class.
+    ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass();
+    ArtMethod* caller_method =
+        &*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin();
+    ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0);
+    shadow_frame->SetLink(caller_frame);
+
+    Transaction transaction;
+    Runtime::Current()->EnterTransactionMode(&transaction);
+    UnstartedThreadLocalGet(self, shadow_frame, &result, 0);
+    Runtime::Current()->ExitTransactionMode();
+    ASSERT_TRUE(self->IsExceptionPending());
+    ASSERT_TRUE(transaction.IsAborted());
+    self->ClearException();
+
+    ShadowFrame::DeleteDeoptimizedFrame(caller_frame);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
+TEST_F(UnstartedRuntimeTest, FloatConversion) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  StackHandleScope<1> hs(self);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Handle<mirror::Class> double_class = hs.NewHandle(
+          class_linker->FindClass(self,
+                                  "Ljava/lang/Double;",
+                                  ScopedNullHandle<mirror::ClassLoader>()));
+  ASSERT_TRUE(double_class.Get() != nullptr);
+  ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true));
+
+  ArtMethod* method = double_class->FindDeclaredDirectMethod("toString",
+                                                             "(D)Ljava/lang/String;",
+                                                             class_linker->GetImagePointerSize());
+  ASSERT_TRUE(method != nullptr);
+
+  // create instruction data for invoke-direct {v0, v1} of method with fake index
+  uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
+  const Instruction* inst = Instruction::At(inst_data);
+
+  JValue result;
+  ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0);
+  shadow_frame->SetVRegDouble(0, 1.23);
+  interpreter::DoCall<false, false>(method, self, *shadow_frame, inst, inst_data[0], &result);
+  ObjPtr<mirror::String> string_result = reinterpret_cast<mirror::String*>(result.GetL());
+  ASSERT_TRUE(string_result != nullptr);
+
+  std::string mod_utf = string_result->ToModifiedUtf8();
+  EXPECT_EQ("1.23", mod_utf);
+
+  ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
 }  // namespace interpreter
 }  // namespace art