blob: b0eb6596e915991273943bf0692b479b25f06c41 [file] [log] [blame]
Carl Shapiro6b6b5f02011-06-21 15:05:09 -07001// Copyright 2011 Google Inc. All Rights Reserved.
2
Carl Shapiro2ed144c2011-07-26 16:52:08 -07003#include <cstring>
4#include <cstdio>
5#include <signal.h>
6#include <string>
Carl Shapiro7b216702011-06-17 15:09:26 -07007
Carl Shapiro2ed144c2011-07-26 16:52:08 -07008#include "jni.h"
9#include "logging.h"
10#include "scoped_ptr.h"
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070011#include "ScopedLocalRef.h"
Carl Shapiro2ed144c2011-07-26 16:52:08 -070012
13// TODO: move this into the runtime.
14static void BlockSigpipe() {
15 sigset_t sigset;
16 if (sigemptyset(&sigset) == -1) {
17 PLOG(ERROR) << "sigemptyset failed";
18 return;
19 }
20 if (sigaddset(&sigset, SIGPIPE) == -1) {
21 PLOG(ERROR) << "sigaddset failed";
22 return;
23 }
24 if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1) {
25 PLOG(ERROR) << "sigprocmask failed";
26 }
27}
28
29// TODO: this code should be shared with other parts of the system
30// that create string arrays.
31//Create a String[] and populate it with the contents of argv.
32static jobjectArray CreateStringArray(JNIEnv* env, char** argv, int argc) {
33 // Find the String class.
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070034 ScopedLocalRef<jclass> klass(env, env->FindClass("java/lang/String"));
Carl Shapiro2ed144c2011-07-26 16:52:08 -070035 if (env->ExceptionCheck()) {
36 fprintf(stderr, "Got exception while finding class String\n");
37 return NULL;
38 }
39 DCHECK(klass.get() != NULL);
40
41 // Create an array of String elements.
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070042 jobjectArray args = env->NewObjectArray(argc, klass.get(), NULL);
Carl Shapiro2ed144c2011-07-26 16:52:08 -070043 if (env->ExceptionCheck()) {
44 fprintf(stderr, "Got exception while creating String array\n");
45 return NULL;
46 }
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070047 DCHECK(args != NULL);
Carl Shapiro2ed144c2011-07-26 16:52:08 -070048
49 // Allocate a string object for each argv element.
50 for (int i = 0; i < argc; ++i) {
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070051 ScopedLocalRef<jstring> elt(env, env->NewStringUTF(argv[i]));
Carl Shapiro2ed144c2011-07-26 16:52:08 -070052 if (env->ExceptionCheck()) {
53 fprintf(stderr, "Got exception while allocating Strings\n");
54 return NULL;
55 }
56 DCHECK(elt.get() != NULL);
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070057 env->SetObjectArrayElement(args, i, elt.get());
Carl Shapiro2ed144c2011-07-26 16:52:08 -070058 }
59
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070060 return args;
Carl Shapiro2ed144c2011-07-26 16:52:08 -070061}
62
63// Determine whether or not the specified method is public.
64//
65// Returns JNI_TRUE on success, JNI_FALSE on failure.
66static bool IsMethodPublic(JNIEnv* env, jclass clazz, jmethodID method_id) {
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070067 ScopedLocalRef<jobject> reflected(env, env->ToReflectedMethod(clazz,
68 method_id, JNI_FALSE));
Carl Shapiro2ed144c2011-07-26 16:52:08 -070069 if (reflected.get() == NULL) {
70 fprintf(stderr, "Unable to get reflected method\n");
71 return false;
72 }
73 // We now have a Method instance. We need to call its
74 // getModifiers() method.
Elliott Hughes0dab4ec2011-08-11 12:19:32 -070075 ScopedLocalRef<jclass> method(env,
76 env->FindClass("java/lang/reflect/Method"));
Carl Shapiro2ed144c2011-07-26 16:52:08 -070077 if (method.get() == NULL) {
78 fprintf(stderr, "Unable to find class Method\n");
79 return false;
80 }
81 jmethodID get_modifiers = env->GetMethodID(method.get(),
82 "getModifiers",
83 "()I");
84 if (get_modifiers == NULL) {
85 fprintf(stderr, "Unable to find reflect.Method.getModifiers\n");
86 return false;
87 }
88 static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC
89 int modifiers = env->CallIntMethod(method.get(), get_modifiers);
90 if ((modifiers & PUBLIC) == 0) {
91 return false;
92 }
93 return true;
94}
95
96static bool InvokeMain(JavaVM* vm, JNIEnv* env, int argc, char** argv) {
97 // We want to call main() with a String array with our arguments in
98 // it. Create an array and populate it. Note argv[0] is not
99 // included.
Elliott Hughes0dab4ec2011-08-11 12:19:32 -0700100 ScopedLocalRef<jobjectArray> args(env,
101 CreateStringArray(env, argv + 1, argc - 1));
Carl Shapiro2ed144c2011-07-26 16:52:08 -0700102 if (args.get() == NULL) {
103 return false;
104 }
105
106 // Find [class].main(String[]).
107
108 // Convert "com.android.Blah" to "com/android/Blah".
109 std::string class_name = argv[0];
110 std::replace(class_name.begin(), class_name.end(), '.', '/');
111
Elliott Hughes0dab4ec2011-08-11 12:19:32 -0700112 ScopedLocalRef<jclass> klass(env, env->FindClass(class_name.c_str()));
Carl Shapiro2ed144c2011-07-26 16:52:08 -0700113 if (klass.get() == NULL) {
114 fprintf(stderr, "Unable to locate class '%s'\n", class_name.c_str());
115 return false;
116 }
117
118 jmethodID method = env->GetStaticMethodID(klass.get(),
119 "main",
120 "([Ljava/lang/String;)V");
121 if (method == NULL) {
122 fprintf(stderr, "Unable to find static main(String[]) in '%s'\n",
123 class_name.c_str());
124 return false;
125 }
126
127 // Make sure the method is public. JNI doesn't prevent us from
128 // calling a private method, so we have to check it explicitly.
129 if (!IsMethodPublic(env, klass.get(), method)) {
130 fprintf(stderr, "Sorry, main() is not public\n");
131 return false;
132 }
133
134 // Invoke main().
135
136 env->CallStaticVoidMethod(klass.get(), method, args.get());
137 if (env->ExceptionCheck()) {
138 return false;
139 } else {
140 return true;
141 }
142}
143
144// Parse arguments. Most of it just gets passed through to the VM.
145// The JNI spec defines a handful of standard arguments.
Carl Shapiro7b216702011-06-17 15:09:26 -0700146int main(int argc, char** argv) {
Carl Shapiro2ed144c2011-07-26 16:52:08 -0700147 setvbuf(stdout, NULL, _IONBF, 0);
148
149 // Skip over argv[0].
150 argv++;
151 argc--;
152
153 // If we're adding any additional stuff, e.g. function hook specifiers,
154 // add them to the count here.
155 //
156 // We're over-allocating, because this includes the options to the VM
157 // plus the options to the program.
158 int option_count = argc;
159 scoped_array<JavaVMOption> options(new JavaVMOption[option_count]());
160
161 // Copy options over. Everything up to the name of the class starts
162 // with a '-' (the function hook stuff is strictly internal).
163 //
164 // [Do we need to catch & handle "-jar" here?]
165 bool need_extra = false;
166 int curr_opt, arg_idx;
167 for (curr_opt = arg_idx = 0; arg_idx < argc; arg_idx++) {
168 if (argv[arg_idx][0] != '-' && !need_extra) {
169 break;
170 }
171 options[curr_opt++].optionString = argv[arg_idx];
172
173 // Some options require an additional argument.
174 need_extra = false;
175 if (strcmp(argv[arg_idx], "-classpath") == 0 ||
176 strcmp(argv[arg_idx], "-cp") == 0) {
177 // others?
178 need_extra = true;
179 }
180 }
181
182 if (need_extra) {
183 fprintf(stderr, "VM requires value after last option flag\n");
184 return EXIT_FAILURE;
185 }
186
187 // Make sure they provided a class name. We do this after VM init
188 // so that things like "-Xrunjdwp:help" have the opportunity to emit
189 // a usage statement.
190 if (arg_idx == argc) {
191 fprintf(stderr, "Class name required\n");
192 return EXIT_FAILURE;
193 }
194
195 // insert additional internal options here
196
197 DCHECK_LE(curr_opt, option_count);
198
199 JavaVMInitArgs init_args;
200 init_args.version = JNI_VERSION_1_4;
201 init_args.options = options.get();
202 init_args.nOptions = curr_opt;
203 init_args.ignoreUnrecognized = JNI_FALSE;
204
205 BlockSigpipe();
206
207 // Start VM. The current thread becomes the main thread of the VM.
208 JavaVM* vm = NULL;
209 JNIEnv* env = NULL;
210 if (JNI_CreateJavaVM(&vm, &env, &init_args) != JNI_OK) {
211 fprintf(stderr, "VM init failed (check log file)\n");
212 return EXIT_FAILURE;
213 }
214
215 bool success = InvokeMain(vm, env, argc - arg_idx, &argv[arg_idx]);
216
217 if (vm != NULL && vm->DetachCurrentThread() != JNI_OK) {
218 fprintf(stderr, "Warning: unable to detach main thread\n");
219 success = false;
220 }
221
222 if (vm != NULL && vm->DestroyJavaVM() != 0) {
223 fprintf(stderr, "Warning: VM did not shut down cleanly\n");
224 success = false;
225 }
226
227 int retval = success ? EXIT_SUCCESS : EXIT_FAILURE;
228 return retval;
Carl Shapiro7b216702011-06-17 15:09:26 -0700229}