Merge "Add switch and comparison tests. Fix ralloc bug" into dalvik-dev
diff --git a/Android.mk b/Android.mk
index bb688e6..27b4a72 100644
--- a/Android.mk
+++ b/Android.mk
@@ -39,7 +39,7 @@
 test-art: test-art-host test-art-target
 
 define run-host-tests-with
-  $(foreach file,$(ART_HOST_TEST_EXECUTABLES),$(1) $(file) &&) true
+  $(foreach file,$(sort $(ART_HOST_TEST_EXECUTABLES)),$(1) $(file) &&) true
 endef
 
 # "mm test-art-host" to build and run all host tests
@@ -64,7 +64,7 @@
 	adb sync
 	adb shell touch /sdcard/test-art-target
 	adb shell rm /sdcard/test-art-target
-	adb shell sh -c "$(foreach file,$(ART_TARGET_TEST_EXECUTABLES), /system/bin/$(notdir $(file)) &&) touch /sdcard/test-art-target"
+	adb shell sh -c "$(foreach file,$(sort $(ART_TARGET_TEST_EXECUTABLES)), /system/bin/$(notdir $(file)) &&) touch /sdcard/test-art-target"
 	adb pull /sdcard/test-art-target /tmp/
 	rm /tmp/test-art-target
 
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 0845c0f..d11f2a7 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -503,6 +503,10 @@
       "%s offset=%d length=%d %s.length=%d",
       type.c_str(), start, length, identifier, array->GetLength());
 }
+void ThrowSIOOBE(ScopedJniThreadState& ts, jsize start, jsize length, jsize array_length) {
+  ts.Self()->ThrowNewException("Ljava/lang/StringIndexOutOfBoundsException;",
+      "offset=%d length=%d string.length()=%d", start, length, array_length);
+}
 
 template <typename JavaArrayT, typename JavaT, typename ArrayT>
 static void GetPrimitiveArrayRegion(ScopedJniThreadState& ts, JavaArrayT java_array, jsize start, jsize length, JavaT* buf) {
@@ -526,6 +530,17 @@
   }
 }
 
+static jclass InitDirectByteBufferClass(JNIEnv* env) {
+  ScopedLocalRef<jclass> buffer_class(env, env->FindClass("java/nio/ReadWriteDirectByteBuffer"));
+  CHECK(buffer_class.get() != NULL);
+  return reinterpret_cast<jclass>(env->NewGlobalRef(buffer_class.get()));
+}
+
+static jclass GetDirectByteBufferClass(JNIEnv* env) {
+  static jclass buffer_class = InitDirectByteBufferClass(env);
+  return buffer_class;
+}
+
 }  // namespace
 
 class JNI {
@@ -1700,14 +1715,26 @@
     return Decode<String*>(ts, java_string)->GetUtfLength();
   }
 
-  static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, jchar* buf) {
+  static void GetStringRegion(JNIEnv* env, jstring java_string, jsize start, jsize length, jchar* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    String* s = Decode<String*>(ts, java_string);
+    if (start < 0 || length < 0 || start + length > s->GetLength()) {
+      ThrowSIOOBE(ts, start, length, s->GetLength());
+    } else {
+      const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
+      memcpy(buf, chars + start, length * sizeof(jchar));
+    }
   }
 
-  static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, jsize len, char* buf) {
+  static void GetStringUTFRegion(JNIEnv* env, jstring java_string, jsize start, jsize length, char* buf) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+    String* s = Decode<String*>(ts, java_string);
+    if (start < 0 || length < 0 || start + length > s->GetLength()) {
+      ThrowSIOOBE(ts, start, length, s->GetLength());
+    } else {
+      const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
+      ConvertUtf16ToModifiedUtf8(buf, chars + start, length);
+    }
   }
 
   static const jchar* GetStringChars(JNIEnv* env, jstring str, jboolean* isCopy) {
@@ -1732,6 +1759,17 @@
     UNIMPLEMENTED(FATAL);
   }
 
+  static const jchar* GetStringCritical(JNIEnv* env, jstring s, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+    UNIMPLEMENTED(FATAL);
+    return NULL;
+  }
+
+  static void ReleaseStringCritical(JNIEnv* env, jstring s, const jchar* cstr) {
+    ScopedJniThreadState ts(env);
+    UNIMPLEMENTED(FATAL);
+  }
+
   static jsize GetArrayLength(JNIEnv* env, jarray java_array) {
     ScopedJniThreadState ts(env);
     Object* obj = Decode<Object*>(ts, java_array);
@@ -1817,6 +1855,17 @@
     return NewPrimitiveArray<jshortArray, ShortArray>(ts, length);
   }
 
+  static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* isCopy) {
+    ScopedJniThreadState ts(env);
+    UNIMPLEMENTED(FATAL);
+    return NULL;
+  }
+
+  static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
+    ScopedJniThreadState ts(env);
+    UNIMPLEMENTED(FATAL);
+  }
+
   static jboolean* GetBooleanArrayElements(JNIEnv* env,
       jbooleanArray array, jboolean* isCopy) {
     ScopedJniThreadState ts(env);
@@ -2091,52 +2140,73 @@
     return (*vm != NULL) ? JNI_OK : JNI_ERR;
   }
 
-  static void* GetPrimitiveArrayCritical(JNIEnv* env,
-      jarray array, jboolean* isCopy) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
-  }
-
-  static void ReleasePrimitiveArrayCritical(JNIEnv* env,
-      jarray array, void* carray, jint mode) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static const jchar* GetStringCritical(JNIEnv* env, jstring s, jboolean* isCopy) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
-  }
-
-  static void ReleaseStringCritical(JNIEnv* env, jstring s, const jchar* cstr) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
   static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
+
+    // The address may not be NULL, and the capacity must be > 0.
+    CHECK(address != NULL);
+    CHECK_GT(capacity, 0);
+
+    jclass buffer_class = GetDirectByteBufferClass(env);
+    jmethodID mid = env->GetMethodID(buffer_class, "<init>", "(II)V");
+    if (mid == NULL) {
+      return NULL;
+    }
+
+    // At the moment, the Java side is limited to 32 bits.
+    CHECK_LE(reinterpret_cast<uintptr_t>(address), 0xffffffff);
+    CHECK_LE(capacity, 0xffffffff);
+    jint address_arg = reinterpret_cast<jint>(address);
+    jint capacity_arg = static_cast<jint>(capacity);
+
+    jobject result = env->NewObject(buffer_class, mid, address_arg, capacity_arg);
+    return ts.Self()->IsExceptionPending() ? NULL : result;
   }
 
-  static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) {
+  static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
+    static jfieldID fid = env->GetFieldID(GetDirectByteBufferClass(env), "effectiveDirectAddress", "I");
+    return reinterpret_cast<void*>(env->GetIntField(java_buffer, fid));
   }
 
-  static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
+  static jlong GetDirectBufferCapacity(JNIEnv* env, jobject java_buffer) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+    static jfieldID fid = env->GetFieldID(GetDirectByteBufferClass(env), "capacity", "I");
+    return static_cast<jlong>(env->GetIntField(java_buffer, fid));
   }
 
-  static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) {
+  static jobjectRefType GetObjectRefType(JNIEnv* env, jobject java_object) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return JNIInvalidRefType;
+
+    CHECK(java_object != NULL);
+
+    // Do we definitely know what kind of reference this is?
+    IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
+    IndirectRefKind kind = GetIndirectRefKind(ref);
+    switch (kind) {
+    case kLocal:
+      return JNILocalRefType;
+    case kGlobal:
+      return JNIGlobalRefType;
+    case kWeakGlobal:
+      return JNIWeakGlobalRefType;
+    case kSirtOrInvalid:
+      // Is it in a stack IRT?
+      if (ts.Self()->SirtContains(java_object)) {
+        return JNILocalRefType;
+      }
+
+      // If we're handing out direct pointers, check whether it's a direct pointer
+      // to a local reference.
+      // TODO: replace 'false' with the replacement for gDvmJni.workAroundAppJniBugs
+      if (false && Decode<Object*>(ts, java_object) == reinterpret_cast<Object*>(java_object)) {
+        if (ts.Env()->locals.Contains(java_object)) {
+          return JNILocalRefType;
+        }
+      }
+
+      return JNIInvalidRefType;
+    }
   }
 };
 
@@ -2699,3 +2769,20 @@
 }
 
 }  // namespace art
+
+std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs) {
+  switch (rhs) {
+  case JNIInvalidRefType:
+    os << "JNIInvalidRefType";
+    return os;
+  case JNILocalRefType:
+    os << "JNILocalRefType";
+    return os;
+  case JNIGlobalRefType:
+    os << "JNIGlobalRefType";
+    return os;
+  case JNIWeakGlobalRefType:
+    os << "JNIWeakGlobalRefType";
+    return os;
+  }
+}
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 98ce7c8..6d1ff96 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -81,4 +81,6 @@
 
 }  // namespace art
 
+std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs);
+
 #endif  // ART_SRC_JNI_INTERNAL_H_
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 81fda42..0964b62 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -19,11 +19,17 @@
     Runtime::Current()->GetJavaVM()->verbose_jni = true;
 
     env_ = Thread::Current()->GetJniEnv();
+
     aioobe_ = env_->FindClass("java/lang/ArrayIndexOutOfBoundsException");
     CHECK(aioobe_ != NULL);
+
+    sioobe_ = env_->FindClass("java/lang/StringIndexOutOfBoundsException");
+    CHECK(sioobe_ != NULL);
   }
+
   JNIEnv* env_;
   jclass aioobe_;
+  jclass sioobe_;
 };
 
 TEST_F(JniInternalTest, AllocObject) {
@@ -398,6 +404,23 @@
   ASSERT_FALSE(env_->IsAssignableFrom(string_class, object_class));
 }
 
+TEST_F(JniInternalTest, GetObjectRefType) {
+  jclass local = env_->FindClass("java/lang/Object");
+  ASSERT_TRUE(local != NULL);
+  EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(local));
+
+  jobject global = env_->NewGlobalRef(local);
+  EXPECT_EQ(JNIGlobalRefType, env_->GetObjectRefType(global));
+
+  jweak weak_global = env_->NewWeakGlobalRef(local);
+  EXPECT_EQ(JNIWeakGlobalRefType, env_->GetObjectRefType(weak_global));
+
+  jobject invalid = reinterpret_cast<jobject>(this);
+  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid));
+
+  // TODO: invoke a native method and test that its arguments are considered local references.
+}
+
 TEST_F(JniInternalTest, NewStringUTF) {
   EXPECT_TRUE(env_->NewStringUTF(NULL) == NULL);
   jstring s;
@@ -431,6 +454,47 @@
   // TODO: check some non-ASCII strings.
 }
 
+TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) {
+  // Already tested in the NewString/NewStringUTF tests.
+}
+
+TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) {
+  jstring s = env_->NewStringUTF("hello");
+  ASSERT_TRUE(s != NULL);
+
+  env_->GetStringRegion(s, -1, 0, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+  env_->GetStringRegion(s, 0, -1, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+  env_->GetStringRegion(s, 0, 10, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+  env_->GetStringRegion(s, 10, 1, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+
+  jchar chars[4] = { 'x', 'x', 'x', 'x' };
+  env_->GetStringRegion(s, 1, 2, &chars[1]);
+  EXPECT_EQ('x', chars[0]);
+  EXPECT_EQ('e', chars[1]);
+  EXPECT_EQ('l', chars[2]);
+  EXPECT_EQ('x', chars[3]);
+
+  env_->GetStringUTFRegion(s, -1, 0, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+  env_->GetStringUTFRegion(s, 0, -1, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+  env_->GetStringUTFRegion(s, 0, 10, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+  env_->GetStringUTFRegion(s, 10, 1, NULL);
+  EXPECT_EXCEPTION(sioobe_);
+
+  char bytes[4] = { 'x', 'x', 'x', 'x' };
+  env_->GetStringUTFRegion(s, 1, 2, &bytes[1]);
+  EXPECT_EQ('x', bytes[0]);
+  EXPECT_EQ('e', bytes[1]);
+  EXPECT_EQ('l', bytes[2]);
+  EXPECT_EQ('x', bytes[3]);
+}
+
 TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
   jclass c = env_->FindClass("[Ljava/lang/Object;");
   ASSERT_TRUE(c != NULL);
@@ -1377,4 +1441,17 @@
   env_->ExceptionClear();
 }
 
+// TODO: this test is DISABLED until we can actually run java.nio.Buffer's <init>.
+TEST_F(JniInternalTest, DISABLED_NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) {
+  jclass buffer_class = env_->FindClass("java/nio/Buffer");
+  ASSERT_TRUE(buffer_class != NULL);
+
+  char bytes[1024];
+  jobject buffer = env_->NewDirectByteBuffer(bytes, sizeof(bytes));
+  ASSERT_TRUE(buffer != NULL);
+  ASSERT_TRUE(env_->IsInstanceOf(buffer, buffer_class));
+  ASSERT_TRUE(env_->GetDirectBufferAddress(buffer) == bytes);
+  ASSERT_TRUE(env_->GetDirectBufferCapacity(buffer) == sizeof(bytes));
+}
+
 }
diff --git a/src/object.h b/src/object.h
index 93e7a7a..c8b8ec3 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1619,23 +1619,10 @@
 
   // Create a modified UTF-8 encoded std::string from a java/lang/String object.
   std::string ToModifiedUtf8() const {
-    std::string result;
-    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) {
-        if (ch > 0x07ff) {
-          result.push_back((ch >> 12) | 0xe0);
-          result.push_back(((ch >> 6) & 0x3f) | 0x80);
-          result.push_back((ch & 0x3f) | 0x80);
-        } else { // (ch > 0x7f || ch == 0)
-          result.push_back((ch >> 6) | 0xc0);
-          result.push_back((ch & 0x3f) | 0x80);
-        }
-      } else {
-        result.push_back(ch);
-      }
-    }
+    uint16_t* chars = array_->GetData() + offset_;
+    size_t byte_count(CountUtf8Bytes(chars, count_));
+    std::string result(byte_count, char(0));
+    ConvertUtf16ToModifiedUtf8(&result[0], chars, count_);
     return result;
   }
 
diff --git a/src/utf.cc b/src/utf.cc
index 81c93ca..9356197 100644
--- a/src/utf.cc
+++ b/src/utf.cc
@@ -2,6 +2,8 @@
 
 #include "utf.h"
 
+#include "logging.h"
+
 namespace art {
 
 size_t CountModifiedUtf8Chars(const char* utf8) {
@@ -31,6 +33,24 @@
   }
 }
 
+void ConvertUtf16ToModifiedUtf8(char* utf8_out, const uint16_t* utf16_in, size_t char_count) {
+  while (char_count--) {
+    uint16_t ch = *utf16_in++;
+    if (ch > 0 && ch <= 0x7f) {
+      *utf8_out++ = ch;
+    } else {
+      if (ch > 0x07ff) {
+        *utf8_out++ = (ch >> 12) | 0xe0;
+        *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
+        *utf8_out++ = (ch & 0x3f) | 0x80;
+      } else /*(ch > 0x7f || ch == 0)*/ {
+        *utf8_out++ = (ch >> 6) | 0xc0;
+        *utf8_out++ = (ch & 0x3f) | 0x80;
+      }
+    }
+  }
+}
+
 int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count) {
   int32_t hash = 0;
   while (char_count--) {
diff --git a/src/utf.h b/src/utf.h
index a9d29a6..2c22f87 100644
--- a/src/utf.h
+++ b/src/utf.h
@@ -6,6 +6,12 @@
 #include <stddef.h>
 #include <stdint.h>
 
+/*
+ * All UTF-8 in art is actually modified UTF-8. Mostly, this distinction
+ * doesn't matter.
+ *
+ * See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details.
+ */
 namespace art {
 
 /*
@@ -20,10 +26,17 @@
 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
+ * Convert from Modified UTF-8 to UTF-16.
  */
-void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in);
+void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in);
+
+/*
+ * Convert from UTF-16 to Modified UTF-8. Note that the output is _not_
+ * NUL-terminated. You probably need to call CountUtf8Bytes before calling
+ * this anyway, so if you want a NUL-terminated string, you know where to
+ * put the NUL byte.
+ */
+void ConvertUtf16ToModifiedUtf8(char* utf8_out, const uint16_t* utf16_in, size_t char_count);
 
 /*
  * The java.lang.String hashCode() algorithm.
diff --git a/src/utils.h b/src/utils.h
index 4283f47..229d123 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -125,7 +125,7 @@
 static inline std::string PrintableString(const StringT& s) {
   std::string result;
   result += '"';
-  for (typename StringT::iterator it = s.begin(); it != s.end(); ++it) {
+  for (typename StringT::const_iterator it = s.begin(); it != s.end(); ++it) {
     char ch = *it;
     if (NeedsEscaping(ch)) {
       StringAppendF(&result, "\\x%02x", ch & 0xff);