ART: Refactor UnstartedRuntime for testing

Expose the UnstartedRuntime implementation functions as private static
methods of a class. Add a gtest that can invoke these functions. Add
sample tests for String and Memory.

Bug: 21173514
Change-Id: Ib5bde6347fafaf7607c642542ea7d5938ff4b1df
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
new file mode 100644
index 0000000..34ab277
--- /dev/null
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unstarted_runtime.h"
+
+#include "class_linker.h"
+#include "common_runtime_test.h"
+#include "handle.h"
+#include "handle_scope-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/string-inl.h"
+#include "runtime.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+namespace interpreter {
+
+class UnstartedRuntimeTest : public CommonRuntimeTest {
+ protected:
+  // Re-expose all UnstartedRuntime implementations so we don't need to declare a million
+  // test friends.
+
+  // Methods that intercept available libcore implementations.
+#define UNSTARTED_DIRECT(Name, SigIgnored)                 \
+  static void Unstarted ## Name(Thread* self,              \
+                                ShadowFrame* shadow_frame, \
+                                JValue* result,            \
+                                size_t arg_offset)         \
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {        \
+    interpreter::UnstartedRuntime::Unstarted ## Name(self, shadow_frame, result, arg_offset); \
+  }
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_DIRECT_LIST(UNSTARTED_DIRECT)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_DIRECT
+
+  // Methods that are native.
+#define UNSTARTED_JNI(Name, SigIgnored)                                   \
+  static void UnstartedJNI ## Name(Thread* self,              \
+                                   mirror::ArtMethod* method, \
+                                   mirror::Object* receiver,  \
+                                   uint32_t* args,            \
+                                   JValue* result)            \
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {           \
+    interpreter::UnstartedRuntime::UnstartedJNI ## Name(self, method, receiver, args, result); \
+  }
+#include "unstarted_runtime_list.h"
+  UNSTARTED_RUNTIME_JNI_LIST(UNSTARTED_JNI)
+#undef UNSTARTED_RUNTIME_DIRECT_LIST
+#undef UNSTARTED_RUNTIME_JNI_LIST
+#undef UNSTARTED_JNI
+};
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekByte) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  for (int32_t i = 0; i < kBaseLen; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekByte(self, tmp, &result, 0);
+
+    EXPECT_EQ(result.GetB(), static_cast<int8_t>(base_array[i]));
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekShort) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  int32_t adjusted_length = kBaseLen - sizeof(int16_t);
+  for (int32_t i = 0; i < adjusted_length; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekShort(self, tmp, &result, 0);
+
+    typedef int16_t unaligned_short __attribute__ ((aligned (1)));
+    const unaligned_short* short_ptr = reinterpret_cast<const unaligned_short*>(base_ptr + i);
+    EXPECT_EQ(result.GetS(), *short_ptr);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekInt) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  int32_t adjusted_length = kBaseLen - sizeof(int32_t);
+  for (int32_t i = 0; i < adjusted_length; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekInt(self, tmp, &result, 0);
+
+    typedef int32_t unaligned_int __attribute__ ((aligned (1)));
+    const unaligned_int* int_ptr = reinterpret_cast<const unaligned_int*>(base_ptr + i);
+    EXPECT_EQ(result.GetI(), *int_ptr);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, MemoryPeekLong) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  constexpr const uint8_t base_array[] = "abcdefghijklmnop";
+  constexpr int32_t kBaseLen = sizeof(base_array) / sizeof(uint8_t);
+  const uint8_t* base_ptr = base_array;
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  int32_t adjusted_length = kBaseLen - sizeof(int64_t);
+  for (int32_t i = 0; i < adjusted_length; ++i) {
+    tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
+
+    UnstartedMemoryPeekLong(self, tmp, &result, 0);
+
+    typedef int64_t unaligned_long __attribute__ ((aligned (1)));
+    const unaligned_long* long_ptr = reinterpret_cast<const unaligned_long*>(base_ptr + i);
+    EXPECT_EQ(result.GetJ(), *long_ptr);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, StringGetCharsNoCheck) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  StackHandleScope<2> hs(self);
+  // TODO: Actual UTF.
+  constexpr const char base_string[] = "abcdefghijklmnop";
+  Handle<mirror::String> h_test_string(hs.NewHandle(
+      mirror::String::AllocFromModifiedUtf8(self, base_string)));
+  constexpr int32_t kBaseLen = sizeof(base_string) / sizeof(char) - 1;
+  Handle<mirror::CharArray> h_char_array(hs.NewHandle(
+      mirror::CharArray::Alloc(self, kBaseLen)));
+  // A buffer so we can make sure we only modify the elements targetted.
+  uint16_t buf[kBaseLen];
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  for (int32_t start_index = 0; start_index < kBaseLen; ++start_index) {
+    for (int32_t count = 0; count <= kBaseLen; ++count) {
+      for (int32_t trg_offset = 0; trg_offset < kBaseLen; ++trg_offset) {
+        // Only do it when in bounds.
+        if (start_index + count <= kBaseLen && trg_offset + count <= kBaseLen) {
+          tmp->SetVRegReference(0, h_test_string.Get());
+          tmp->SetVReg(1, start_index);
+          tmp->SetVReg(2, count);
+          tmp->SetVRegReference(3, h_char_array.Get());
+          tmp->SetVReg(3, trg_offset);
+
+          // Copy the char_array into buf.
+          memcpy(buf, h_char_array->GetData(), kBaseLen * sizeof(uint16_t));
+
+          UnstartedStringCharAt(self, tmp, &result, 0);
+
+          uint16_t* data = h_char_array->GetData();
+
+          bool success = true;
+
+          // First segment should be unchanged.
+          for (int32_t i = 0; i < trg_offset; ++i) {
+            success = success && (data[i] == buf[i]);
+          }
+          // Second segment should be a copy.
+          for (int32_t i = trg_offset; i < trg_offset + count; ++i) {
+            success = success && (data[i] == buf[i - trg_offset + start_index]);
+          }
+          // Third segment should be unchanged.
+          for (int32_t i = trg_offset + count; i < kBaseLen; ++i) {
+            success = success && (data[i] == buf[i]);
+          }
+
+          EXPECT_TRUE(success);
+        }
+      }
+    }
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, StringCharAt) {
+  Thread* self = Thread::Current();
+
+  ScopedObjectAccess soa(self);
+  // TODO: Actual UTF.
+  constexpr const char* base_string = "abcdefghijklmnop";
+  int32_t base_len = static_cast<int32_t>(strlen(base_string));
+  mirror::String* test_string = mirror::String::AllocFromModifiedUtf8(self, base_string);
+
+  JValue result;
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  for (int32_t i = 0; i < base_len; ++i) {
+    tmp->SetVRegReference(0, test_string);
+    tmp->SetVReg(1, i);
+
+    UnstartedStringCharAt(self, tmp, &result, 0);
+
+    EXPECT_EQ(result.GetI(), base_string[i]);
+  }
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+}  // namespace interpreter
+}  // namespace art