diff options
Diffstat (limited to 'dalvikvm/Main.cpp')
-rw-r--r-- | dalvikvm/Main.cpp | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/dalvikvm/Main.cpp b/dalvikvm/Main.cpp new file mode 100644 index 0000000..4aa6e20 --- /dev/null +++ b/dalvikvm/Main.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Command-line invocation of the Dalvik VM. + */ +#include "jni.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <assert.h> + + +/* + * We want failed write() calls to just return with an error. + */ +static void blockSigpipe() +{ + sigset_t mask; + + sigemptyset(&mask); + sigaddset(&mask, SIGPIPE); + if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0) + fprintf(stderr, "WARNING: SIGPIPE not blocked\n"); +} + +/* + * Create a String[] and populate it with the contents of argv. + */ +static jobjectArray createStringArray(JNIEnv* env, char* const argv[], int argc) +{ + jclass stringClass = NULL; + jobjectArray strArray = NULL; + jobjectArray result = NULL; + int i; + + stringClass = env->FindClass("java/lang/String"); + if (env->ExceptionCheck()) { + fprintf(stderr, "Got exception while finding class String\n"); + goto bail; + } + assert(stringClass != NULL); + strArray = env->NewObjectArray(argc, stringClass, NULL); + if (env->ExceptionCheck()) { + fprintf(stderr, "Got exception while creating String array\n"); + goto bail; + } + assert(strArray != NULL); + + for (i = 0; i < argc; i++) { + jstring argStr; + + argStr = env->NewStringUTF(argv[i]); + if (env->ExceptionCheck()) { + fprintf(stderr, "Got exception while allocating Strings\n"); + goto bail; + } + assert(argStr != NULL); + env->SetObjectArrayElement(strArray, i, argStr); + env->DeleteLocalRef(argStr); + } + + /* return the array, and ensure we don't delete the local ref to it */ + result = strArray; + strArray = NULL; + +bail: + env->DeleteLocalRef(stringClass); + env->DeleteLocalRef(strArray); + return result; +} + +/* + * Determine whether or not the specified method is public. + * + * Returns JNI_TRUE on success, JNI_FALSE on failure. + */ +static int methodIsPublic(JNIEnv* env, jclass clazz, jmethodID methodId) +{ + static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC + jobject refMethod = NULL; + jclass methodClass = NULL; + jmethodID getModifiersId; + int modifiers; + int result = JNI_FALSE; + + refMethod = env->ToReflectedMethod(clazz, methodId, JNI_FALSE); + if (refMethod == NULL) { + fprintf(stderr, "Dalvik VM unable to get reflected method\n"); + goto bail; + } + + /* + * We now have a Method instance. We need to call + * its getModifiers() method. + */ + methodClass = env->FindClass("java/lang/reflect/Method"); + if (methodClass == NULL) { + fprintf(stderr, "Dalvik VM unable to find class Method\n"); + goto bail; + } + getModifiersId = env->GetMethodID(methodClass, + "getModifiers", "()I"); + if (getModifiersId == NULL) { + fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n"); + goto bail; + } + + modifiers = env->CallIntMethod(refMethod, getModifiersId); + if ((modifiers & PUBLIC) == 0) { + fprintf(stderr, "Dalvik VM: main() is not public\n"); + goto bail; + } + + result = JNI_TRUE; + +bail: + env->DeleteLocalRef(refMethod); + env->DeleteLocalRef(methodClass); + return result; +} + +/* + * 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* const argv[]) +{ + JavaVM* vm = NULL; + JNIEnv* env = NULL; + JavaVMInitArgs initArgs; + JavaVMOption* options = NULL; + char* slashClass = NULL; + int optionCount, curOpt, i, argIdx; + int needExtra = JNI_FALSE; + int result = 1; + + setvbuf(stdout, NULL, _IONBF, 0); + + /* ignore 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. + */ + optionCount = argc; + + options = (JavaVMOption*) malloc(sizeof(JavaVMOption) * optionCount); + memset(options, 0, sizeof(JavaVMOption) * optionCount); + + /* + * 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?] + */ + for (curOpt = argIdx = 0; argIdx < argc; argIdx++) { + if (argv[argIdx][0] != '-' && !needExtra) + break; + options[curOpt++].optionString = strdup(argv[argIdx]); + + /* some options require an additional arg */ + needExtra = JNI_FALSE; + if (strcmp(argv[argIdx], "-classpath") == 0 || + strcmp(argv[argIdx], "-cp") == 0) + /* others? */ + { + needExtra = JNI_TRUE; + } + } + + if (needExtra) { + fprintf(stderr, "Dalvik VM requires value after last option flag\n"); + goto bail; + } + + /* insert additional internal options here */ + + assert(curOpt <= optionCount); + + initArgs.version = JNI_VERSION_1_4; + initArgs.options = options; + initArgs.nOptions = curOpt; + initArgs.ignoreUnrecognized = JNI_FALSE; + + //printf("nOptions = %d\n", initArgs.nOptions); + + blockSigpipe(); + + /* + * Start VM. The current thread becomes the main thread of the VM. + */ + if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) { + fprintf(stderr, "Dalvik VM init failed (check log file)\n"); + goto bail; + } + + /* + * 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 (argIdx == argc) { + fprintf(stderr, "Dalvik VM requires a class name\n"); + goto bail; + } + + /* + * 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. + */ + jobjectArray strArray; + strArray = createStringArray(env, &argv[argIdx+1], argc-argIdx-1); + if (strArray == NULL) + goto bail; + + /* + * Find [class].main(String[]). + */ + jclass startClass; + jmethodID startMeth; + char* cp; + + /* convert "com.android.Blah" to "com/android/Blah" */ + slashClass = strdup(argv[argIdx]); + for (cp = slashClass; *cp != '\0'; cp++) + if (*cp == '.') + *cp = '/'; + + startClass = env->FindClass(slashClass); + if (startClass == NULL) { + fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass); + goto bail; + } + + startMeth = env->GetStaticMethodID(startClass, + "main", "([Ljava/lang/String;)V"); + if (startMeth == NULL) { + fprintf(stderr, "Dalvik VM unable to find static main(String[]) in '%s'\n", + slashClass); + goto bail; + } + + /* + * Make sure the method is public. JNI doesn't prevent us from calling + * a private method, so we have to check it explicitly. + */ + if (!methodIsPublic(env, startClass, startMeth)) + goto bail; + + /* + * Invoke main(). + */ + env->CallStaticVoidMethod(startClass, startMeth, strArray); + + if (!env->ExceptionCheck()) + result = 0; + +bail: + /*printf("Shutting down Dalvik VM\n");*/ + if (vm != NULL) { + /* + * This allows join() and isAlive() on the main thread to work + * correctly, and also provides uncaught exception handling. + */ + if (vm->DetachCurrentThread() != JNI_OK) { + fprintf(stderr, "Warning: unable to detach main thread\n"); + result = 1; + } + + if (vm->DestroyJavaVM() != 0) + fprintf(stderr, "Warning: Dalvik VM did not shut down cleanly\n"); + /*printf("\nDalvik VM has exited\n");*/ + } + + for (i = 0; i < optionCount; i++) + free((char*) options[i].optionString); + free(options); + free(slashClass); + /*printf("--- VM is down, process exiting\n");*/ + return result; +} |