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_