Preliminary implementation of the JNI invocation interface.
Change-Id: Ib144cb887864cd232a8cb8167b20d1540829a6a5
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 6ee3af3..78b6d33 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -20,14 +20,14 @@
namespace art {
-ClassLinker* ClassLinker::Create(std::vector<DexFile*> boot_class_path) {
+ClassLinker* ClassLinker::Create(const std::vector<DexFile*>& boot_class_path) {
scoped_ptr<ClassLinker> class_linker(new ClassLinker);
class_linker->Init(boot_class_path);
// TODO: check for failure during initialization
return class_linker.release();
}
-void ClassLinker::Init(std::vector<DexFile*> boot_class_path) {
+void ClassLinker::Init(const std::vector<DexFile*>& boot_class_path) {
// Allocate and partially initialize the Class, Object, Field, Method classes.
// Initialization will be completed when the definitions are loaded.
@@ -417,11 +417,13 @@
}
void ClassLinker::AppendToBootClassPath(DexFile* dex_file) {
+ CHECK(dex_file != NULL);
boot_class_path_.push_back(dex_file);
RegisterDexFile(dex_file);
}
void ClassLinker::RegisterDexFile(DexFile* dex_file) {
+ CHECK(dex_file != NULL);
dex_files_.push_back(dex_file);
DexCache* dex_cache = AllocDexCache();
CHECK(dex_cache != NULL);
diff --git a/src/class_linker.h b/src/class_linker.h
index 622eaea..a2f2770 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -18,7 +18,7 @@
class ClassLinker {
public:
// Initializes the class linker.
- static ClassLinker* Create(std::vector<DexFile*> boot_class_path);
+ static ClassLinker* Create(const std::vector<DexFile*>& boot_class_path);
~ClassLinker() {}
@@ -54,7 +54,7 @@
private:
ClassLinker() {}
- void Init(std::vector<DexFile*> boot_class_path_);
+ void Init(const std::vector<DexFile*>& boot_class_path_);
Class* CreatePrimitiveClass(const StringPiece& descriptor);
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 74f8961..6c6f493 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -1,7 +1,13 @@
// Copyright 2011 Google Inc. All Rights Reserved.
#include "jni_internal.h"
+
+#include <vector>
+#include <utility>
+
#include "logging.h"
+#include "runtime.h"
+#include "thread.h"
namespace art {
@@ -18,4 +24,120 @@
monitor_exit_ = &JniMonitorExit;
}
+extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, void** p_env, void* vm_args) {
+ const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
+ if (args->version < JNI_VERSION_1_2) {
+ return JNI_EVERSION;
+ }
+ Runtime::Options options;
+ for (int i = 0; i < args->nOptions; ++i) {
+ JavaVMOption* option = &args->options[i];
+ options.push_back(std::make_pair(option->optionString, option->extraInfo));
+ }
+ bool ignore_unrecognized = args->ignoreUnrecognized;
+ scoped_ptr<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());
+ return JNI_OK;
+ }
+}
+
+extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen,
+ jsize* nVMs) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime == NULL) {
+ *nVMs = 0;
+ } else {
+ *nVMs = 1;
+ vmBuf[0] = reinterpret_cast<JavaVM*>(runtime);
+ }
+ return JNI_OK;
+}
+
+// Historically unsupported.
+extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
+ return JNI_ERR;
+}
+
+jint JniInvoke::DestroyJavaVM(JavaVM* vm) {
+ if (vm == NULL) {
+ return JNI_ERR;
+ } else {
+ Runtime* runtime = reinterpret_cast<Runtime*>(vm);
+ delete runtime;
+ return JNI_OK;
+ }
+}
+
+jint JniInvoke::AttachCurrentThread(JavaVM* vm, JNIEnv** p_env,
+ void* thr_args) {
+ if (vm == NULL || p_env == NULL) {
+ return JNI_ERR;
+ }
+ Runtime* runtime = reinterpret_cast<Runtime*>(vm);
+ JniEnvironment** jni_env = reinterpret_cast<JniEnvironment**>(p_env);
+ const char* name = NULL;
+ if (thr_args != NULL) {
+ // TODO: check version
+ name = static_cast<JavaVMAttachArgs*>(thr_args)->name;
+ // TODO: thread group
+ }
+ bool success = runtime->AttachCurrentThread(name, jni_env);
+ if (!success) {
+ return JNI_ERR;
+ } else {
+ return JNI_OK;
+ }
+}
+
+jint JniInvoke::DetachCurrentThread(JavaVM* vm) {
+ if (vm == NULL) {
+ return JNI_ERR;
+ } else {
+ Runtime* runtime = reinterpret_cast<Runtime*>(vm);
+ runtime->DetachCurrentThread();
+ return JNI_OK;
+ }
+}
+
+jint JniInvoke::GetEnv(JavaVM *vm, void **env, jint version) {
+ if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6) {
+ return JNI_EVERSION;
+ }
+ if (vm == NULL || env == NULL) {
+ return JNI_ERR;
+ }
+ Thread* thread = Thread::Current();
+ if (thread == NULL) {
+ *env = NULL;
+ return JNI_EDETACHED;
+ }
+ *env = thread->GetJniEnv();
+ return JNI_OK;
+}
+
+jint JniInvoke::AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
+ void* thr_args) {
+ if (vm == NULL || p_env == NULL) {
+ return JNI_ERR;
+ }
+ Runtime* runtime = reinterpret_cast<Runtime*>(vm);
+ JniEnvironment** jni_env = reinterpret_cast<JniEnvironment**>(p_env);
+ const char* name = NULL;
+ if (thr_args != NULL) {
+ // TODO: check version
+ name = static_cast<JavaVMAttachArgs*>(thr_args)->name;
+ // TODO: thread group
+ }
+ bool success = runtime->AttachCurrentThreadAsDaemon(name, jni_env);
+ if (!success) {
+ return JNI_ERR;
+ } else {
+ return JNI_OK;
+ }
+}
+
} // namespace art
diff --git a/src/jni_internal.h b/src/jni_internal.h
index e1474ab..16bf96e 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -27,6 +27,29 @@
private:
void (*monitor_enter_)(JniEnvironment*, jobject);
void (*monitor_exit_)(JniEnvironment*, jobject);
+
+ DISALLOW_COPY_AND_ASSIGN(JniEnvironment);
+};
+
+class JniInvoke {
+ public:
+ // Index 3
+ int DestroyJavaVM(JavaVM* vm);
+
+ // Index 4
+ int AttachCurrentThread(JavaVM* vm, JNIEnv** penv, void* thr_args);
+
+ // Index 5
+ int DetachCurrentThread(JavaVM* vm);
+
+ // Index 6
+ int GetEnv(JavaVM* vm, void** penv, int version);
+
+ // Index 7
+ int AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** penv, void* thr_args);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JniInvoke);
};
} // namespace art
diff --git a/src/main.cc b/src/main.cc
index 6f277d1..4afcc29 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -1,8 +1,261 @@
// Copyright 2011 Google Inc. All Rights Reserved.
-#include <iostream>
+#include <cstring>
+#include <cstdio>
+#include <signal.h>
+#include <string>
+#include "jni.h"
+#include "logging.h"
+#include "scoped_ptr.h"
+
+// TODO: move this into a publicly accessible location
+template<typename T>
+class scoped_local_ref {
+ public:
+ scoped_local_ref(JNIEnv* env, T ref) : env_(env), ref_(ref) {}
+ ~scoped_local_ref() { reset(); }
+
+ T get() const { return ref_; }
+
+ void reset() {
+ if (ref_ != NULL) {
+ env_->DeleteLocalRef(ref_);
+ ref_ = NULL;
+ }
+ }
+
+ T release() {
+ T ref = ref_;
+ ref_ = NULL;
+ return ref;
+ }
+
+ private:
+ JNIEnv* env_;
+ T ref_;
+ DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
+ };
+
+// TODO: move this into the runtime.
+static void BlockSigpipe() {
+ sigset_t sigset;
+ if (sigemptyset(&sigset) == -1) {
+ PLOG(ERROR) << "sigemptyset failed";
+ return;
+ }
+ if (sigaddset(&sigset, SIGPIPE) == -1) {
+ PLOG(ERROR) << "sigaddset failed";
+ return;
+ }
+ if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) {
+ PLOG(ERROR) << "sigprocmask failed";
+ }
+}
+
+// TODO: this code should be shared with other parts of the system
+// that create string arrays.
+//Create a String[] and populate it with the contents of argv.
+static jobjectArray CreateStringArray(JNIEnv* env, char** argv, int argc) {
+ // Find the String class.
+ scoped_local_ref<jclass> klass(env, env->FindClass("java/lang/String"));
+ if (env->ExceptionCheck()) {
+ fprintf(stderr, "Got exception while finding class String\n");
+ return NULL;
+ }
+ DCHECK(klass.get() != NULL);
+
+ // Create an array of String elements.
+ scoped_local_ref<jobjectArray> args(env, env->NewObjectArray(argc,
+ klass.get(),
+ NULL));
+ if (env->ExceptionCheck()) {
+ fprintf(stderr, "Got exception while creating String array\n");
+ return NULL;
+ }
+ DCHECK(args.get() != NULL);
+
+ // Allocate a string object for each argv element.
+ for (int i = 0; i < argc; ++i) {
+ scoped_local_ref<jstring> elt(env, env->NewStringUTF(argv[i]));
+ if (env->ExceptionCheck()) {
+ fprintf(stderr, "Got exception while allocating Strings\n");
+ return NULL;
+ }
+ DCHECK(elt.get() != NULL);
+ env->SetObjectArrayElement(args.get(), i, elt.get());
+ }
+
+ // Return a local reference to the newly created array.
+ return args.release();
+}
+
+// Determine whether or not the specified method is public.
+//
+// Returns JNI_TRUE on success, JNI_FALSE on failure.
+static bool IsMethodPublic(JNIEnv* env, jclass clazz, jmethodID method_id) {
+ scoped_local_ref<jobject> reflected(env, env->ToReflectedMethod(clazz,
+ method_id,
+ JNI_FALSE));
+ if (reflected.get() == NULL) {
+ fprintf(stderr, "Unable to get reflected method\n");
+ return false;
+ }
+ // We now have a Method instance. We need to call its
+ // getModifiers() method.
+ scoped_local_ref<jclass> method(env,
+ env->FindClass("java/lang/reflect/Method"));
+ if (method.get() == NULL) {
+ fprintf(stderr, "Unable to find class Method\n");
+ return false;
+ }
+ jmethodID get_modifiers = env->GetMethodID(method.get(),
+ "getModifiers",
+ "()I");
+ if (get_modifiers == NULL) {
+ fprintf(stderr, "Unable to find reflect.Method.getModifiers\n");
+ return false;
+ }
+ static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC
+ int modifiers = env->CallIntMethod(method.get(), get_modifiers);
+ if ((modifiers & PUBLIC) == 0) {
+ return false;
+ }
+ return true;
+}
+
+static bool InvokeMain(JavaVM* vm, 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.
+ scoped_local_ref<jobjectArray> args(env, CreateStringArray(env,
+ argv + 1,
+ argc - 1));
+ if (args.get() == NULL) {
+ return false;
+ }
+
+ // Find [class].main(String[]).
+
+ // Convert "com.android.Blah" to "com/android/Blah".
+ std::string class_name = argv[0];
+ std::replace(class_name.begin(), class_name.end(), '.', '/');
+
+ scoped_local_ref<jclass> klass(env, env->FindClass(class_name.c_str()));
+ if (klass.get() == NULL) {
+ fprintf(stderr, "Unable to locate class '%s'\n", class_name.c_str());
+ return false;
+ }
+
+ jmethodID method = env->GetStaticMethodID(klass.get(),
+ "main",
+ "([Ljava/lang/String;)V");
+ if (method == NULL) {
+ fprintf(stderr, "Unable to find static main(String[]) in '%s'\n",
+ class_name.c_str());
+ return false;
+ }
+
+ // Make sure the method is public. JNI doesn't prevent us from
+ // calling a private method, so we have to check it explicitly.
+ if (!IsMethodPublic(env, klass.get(), method)) {
+ fprintf(stderr, "Sorry, main() is not public\n");
+ return false;
+ }
+
+ // Invoke main().
+
+ env->CallStaticVoidMethod(klass.get(), method, args.get());
+ if (env->ExceptionCheck()) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+// Parse arguments. Most of it just gets passed through to the VM.
+// The JNI spec defines a handful of standard arguments.
int main(int argc, char** argv) {
- std::cout << "hello, world" << std::endl;
- return 0;
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ // Skip over argv[0].
+ argv++;
+ argc--;
+
+ // If we're adding any additional stuff, e.g. function hook specifiers,
+ // add them to the count here.
+ //
+ // We're over-allocating, because this includes the options to the VM
+ // plus the options to the program.
+ int option_count = argc;
+ scoped_array<JavaVMOption> options(new JavaVMOption[option_count]());
+
+ // Copy options over. Everything up to the name of the class starts
+ // with a '-' (the function hook stuff is strictly internal).
+ //
+ // [Do we need to catch & handle "-jar" here?]
+ bool need_extra = false;
+ int curr_opt, arg_idx;
+ for (curr_opt = arg_idx = 0; arg_idx < argc; arg_idx++) {
+ if (argv[arg_idx][0] != '-' && !need_extra) {
+ break;
+ }
+ options[curr_opt++].optionString = argv[arg_idx];
+
+ // Some options require an additional argument.
+ need_extra = false;
+ if (strcmp(argv[arg_idx], "-classpath") == 0 ||
+ strcmp(argv[arg_idx], "-cp") == 0) {
+ // others?
+ need_extra = true;
+ }
+ }
+
+ if (need_extra) {
+ fprintf(stderr, "VM requires value after last option flag\n");
+ return EXIT_FAILURE;
+ }
+
+ // Make sure they provided a class name. We do this after VM init
+ // so that things like "-Xrunjdwp:help" have the opportunity to emit
+ // a usage statement.
+ if (arg_idx == argc) {
+ fprintf(stderr, "Class name required\n");
+ return EXIT_FAILURE;
+ }
+
+ // insert additional internal options here
+
+ DCHECK_LE(curr_opt, option_count);
+
+ JavaVMInitArgs init_args;
+ init_args.version = JNI_VERSION_1_4;
+ init_args.options = options.get();
+ init_args.nOptions = curr_opt;
+ init_args.ignoreUnrecognized = JNI_FALSE;
+
+ BlockSigpipe();
+
+ // Start VM. The current thread becomes the main thread of the VM.
+ JavaVM* vm = NULL;
+ JNIEnv* env = NULL;
+ if (JNI_CreateJavaVM(&vm, &env, &init_args) != JNI_OK) {
+ fprintf(stderr, "VM init failed (check log file)\n");
+ return EXIT_FAILURE;
+ }
+
+ bool success = InvokeMain(vm, env, argc - arg_idx, &argv[arg_idx]);
+
+ if (vm != NULL && vm->DetachCurrentThread() != JNI_OK) {
+ fprintf(stderr, "Warning: unable to detach main thread\n");
+ success = false;
+ }
+
+ if (vm != NULL && vm->DestroyJavaVM() != 0) {
+ fprintf(stderr, "Warning: VM did not shut down cleanly\n");
+ success = false;
+ }
+
+ int retval = success ? EXIT_SUCCESS : EXIT_FAILURE;
+ return retval;
}
diff --git a/src/runtime.cc b/src/runtime.cc
index eadc50e..eee84cd 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -4,6 +4,7 @@
#include <cstdio>
#include <cstdlib>
+#include <vector>
#include "class_linker.h"
#include "heap.h"
@@ -11,6 +12,8 @@
namespace art {
+Runtime* Runtime::instance_ = NULL;
+
Runtime::~Runtime() {
// TODO: use a smart pointer instead.
delete class_linker_;
@@ -44,17 +47,27 @@
// notreached
}
-Runtime* Runtime::Create(std::vector<DexFile*> boot_class_path) {
+Runtime* Runtime::Create(const Options& options, bool ignore_unrecognized) {
+ // TODO: parse arguments
+ std::vector<DexFile*> boot_class_path; // empty
+ return Runtime::Create(boot_class_path);
+}
+
+Runtime* Runtime::Create(const std::vector<DexFile*>& boot_class_path) {
+ // TODO: acquire a static mutex on Runtime to avoid racing.
+ if (Runtime::instance_ != NULL) {
+ return NULL;
+ }
scoped_ptr<Runtime> runtime(new Runtime());
bool success = runtime->Init(boot_class_path);
if (!success) {
return NULL;
} else {
- return runtime.release();
+ return Runtime::instance_ = runtime.release();
}
}
-bool Runtime::Init(std::vector<DexFile*> boot_class_path) {
+bool Runtime::Init(const std::vector<DexFile*>& boot_class_path) {
thread_list_ = ThreadList::Create();
Heap::Init(Heap::kStartupSize, Heap::kMaximumSize);
Thread::Init();
@@ -64,7 +77,13 @@
return true;
}
-bool Runtime::AttachCurrentThread() {
+bool Runtime::AttachCurrentThread(const char* name, JniEnvironment** penv) {
+ return Thread::Attach() != NULL;
+}
+
+bool Runtime::AttachCurrentThreadAsDaemon(const char* name,
+ JniEnvironment** penv) {
+ // TODO: do something different for daemon threads.
return Thread::Attach() != NULL;
}
diff --git a/src/runtime.h b/src/runtime.h
index b57f1cf..9ff532c 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -14,12 +14,20 @@
class ClassLinker;
class Heap;
+class JniEnvironment;
class ThreadList;
class Runtime {
public:
+ typedef std::vector<std::pair<const char*, void*> > Options;
+
// Creates and initializes a new runtime.
- static Runtime* Create(std::vector<DexFile*> boot_class_path);
+ static Runtime* Create(const Options& options, bool ignore_unrecognized);
+ static Runtime* Create(const std::vector<DexFile*>& boot_class_path);
+
+ static Runtime* Current() {
+ return instance_;
+ }
// Compiles a dex file.
static void Compile(const StringPiece& filename);
@@ -32,7 +40,8 @@
static void Abort(const char* file, int line);
// Attaches the current native thread to the runtime.
- bool AttachCurrentThread();
+ bool AttachCurrentThread(const char* name, JniEnvironment** jni_env);
+ bool AttachCurrentThreadAsDaemon(const char* name, JniEnvironment** jni_env);
// Detaches the current native thread from the runtime.
bool DetachCurrentThread();
@@ -43,18 +52,27 @@
return class_linker_;
}
+ void SetVfprintfHook(void* hook);
+
+ void SetExitHook(void* hook);
+
+ void SetAbortHook(void* hook);
+
private:
static void PlatformAbort(const char*, int);
Runtime() : class_linker_(NULL), thread_list_(NULL) {}
// Initializes a new uninitialized runtime.
- bool Init(std::vector<DexFile*> boot_class_path);
+ bool Init(const std::vector<DexFile*>& boot_class_path);
ClassLinker* class_linker_;
ThreadList* thread_list_;
+ // A pointer to the active runtime or NULL.
+ static Runtime* instance_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/src/runtime_linux.cc b/src/runtime_linux.cc
index e150d8e..4aa90f0 100644
--- a/src/runtime_linux.cc
+++ b/src/runtime_linux.cc
@@ -22,7 +22,7 @@
// Turn them into something human-readable with symbols.
// TODO: in practice, we may find that we should use backtrace_symbols_fd
// to avoid allocation, rather than use our own custom formatting.
- art::scoped_ptr_malloc<char*> symbols(backtrace_symbols(frames, frame_count));
+ scoped_ptr_malloc<char*> symbols(backtrace_symbols(frames, frame_count));
if (symbols == NULL) {
PLOG(ERROR) << "backtrace_symbols failed";
return;
diff --git a/src/scoped_ptr.h b/src/scoped_ptr.h
index a3b30d5..c76a64e 100644
--- a/src/scoped_ptr.h
+++ b/src/scoped_ptr.h
@@ -26,8 +26,6 @@
#include <algorithm>
#include <cstddef>
-namespace art {
-
template <class C> class scoped_ptr;
template <class C, class Free> class scoped_ptr_malloc;
template <class C> class scoped_array;
@@ -375,6 +373,5 @@
bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
return p != b.get();
}
-} // namespace art
#endif // ART_SRC_SCOPED_PTR_H_