Add some missing JNI string functions, GetObjectArrayElement, and all the primitive array region functions.

This also pulls the UTF-8/UTF-16 functions out of class String.

Change-Id: I75936b84fd619c9cf91f6e6a6037488220b05781
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 306e8e3..f13fdd0 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -69,6 +69,7 @@
 	src/stringpiece.cc \
 	src/stringprintf.cc \
 	src/thread.cc \
+	src/utf.cc \
 	src/utils.cc \
 	src/zip_archive.cc
 
diff --git a/src/intern_table.cc b/src/intern_table.cc
index e070573..703bbe6 100644
--- a/src/intern_table.cc
+++ b/src/intern_table.cc
@@ -3,6 +3,7 @@
 #include "intern_table.h"
 
 #include "scoped_ptr.h"
+#include "utf.h"
 
 namespace art {
 
@@ -28,8 +29,8 @@
 
 String* InternTable::Intern(int32_t utf16_length, const char* utf8_data_in) {
   scoped_array<uint16_t> utf16_data_out(new uint16_t[utf16_length]);
-  String::ConvertModifiedUtf8ToUtf16(utf16_data_out.get(), utf8_data_in);
-  int32_t hash_code = String::ComputeUtf16Hash(utf16_data_out.get(), utf16_length);
+  ConvertModifiedUtf8ToUtf16(utf16_data_out.get(), utf8_data_in);
+  int32_t hash_code = ComputeUtf16Hash(utf16_data_out.get(), utf16_length);
   {
     MutexLock mu(intern_table_lock_);
     typedef Table::const_iterator It; // TODO: C++0x auto
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 0d46f29..79986c2 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -532,6 +532,35 @@
   return AddLocalReference<JniT>(ts, result);
 }
 
+void ThrowAIOOBE(ScopedJniThreadState& ts, Array* array, jsize start, jsize length, const char* identifier) {
+  std::string type(PrettyType(array));
+  ts.Self()->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+      "%s offset=%d length=%d %s.length=%d",
+      type.c_str(), start, length, identifier, array->GetLength());
+}
+
+template <typename JavaArrayT, typename JavaT, typename ArrayT>
+static void GetPrimitiveArrayRegion(ScopedJniThreadState& ts, JavaArrayT java_array, jsize start, jsize length, JavaT* buf) {
+  ArrayT* array = Decode<ArrayT*>(ts, java_array);
+  if (start < 0 || length < 0 || start + length > array->GetLength()) {
+    ThrowAIOOBE(ts, array, start, length, "src");
+  } else {
+    JavaT* data = array->GetData();
+    memcpy(buf, data + start, length * sizeof(JavaT));
+  }
+}
+
+template <typename JavaArrayT, typename JavaT, typename ArrayT>
+static void SetPrimitiveArrayRegion(ScopedJniThreadState& ts, JavaArrayT java_array, jsize start, jsize length, const JavaT* buf) {
+  ArrayT* array = Decode<ArrayT*>(ts, java_array);
+  if (start < 0 || length < 0 || start + length > array->GetLength()) {
+    ThrowAIOOBE(ts, array, start, length, "dst");
+  } else {
+    JavaT* data = array->GetData();
+    memcpy(data + start, buf, length * sizeof(JavaT));
+  }
+}
+
 }  // namespace
 
 class JNI {
@@ -1667,27 +1696,13 @@
     InvokeWithJValues(ts, NULL, methodID, args);
   }
 
-  static jstring NewString(JNIEnv* env, const jchar* unicode, jsize len) {
+  static jstring NewString(JNIEnv* env, const jchar* chars, jsize char_count) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
-  }
-
-  static jsize GetStringLength(JNIEnv* env, jstring str) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static const jchar* GetStringChars(JNIEnv* env, jstring str, jboolean* isCopy) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
-  }
-
-  static void ReleaseStringChars(JNIEnv* env, jstring str, const jchar* chars) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    if (chars == NULL && char_count == 0) {
+      return NULL;
+    }
+    String* result = String::AllocFromUtf16(char_count, chars);
+    return AddLocalReference<jstring>(ts, result);
   }
 
   static jstring NewStringUTF(JNIEnv* env, const char* utf) {
@@ -1699,10 +1714,35 @@
     return AddLocalReference<jstring>(ts, result);
   }
 
-  static jsize GetStringUTFLength(JNIEnv* env, jstring str) {
+  static jsize GetStringLength(JNIEnv* env, jstring java_string) {
+    ScopedJniThreadState ts(env);
+    return Decode<String*>(ts, java_string)->GetLength();
+  }
+
+  static jsize GetStringUTFLength(JNIEnv* env, jstring java_string) {
+    ScopedJniThreadState ts(env);
+    return Decode<String*>(ts, java_string)->GetUtfLength();
+  }
+
+  static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, jchar* buf) {
     ScopedJniThreadState ts(env);
     UNIMPLEMENTED(FATAL);
-    return 0;
+  }
+
+  static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, jsize len, char* buf) {
+    ScopedJniThreadState ts(env);
+    UNIMPLEMENTED(FATAL);
+  }
+
+  static const jchar* GetStringChars(JNIEnv* env, jstring str, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+    UNIMPLEMENTED(FATAL);
+    return NULL;
+  }
+
+  static void ReleaseStringChars(JNIEnv* env, jstring str, const jchar* chars) {
+    ScopedJniThreadState ts(env);
+    UNIMPLEMENTED(FATAL);
   }
 
   static const char* GetStringUTFChars(JNIEnv* env, jstring str, jboolean* isCopy) {
@@ -1724,10 +1764,10 @@
     return array->GetLength();
   }
 
-  static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
+  static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray java_array, jsize index) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
+    ObjectArray<Object>* array = Decode<ObjectArray<Object>*>(ts, java_array);
+    return AddLocalReference<jobject>(ts, array->Get(index));
   }
 
   static void SetObjectArrayElement(JNIEnv* env,
@@ -1901,100 +1941,84 @@
     UNIMPLEMENTED(FATAL);
   }
 
-  static void GetBooleanArrayRegion(JNIEnv* env,
-      jbooleanArray array, jsize start, jsize l, jboolean* buf) {
+  static void GetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, jboolean* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jbooleanArray, jboolean, BooleanArray>(ts, array, start, length, buf);
   }
 
-  static void GetByteArrayRegion(JNIEnv* env,
-      jbyteArray array, jsize start, jsize len, jbyte* buf) {
+  static void GetByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize length, jbyte* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jbyteArray, jbyte, ByteArray>(ts, array, start, length, buf);
   }
 
-  static void GetCharArrayRegion(JNIEnv* env,
-      jcharArray array, jsize start, jsize len, jchar* buf) {
+  static void GetCharArrayRegion(JNIEnv* env, jcharArray array, jsize start, jsize length, jchar* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jcharArray, jchar, CharArray>(ts, array, start, length, buf);
   }
 
-  static void GetShortArrayRegion(JNIEnv* env,
-      jshortArray array, jsize start, jsize len, jshort* buf) {
+  static void GetDoubleArrayRegion(JNIEnv* env, jdoubleArray array, jsize start, jsize length, jdouble* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jdoubleArray, jdouble, DoubleArray>(ts, array, start, length, buf);
   }
 
-  static void GetIntArrayRegion(JNIEnv* env,
-      jintArray array, jsize start, jsize len, jint* buf) {
+  static void GetFloatArrayRegion(JNIEnv* env, jfloatArray array, jsize start, jsize length, jfloat* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jfloatArray, jfloat, FloatArray>(ts, array, start, length, buf);
   }
 
-  static void GetLongArrayRegion(JNIEnv* env,
-      jlongArray array, jsize start, jsize len, jlong* buf) {
+  static void GetIntArrayRegion(JNIEnv* env, jintArray array, jsize start, jsize length, jint* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jintArray, jint, IntArray>(ts, array, start, length, buf);
   }
 
-  static void GetFloatArrayRegion(JNIEnv* env,
-      jfloatArray array, jsize start, jsize len, jfloat* buf) {
+  static void GetLongArrayRegion(JNIEnv* env, jlongArray array, jsize start, jsize length, jlong* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jlongArray, jlong, LongArray>(ts, array, start, length, buf);
   }
 
-  static void GetDoubleArrayRegion(JNIEnv* env,
-      jdoubleArray array, jsize start, jsize len, jdouble* buf) {
+  static void GetShortArrayRegion(JNIEnv* env, jshortArray array, jsize start, jsize length, jshort* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    GetPrimitiveArrayRegion<jshortArray, jshort, ShortArray>(ts, array, start, length, buf);
   }
 
-  static void SetBooleanArrayRegion(JNIEnv* env,
-      jbooleanArray array, jsize start, jsize l, const jboolean* buf) {
+  static void SetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, const jboolean* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jbooleanArray, jboolean, BooleanArray>(ts, array, start, length, buf);
   }
 
-  static void SetByteArrayRegion(JNIEnv* env,
-      jbyteArray array, jsize start, jsize len, const jbyte* buf) {
+  static void SetByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize length, const jbyte* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jbyteArray, jbyte, ByteArray>(ts, array, start, length, buf);
   }
 
-  static void SetCharArrayRegion(JNIEnv* env,
-      jcharArray array, jsize start, jsize len, const jchar* buf) {
+  static void SetCharArrayRegion(JNIEnv* env, jcharArray array, jsize start, jsize length, const jchar* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jcharArray, jchar, CharArray>(ts, array, start, length, buf);
   }
 
-  static void SetShortArrayRegion(JNIEnv* env,
-      jshortArray array, jsize start, jsize len, const jshort* buf) {
+  static void SetDoubleArrayRegion(JNIEnv* env, jdoubleArray array, jsize start, jsize length, const jdouble* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jdoubleArray, jdouble, DoubleArray>(ts, array, start, length, buf);
   }
 
-  static void SetIntArrayRegion(JNIEnv* env,
-      jintArray array, jsize start, jsize len, const jint* buf) {
+  static void SetFloatArrayRegion(JNIEnv* env, jfloatArray array, jsize start, jsize length, const jfloat* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jfloatArray, jfloat, FloatArray>(ts, array, start, length, buf);
   }
 
-  static void SetLongArrayRegion(JNIEnv* env,
-      jlongArray array, jsize start, jsize len, const jlong* buf) {
+  static void SetIntArrayRegion(JNIEnv* env, jintArray array, jsize start, jsize length, const jint* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jintArray, jint, IntArray>(ts, array, start, length, buf);
   }
 
-  static void SetFloatArrayRegion(JNIEnv* env,
-      jfloatArray array, jsize start, jsize len, const jfloat* buf) {
+  static void SetLongArrayRegion(JNIEnv* env, jlongArray array, jsize start, jsize length, const jlong* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jlongArray, jlong, LongArray>(ts, array, start, length, buf);
   }
 
-  static void SetDoubleArrayRegion(JNIEnv* env,
-      jdoubleArray array, jsize start, jsize len, const jdouble* buf) {
+  static void SetShortArrayRegion(JNIEnv* env, jshortArray array, jsize start, jsize length, const jshort* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    SetPrimitiveArrayRegion<jshortArray, jshort, ShortArray>(ts, array, start, length, buf);
   }
 
   static jint RegisterNatives(JNIEnv* env,
@@ -2063,18 +2087,6 @@
     return (*vm != NULL) ? JNI_OK : JNI_ERR;
   }
 
-  static void GetStringRegion(JNIEnv* env,
-      jstring str, jsize start, jsize len, jchar* buf) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void GetStringUTFRegion(JNIEnv* env,
-      jstring str, jsize start, jsize len, char* buf) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
   static void* GetPrimitiveArrayCritical(JNIEnv* env,
       jarray array, jboolean* isCopy) {
     ScopedJniThreadState ts(env);
@@ -2549,7 +2561,7 @@
  * human-readable description of the error or NULL if no detail is
  * available; ownership of the string is transferred to the caller.
  */
-bool JavaVMExt::LoadNativeLibrary(const std::string& path, Object* class_loader, char** detail) {
+bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, char** detail) {
   *detail = NULL;
 
   // See if we've already loaded this library.  If we have, and the class loader
@@ -2644,11 +2656,10 @@
       // loader, which will always be "null" since the stuff at the
       // top of the stack is around Runtime.loadLibrary().  (See
       // the comments in the JNI FindClass function.)
-      UNIMPLEMENTED(WARNING) << "need to override current class loader";
       typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
       JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
-      //Object* prevOverride = self->classLoaderOverride;
-      //self->classLoaderOverride = classLoader;
+      ClassLoader* old_class_loader = self->GetClassLoaderOverride();
+      self->SetClassLoaderOverride(class_loader);
 
       old_state = self->GetState();
       self->SetState(Thread::kNative);
@@ -2658,8 +2669,7 @@
       int version = (*jni_on_load)(reinterpret_cast<JavaVM*>(this), NULL);
       self->SetState(old_state);
 
-      UNIMPLEMENTED(WARNING) << "need to restore current class loader";
-      //self->classLoaderOverride = prevOverride;
+      self->SetClassLoaderOverride(old_class_loader);;
 
       if (version != JNI_VERSION_1_2 &&
       version != JNI_VERSION_1_4 &&
diff --git a/src/jni_internal.h b/src/jni_internal.h
index a6fce05..d9a1832 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -14,6 +14,7 @@
 
 namespace art {
 
+class ClassLoader;
 class Mutex;
 class Runtime;
 class SharedLibrary;
@@ -40,7 +41,7 @@
    * human-readable description of the error or NULL if no detail is
    * available; ownership of the string is transferred to the caller.
    */
-  bool LoadNativeLibrary(const std::string& path, Object* class_loader, char** detail);
+  bool LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, char** detail);
 
   // Must be first to correspond with JNIEnv.
   const struct JNIInvokeInterface* fns;
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index ee4f2b1..53b8334 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -15,8 +15,11 @@
   virtual void SetUp() {
     CommonTest::SetUp();
     env_ = Thread::Current()->GetJniEnv();
+    aioobe_ = env_->FindClass("java/lang/ArrayIndexOutOfBoundsException");
+    CHECK(aioobe_ != NULL);
   }
   JNIEnv* env_;
+  jclass aioobe_;
 };
 
 TEST_F(JniInternalTest, AllocObject) {
@@ -260,35 +263,69 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 }
 
-#define EXPECT_PRIMITIVE_ARRAY(fn, size, expected_class_descriptor) \
-  do { \
-    jarray a = env_->fn(size); \
-    EXPECT_TRUE(a != NULL); \
-    EXPECT_TRUE(env_->IsInstanceOf(a, \
-        env_->FindClass(expected_class_descriptor))); \
-    EXPECT_EQ(size, env_->GetArrayLength(a)); \
-  } while (false)
+#define EXPECT_PRIMITIVE_ARRAY(new_fn, get_region_fn, set_region_fn, scalar_type, expected_class_descriptor) \
+  jsize size = 4; \
+  /* Allocate an array and check it has the right type and length. */ \
+  scalar_type ## Array a = env_->new_fn(size); \
+  EXPECT_TRUE(a != NULL); \
+  EXPECT_TRUE(env_->IsInstanceOf(a, env_->FindClass(expected_class_descriptor))); \
+  EXPECT_EQ(size, env_->GetArrayLength(a)); \
+  /* AIOOBE for negative start offset. */ \
+  env_->get_region_fn(a, -1, 1, NULL); \
+  EXPECT_EXCEPTION(aioobe_); \
+  env_->set_region_fn(a, -1, 1, NULL); \
+  EXPECT_EXCEPTION(aioobe_); \
+  /* AIOOBE for negative length. */ \
+  env_->get_region_fn(a, 0, -1, NULL); \
+  EXPECT_EXCEPTION(aioobe_); \
+  env_->set_region_fn(a, 0, -1, NULL); \
+  EXPECT_EXCEPTION(aioobe_); \
+  /* AIOOBE for buffer overrun. */ \
+  env_->get_region_fn(a, size - 1, size, NULL); \
+  EXPECT_EXCEPTION(aioobe_); \
+  env_->set_region_fn(a, size - 1, size, NULL); \
+  EXPECT_EXCEPTION(aioobe_); \
+  /* Prepare a couple of buffers. */ \
+  scalar_type src_buf[size]; \
+  scalar_type dst_buf[size]; \
+  for (jsize i = 0; i < size; ++i) { src_buf[i] = scalar_type(i); } \
+  for (jsize i = 0; i < size; ++i) { dst_buf[i] = scalar_type(-1); } \
+  /* Copy all of src_buf onto the heap. */ \
+  env_->set_region_fn(a, 0, size, src_buf); \
+  /* Copy back only part. */ \
+  env_->get_region_fn(a, 1, size - 2, &dst_buf[1]); \
+  EXPECT_FALSE(memcmp(src_buf, dst_buf, sizeof(src_buf)) == 0) << "short copy equal"; \
+  /* Copy the missing pieces. */ \
+  env_->get_region_fn(a, 0, 1, dst_buf); \
+  env_->get_region_fn(a, size - 1, 1, &dst_buf[size - 1]); \
+  EXPECT_TRUE(memcmp(src_buf, dst_buf, sizeof(src_buf)) == 0) << "fixed copy not equal"; \
+  /* Copy back the whole array. */ \
+  env_->get_region_fn(a, 0, size, dst_buf); \
+  EXPECT_TRUE(memcmp(src_buf, dst_buf, sizeof(src_buf)) == 0) << "full copy not equal"
 
-TEST_F(JniInternalTest, NewPrimitiveArray) {
-  // TODO: death tests for negative array sizes.
-
-  EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, 0, "[Z");
-  EXPECT_PRIMITIVE_ARRAY(NewByteArray, 0, "[B");
-  EXPECT_PRIMITIVE_ARRAY(NewCharArray, 0, "[C");
-  EXPECT_PRIMITIVE_ARRAY(NewDoubleArray, 0, "[D");
-  EXPECT_PRIMITIVE_ARRAY(NewFloatArray, 0, "[F");
-  EXPECT_PRIMITIVE_ARRAY(NewIntArray, 0, "[I");
-  EXPECT_PRIMITIVE_ARRAY(NewLongArray, 0, "[J");
-  EXPECT_PRIMITIVE_ARRAY(NewShortArray, 0, "[S");
-
-  EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, 1, "[Z");
-  EXPECT_PRIMITIVE_ARRAY(NewByteArray, 1, "[B");
-  EXPECT_PRIMITIVE_ARRAY(NewCharArray, 1, "[C");
-  EXPECT_PRIMITIVE_ARRAY(NewDoubleArray, 1, "[D");
-  EXPECT_PRIMITIVE_ARRAY(NewFloatArray, 1, "[F");
-  EXPECT_PRIMITIVE_ARRAY(NewIntArray, 1, "[I");
-  EXPECT_PRIMITIVE_ARRAY(NewLongArray, 1, "[J");
-  EXPECT_PRIMITIVE_ARRAY(NewShortArray, 1, "[S");
+TEST_F(JniInternalTest, BooleanArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, GetBooleanArrayRegion, SetBooleanArrayRegion, jboolean, "[Z");
+}
+TEST_F(JniInternalTest, ByteArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewByteArray, GetByteArrayRegion, SetByteArrayRegion, jbyte, "[B");
+}
+TEST_F(JniInternalTest, CharArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewCharArray, GetCharArrayRegion, SetCharArrayRegion, jchar, "[C");
+}
+TEST_F(JniInternalTest, DoubleArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewDoubleArray, GetDoubleArrayRegion, SetDoubleArrayRegion, jdouble, "[D");
+}
+TEST_F(JniInternalTest, FloatArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewFloatArray, GetFloatArrayRegion, SetFloatArrayRegion, jfloat, "[F");
+}
+TEST_F(JniInternalTest, IntArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewIntArray, GetIntArrayRegion, SetIntArrayRegion, jint, "[I");
+}
+TEST_F(JniInternalTest, LongArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewLongArray, GetLongArrayRegion, SetLongArrayRegion, jlong, "[J");
+}
+TEST_F(JniInternalTest, ShortArrays) {
+  EXPECT_PRIMITIVE_ARRAY(NewShortArray, GetShortArrayRegion, SetShortArrayRegion, jshort, "[S");
 }
 
 TEST_F(JniInternalTest, NewObjectArray) {
@@ -353,28 +390,54 @@
 
 TEST_F(JniInternalTest, NewStringUTF) {
   EXPECT_TRUE(env_->NewStringUTF(NULL) == NULL);
-  EXPECT_TRUE(env_->NewStringUTF("") != NULL);
-  EXPECT_TRUE(env_->NewStringUTF("hello") != NULL);
+  jstring s;
+
+  s = env_->NewStringUTF("");
+  EXPECT_TRUE(s != NULL);
+  EXPECT_EQ(0, env_->GetStringLength(s));
+  EXPECT_EQ(0, env_->GetStringUTFLength(s));
+  s = env_->NewStringUTF("hello");
+  EXPECT_TRUE(s != NULL);
+  EXPECT_EQ(5, env_->GetStringLength(s));
+  EXPECT_EQ(5, env_->GetStringUTFLength(s));
+
   // TODO: check some non-ASCII strings.
 }
 
-TEST_F(JniInternalTest, SetObjectArrayElement) {
-  jclass aioobe = env_->FindClass("java/lang/ArrayIndexOutOfBoundsException");
+TEST_F(JniInternalTest, NewString) {
+  EXPECT_TRUE(env_->NewString(NULL, 0) == NULL);
+
+  jchar chars[] = { 'h', 'i' };
+  jstring s;
+  s = env_->NewString(chars, 0);
+  EXPECT_TRUE(s != NULL);
+  EXPECT_EQ(0, env_->GetStringLength(s));
+  EXPECT_EQ(0, env_->GetStringUTFLength(s));
+  s = env_->NewString(chars, 2);
+  EXPECT_TRUE(s != NULL);
+  EXPECT_EQ(2, env_->GetStringLength(s));
+  EXPECT_EQ(2, env_->GetStringUTFLength(s));
+
+  // TODO: check some non-ASCII strings.
+}
+
+TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
   jclass c = env_->FindClass("[Ljava/lang/Object;");
   ASSERT_TRUE(c != NULL);
 
   jobjectArray array = env_->NewObjectArray(1, c, NULL);
   EXPECT_TRUE(array != NULL);
+  EXPECT_TRUE(env_->GetObjectArrayElement(array, 0) == NULL);
   env_->SetObjectArrayElement(array, 0, c);
-  // TODO: check reading value back
+  EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(array, 0), c));
 
   // ArrayIndexOutOfBounds for negative index.
   env_->SetObjectArrayElement(array, -1, c);
-  EXPECT_EXCEPTION(aioobe);
+  EXPECT_EXCEPTION(aioobe_);
 
   // ArrayIndexOutOfBounds for too-large index.
   env_->SetObjectArrayElement(array, 1, c);
-  EXPECT_EXCEPTION(aioobe);
+  EXPECT_EXCEPTION(aioobe_);
 
   // TODO: check ArrayStoreException thrown for bad types.
 }
diff --git a/src/object.h b/src/object.h
index f86142a..7004e59 100644
--- a/src/object.h
+++ b/src/object.h
@@ -3,15 +3,16 @@
 #ifndef ART_SRC_OBJECT_H_
 #define ART_SRC_OBJECT_H_
 
-#include "constants.h"
 #include "casts.h"
+#include "constants.h"
 #include "globals.h"
 #include "heap.h"
 #include "logging.h"
 #include "macros.h"
+#include "monitor.h"
 #include "offsets.h"
 #include "stringpiece.h"
-#include "monitor.h"
+#include "utf.h"
 
 namespace art {
 
@@ -1486,14 +1487,18 @@
     return hash_code_;
   }
 
-  uint32_t GetOffset() const {
+  int32_t GetOffset() const {
     return offset_;
   }
 
-  uint32_t GetLength() const {
+  int32_t GetLength() const {
     return count_;
   }
 
+  int32_t GetUtfLength() const {
+    return CountUtf8Bytes(array_->GetData(), count_);
+  }
+
   // TODO: do we need this? Equals is the only caller, and could
   // bounds check itself.
   uint16_t CharAt(int32_t index) const {
@@ -1508,19 +1513,23 @@
 
   static String* AllocFromUtf16(int32_t utf16_length,
                                 const uint16_t* utf16_data_in,
-                                int32_t hash_code) {
+                                int32_t hash_code = 0) {
     String* string = Alloc(GetJavaLangString(),
                            utf16_length);
     // TODO: use 16-bit wide memset variant
     for (int i = 0; i < utf16_length; i++ ) {
         string->array_->Set(i, utf16_data_in[i]);
     }
-    string->ComputeHashCode();
+    if (hash_code != 0) {
+      string->hash_code_ = hash_code;
+    } else {
+      string->ComputeHashCode();
+    }
     return string;
   }
 
   static String* AllocFromModifiedUtf8(const char* utf) {
-    size_t char_count = ModifiedUtf8Len(utf);
+    size_t char_count = CountModifiedUtf8Chars(utf);
     return AllocFromModifiedUtf8(char_count, utf);
   }
 
@@ -1536,103 +1545,24 @@
   static void SetClass(Class* java_lang_String);
   static void ResetClass();
 
-  static String* Alloc(Class* java_lang_String,
-                       int32_t utf16_length) {
+  static String* Alloc(Class* java_lang_String, int32_t utf16_length) {
     return Alloc(java_lang_String, CharArray::Alloc(utf16_length));
   }
 
-  static String* Alloc(Class* java_lang_String,
-                       CharArray* array) {
+  static String* Alloc(Class* java_lang_String, CharArray* array) {
     String* string = down_cast<String*>(java_lang_String->NewInstance());
     string->array_ = array;
     string->count_ = array->GetLength();
     return string;
   }
 
-  // Convert Modified UTF-8 to UTF-16
-  // http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8
-  static void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in) {
-    while (*utf8_data_in != '\0') {
-      *utf16_data_out++ = GetUtf16FromUtf8(&utf8_data_in);
-    }
-  }
-
-  // Retrieve the next UTF-16 character from a UTF-8 string.
-  //
-  // Advances "*pUtf8Ptr" to the start of the next character.
-  //
-  // WARNING: If a string is corrupted by dropping a '\0' in the middle
-  // of a 3-byte sequence, you can end up overrunning the buffer with
-  // reads (and possibly with the writes if the length was computed and
-  // cached before the damage). For performance reasons, this function
-  // assumes that the string being parsed is known to be valid (e.g., by
-  // already being verified). Most strings we process here are coming
-  // out of dex files or other internal translations, so the only real
-  // risk comes from the JNI NewStringUTF call.
-  static uint16_t GetUtf16FromUtf8(const char** utf8_data_in) {
-    uint8_t one = *(*utf8_data_in)++;
-    if ((one & 0x80) == 0) {
-      // one-byte encoding
-      return one;
-    }
-    // two- or three-byte encoding
-    uint8_t two = *(*utf8_data_in)++;
-    if ((one & 0x20) == 0) {
-      // two-byte encoding
-      return ((one & 0x1f) << 6) |
-              (two & 0x3f);
-    }
-    // three-byte encoding
-    uint8_t three = *(*utf8_data_in)++;
-    return ((one & 0x0f) << 12) |
-            ((two & 0x3f) << 6) |
-            (three & 0x3f);
-  }
-
-  // Like "strlen", but for strings encoded with "modified" UTF-8.
-  //
-  // The value returned is the number of characters, which may or may not
-  // be the same as the number of bytes.
-  //
-  // (If this needs optimizing, try: mask against 0xa0, shift right 5,
-  // get increment {1-3} from table of 8 values.)
-  static size_t ModifiedUtf8Len(const char* utf8) {
-    size_t len = 0;
-    int ic;
-    while ((ic = *utf8++) != '\0') {
-      len++;
-      if ((ic & 0x80) == 0) {
-        // one-byte encoding
-        continue;
-      }
-      // two- or three-byte encoding
-      utf8++;
-      if ((ic & 0x20) == 0) {
-        // two-byte encoding
-        continue;
-      }
-      // three-byte encoding
-      utf8++;
-    }
-    return len;
-  }
-
-  // The java/lang/String.computeHashCode() algorithm
-  static int32_t ComputeUtf16Hash(const uint16_t* string_data, size_t string_length) {
-    int32_t hash = 0;
-    while (string_length--) {
-      hash = hash * 31 + *string_data++;
-    }
-    return hash;
-  }
-
   void ComputeHashCode() {
     hash_code_ = ComputeUtf16Hash(array_->GetData(), count_);
   }
 
   // TODO: do we need this overload? give it a more intention-revealing name.
   bool Equals(const char* modified_utf8) const {
-    for (uint32_t i = 0; i < GetLength(); ++i) {
+    for (int32_t i = 0; i < GetLength(); ++i) {
       uint16_t ch = GetUtf16FromUtf8(&modified_utf8);
       if (ch == '\0' || ch != CharAt(i)) {
         return false;
@@ -1652,7 +1582,7 @@
     if (this->GetLength() != that->GetLength()) {
       return false;
     }
-    for (uint32_t i = 0; i < that->GetLength(); ++i) {
+    for (int32_t i = 0; i < that->GetLength(); ++i) {
       if (this->CharAt(i) != that->CharAt(i)) {
         return false;
       }
@@ -1661,11 +1591,11 @@
   }
 
   // TODO: do we need this overload? give it a more intention-revealing name.
-  bool Equals(const uint16_t* that_chars, uint32_t that_offset, uint32_t that_length) const {
+  bool Equals(const uint16_t* that_chars, int32_t that_offset, int32_t that_length) const {
     if (this->GetLength() != that_length) {
       return false;
     }
-    for (uint32_t i = 0; i < that_length; ++i) {
+    for (int32_t i = 0; i < that_length; ++i) {
       if (this->CharAt(i) != that_chars[that_offset + i]) {
         return false;
       }
@@ -1676,7 +1606,7 @@
   // Create a modified UTF-8 encoded std::string from a java/lang/String object.
   std::string ToModifiedUtf8() const {
     std::string result;
-    for (uint32_t i = 0; i < GetLength(); i++) {
+    for (int32_t i = 0; i < GetLength(); i++) {
       uint16_t ch = CharAt(i);
       // The most common case is (ch > 0 && ch <= 0x7f).
       if (ch == 0 || ch > 0x7f) {
@@ -1695,7 +1625,6 @@
     return result;
   }
 
-
  private:
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   CharArray* array_;
diff --git a/src/object_test.cc b/src/object_test.cc
index aa3cf86..fc58778 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -15,12 +15,12 @@
 
 class ObjectTest : public CommonTest {
  protected:
-  void AssertString(size_t length,
+  void AssertString(int32_t length,
                     const char* utf8_in,
                     const char* utf16_expected_le,
                     uint32_t hash_expected) {
     uint16_t utf16_expected[length];
-    for (size_t i = 0; i < length; i++) {
+    for (int32_t i = 0; i < length; i++) {
       uint16_t ch = (((utf16_expected_le[i*2 + 0] & 0xff) << 8) |
                      ((utf16_expected_le[i*2 + 1] & 0xff) << 0));
       utf16_expected[i] = ch;
@@ -31,8 +31,8 @@
     ASSERT_TRUE(string->GetCharArray() != NULL);
     ASSERT_TRUE(string->GetCharArray()->GetData() != NULL);
     // strlen is necessary because the 1-character string "\0" is interpreted as ""
-    ASSERT_TRUE(string->Equals(utf8_in) || length != strlen(utf8_in));
-    for (size_t i = 0; i < length; i++) {
+    ASSERT_TRUE(string->Equals(utf8_in) || length != static_cast<int32_t>(strlen(utf8_in)));
+    for (int32_t i = 0; i < length; i++) {
       EXPECT_EQ(utf16_expected[i], string->CharAt(i));
     }
     EXPECT_EQ(hash_expected, string->GetHashCode());
diff --git a/src/utf.cc b/src/utf.cc
new file mode 100644
index 0000000..81c93ca
--- /dev/null
+++ b/src/utf.cc
@@ -0,0 +1,76 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "utf.h"
+
+namespace art {
+
+size_t CountModifiedUtf8Chars(const char* utf8) {
+  size_t len = 0;
+  int ic;
+  while ((ic = *utf8++) != '\0') {
+    len++;
+    if ((ic & 0x80) == 0) {
+      // one-byte encoding
+      continue;
+    }
+    // two- or three-byte encoding
+    utf8++;
+    if ((ic & 0x20) == 0) {
+      // two-byte encoding
+      continue;
+    }
+    // three-byte encoding
+    utf8++;
+  }
+  return len;
+}
+
+void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in) {
+  while (*utf8_data_in != '\0') {
+    *utf16_data_out++ = GetUtf16FromUtf8(&utf8_data_in);
+  }
+}
+
+int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count) {
+  int32_t hash = 0;
+  while (char_count--) {
+    hash = hash * 31 + *chars++;
+  }
+  return hash;
+}
+
+uint16_t GetUtf16FromUtf8(const char** utf8_data_in) {
+  uint8_t one = *(*utf8_data_in)++;
+  if ((one & 0x80) == 0) {
+    // one-byte encoding
+    return one;
+  }
+  // two- or three-byte encoding
+  uint8_t two = *(*utf8_data_in)++;
+  if ((one & 0x20) == 0) {
+    // two-byte encoding
+    return ((one & 0x1f) << 6) | (two & 0x3f);
+  }
+  // three-byte encoding
+  uint8_t three = *(*utf8_data_in)++;
+  return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
+}
+
+size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+  size_t result = 0;
+  while (char_count--) {
+    uint16_t ch = *chars++;
+    if (ch > 0 && ch <= 0x7f) {
+      ++result;
+    } else {
+      if (ch > 0x7ff) {
+        result += 3;
+      } else {
+        result += 2;
+      }
+    }
+  }
+  return result;
+}
+
+}  // namespace art
diff --git a/src/utf.h b/src/utf.h
new file mode 100644
index 0000000..a9d29a6
--- /dev/null
+++ b/src/utf.h
@@ -0,0 +1,51 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_UTF_H_
+#define ART_SRC_UTF_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace art {
+
+/*
+ * Returns the number of UTF-16 characters in the given modified UTF-8 string.
+ */
+size_t CountModifiedUtf8Chars(const char* utf8);
+
+/*
+ * Returns the number of modified UTF-8 bytes needed to represent the given
+ * UTF-16 string.
+ */
+size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count);
+
+/*
+ * Convert Modified UTF-8 to UTF-16.
+ * http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8
+ */
+void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in);
+
+/*
+ * The java.lang.String hashCode() algorithm.
+ */
+int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count);
+
+/*
+ * Retrieve the next UTF-16 character from a UTF-8 string.
+ *
+ * Advances "*utf8_data_in" to the start of the next character.
+ *
+ * WARNING: If a string is corrupted by dropping a '\0' in the middle
+ * of a 3-byte sequence, you can end up overrunning the buffer with
+ * reads (and possibly with the writes if the length was computed and
+ * cached before the damage). For performance reasons, this function
+ * assumes that the string being parsed is known to be valid (e.g., by
+ * already being verified). Most strings we process here are coming
+ * out of dex files or other internal translations, so the only real
+ * risk comes from the JNI NewStringUTF call.
+ */
+uint16_t GetUtf16FromUtf8(const char** utf8_data_in);
+
+}  // namespace art
+
+#endif  // ART_SRC_UTF_H_