Fix the invocation interface.

Previously, we just cast the Runtime* to a JavaVM*, which blew up as soon as
the caller tried to use their supposed JavaVM*.

This also implements NewObjectArray. Running aexecd on the host, this gets us
as far as not having an x86 CreateInvokeStub...

Change-Id: Iba5f148797d053fba1c69af99b20508ea6aff5cb
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 9a0563b..5cc9ff6 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -98,7 +98,7 @@
         offset += 4;
         break;
       case 'L': {
-        // TODO: local reference
+        // TODO: DecodeReference
         Object* obj = reinterpret_cast<Object*>(va_arg(ap, jobject));
         *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
         offset += sizeof(Object*);
@@ -136,7 +136,8 @@
         offset += 4;
         break;
       case 'L': {
-        Object* obj = reinterpret_cast<Object*>(args[i - 1].l);  // TODO: local reference
+        // TODO: DecodeReference
+        Object* obj = reinterpret_cast<Object*>(args[i - 1].l);
         *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
         offset += sizeof(Object*);
         break;
@@ -156,7 +157,8 @@
 
 JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id,
                           byte* args) {
-  Method* method = reinterpret_cast<Method*>(method_id);  // TODO
+  // TODO: DecodeReference
+  Method* method = reinterpret_cast<Method*>(method_id);
   // Call the invoke stub associated with the method
   // Pass everything as arguments
   const Method::InvokeStub* stub = method->GetInvokeStub();
@@ -221,7 +223,7 @@
   std::string descriptor(NormalizeJniClassDescriptor(name));
   // TODO: need to get the appropriate ClassLoader.
   Class* c = class_linker->FindClass(descriptor, NULL);
-  // TODO: local reference.
+  // TODO: AddLocalReference.
   return reinterpret_cast<jclass>(c);
 }
 
@@ -321,7 +323,7 @@
 
 void DeleteLocalRef(JNIEnv* env, jobject obj) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
+  UNIMPLEMENTED(WARNING);
 }
 
 jboolean IsSameObject(JNIEnv* env, jobject obj1, jobject obj2) {
@@ -900,7 +902,7 @@
 jmethodID GetStaticMethodID(JNIEnv* env,
     jclass clazz, const char* name, const char* sig) {
   ScopedJniThreadState ts(env);
-  // TODO: retrieve handle value for class
+  // TODO: DecodeReference
   Class* klass = reinterpret_cast<Class*>(clazz);
   // TODO: check that klass is initialized
   Method* method = klass->FindDirectMethod(name, sig);
@@ -923,7 +925,7 @@
   va_list ap;
   va_start(ap, methodID);
   JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, ap);
-  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: local reference
+  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: AddLocalReference
   return obj;
 }
 
@@ -931,7 +933,7 @@
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
   JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, args);
-  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: local reference
+  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: AddLocalReference
   return obj;
 }
 
@@ -939,7 +941,7 @@
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
   JValue result = InvokeWithJValues(ts.Self(), NULL, methodID, args);
-  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: local reference
+  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: AddLocalReference
   return obj;
 }
 
@@ -1282,13 +1284,6 @@
   return 0;
 }
 
-jobjectArray NewObjectArray(JNIEnv* env,
-    jsize len, jclass clazz, jobject init) {
-  ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
-}
-
 jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
   ScopedJniThreadState ts(env);
   UNIMPLEMENTED(FATAL);
@@ -1305,48 +1300,73 @@
 JniT NewPrimitiveArray(ScopedJniThreadState& ts, jsize length) {
   CHECK_GE(length, 0); // TODO: ReportJniError
   ArtT* result = ArtT::Alloc(length);
-  // TODO: local reference
+  // TODO: AddLocalReference
   return reinterpret_cast<JniT>(result);
 }
 
-jbooleanArray NewBooleanArray(JNIEnv* env, jsize len) {
+jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jbooleanArray, BooleanArray>(ts, len);
+  return NewPrimitiveArray<jbooleanArray, BooleanArray>(ts, length);
 }
 
-jbyteArray NewByteArray(JNIEnv* env, jsize len) {
+jbyteArray NewByteArray(JNIEnv* env, jsize length) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jbyteArray, ByteArray>(ts, len);
+  return NewPrimitiveArray<jbyteArray, ByteArray>(ts, length);
 }
 
-jcharArray NewCharArray(JNIEnv* env, jsize len) {
+jcharArray NewCharArray(JNIEnv* env, jsize length) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jcharArray, CharArray>(ts, len);
+  return NewPrimitiveArray<jcharArray, CharArray>(ts, length);
 }
 
-jdoubleArray NewDoubleArray(JNIEnv* env, jsize len) {
+jdoubleArray NewDoubleArray(JNIEnv* env, jsize length) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jdoubleArray, DoubleArray>(ts, len);
+  return NewPrimitiveArray<jdoubleArray, DoubleArray>(ts, length);
 }
 
-jfloatArray NewFloatArray(JNIEnv* env, jsize len) {
+jfloatArray NewFloatArray(JNIEnv* env, jsize length) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jfloatArray, FloatArray>(ts, len);
+  return NewPrimitiveArray<jfloatArray, FloatArray>(ts, length);
 }
 
-jintArray NewIntArray(JNIEnv* env, jsize len) {
+jintArray NewIntArray(JNIEnv* env, jsize length) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jintArray, IntArray>(ts, len);
+  return NewPrimitiveArray<jintArray, IntArray>(ts, length);
 }
 
-jlongArray NewLongArray(JNIEnv* env, jsize len) {
+jlongArray NewLongArray(JNIEnv* env, jsize length) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jlongArray, LongArray>(ts, len);
+  return NewPrimitiveArray<jlongArray, LongArray>(ts, length);
 }
 
-jshortArray NewShortArray(JNIEnv* env, jsize len) {
+jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, jobject initial_element) {
   ScopedJniThreadState ts(env);
-  return NewPrimitiveArray<jshortArray, ShortArray>(ts, len);
+  CHECK_GE(length, 0); // TODO: ReportJniError
+
+  // Compute the array class corresponding to the given element class.
+  // TODO: DecodeReference
+  Class* element_class = reinterpret_cast<Class*>(element_jclass);
+  std::string descriptor;
+  descriptor += "[";
+  descriptor += element_class->GetDescriptor().ToString();
+
+  // Find the class.
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  // TODO: need to get the appropriate ClassLoader.
+  Class* array_class = class_linker->FindClass(descriptor, NULL);
+  if (array_class == NULL) {
+    return NULL;
+  }
+
+  ObjectArray<Object>* result = ObjectArray<Object>::Alloc(array_class, length);
+  CHECK(initial_element == NULL);  // TODO: support initial_element
+  // TODO: AddLocalReference.
+  return reinterpret_cast<jobjectArray>(result);
+}
+
+jshortArray NewShortArray(JNIEnv* env, jsize length) {
+  ScopedJniThreadState ts(env);
+  return NewPrimitiveArray<jshortArray, ShortArray>(ts, length);
 }
 
 jboolean* GetBooleanArrayElements(JNIEnv* env,
@@ -1572,8 +1592,13 @@
 
 jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  Runtime* runtime = Runtime::Current();
+  if (runtime != NULL) {
+    *vm = runtime->GetJavaVM();
+  } else {
+    *vm = NULL;
+  }
+  return (*vm != NULL) ? JNI_OK : JNI_ERR;
 }
 
 void GetStringRegion(JNIEnv* env,
@@ -1902,9 +1927,10 @@
 JNIEnv* CreateJNIEnv() {
   Thread* self = Thread::Current();
   CHECK(self != NULL);
-  JNIEnvExt* result = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
+  JNIEnvExt* result = new JNIEnvExt;
   result->fns = &gNativeInterface;
   result->self = self;
+  result->critical = false;
   result->MonitorEnterHelper = &MonitorEnterHelper;
   result->MonitorExitHelper = &MonitorExitHelper;
   return reinterpret_cast<JNIEnv*>(result);
@@ -1924,24 +1950,23 @@
                                      option->extraInfo));
   }
   bool ignore_unrecognized = args->ignoreUnrecognized;
-  scoped_ptr<Runtime> runtime(Runtime::Create(options, ignore_unrecognized));
+  Runtime* runtime = Runtime::Create(options, ignore_unrecognized);
   if (runtime == NULL) {
     return JNI_ERR;
   } else {
     *p_env = reinterpret_cast<JNIEnv*>(Thread::Current()->GetJniEnv());
-    *p_vm = reinterpret_cast<JavaVM*>(runtime.release());
+    *p_vm = runtime->GetJavaVM();
     return JNI_OK;
   }
 }
 
-extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen,
-                                      jsize* nVMs) {
+extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
   Runtime* runtime = Runtime::Current();
   if (runtime == NULL) {
-    *nVMs = 0;
+    *vm_count = 0;
   } else {
-    *nVMs = 1;
-    vmBuf[0] = reinterpret_cast<JavaVM*>(runtime);
+    *vm_count = 1;
+    vms[0] = runtime->GetJavaVM();
   }
   return JNI_OK;
 }
@@ -1951,23 +1976,23 @@
   return JNI_ERR;
 }
 
-jint JniInvokeInterface::DestroyJavaVM(JavaVM* vm) {
+jint DestroyJavaVM(JavaVM* vm) {
   if (vm == NULL) {
     return JNI_ERR;
   } else {
-    Runtime* runtime = reinterpret_cast<Runtime*>(vm);
-    delete runtime;
+    JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+    delete raw_vm->runtime;
+    raw_vm->runtime = NULL;
     return JNI_OK;
   }
 }
 
-jint JniInvokeInterface::AttachCurrentThread(JavaVM* vm,
-                                             JNIEnv** p_env,
-                                             void* thr_args) {
+jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
   if (vm == NULL || p_env == NULL) {
     return JNI_ERR;
   }
-  Runtime* runtime = reinterpret_cast<Runtime*>(vm);
+  JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+  Runtime* runtime = raw_vm->runtime;
   const char* name = NULL;
   if (thr_args != NULL) {
     // TODO: check version
@@ -1982,17 +2007,18 @@
   }
 }
 
-jint JniInvokeInterface::DetachCurrentThread(JavaVM* vm) {
+jint DetachCurrentThread(JavaVM* vm) {
   if (vm == NULL) {
     return JNI_ERR;
   } else {
-    Runtime* runtime = reinterpret_cast<Runtime*>(vm);
+    JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+    Runtime* runtime = raw_vm->runtime;
     runtime->DetachCurrentThread();
     return JNI_OK;
   }
 }
 
-jint JniInvokeInterface::GetEnv(JavaVM* vm, void** env, jint version) {
+jint GetEnv(JavaVM* vm, void** env, jint version) {
   if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6) {
     return JNI_EVERSION;
   }
@@ -2008,13 +2034,12 @@
   return JNI_OK;
 }
 
-jint JniInvokeInterface::AttachCurrentThreadAsDaemon(JavaVM* vm,
-                                                     JNIEnv** p_env,
-                                                     void* thr_args) {
+jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
   if (vm == NULL || p_env == NULL) {
     return JNI_ERR;
   }
-  Runtime* runtime = reinterpret_cast<Runtime*>(vm);
+  JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+  Runtime* runtime = raw_vm->runtime;
   const char* name = NULL;
   if (thr_args != NULL) {
     // TODO: check version
@@ -2029,7 +2054,7 @@
   }
 }
 
-struct JNIInvokeInterface JniInvokeInterface::invoke_interface_ = {
+struct JNIInvokeInterface gInvokeInterface = {
   NULL,  // reserved0
   NULL,  // reserved1
   NULL,  // reserved2
@@ -2040,4 +2065,11 @@
   AttachCurrentThreadAsDaemon
 };
 
+JavaVM* CreateJavaVM(Runtime* runtime) {
+  JavaVMExt* result = new JavaVMExt;
+  result->fns = &gInvokeInterface;
+  result->runtime = runtime;
+  return reinterpret_cast<JavaVM*>(result);
+}
+
 }  // namespace art
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 39ce2cd..3ff12d8 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -10,12 +10,22 @@
 
 namespace art {
 
+class Runtime;
 class Thread;
 
+JavaVM* CreateJavaVM(Runtime* runtime);
 JNIEnv* CreateJNIEnv();
 
+struct JavaVMExt {
+  // Must be first to correspond with JNIEnv.
+  const struct JNIInvokeInterface* fns;
+
+  Runtime* runtime;
+};
+
 struct JNIEnvExt {
-  const struct JNINativeInterface* fns;  // Must be first.
+  // Must be first to correspond with JavaVM.
+  const struct JNINativeInterface* fns;
 
   Thread* self;
 
@@ -28,23 +38,6 @@
   void (*MonitorExitHelper)(JNIEnv*, jobject);
 };
 
-class JniInvokeInterface {
- public:
-  static struct JNIInvokeInterface* GetInterface() {
-    return &invoke_interface_;
-  }
- private:
-  static jint DestroyJavaVM(JavaVM* vm);
-  static jint AttachCurrentThread(JavaVM* vm, JNIEnv** penv, void* thr_args);
-  static jint DetachCurrentThread(JavaVM* vm);
-  static jint GetEnv(JavaVM* vm, void** penv, int version);
-  static jint AttachCurrentThreadAsDaemon(JavaVM* vm,
-                                          JNIEnv** penv,
-                                          void* thr_args);
-  static struct JNIInvokeInterface invoke_interface_;
-  DISALLOW_IMPLICIT_CONSTRUCTORS(JniInvokeInterface);
-};
-
 }  // namespace art
 
 #endif  // ART_SRC_JNI_INTERNAL_H_
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index ce640e2..df9844f 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -60,6 +60,10 @@
 TEST_F(JniInternalTest, NewPrimitiveArray) {
   // TODO: death tests for negative array sizes.
 
+  // TODO: check returned array size.
+
+  // TODO: check returned array class.
+
   CHECK(env_->NewBooleanArray(0) != NULL);
   CHECK(env_->NewByteArray(0) != NULL);
   CHECK(env_->NewCharArray(0) != NULL);
@@ -79,6 +83,22 @@
   CHECK(env_->NewShortArray(1) != NULL);
 }
 
+TEST_F(JniInternalTest, NewObjectArray) {
+  // TODO: death tests for negative array sizes.
+
+  // TODO: check returned array size.
+
+  // TODO: check returned array class.
+
+  // TODO: check non-NULL initial elements.
+
+  jclass c = env_->FindClass("[Ljava.lang.String;");
+
+  CHECK(env_->NewObjectArray(0, c, NULL) != NULL);
+
+  CHECK(env_->NewObjectArray(1, c, NULL) != NULL);
+}
+
 bool EnsureInvokeStub(Method* method);
 
 byte* AllocateCode(void* code, size_t length) {
diff --git a/src/main.cc b/src/main.cc
index b0eb659..091cda0 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -93,7 +93,7 @@
   return true;
 }
 
-static bool InvokeMain(JavaVM* vm, JNIEnv* env, int argc, char** argv) {
+static bool InvokeMain(JNIEnv* env, int argc, char** argv) {
   // We want to call main() with a String array with our arguments in
   // it.  Create an array and populate it.  Note argv[0] is not
   // included.
@@ -197,7 +197,7 @@
   DCHECK_LE(curr_opt, option_count);
 
   JavaVMInitArgs init_args;
-  init_args.version = JNI_VERSION_1_4;
+  init_args.version = JNI_VERSION_1_6;
   init_args.options = options.get();
   init_args.nOptions = curr_opt;
   init_args.ignoreUnrecognized = JNI_FALSE;
@@ -212,18 +212,17 @@
     return EXIT_FAILURE;
   }
 
-  bool success = InvokeMain(vm, env, argc - arg_idx, &argv[arg_idx]);
+  bool success = InvokeMain(env, argc - arg_idx, &argv[arg_idx]);
 
-  if (vm != NULL && vm->DetachCurrentThread() != JNI_OK) {
+  if (vm->DetachCurrentThread() != JNI_OK) {
     fprintf(stderr, "Warning: unable to detach main thread\n");
     success = false;
   }
 
-  if (vm != NULL && vm->DestroyJavaVM() != 0) {
+  if (vm->DestroyJavaVM() != 0) {
     fprintf(stderr, "Warning: VM did not shut down cleanly\n");
     success = false;
   }
 
-  int retval = success ? EXIT_SUCCESS : EXIT_FAILURE;
-  return retval;
+  return success ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/src/runtime.cc b/src/runtime.cc
index c9a2794..05a89b2 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -146,6 +146,7 @@
   Thread* current_thread = Thread::Attach();
   thread_list_->Register(current_thread);
   class_linker_ = ClassLinker::Create(boot_class_path);
+  java_vm_.reset(CreateJavaVM(this));
   return true;
 }
 
diff --git a/src/runtime.h b/src/runtime.h
index f04aefe..56a8970 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -9,6 +9,7 @@
 #include "jni.h"
 #include "globals.h"
 #include "macros.h"
+#include "scoped_ptr.h"
 #include "stringpiece.h"
 
 namespace art {
@@ -53,6 +54,10 @@
     return class_linker_;
   }
 
+  JavaVM* GetJavaVM() {
+    return java_vm_.get();
+  }
+
   void SetVfprintfHook(void* hook);
 
   void SetExitHook(void* hook);
@@ -71,6 +76,8 @@
 
   ClassLinker* class_linker_;
 
+  scoped_ptr<JavaVM> java_vm_;
+
   // A pointer to the active runtime or NULL.
   static Runtime* instance_;
 
diff --git a/tools/art b/tools/art
index ab252eb..8033c51 100755
--- a/tools/art
+++ b/tools/art
@@ -18,9 +18,11 @@
 ANDROID_DATA=/tmp/android-data \
 ANDROID_ROOT=$ANDROID_BUILD_TOP/out/host/linux-x86 \
 LD_LIBRARY_PATH=$ANDROID_BUILD_TOP/out/host/linux-x86/lib \
-$ANDROID_BUILD_TOP/out/host/linux-x86/bin/aexecd \
+$ANDROID_BUILD_TOP/out/host/linux-x86/bin/aexec \
 -Xbootclasspath\
 :$ANDROID_BUILD_TOP/out/host/linux-x86/framework/core-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/core-junit-hostdex.jar\
+:$ANDROID_BUILD_TOP/out/host/linux-x86/framework/core-tests-hostdex.jar\
 :$ANDROID_BUILD_TOP/out/host/linux-x86/framework/bouncycastle-hostdex.jar\
 :$ANDROID_BUILD_TOP/out/host/linux-x86/framework/apache-xml-hostdex.jar \
 $*