Add semi-functional exception throwing.
We still need a way to call <init> on the new exception instance, in particular.
Change-Id: I839365d20288208cb8847253380cf654a35aeddd
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 683310d..3b70aa9 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -255,8 +255,8 @@
EXPECT_FALSE(JavaLangObject->IsFinal());
EXPECT_FALSE(JavaLangObject->IsPrimitive());
EXPECT_FALSE(JavaLangObject->IsSynthetic());
- EXPECT_EQ(1U, JavaLangObject->NumDirectMethods());
- EXPECT_EQ(0U, JavaLangObject->NumVirtualMethods());
+ EXPECT_EQ(2U, JavaLangObject->NumDirectMethods());
+ EXPECT_EQ(11U, JavaLangObject->NumVirtualMethods());
EXPECT_EQ(0U, JavaLangObject->NumInstanceFields());
EXPECT_EQ(0U, JavaLangObject->NumStaticFields());
EXPECT_EQ(0U, JavaLangObject->NumInterfaces());
@@ -305,7 +305,6 @@
}
TEST_F(ClassLinkerTest, LibCore) {
- UseLibCoreDex();
scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
EXPECT_TRUE(libcore_dex_file.get() != NULL);
AssertDexFile(libcore_dex_file.get(), NULL);
@@ -315,7 +314,6 @@
// reorder the fields in the C++ class. Managed class fields are ordered by
// ClassLinker::LinkInstanceFields.
TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) {
- UseLibCoreDex();
Class* string = class_linker_->FindSystemClass( "Ljava/lang/String;");
ASSERT_EQ(4U, string->NumInstanceFields());
EXPECT_TRUE(string->GetInstanceField(0)->GetName()->Equals("value"));
diff --git a/src/common_test.h b/src/common_test.h
index 9acb06c..9f8d6df 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -17,118 +17,6 @@
// package java.lang;
// public class Object {}
-
-// package java.lang;
-// public class Class {}
-//
-// package java.lang.reflect;
-// public class Field {}
-//
-// package java.lang.reflect;
-// public class Method {}
-//
-// package java.lang;
-// public class String {
-// char[] value;
-// int hashCode;
-// int offset;
-// int count;
-// }
-//
-// package java.lang;
-// public class ClassLoader {
-// Object packages;
-// ClassLoader parent;
-// }
-//
-// package dalvik.system;
-// public class BaseDexClassLoader extends ClassLoader {
-// String originalPath;
-// Object pathList;
-// }
-//
-// package dalvik.system;
-// public class PathClassLoader extends BaseDexClassLoader {}
-//
-// package java.lang;
-// public interface Cloneable {}
-//
-// package java.io;
-// public interface Serializable {}
-//
-// package java.lang;
-// public class StackTraceElement {
-// String declaringClass;
-// String methodName;
-// String fileName;
-// int lineNumber;
-// }
-//
-// package java.lang;
-// public class Throwable {
-// String detailMessage;
-// Throwable cause;
-// Object suppressedExceptions;
-// Object stackState;
-// StackTraceElement[] stackTrace;
-// }
-//
-// package java.lang;
-// public class Exception extends Throwable {}
-//
-// package java.lang;
-// public class NullPointerException extends Exception {}
-//
-static const char kJavaLangDex[] =
- "ZGV4CjAzNQCEjWBqSq808bjn0gC+ptCv0wtDNadp4vEQCgAAcAAAAHhWNBIAAAAAAAAAAHwJAAAy"
- "AAAAcAAAABIAAAA4AQAAAQAAAIABAAARAAAAjAEAAAwAAAAUAgAADgAAAHQCAADcBQAANAQAAFAF"
- "AABYBQAAcQUAAH0FAACPBQAAnwUAAK8FAAC7BQAAvgUAAOIFAAADBgAAGwYAAC4GAABHBgAAXgYA"
- "AHUGAACXBgAAqwYAAMoGAADeBgAA9QYAABAHAAAsBwAAOQcAAFQHAABhBwAAdwcAAIoHAACiBwAA"
- "rwcAAL8HAADCBwAAxgcAAOYHAADtBwAA9AcAAAQIAAATCAAAHQgAACcIAAAzCAAAPwgAAEcIAABV"
- "CAAAXwgAAGcIAABxCAAAfQgAAIkIAACfCAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4A"
- "AAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAHgAAAB8AAAAgAAAAHgAAAA8AAAAAAAAAAQAL"
- "ACoAAAABAAkALQAAAAUACQArAAAABQAFACwAAAAKAAsAIwAAAAoACwAlAAAACgAAACcAAAAKAAsA"
- "KAAAAAsAAAAiAAAACwAAACYAAAALAAAAKQAAAAsAEAAxAAAADAAMACEAAAAMAAsAJAAAAAwACQAu"
- "AAAADAARAC8AAAAMAAkAMAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAUAAAAAAAAABwAAAAAA"
- "AAAIAAAAAAAAAAkAAAAAAAAACgAAAAAAAAALAAAAAAAAAAwAAAAAAAAADQAAAAAAAAAOAAAAAAAA"
- "AAkAAAABAAAA/////wAAAAAYAAAAAAAAAOIIAAAAAAAABQAAAAEAAAAJAAAAAAAAAAMAAAAAAAAA"
- "7AgAAAAAAAABAAAAAQAAAAUAAAAAAAAAAQAAAAAAAAD6CAAAAAAAAAIAAAABAAAAAQAAAAAAAAAZ"
- "AAAAAAAAAAgJAAAAAAAAAwAAAAEGAAAJAAAAAAAAABoAAAAAAAAAAAAAAAAAAAAEAAAAAQAAAAkA"
- "AAAAAAAAAgAAAAAAAAASCQAAAAAAAAYAAAABBgAACQAAAAAAAAAEAAAAAAAAAAAAAAAAAAAADAAA"
- "AAEAAAAJAAAAAAAAAB0AAAAAAAAAHAkAAAAAAAAHAAAAAQAAAAwAAAAAAAAABQAAAAAAAAAwCQAA"
- "AAAAAAgAAAABAAAABwAAAAAAAAAXAAAAAAAAADoJAAAAAAAACgAAAAEAAAAJAAAAAAAAABsAAAAA"
- "AAAARAkAAAAAAAALAAAAAQAAAAkAAAAAAAAAHAAAAAAAAABWCQAAAAAAAA0AAAABAAAACQAAAAAA"
- "AAAGAAAAAAAAAGgJAAAAAAAADgAAAAEAAAAJAAAAAAAAABYAAAAAAAAAcgkAAAAAAAABAAEAAAAA"
- "AKYIAAABAAAADgAAAAEAAQABAAAAqwgAAAQAAABwEAYAAAAOAAEAAQABAAAAsAgAAAQAAABwEAMA"
- "AAAOAAEAAQABAAAAtQgAAAQAAABwEAAAAAAOAAEAAQABAAAAuggAAAQAAABwEAYAAAAOAAEAAQAB"
- "AAAAvwgAAAQAAABwEAYAAAAOAAEAAQABAAAAxAgAAAQAAABwEAkAAAAOAAEAAQABAAAAyQgAAAQA"
- "AABwEAQAAAAOAAEAAQABAAAAzggAAAQAAABwEAYAAAAOAAEAAQABAAAA0wgAAAQAAABwEAYAAAAO"
- "AAEAAQABAAAA2AgAAAQAAABwEAYAAAAOAAEAAQABAAAA3QgAAAQAAABwEAYAAAAOAAY8aW5pdD4A"
- "F0Jhc2VEZXhDbGFzc0xvYWRlci5qYXZhAApDbGFzcy5qYXZhABBDbGFzc0xvYWRlci5qYXZhAA5D"
- "bG9uZWFibGUuamF2YQAORXhjZXB0aW9uLmphdmEACkZpZWxkLmphdmEAAUkAIkxkYWx2aWsvc3lz"
- "dGVtL0Jhc2VEZXhDbGFzc0xvYWRlcjsAH0xkYWx2aWsvc3lzdGVtL1BhdGhDbGFzc0xvYWRlcjsA"
- "FkxqYXZhL2lvL1NlcmlhbGl6YWJsZTsAEUxqYXZhL2xhbmcvQ2xhc3M7ABdMamF2YS9sYW5nL0Ns"
- "YXNzTG9hZGVyOwAVTGphdmEvbGFuZy9DbG9uZWFibGU7ABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsA"
- "IExqYXZhL2xhbmcvTnVsbFBvaW50ZXJFeGNlcHRpb247ABJMamF2YS9sYW5nL09iamVjdDsAHUxq"
- "YXZhL2xhbmcvU3RhY2tUcmFjZUVsZW1lbnQ7ABJMamF2YS9sYW5nL1N0cmluZzsAFUxqYXZhL2xh"
- "bmcvVGhyb3dhYmxlOwAZTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwAaTGphdmEvbGFuZy9yZWZs"
- "ZWN0L01ldGhvZDsAC01ldGhvZC5qYXZhABlOdWxsUG9pbnRlckV4Y2VwdGlvbi5qYXZhAAtPYmpl"
- "Y3QuamF2YQAUUGF0aENsYXNzTG9hZGVyLmphdmEAEVNlcmlhbGl6YWJsZS5qYXZhABZTdGFja1Ry"
- "YWNlRWxlbWVudC5qYXZhAAtTdHJpbmcuamF2YQAOVGhyb3dhYmxlLmphdmEAAVYAAltDAB5bTGph"
- "dmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDsABWNhdXNlAAVjb3VudAAOZGVjbGFyaW5nQ2xhc3MA"
- "DWRldGFpbE1lc3NhZ2UACGZpbGVOYW1lAAhoYXNoQ29kZQAKbGluZU51bWJlcgAKbWV0aG9kTmFt"
- "ZQAGb2Zmc2V0AAxvcmlnaW5hbFBhdGgACHBhY2thZ2VzAAZwYXJlbnQACHBhdGhMaXN0AApzdGFj"
- "a1N0YXRlAApzdGFja1RyYWNlABRzdXBwcmVzc2VkRXhjZXB0aW9ucwAFdmFsdWUAAwAHDgAFAAcO"
- "AAUABw4ABQAHDgAFAAcOAAUABw4ABQAHDgAFAAcOAAUABw4ABQAHDgAFAAcOAAUABw4AAAABAAaB"
- "gAS0CAACAQACAAEAA4GABMgIAAIBAAAAAQAAgYAE4AgAAAEAAYGABPgIAAABAAKBgASQCQAFAQAM"
- "AAEAAQABAAEACYGABKgJAAABAASBgATACQAAAQAFgYAE2AkABAEABAABAAEAAQAHgYAE8AkABAEA"
- "CAABAAEAAQAIgYAEiAoAAAEACoGABKAKAAABAAuBgAS4CgwAAAAAAAAAAQAAAAAAAAABAAAAMgAA"
- "AHAAAAACAAAAEgAAADgBAAADAAAAAQAAAIABAAAEAAAAEQAAAIwBAAAFAAAADAAAABQCAAAGAAAA"
- "DgAAAHQCAAABIAAADAAAADQEAAACIAAAMgAAAFAFAAADIAAADAAAAKYIAAAAIAAADAAAAOIIAAAA"
- "EAAAAQAAAHwJAAA=";
-
-// package java.lang;
-// public class Object {}
//
// class MyClass {}
static const char kMyClassDex[] =
@@ -443,7 +331,7 @@
int mkdir_result = mkdir(art_cache_.c_str(), 0700);
ASSERT_EQ(mkdir_result, 0);
- java_lang_dex_file_.reset(OpenDexFileBase64(kJavaLangDex));
+ java_lang_dex_file_.reset(GetLibCoreDex());
std::vector<DexFile*> boot_class_path;
boot_class_path.push_back(java_lang_dex_file_.get());
@@ -496,18 +384,6 @@
return DexFile::OpenZip(libcore_dex_file_name);
}
- void UseLibCoreDex() {
- delete runtime_.release();
- java_lang_dex_file_.reset(GetLibCoreDex());
-
- std::vector<DexFile*> boot_class_path;
- boot_class_path.push_back(java_lang_dex_file_.get());
-
- runtime_.reset(Runtime::Create(boot_class_path));
- ASSERT_TRUE(runtime_ != NULL);
- class_linker_ = runtime_->GetClassLinker();
- }
-
PathClassLoader* AllocPathClassLoader(const DexFile* dex_file) {
class_linker_->RegisterDexFile(dex_file);
std::vector<const DexFile*> dex_files;
diff --git a/src/image_test.cc b/src/image_test.cc
index 80ccfa0..5de860e 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -10,7 +10,6 @@
class ImageTest : public RuntimeTest {};
TEST_F(ImageTest, WriteRead) {
- UseLibCoreDex();
scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
EXPECT_TRUE(libcore_dex_file.get() != NULL);
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 9a2492e..eda3e9b 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -215,10 +215,10 @@
result += ';';
}
// Rewrite '.' as '/' for backwards compatibility.
- for (size_t i = 0; i < result.size(); ++i) {
- if (result[i] == '.') {
- result[i] = '/';
- }
+ if (result.find('.') != std::string::npos) {
+ LOG(WARNING) << "Call to JNI FindClass with dots in name: "
+ << "\"" << name << "\"";
+ std::replace(result.begin(), result.end(), '.', '/');
}
return result;
}
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 9394ff6..569dfda 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -115,7 +115,19 @@
EXPECT_TRUE(array != NULL);
env_->SetObjectArrayElement(array, 0, c);
// TODO: check reading value back
- // TODO: check IndexOutOfBoundsExceptions thrown for bad indexes.
+
+ // ArrayIndexOutOfBounds for negative index.
+ // TODO: check exception type
+ env_->SetObjectArrayElement(array, -1, c);
+ EXPECT_TRUE(env_->ExceptionCheck());
+ env_->ExceptionClear();
+
+ // ArrayIndexOutOfBounds for too-large index.
+ // TODO: check exception type
+ env_->SetObjectArrayElement(array, 1, c);
+ EXPECT_TRUE(env_->ExceptionCheck());
+ env_->ExceptionClear();
+
// TODO: check ArrayStoreException thrown for bad types.
}
diff --git a/src/object.h b/src/object.h
index abcf51f..4da6725 100644
--- a/src/object.h
+++ b/src/object.h
@@ -628,11 +628,9 @@
protected:
bool IsValidIndex(int32_t index) const {
if (index < 0 || index >= length_) {
- // TODO: throw ArrayIndexOutOfBoundsException with the detail message
- // "length=%d; index=%d", length_, index;
- CHECK(false) << "ArrayIndexOutOfBoundsException: length="
- << length_ << "; index=" << index;
- return false;
+ Thread* self = Thread::Current();
+ self->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+ "length=%i; index=%i", length_, index);
}
return true;
}
@@ -1275,10 +1273,13 @@
return count_;
}
+ // TODO: do we need this? Equals is the only caller, and could
+ // bounds check itself.
uint16_t CharAt(int32_t index) const {
if (index < 0 || index >= count_) {
- // TODO: throw new StringIndexOutOfBounds(this, index);
- CHECK(false) << "StringIndexOutOfBounds: " << index;
+ Thread* self = Thread::Current();
+ self->ThrowNewException("Ljava/lang/StringIndexOutOfBoundsException;",
+ "length=%i; index=%i", count_, index);
return 0;
} else {
return GetCharArray()->Get(index + GetOffset());
@@ -1414,6 +1415,7 @@
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) {
uint16_t ch = GetUtf16FromUtf8(&modified_utf8);
@@ -1424,6 +1426,7 @@
return *modified_utf8 == '\0';
}
+ // TODO: do we need this overload? give it a more intention-revealing name.
bool Equals(const StringPiece& modified_utf8) const {
// TODO: do not assume C-string representation.
return Equals(modified_utf8.data());
@@ -1442,6 +1445,7 @@
return true;
}
+ // 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 {
if (this->GetLength() != that_length) {
return false;
diff --git a/src/thread.cc b/src/thread.cc
index 1dee238..51b72ce 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -8,6 +8,8 @@
#include <cerrno>
#include <list>
+#include "class_linker.h"
+#include "object.h"
#include "runtime.h"
#include "utils.h"
@@ -128,6 +130,46 @@
return true;
}
+void ThrowNewException(Thread* thread, const char* exception_class_name, const char* msg) {
+ CHECK(thread != NULL);
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* exception_class = class_linker->FindSystemClass(exception_class_name);
+ CHECK(exception_class != NULL);
+
+ Object* exception = exception_class->NewInstance();
+ CHECK(exception != NULL);
+
+ size_t char_count = String::ModifiedUtf8Len(msg);
+ String* java_msg = String::AllocFromModifiedUtf8(char_count, msg);
+ CHECK(java_msg != NULL);
+
+ // TODO: what if there's already a pending exception?
+ // TODO: support the other constructors.
+ Method* ctor = exception_class->FindDirectMethod("<init>", "(Ljava/lang/String;)V");
+
+ // TODO: need to *call* the constructor!
+ UNIMPLEMENTED(WARNING) << "can't call "
+ << exception_class->GetDescriptor() << "."
+ << ctor->GetDescriptor() << " "
+ << "\"" << msg << "\"";
+
+ thread->SetException(exception);
+}
+
+void ThrowNewExceptionV(Thread* thread, const char* exception_class_name, const char* fmt, va_list args) {
+ char msg[512];
+ vsnprintf(msg, sizeof(msg), fmt, args);
+ ThrowNewException(thread, exception_class_name, msg);
+}
+
+void Thread::ThrowNewException(const char* exception_class_name, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowNewExceptionV(this, exception_class_name, fmt, args);
+ va_end(args);
+}
+
static const char* kStateNames[] = {
"New",
"Runnable",
diff --git a/src/thread.h b/src/thread.h
index e065f68..8bde370 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -147,6 +147,9 @@
exception_ = new_exception; // TODO
}
+ void ThrowNewException(const char* exception_class_name, const char* fmt, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
void ClearException() {
exception_ = NULL;
}