diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:03:55 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:03:55 -0800 |
| commit | 89c1feb0a69a7707b271086e749975b3f7acacf7 (patch) | |
| tree | 003624a03635e05020a47fc72a2c42934e3f0703 /vm/native | |
| parent | 2ad60cfc28e14ee8f0bb038720836a4696c478ad (diff) | |
| download | android_dalvik-89c1feb0a69a7707b271086e749975b3f7acacf7.tar.gz android_dalvik-89c1feb0a69a7707b271086e749975b3f7acacf7.tar.bz2 android_dalvik-89c1feb0a69a7707b271086e749975b3f7acacf7.zip | |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'vm/native')
30 files changed, 6247 insertions, 0 deletions
diff --git a/vm/native/InternalNative.c b/vm/native/InternalNative.c new file mode 100644 index 000000000..9e90a87c4 --- /dev/null +++ b/vm/native/InternalNative.c @@ -0,0 +1,356 @@ +/* + * 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. + */ + +/* + * Internal-native initialization and some common utility functions. + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + +/* + * Set of classes for which we provide methods. + * + * The last field, classNameHash, is filled in at startup. + */ +static DalvikNativeClass gDvmNativeMethodSet[] = { + { "Ljava/lang/Object;", dvm_java_lang_Object, 0 }, + { "Ljava/lang/Class;", dvm_java_lang_Class, 0 }, + { "Ljava/lang/Runtime;", dvm_java_lang_Runtime, 0 }, + { "Ljava/lang/String;", dvm_java_lang_String, 0 }, + { "Ljava/lang/System;", dvm_java_lang_System, 0 }, + { "Ljava/lang/SystemProperties;", dvm_java_lang_SystemProperties, 0 }, + { "Ljava/lang/Throwable;", dvm_java_lang_Throwable, 0 }, + { "Ljava/lang/VMClassLoader;", dvm_java_lang_VMClassLoader, 0 }, + { "Ljava/lang/VMThread;", dvm_java_lang_VMThread, 0 }, + { "Ljava/lang/reflect/AccessibleObject;", + dvm_java_lang_reflect_AccessibleObject, 0 }, + { "Ljava/lang/reflect/Array;", dvm_java_lang_reflect_Array, 0 }, + { "Ljava/lang/reflect/Constructor;", + dvm_java_lang_reflect_Constructor, 0 }, + { "Ljava/lang/reflect/Field;", dvm_java_lang_reflect_Field, 0 }, + { "Ljava/lang/reflect/Method;", dvm_java_lang_reflect_Method, 0 }, + { "Ljava/lang/reflect/Proxy;", dvm_java_lang_reflect_Proxy, 0 }, + { "Ljava/security/AccessController;", + dvm_java_security_AccessController, 0 }, + { "Ljava/util/concurrent/atomic/AtomicLong;", + dvm_java_util_concurrent_atomic_AtomicLong, 0 }, + { "Ldalvik/system/VMDebug;", dvm_dalvik_system_VMDebug, 0 }, + { "Ldalvik/system/DexFile;", dvm_dalvik_system_DexFile, 0 }, + { "Ldalvik/system/VMRuntime;", dvm_dalvik_system_VMRuntime, 0 }, + { "Ldalvik/system/Zygote;", dvm_dalvik_system_Zygote, 0 }, + { "Ldalvik/system/VMStack;", dvm_dalvik_system_VMStack, 0 }, + { "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", + dvm_org_apache_harmony_dalvik_ddmc_DdmServer, 0 }, + { "Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;", + dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal, 0 }, + { "Lorg/apache/harmony/dalvik/NativeTestTarget;", + dvm_org_apache_harmony_dalvik_NativeTestTarget, 0 }, + { "Lsun/misc/Unsafe;", dvm_sun_misc_Unsafe, 0 }, + { NULL, NULL, 0 }, +}; + + +/* + * Set up hash values on the class names. + */ +bool dvmInternalNativeStartup(void) +{ + DalvikNativeClass* classPtr = gDvmNativeMethodSet; + + while (classPtr->classDescriptor != NULL) { + classPtr->classDescriptorHash = + dvmComputeUtf8Hash(classPtr->classDescriptor); + classPtr++; + } + + gDvm.userDexFiles = dvmHashTableCreate(2, dvmFreeDexOrJar); + if (gDvm.userDexFiles == NULL) + return false; + + return true; +} + +/* + * Clean up. + */ +void dvmInternalNativeShutdown(void) +{ + dvmHashTableFree(gDvm.userDexFiles); +} + +/* + * Search the internal native set for a match. + */ +DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method) +{ + const char* classDescriptor = method->clazz->descriptor; + const DalvikNativeClass* pClass; + u4 hash; + + hash = dvmComputeUtf8Hash(classDescriptor); + pClass = gDvmNativeMethodSet; + while (true) { + if (pClass->classDescriptor == NULL) + break; + if (pClass->classDescriptorHash == hash && + strcmp(pClass->classDescriptor, classDescriptor) == 0) + { + const DalvikNativeMethod* pMeth = pClass->methodInfo; + while (true) { + if (pMeth->name == NULL) + break; + + if (dvmCompareNameDescriptorAndMethod(pMeth->name, + pMeth->signature, method) == 0) + { + /* match */ + //LOGV("+++ match on %s.%s %s at %p\n", + // className, methodName, methodSignature, pMeth->fnPtr); + return pMeth->fnPtr; + } + + pMeth++; + } + } + + pClass++; + } + + return NULL; +} + + +/* + * Magic "internal native" code stub, inserted into abstract method + * definitions when a class is first loaded. This throws the expected + * exception so we don't have to explicitly check for it in the interpreter. + */ +void dvmAbstractMethodStub(const u4* args, JValue* pResult) +{ + LOGD("--- called into dvmAbstractMethodStub\n"); + dvmThrowException("Ljava/lang/AbstractMethodError;", + "abstract method not implemented"); +} + + +/* + * Verify that "obj" is non-null and is an instance of "clazz". + * + * Returns "false" and throws an exception if not. + */ +bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz) +{ + if (obj == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + return false; + } + if (!dvmInstanceof(obj->clazz, clazz)) { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "object is not an instance of the class"); + return false; + } + + return true; +} + +/* + * Validate a "fully qualified" class name, e.g. "Ljava/lang/String;" or "[I". + */ +static bool validateClassName(const char* name) +{ + int len = strlen(name); + int i = 0; + + /* check for reasonable array types */ + if (name[0] == '[') { + while (name[i] == '[') + i++; + + if (name[i] == 'L') { + /* array of objects, make sure it ends well */ + if (name[len-1] != ';') + return false; + } else if (strchr(PRIM_TYPE_TO_LETTER, name[i]) != NULL) { + if (i != len-1) + return false; + } else { + return false; + } + } + + /* quick check for illegal chars */ + for ( ; i < len; i++) { + if (name[i] == '/') + return false; + } + + return true; +} + +/* + * Find a class by name, initializing it if requested. + */ +ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader, + bool doInit) +{ + ClassObject* clazz = NULL; + char* name = NULL; + char* descriptor = NULL; + + if (nameObj == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + goto bail; + } + name = dvmCreateCstrFromString(nameObj); + + /* + * We need to validate and convert the name (from x.y.z to x/y/z). This + * is especially handy for array types, since we want to avoid + * auto-generating bogus array classes. + */ + if (!validateClassName(name)) { + LOGW("dvmFindClassByName rejecting '%s'\n", name); + dvmThrowException("Ljava/lang/ClassNotFoundException;", name); + goto bail; + } + + descriptor = dvmDotToDescriptor(name); + if (descriptor == NULL) { + goto bail; + } + + if (doInit) + clazz = dvmFindClass(descriptor, loader); + else + clazz = dvmFindClassNoInit(descriptor, loader); + + if (clazz == NULL) { + LOGVV("FAIL: load %s (%d)\n", descriptor, doInit); + Thread* self = dvmThreadSelf(); + Object* oldExcep = dvmGetException(self); + dvmAddTrackedAlloc(oldExcep, self); /* don't let this be GCed */ + dvmClearException(self); + dvmThrowChainedException("Ljava/lang/ClassNotFoundException;", + name, oldExcep); + dvmReleaseTrackedAlloc(oldExcep, self); + } else { + LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n", + descriptor, doInit, clazz, clazz->classLoader); + } + +bail: + free(name); + free(descriptor); + return clazz; +} + +/* + * We insert native method stubs for abstract methods so we don't have to + * check the access flags at the time of the method call. This results in + * "native abstract" methods, which can't exist. If we see the "abstract" + * flag set, clear the "native" flag. + * + * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED + * position, because the callers of this function are trying to convey + * the "traditional" meaning of the flags to their callers. + */ +u4 dvmFixMethodFlags(u4 flags) +{ + if ((flags & ACC_ABSTRACT) != 0) { + flags &= ~ACC_NATIVE; + } + + flags &= ~ACC_SYNCHRONIZED; + + if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) { + flags |= ACC_SYNCHRONIZED; + } + + return flags & JAVA_FLAGS_MASK; +} + + +/* + * Return the hash code for the specified object. + */ +u4 dvmGetObjectHashCode(Object* obj) +{ + return (u4) obj; +} + + +#define NUM_DOPRIV_FUNCS 4 + +/* + * Determine if "method" is a "privileged" invocation, i.e. is it one + * of the variations of AccessController.doPrivileged(). + * + * Because the security stuff pulls in a pile of stuff that we may not + * want or need, we don't do the class/method lookups at init time, but + * instead on first use. + */ +bool dvmIsPrivilegedMethod(const Method* method) +{ + int i; + + assert(method != NULL); + + if (!gDvm.javaSecurityAccessControllerReady) { + /* + * Populate on first use. No concurrency risk since we're just + * finding pointers to fixed structures. + */ + static const char* kSignatures[NUM_DOPRIV_FUNCS] = { + "(Ljava/security/PrivilegedAction;)Ljava/lang/Object;", + "(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;", + "(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;", + "(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;", + }; + ClassObject* clazz; + + clazz = dvmFindClassNoInit("Ljava/security/AccessController;", NULL); + if (clazz == NULL) { + LOGW("Couldn't find java/security/AccessController\n"); + return false; + } + + assert(NELEM(gDvm.methJavaSecurityAccessController_doPrivileged) == + NELEM(kSignatures)); + + /* verify init */ + for (i = 0; i < NUM_DOPRIV_FUNCS; i++) { + gDvm.methJavaSecurityAccessController_doPrivileged[i] = + dvmFindDirectMethodByDescriptor(clazz, "doPrivileged", kSignatures[i]); + if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == NULL) { + LOGW("Warning: couldn't find java/security/AccessController" + ".doPrivileged %s\n", kSignatures[i]); + return false; + } + } + + /* all good, raise volatile readiness flag */ + gDvm.javaSecurityAccessControllerReady = true; + } + + for (i = 0; i < NUM_DOPRIV_FUNCS; i++) { + if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == method) { + //LOGI("+++ doPriv match\n"); + return true; + } + } + return false; +} + diff --git a/vm/native/InternalNative.h b/vm/native/InternalNative.h new file mode 100644 index 000000000..7c82dc0cb --- /dev/null +++ b/vm/native/InternalNative.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef _DALVIK_NATIVE_INTERNALNATIVE +#define _DALVIK_NATIVE_INTERNALNATIVE + +/* + * Some setup for internal native functions. + */ +bool dvmInternalNativeStartup(void); +void dvmInternalNativeShutdown(void); + +/* search the internal native set for a match */ +DalvikNativeFunc dvmLookupInternalNativeMethod(const Method* method); + +/* exception-throwing stub for abstract methods (DalvikNativeFunc) */ +void dvmAbstractMethodStub(const u4* args, JValue* pResult); + +#endif /*_DALVIK_NATIVE_INTERNALNATIVE*/ diff --git a/vm/native/InternalNativePriv.h b/vm/native/InternalNativePriv.h new file mode 100644 index 000000000..5e08bf9e8 --- /dev/null +++ b/vm/native/InternalNativePriv.h @@ -0,0 +1,116 @@ +/* + * 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. + */ + +/* + * Declarations and definitions common to internal native code. + */ +#ifndef _DALVIK_NATIVE_INTERNALNATIVEPRIV +#define _DALVIK_NATIVE_INTERNALNATIVEPRIV + +/* + * Return macros. Note we use "->i" instead of "->z" for boolean; this + * is because the interpreter expects everything to be a 32-bit value. + */ +#ifdef NDEBUG +# define RETURN_VOID() do { (void)(pResult); return; } while(0) +#else +# define RETURN_VOID() do { pResult->i = 0xfefeabab; return; }while(0) +#endif +#define RETURN_BOOLEAN(_val) do { pResult->i = (_val); return; } while(0) +#define RETURN_INT(_val) do { pResult->i = (_val); return; } while(0) +#define RETURN_LONG(_val) do { pResult->j = (_val); return; } while(0) +#define RETURN_FLOAT(_val) do { pResult->f = (_val); return; } while(0) +#define RETURN_DOUBLE(_val) do { pResult->d = (_val); return; } while(0) +#define RETURN_PTR(_val) do { pResult->l = (_val); return; } while(0) + + +/* + * Verify that "obj" is non-null and is an instance of "clazz". + * + * Returns "false" and throws an exception if not. + */ +bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz); + +/* + * Find a class by name, initializing it if requested. + */ +ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader, + bool doInit); + +/* + * We insert native method stubs for abstract methods so we don't have to + * check the access flags at the time of the method call. This results in + * "native abstract" methods, which can't exist. If we see the "abstract" + * flag set, clear the "native" flag. + * + * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED + * position, because the callers of this function are trying to convey + * the "traditional" meaning of the flags to their callers. + */ +u4 dvmFixMethodFlags(u4 flags); + +/* + * dvmHashTableFree callback for some DexFile operations. + */ +void dvmFreeDexOrJar(void* vptr); + +/* + * Determine if "method" is a "privileged" invocation, i.e. is it one + * of the variations of AccessController.doPrivileged(). + * + * Because the security stuff pulls in a pile of stuff that we may not + * want or need, we don't do the class/method lookups at init time, but + * instead on first use. + */ +bool dvmIsPrivilegedMethod(const Method* method); + +/* + * Return the hash code for the specified object. + */ +u4 dvmGetObjectHashCode(Object* obj); + + +/* + * Tables of methods. + */ +extern const DalvikNativeMethod dvm_java_lang_Object[]; +extern const DalvikNativeMethod dvm_java_lang_Class[]; +extern const DalvikNativeMethod dvm_java_lang_Runtime[]; +extern const DalvikNativeMethod dvm_java_lang_String[]; +extern const DalvikNativeMethod dvm_java_lang_System[]; +extern const DalvikNativeMethod dvm_java_lang_SystemProperties[]; +extern const DalvikNativeMethod dvm_java_lang_Throwable[]; +extern const DalvikNativeMethod dvm_java_lang_VMClassLoader[]; +extern const DalvikNativeMethod dvm_java_lang_VMThread[]; +extern const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[]; +extern const DalvikNativeMethod dvm_java_lang_reflect_Array[]; +extern const DalvikNativeMethod dvm_java_lang_reflect_Constructor[]; +extern const DalvikNativeMethod dvm_java_lang_reflect_Field[]; +extern const DalvikNativeMethod dvm_java_lang_reflect_Method[]; +extern const DalvikNativeMethod dvm_java_lang_reflect_Proxy[]; +extern const DalvikNativeMethod dvm_java_security_AccessController[]; +extern const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[]; +extern const DalvikNativeMethod dvm_dalvik_system_VMDebug[]; +extern const DalvikNativeMethod dvm_dalvik_system_DexFile[]; +extern const DalvikNativeMethod dvm_dalvik_system_VMRuntime[]; +extern const DalvikNativeMethod dvm_dalvik_system_Zygote[]; +extern const DalvikNativeMethod dvm_dalvik_system_VMStack[]; +extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmServer[]; +extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal[]; +extern const DalvikNativeMethod dvm_org_apache_harmony_dalvik_NativeTestTarget[]; +extern const DalvikNativeMethod dvm_sun_misc_Unsafe[]; + +#endif /*_DALVIK_NATIVE_INTERNALNATIVEPRIV*/ diff --git a/vm/native/README.txt b/vm/native/README.txt new file mode 100644 index 000000000..78630ebb9 --- /dev/null +++ b/vm/native/README.txt @@ -0,0 +1,24 @@ +Internal native functions. + +All of the functions defined here make direct use of VM functions or data +structures, so they can't be written with JNI and shouldn't really be in +a separate shared library. Do not add additional functions here unless +they need to access VM internals directly. + +All functions here either complete quickly or are used to enter a wait +state, so we don't set the thread status to THREAD_NATIVE when executing +these methods. This means that the GC will wait for these functions +to finish. DO NOT perform long operations or blocking I/O in here. +These methods should not be declared "synchronized", because we don't +check for that flag when issuing the call. + +We use "late" binding on these, rather than explicit registration, +because it's easier to handle the core system classes that way. + +The functions here use the DalvikNativeFunc prototype, but we can +also treat them as DalvikBridgeFunc, which takes two extra arguments. +The former represents the API that we're most likely to expose should +JNI performance be deemed insufficient. The Bridge version is used as +an optimization for a few high-volume Object calls, and should generally +not be used as we may drop support for it at some point. + diff --git a/vm/native/dalvik_system_DexFile.c b/vm/native/dalvik_system_DexFile.c new file mode 100644 index 000000000..f4e576ca3 --- /dev/null +++ b/vm/native/dalvik_system_DexFile.c @@ -0,0 +1,444 @@ +/* + * 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. + */ + +/* + * dalvik.system.DexFile + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * Internal struct for managing DexFile. + */ +typedef struct DexOrJar { + char* fileName; + bool isDex; + bool okayToFree; + RawDexFile* pRawDexFile; + JarFile* pJarFile; +} DexOrJar; + +/* + * (This is a dvmHashTableFree callback.) + */ +void dvmFreeDexOrJar(void* vptr) +{ + DexOrJar* pDexOrJar = (DexOrJar*) vptr; + + LOGV("Freeing DexOrJar '%s'\n", pDexOrJar->fileName); + + if (pDexOrJar->isDex) + dvmRawDexFileFree(pDexOrJar->pRawDexFile); + else + dvmJarFileFree(pDexOrJar->pJarFile); + free(pDexOrJar->fileName); + free(pDexOrJar); +} + +/* + * (This is a dvmHashTableLookup compare func.) + * + * Args are DexOrJar*. + */ +static int hashcmpDexOrJar(const void* tableVal, const void* newVal) +{ + return (int) newVal - (int) tableVal; +} + +/* + * Verify that the "cookie" is a DEX file we opened. + * + * Expects that the hash table will be *unlocked* here. + */ +static bool validateCookie(int cookie) +{ + DexOrJar* pDexOrJar = (DexOrJar*) cookie; + + LOGVV("+++ dex verifying cookie %p\n", pDexOrJar); + + if (pDexOrJar == NULL) + return false; + + u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName); + dvmHashTableLock(gDvm.userDexFiles); + void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, + hashcmpDexOrJar, false); + dvmHashTableUnlock(gDvm.userDexFiles); + if (result == NULL) + return false; + + return true; +} + +/* + * private static int openDexFile(String sourceName, String outputName, + * int flags) throws IOException + * + * Open a DEX file, returning a pointer to our internal data structure. + * + * "sourceName" should point to the "source" jar or DEX file. + * + * If "outputName" is NULL, the DEX code will automatically find the + * "optimized" version in the cache directory, creating it if necessary. + * If it's non-NULL, the specified file will be used instead. + * + * TODO: at present we will happily open the same file more than once. + * To optimize this away we could search for existing entries in the hash + * table and refCount them. Requires atomic ops or adding "synchronized" + * to the non-native code that calls here. + */ +static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args, + JValue* pResult) +{ + StringObject* sourceNameObj = (StringObject*) args[0]; + StringObject* outputNameObj = (StringObject*) args[1]; + int flags = args[2]; + DexOrJar* pDexOrJar = NULL; + JarFile* pJarFile; + RawDexFile* pRawDexFile; + char* sourceName; + char* outputName; + + if (sourceNameObj == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + RETURN_VOID(); + } + + sourceName = dvmCreateCstrFromString(sourceNameObj); + if (outputNameObj != NULL) + outputName = dvmCreateCstrFromString(outputNameObj); + else + outputName = NULL; + + /* + * We have to deal with the possibility that somebody might try to + * open one of our bootstrap class DEX files. The set of dependencies + * will be different, and hence the results of optimization might be + * different, which means we'd actually need to have two versions of + * the optimized DEX: one that only knows about part of the boot class + * path, and one that knows about everything in it. The latter might + * optimize field/method accesses based on a class that appeared later + * in the class path. + * + * We can't let the user-defined class loader open it and start using + * the classes, since the optimized form of the code skips some of + * the method and field resolution that we would ordinarily do, and + * we'd have the wrong semantics. + * + * We have to reject attempts to manually open a DEX file from the boot + * class path. The easiest way to do this is by filename, which works + * out because variations in name (e.g. "/system/framework/./ext.jar") + * result in us hitting a different dalvik-cache entry. It's also fine + * if the caller specifies their own output file. + */ + if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { + LOGW("Refusing to reopen boot DEX '%s'\n", sourceName); + dvmThrowException("Ljava/io/IOException;", + "Re-opening BOOTCLASSPATH DEX files is not allowed"); + free(sourceName); + RETURN_VOID(); + } + + /* + * Try to open it directly as a DEX. If that fails, try it as a Zip + * with a "classes.dex" inside. + */ + if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { + LOGV("Opening DEX file '%s' (DEX)\n", sourceName); + + pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); + pDexOrJar->isDex = true; + pDexOrJar->pRawDexFile = pRawDexFile; + } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) { + LOGV("Opening DEX file '%s' (Jar)\n", sourceName); + + pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); + pDexOrJar->isDex = false; + pDexOrJar->pJarFile = pJarFile; + } else { + LOGV("Unable to open DEX file '%s'\n", sourceName); + dvmThrowException("Ljava/io/IOException;", "unable to open DEX file"); + } + + if (pDexOrJar != NULL) { + pDexOrJar->fileName = sourceName; + + /* add to hash table */ + u4 hash = dvmComputeUtf8Hash(sourceName); + void* result; + dvmHashTableLock(gDvm.userDexFiles); + result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, + hashcmpDexOrJar, true); + dvmHashTableUnlock(gDvm.userDexFiles); + if (result != pDexOrJar) { + LOGE("Pointer has already been added?\n"); + dvmAbort(); + } + + pDexOrJar->okayToFree = true; + } else + free(sourceName); + + RETURN_PTR(pDexOrJar); +} + +/* + * private static void closeDexFile(int cookie) + * + * Release resources associated with a user-loaded DEX file. + */ +static void Dalvik_dalvik_system_DexFile_closeDexFile(const u4* args, + JValue* pResult) +{ + int cookie = args[0]; + DexOrJar* pDexOrJar = (DexOrJar*) cookie; + + if (pDexOrJar == NULL) + RETURN_VOID(); + + LOGV("Closing DEX file %p (%s)\n", pDexOrJar, pDexOrJar->fileName); + + if (!validateCookie(cookie)) + dvmAbort(); + + /* + * We can't just free arbitrary DEX files because they have bits and + * pieces of loaded classes. The only exception to this rule is if + * they were never used to load classes. + * + * If we can't free them here, dvmInternalNativeShutdown() will free + * them when the VM shuts down. + */ + if (pDexOrJar->okayToFree) { + u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName); + dvmHashTableLock(gDvm.userDexFiles); + if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) { + LOGW("WARNING: could not remove '%s' from DEX hash table\n", + pDexOrJar->fileName); + } + dvmHashTableUnlock(gDvm.userDexFiles); + LOGV("+++ freeing DexFile '%s' resources\n", pDexOrJar->fileName); + dvmFreeDexOrJar(pDexOrJar); + } else { + LOGV("+++ NOT freeing DexFile '%s' resources\n", pDexOrJar->fileName); + } + + RETURN_VOID(); +} + +/* + * private static Class defineClass(String name, ClassLoader loader, + * int cookie, ProtectionDomain pd) + * + * Load a class from a DEX file. This is roughly equivalent to defineClass() + * in a regular VM -- it's invoked by the class loader to cause the + * creation of a specific class. The difference is that the search for and + * reading of the bytes is done within the VM. + * + * Returns a null pointer with no exception if the class was not found. + * Throws an exception on other failures. + */ +static void Dalvik_dalvik_system_DexFile_defineClass(const u4* args, + JValue* pResult) +{ + StringObject* nameObj = (StringObject*) args[0]; + Object* loader = (Object*) args[1]; + int cookie = args[2]; + Object* pd = (Object*) args[3]; + ClassObject* clazz = NULL; + DexOrJar* pDexOrJar = (DexOrJar*) cookie; + DvmDex* pDvmDex; + char* name; + char* descriptor; + + name = dvmCreateCstrFromString(nameObj); + descriptor = dvmNameToDescriptor(name); + LOGV("--- Explicit class load '%s' 0x%08x\n", name, cookie); + free(name); + + if (!validateCookie(cookie)) + dvmAbort(); + + if (pDexOrJar->isDex) + pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); + else + pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); + + /* once we load something, we can't unmap the storage */ + pDexOrJar->okayToFree = false; + + clazz = dvmDefineClass(pDvmDex, descriptor, loader); + Thread* self = dvmThreadSelf(); + if (dvmCheckException(self)) { + /* + * If we threw a "class not found" exception, stifle it, since the + * contract in the higher method says we simply return null if + * the class is not found. + */ + Object* excep = dvmGetException(self); + if (strcmp(excep->clazz->descriptor, + "Ljava/lang/ClassNotFoundException;") == 0 || + strcmp(excep->clazz->descriptor, + "Ljava/lang/NoClassDefFoundError;") == 0) + { + dvmClearException(self); + } + clazz = NULL; + } + + /* + * Set the ProtectionDomain -- do we need this to happen before we + * link the class and make it available? If so, we need to pass it + * through dvmDefineClass (and figure out some other + * stuff, like where it comes from for bootstrap classes). + */ + if (clazz != NULL) { + //LOGI("SETTING pd '%s' to %p\n", clazz->descriptor, pd); + dvmSetFieldObject((Object*) clazz, gDvm.offJavaLangClass_pd, pd); + } + + free(descriptor); + RETURN_PTR(clazz); +} + +/* + * private static String[] getClassNameList(int cookie) + * + * Returns a String array that holds the names of all classes in the + * specified DEX file. + */ +static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args, + JValue* pResult) +{ + int cookie = args[0]; + DexOrJar* pDexOrJar = (DexOrJar*) cookie; + DvmDex* pDvmDex; + DexFile* pDexFile; + ArrayObject* stringArray; + + if (!validateCookie(cookie)) + dvmAbort(); + + if (pDexOrJar->isDex) + pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); + else + pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); + assert(pDvmDex != NULL); + pDexFile = pDvmDex->pDexFile; + + int count = pDexFile->pHeader->classDefsSize; + stringArray = dvmAllocObjectArray(gDvm.classJavaLangString, count, + ALLOC_DEFAULT); + if (stringArray == NULL) + RETURN_VOID(); // should be an OOM pending + + StringObject** contents = (StringObject**) stringArray->contents; + int i; + for (i = 0; i < count; i++) { + const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i); + const char* descriptor = + dexStringByTypeIdx(pDexFile, pClassDef->classIdx); + + char* className = dvmDescriptorToDot(descriptor); + contents[i] = dvmCreateStringFromCstr(className, ALLOC_DEFAULT); + dvmReleaseTrackedAlloc((Object*) contents[i], NULL); + free(className); + } + + dvmReleaseTrackedAlloc((Object*)stringArray, NULL); + RETURN_PTR(stringArray); +} + +/* + * public static boolean isDexOptNeeded(String apkName) + * throws FileNotFoundException, IOException + * + * Returns true if the VM believes that the apk/jar file is out of date + * and should be passed through "dexopt" again. + * + * @param fileName the absolute path to the apk/jar file to examine. + * @return true if dexopt should be called on the file, false otherwise. + * @throws java.io.FileNotFoundException if fileName is not readable, + * not a file, or not present. + * @throws java.io.IOException if fileName is not a valid apk/jar file or + * if problems occur while parsing it. + * @throws java.lang.NullPointerException if fileName is null. + * @throws dalvik.system.StaleDexCacheError if the optimized dex file + * is stale but exists on a read-only partition. + */ +static void Dalvik_dalvik_system_DexFile_isDexOptNeeded(const u4* args, + JValue* pResult) +{ + StringObject* nameObj = (StringObject*) args[0]; + char* name; + DexCacheStatus status; + int result; + + name = dvmCreateCstrFromString(nameObj); + if (name == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + RETURN_VOID(); + } + if (access(name, R_OK) != 0) { + dvmThrowException("Ljava/io/FileNotFoundException;", name); + free(name); + RETURN_VOID(); + } + status = dvmDexCacheStatus(name); + LOGV("dvmDexCacheStatus(%s) returned %d\n", name, status); + + result = true; + switch (status) { + default: //FALLTHROUGH + case DEX_CACHE_BAD_ARCHIVE: + dvmThrowException("Ljava/io/IOException;", name); + result = -1; + break; + case DEX_CACHE_OK: + result = false; + break; + case DEX_CACHE_STALE: + result = true; + break; + case DEX_CACHE_STALE_ODEX: + dvmThrowException("Ldalvik/system/StaleDexCacheError;", name); + result = -1; + break; + } + free(name); + + if (result >= 0) { + RETURN_BOOLEAN(result); + } else { + RETURN_VOID(); + } +} + +const DalvikNativeMethod dvm_dalvik_system_DexFile[] = { + { "openDexFile", "(Ljava/lang/String;Ljava/lang/String;I)I", + Dalvik_dalvik_system_DexFile_openDexFile }, + { "closeDexFile", "(I)V", + Dalvik_dalvik_system_DexFile_closeDexFile }, + { "defineClass", "(Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/security/ProtectionDomain;)Ljava/lang/Class;", + Dalvik_dalvik_system_DexFile_defineClass }, + { "getClassNameList", "(I)[Ljava/lang/String;", + Dalvik_dalvik_system_DexFile_getClassNameList }, + { "isDexOptNeeded", "(Ljava/lang/String;)Z", + Dalvik_dalvik_system_DexFile_isDexOptNeeded }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/dalvik_system_VMDebug.c b/vm/native/dalvik_system_VMDebug.c new file mode 100644 index 000000000..990494e18 --- /dev/null +++ b/vm/native/dalvik_system_VMDebug.c @@ -0,0 +1,564 @@ +/* + * 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. + */ + +/* + * dalvik.system.VMDebug + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +#ifdef WITH_PROFILER +/* These must match the values in dalvik.system.VMDebug. + */ +enum { + KIND_ALLOCATED_OBJECTS = 1<<0, + KIND_ALLOCATED_BYTES = 1<<1, + KIND_FREED_OBJECTS = 1<<2, + KIND_FREED_BYTES = 1<<3, + KIND_GC_INVOCATIONS = 1<<4, +#if PROFILE_EXTERNAL_ALLOCATIONS + KIND_EXT_ALLOCATED_OBJECTS = 1<<12, + KIND_EXT_ALLOCATED_BYTES = 1<<13, + KIND_EXT_FREED_OBJECTS = 1<<14, + KIND_EXT_FREED_BYTES = 1<<15, +#endif // PROFILE_EXTERNAL_ALLOCATIONS + + KIND_GLOBAL_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS, + KIND_GLOBAL_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES, + KIND_GLOBAL_FREED_OBJECTS = KIND_FREED_OBJECTS, + KIND_GLOBAL_FREED_BYTES = KIND_FREED_BYTES, + KIND_GLOBAL_GC_INVOCATIONS = KIND_GC_INVOCATIONS, +#if PROFILE_EXTERNAL_ALLOCATIONS + KIND_GLOBAL_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS, + KIND_GLOBAL_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES, + KIND_GLOBAL_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS, + KIND_GLOBAL_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES, +#endif // PROFILE_EXTERNAL_ALLOCATIONS + + KIND_THREAD_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS << 16, + KIND_THREAD_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES << 16, + KIND_THREAD_FREED_OBJECTS = KIND_FREED_OBJECTS << 16, + KIND_THREAD_FREED_BYTES = KIND_FREED_BYTES << 16, +#if PROFILE_EXTERNAL_ALLOCATIONS + KIND_THREAD_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS << 16, + KIND_THREAD_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES << 16, + KIND_THREAD_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS << 16, + KIND_THREAD_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES << 16, +#endif // PROFILE_EXTERNAL_ALLOCATIONS + KIND_THREAD_GC_INVOCATIONS = KIND_GC_INVOCATIONS << 16, + + // TODO: failedAllocCount, failedAllocSize +}; + +#define KIND_ALL_COUNTS 0xffffffff + +/* + * Zero out the specified fields. + */ +static void clearAllocProfStateFields(AllocProfState *allocProf, + unsigned int kinds) +{ + if (kinds & KIND_ALLOCATED_OBJECTS) { + allocProf->allocCount = 0; + } + if (kinds & KIND_ALLOCATED_BYTES) { + allocProf->allocSize = 0; + } + if (kinds & KIND_FREED_OBJECTS) { + allocProf->freeCount = 0; + } + if (kinds & KIND_FREED_BYTES) { + allocProf->freeSize = 0; + } + if (kinds & KIND_GC_INVOCATIONS) { + allocProf->gcCount = 0; + } +#if PROFILE_EXTERNAL_ALLOCATIONS + if (kinds & KIND_EXT_ALLOCATED_OBJECTS) { + allocProf->externalAllocCount = 0; + } + if (kinds & KIND_EXT_ALLOCATED_BYTES) { + allocProf->externalAllocSize = 0; + } + if (kinds & KIND_EXT_FREED_OBJECTS) { + allocProf->externalFreeCount = 0; + } + if (kinds & KIND_EXT_FREED_BYTES) { + allocProf->externalFreeSize = 0; + } +#endif // PROFILE_EXTERNAL_ALLOCATIONS +} +#endif + +/* + * static void startAllocCounting() + * + * Reset the counters and enable counting. + * + * TODO: this currently only resets the per-thread counters for the current + * thread. If we actually start using the per-thread counters we'll + * probably want to fix this. + */ +static void Dalvik_dalvik_system_VMDebug_startAllocCounting(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + +#ifdef WITH_PROFILER + clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS); + clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS); + dvmStartAllocCounting(); +#endif + RETURN_VOID(); +} + +/* + * public static void stopAllocCounting() + */ +static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + +#ifdef WITH_PROFILER + dvmStopAllocCounting(); +#endif + RETURN_VOID(); +} + +/* + * private static int getAllocCount(int kind) + */ +static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args, + JValue* pResult) +{ +#ifdef WITH_PROFILER + AllocProfState *allocProf; + unsigned int kind = args[0]; + if (kind < (1<<16)) { + allocProf = &gDvm.allocProf; + } else { + allocProf = &dvmThreadSelf()->allocProf; + kind >>= 16; + } + switch (kind) { + case KIND_ALLOCATED_OBJECTS: + pResult->i = allocProf->allocCount; + break; + case KIND_ALLOCATED_BYTES: + pResult->i = allocProf->allocSize; + break; + case KIND_FREED_OBJECTS: + pResult->i = allocProf->freeCount; + break; + case KIND_FREED_BYTES: + pResult->i = allocProf->freeSize; + break; + case KIND_GC_INVOCATIONS: + pResult->i = allocProf->gcCount; + break; +#if PROFILE_EXTERNAL_ALLOCATIONS + case KIND_EXT_ALLOCATED_OBJECTS: + pResult->i = allocProf->externalAllocCount; + break; + case KIND_EXT_ALLOCATED_BYTES: + pResult->i = allocProf->externalAllocSize; + break; + case KIND_EXT_FREED_OBJECTS: + pResult->i = allocProf->externalFreeCount; + break; + case KIND_EXT_FREED_BYTES: + pResult->i = allocProf->externalFreeSize; + break; +#endif // PROFILE_EXTERNAL_ALLOCATIONS + default: + assert(false); + pResult->i = -1; + } +#else + RETURN_INT(-1); +#endif +} + +/* + * public static void resetAllocCount(int kinds) + */ +static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args, + JValue* pResult) +{ +#ifdef WITH_PROFILER + unsigned int kinds = args[0]; + clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff); + clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16); +#endif + RETURN_VOID(); +} + +/* + * static void startMethodTracing(String traceFileName, + * int bufferSize, int flags) + * + * Start method trace profiling. + */ +static void Dalvik_dalvik_system_VMDebug_startMethodTracing(const u4* args, + JValue* pResult) +{ +#ifdef WITH_PROFILER + StringObject* traceFileStr = (StringObject*) args[0]; + int bufferSize = args[1]; + int flags = args[2]; + char* traceFileName; + + if (bufferSize == 0) { + // Default to 8MB per the documentation. + bufferSize = 8 * 1024 * 1024; + } + + if (traceFileStr == NULL || bufferSize < 1024) { + dvmThrowException("Ljava/lang/InvalidArgument;", NULL); + RETURN_VOID(); + } + + traceFileName = dvmCreateCstrFromString(traceFileStr); + + dvmMethodTraceStart(traceFileName, bufferSize, flags); + free(traceFileName); +#else + // throw exception? +#endif + RETURN_VOID(); +} + +/* + * static void stopMethodTracing() + * + * Stop method tracing. + */ +static void Dalvik_dalvik_system_VMDebug_stopMethodTracing(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + +#ifdef WITH_PROFILER + dvmMethodTraceStop(); +#else + // throw exception? +#endif + RETURN_VOID(); +} + +/* + * static void startEmulatorTracing() + * + * Start sending method trace info to the emulator. + */ +static void Dalvik_dalvik_system_VMDebug_startEmulatorTracing(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + +#ifdef WITH_PROFILER + dvmEmulatorTraceStart(); +#else + // throw exception? +#endif + RETURN_VOID(); +} + +/* + * static void stopEmulatorTracing() + * + * Start sending method trace info to the emulator. + */ +static void Dalvik_dalvik_system_VMDebug_stopEmulatorTracing(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + +#ifdef WITH_PROFILER + dvmEmulatorTraceStop(); +#else + // throw exception? +#endif + RETURN_VOID(); +} + +/* + * static int setAllocationLimit(int limit) + * + * Set the current allocation limit in this thread. Return the previous + * value. + */ +static void Dalvik_dalvik_system_VMDebug_setAllocationLimit(const u4* args, + JValue* pResult) +{ +#if defined(WITH_ALLOC_LIMITS) + gDvm.checkAllocLimits = true; + + Thread* self = dvmThreadSelf(); + int newLimit = args[0]; + int oldLimit = self->allocLimit; + + if (newLimit < -1) { + LOGE("WARNING: bad limit request (%d)\n", newLimit); + newLimit = -1; + } + self->allocLimit = newLimit; + RETURN_INT(oldLimit); +#else + UNUSED_PARAMETER(args); + RETURN_INT(-1); +#endif +} + +/* + * static int setGlobalAllocationLimit(int limit) + * + * Set the allocation limit for this process. Returns the previous value. + */ +static void Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit(const u4* args, + JValue* pResult) +{ +#if defined(WITH_ALLOC_LIMITS) + gDvm.checkAllocLimits = true; + + int newLimit = args[0]; + int oldLimit = gDvm.allocationLimit; + + if (newLimit < -1 || newLimit > 0) { + LOGE("WARNING: bad limit request (%d)\n", newLimit); + newLimit = -1; + } + // TODO: should use an atomic swap here + gDvm.allocationLimit = newLimit; + RETURN_INT(oldLimit); +#else + UNUSED_PARAMETER(args); + RETURN_INT(-1); +#endif +} + +/* + * static boolean isDebuggerConnected() + * + * Returns "true" if a debugger is attached. + */ +static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + + RETURN_BOOLEAN(dvmDbgIsDebuggerConnected()); +} + +/* + * static boolean isDebuggingEnabled() + * + * Returns "true" if debugging is enabled. + */ +static void Dalvik_dalvik_system_VMDebug_isDebuggingEnabled(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + + RETURN_BOOLEAN(gDvm.jdwpConfigured); +} + +/* + * static long lastDebuggerActivity() + * + * Returns the time, in msec, since we last had an interaction with the + * debugger (send or receive). + */ +static void Dalvik_dalvik_system_VMDebug_lastDebuggerActivity(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + + RETURN_LONG(dvmDbgLastDebuggerActivity()); +} + +/* + * static void startInstructionCounting() + */ +static void Dalvik_dalvik_system_VMDebug_startInstructionCounting(const u4* args, + JValue* pResult) +{ +#if defined(WITH_PROFILER) + dvmStartInstructionCounting(); + RETURN_VOID(); +#else + dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); +#endif +} + +/* + * static void stopInstructionCounting() + */ +static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args, + JValue* pResult) +{ +#if defined(WITH_PROFILER) + dvmStopInstructionCounting(); + RETURN_VOID(); +#else + dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); +#endif +} + +/* + * static boolean getInstructionCount(int[] counts) + * + * Grab a copy of the global instruction count array. + * + * Since the instruction counts aren't synchronized, we use sched_yield + * to improve our chances of finishing without contention. (Only makes + * sense on a uniprocessor.) + */ +static void Dalvik_dalvik_system_VMDebug_getInstructionCount(const u4* args, + JValue* pResult) +{ +#if defined(WITH_PROFILER) + ArrayObject* countArray = (ArrayObject*) args[0]; + int* storage; + + storage = (int*) countArray->contents; + sched_yield(); + memcpy(storage, gDvm.executedInstrCounts, + kNumDalvikInstructions * sizeof(int)); + + RETURN_VOID(); +#else + dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); +#endif +} + +/* + * static boolean resetInstructionCount() + * + * Reset the instruction count array. + */ +static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args, + JValue* pResult) +{ +#if defined(WITH_PROFILER) + sched_yield(); + memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int)); + RETURN_VOID(); +#else + dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); +#endif +} + +/* + * static void printLoadedClasses(int flags) + * + * Dump the list of loaded classes. + */ +static void Dalvik_dalvik_system_VMDebug_printLoadedClasses(const u4* args, + JValue* pResult) +{ + int flags = args[0]; + + dvmDumpAllClasses(flags); + + RETURN_VOID(); +} + +/* + * static int getLoadedClassCount() + * + * Return the number of loaded classes + */ +static void Dalvik_dalvik_system_VMDebug_getLoadedClassCount(const u4* args, + JValue* pResult) +{ + int count; + + UNUSED_PARAMETER(args); + + count = dvmGetNumLoadedClasses(); + + RETURN_INT(count); +} + +/* + * Returns the thread-specific CPU-time clock value for the current thread, + * or -1 if the feature isn't supported. + */ +static void Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos(const u4* args, + JValue* pResult) +{ + jlong result; + +#ifdef HAVE_POSIX_CLOCKS + struct timespec now; + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now); + result = (jlong) (now.tv_sec*1000000000LL + now.tv_nsec); +#else + result = (jlong) -1; +#endif + + RETURN_LONG(result); +} + +const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = { + { "getAllocCount", "(I)I", + Dalvik_dalvik_system_VMDebug_getAllocCount }, + { "resetAllocCount", "(I)V", + Dalvik_dalvik_system_VMDebug_resetAllocCount }, + //{ "print", "(Ljava/lang/String;)V", + // Dalvik_dalvik_system_VMDebug_print }, + { "startAllocCounting", "()V", + Dalvik_dalvik_system_VMDebug_startAllocCounting }, + { "stopAllocCounting", "()V", + Dalvik_dalvik_system_VMDebug_stopAllocCounting }, + { "startMethodTracing", "(Ljava/lang/String;II)V", + Dalvik_dalvik_system_VMDebug_startMethodTracing }, + { "stopMethodTracing", "()V", + Dalvik_dalvik_system_VMDebug_stopMethodTracing }, + { "startEmulatorTracing", "()V", + Dalvik_dalvik_system_VMDebug_startEmulatorTracing }, + { "stopEmulatorTracing", "()V", + Dalvik_dalvik_system_VMDebug_stopEmulatorTracing }, + { "setAllocationLimit", "(I)I", + Dalvik_dalvik_system_VMDebug_setAllocationLimit }, + { "setGlobalAllocationLimit", "(I)I", + Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit }, + { "startInstructionCounting", "()V", + Dalvik_dalvik_system_VMDebug_startInstructionCounting }, + { "stopInstructionCounting", "()V", + Dalvik_dalvik_system_VMDebug_stopInstructionCounting }, + { "resetInstructionCount", "()V", + Dalvik_dalvik_system_VMDebug_resetInstructionCount }, + { "getInstructionCount", "([I)V", + Dalvik_dalvik_system_VMDebug_getInstructionCount }, + { "isDebuggerConnected", "()Z", + Dalvik_dalvik_system_VMDebug_isDebuggerConnected }, + { "isDebuggingEnabled", "()Z", + Dalvik_dalvik_system_VMDebug_isDebuggingEnabled }, + { "lastDebuggerActivity", "()J", + Dalvik_dalvik_system_VMDebug_lastDebuggerActivity }, + { "printLoadedClasses", "(I)V", + Dalvik_dalvik_system_VMDebug_printLoadedClasses }, + { "getLoadedClassCount", "()I", + Dalvik_dalvik_system_VMDebug_getLoadedClassCount }, + { "threadCpuTimeNanos", "()J", + Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/dalvik_system_VMRuntime.c b/vm/native/dalvik_system_VMRuntime.c new file mode 100644 index 000000000..0ec3ced24 --- /dev/null +++ b/vm/native/dalvik_system_VMRuntime.c @@ -0,0 +1,200 @@ +/* + * 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. + */ + +/* + * dalvik.system.VMRuntime + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + +#include <limits.h> + + +/* + * public native float getTargetHeapUtilization() + * + * Gets the current ideal heap utilization, represented as a number + * between zero and one. + */ +static void Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization( + const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + + RETURN_FLOAT(dvmGetTargetHeapUtilization()); +} + +/* + * native float nativeSetTargetHeapUtilization() + * + * Sets the current ideal heap utilization, represented as a number + * between zero and one. Returns the old utilization. + * + * Note that this is NOT static. + */ +static void Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization( + const u4* args, JValue* pResult) +{ + dvmSetTargetHeapUtilization(dvmU4ToFloat(args[1])); + + RETURN_VOID(); +} + +/* + * native long nativeMinimumHeapSize(long size, boolean set) + * + * If set is true, sets the new minimum heap size to size; always + * returns the current (or previous) size. If size is negative or + * zero, removes the current minimum constraint (if present). + */ +static void Dalvik_dalvik_system_VMRuntime_nativeMinimumHeapSize( + const u4* args, JValue* pResult) +{ + s8 longSize = GET_ARG_LONG(args, 1); + size_t size; + bool set = (args[3] != 0); + + /* Fit in 32 bits. */ + if (longSize < 0) { + size = 0; + } else if (longSize > INT_MAX) { + size = INT_MAX; + } else { + size = (size_t)longSize; + } + + size = dvmMinimumHeapSize(size, set); + + RETURN_LONG(size); +} + +/* + * public native void gcSoftReferences() + * + * Does a GC and forces collection of SoftReferences that are + * not strongly-reachable. + */ +static void Dalvik_dalvik_system_VMRuntime_gcSoftReferences(const u4* args, + JValue* pResult) +{ + dvmCollectGarbage(true); + + RETURN_VOID(); +} + +/* + * public native void runFinalizationSync() + * + * Does not return until any pending finalizers have been called. + * This may or may not happen in the context of the calling thread. + * No exceptions will escape. + * + * Used by zygote, which doesn't have a HeapWorker thread. + */ +static void Dalvik_dalvik_system_VMRuntime_runFinalizationSync(const u4* args, + JValue* pResult) +{ + dvmRunFinalizationSync(); + + RETURN_VOID(); +} + +/* + * public native boolean trackExternalAllocation(long size) + * + * Asks the VM if <size> bytes can be allocated in an external heap. + * This information may be used to limit the amount of memory available + * to Dalvik threads. Returns false if the VM would rather that the caller + * did not allocate that much memory. If the call returns false, the VM + * will not update its internal counts. + */ +static void Dalvik_dalvik_system_VMRuntime_trackExternalAllocation( + const u4* args, JValue* pResult) +{ + s8 longSize = GET_ARG_LONG(args, 1); + + /* Fit in 32 bits. */ + if (longSize < 0) { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "size must be positive"); + RETURN_VOID(); + } else if (longSize > INT_MAX) { + dvmThrowException("Ljava/lang/UnsupportedOperationException;", + "size must fit in 32 bits"); + RETURN_VOID(); + } + RETURN_BOOLEAN(dvmTrackExternalAllocation((size_t)longSize)); +} + +/* + * public native void trackExternalFree(long size) + * + * Tells the VM that <size> bytes have been freed in an external + * heap. This information may be used to control the amount of memory + * available to Dalvik threads. + */ +static void Dalvik_dalvik_system_VMRuntime_trackExternalFree( + const u4* args, JValue* pResult) +{ + s8 longSize = GET_ARG_LONG(args, 1); + + /* Fit in 32 bits. */ + if (longSize < 0) { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "size must be positive"); + RETURN_VOID(); + } else if (longSize > INT_MAX) { + dvmThrowException("Ljava/lang/UnsupportedOperationException;", + "size must fit in 32 bits"); + RETURN_VOID(); + } + dvmTrackExternalFree((size_t)longSize); + + RETURN_VOID(); +} + +/* + * public native long getExternalBytesAllocated() + * + * Returns the number of externally-allocated bytes being tracked by + * trackExternalAllocation/Free(). + */ +static void Dalvik_dalvik_system_VMRuntime_getExternalBytesAllocated( + const u4* args, JValue* pResult) +{ + RETURN_LONG((s8)dvmGetExternalBytesAllocated()); +} + +const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = { + { "getTargetHeapUtilization", "()F", + Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization }, + { "nativeSetTargetHeapUtilization", "(F)V", + Dalvik_dalvik_system_VMRuntime_nativeSetTargetHeapUtilization }, + { "nativeMinimumHeapSize", "(JZ)J", + Dalvik_dalvik_system_VMRuntime_nativeMinimumHeapSize }, + { "gcSoftReferences", "()V", + Dalvik_dalvik_system_VMRuntime_gcSoftReferences }, + { "runFinalizationSync", "()V", + Dalvik_dalvik_system_VMRuntime_runFinalizationSync }, + { "trackExternalAllocation", "(J)Z", + Dalvik_dalvik_system_VMRuntime_trackExternalAllocation }, + { "trackExternalFree", "(J)V", + Dalvik_dalvik_system_VMRuntime_trackExternalFree }, + { "getExternalBytesAllocated", "()J", + Dalvik_dalvik_system_VMRuntime_getExternalBytesAllocated }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/dalvik_system_VMStack.c b/vm/native/dalvik_system_VMStack.c new file mode 100644 index 000000000..cb771dc14 --- /dev/null +++ b/vm/native/dalvik_system_VMStack.c @@ -0,0 +1,221 @@ +/* + * 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. + */ + +/* + * dalvik.system.VMStack + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * public static ClassLoader getCallingClassLoader() + * + * Return the defining class loader of the caller's caller. + */ +static void Dalvik_dalvik_system_VMStack_getCallingClassLoader(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = dvmGetCaller2Class(dvmThreadSelf()->curFrame); + + UNUSED_PARAMETER(args); + + if (clazz == NULL) + RETURN_PTR(NULL); + RETURN_PTR(clazz->classLoader); +} + +/* + * public static ClassLoader getCallingClassLoader2() + * + * Return the defining class loader of the caller's caller's caller. + */ +static void Dalvik_dalvik_system_VMStack_getCallingClassLoader2(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = dvmGetCaller3Class(dvmThreadSelf()->curFrame); + + UNUSED_PARAMETER(args); + + if (clazz == NULL) + RETURN_PTR(NULL); + RETURN_PTR(clazz->classLoader); +} + +/* + * public static Class<?>[] getClasses(int maxDepth, boolean stopAtPrivileged) + * + * Create an array of classes for the methods on the stack, skipping the + * first two and all reflection methods. If "stopAtPrivileged" is set, + * stop shortly after we encounter a privileged class. + */ +static void Dalvik_dalvik_system_VMStack_getClasses(const u4* args, + JValue* pResult) +{ + /* note "maxSize" is unsigned, so -1 turns into a very large value */ + unsigned int maxSize = args[0]; + bool stopAtPrivileged = args[1]; + unsigned int size = 0; + const unsigned int kSkip = 2; + const Method** methods = NULL; + int methodCount; + + /* + * Get an array with the stack trace in it. + */ + if (!dvmCreateStackTraceArray(dvmThreadSelf()->curFrame, &methods, + &methodCount)) + { + LOGE("Failed to create stack trace array\n"); + dvmThrowException("Ljava/lang/InternalError;", NULL); + RETURN_VOID(); + } + + //int i; + //LOGI("dvmCreateStackTraceArray results:\n"); + //for (i = 0; i < methodCount; i++) { + // LOGI(" %2d: %s.%s\n", + // i, methods[i]->clazz->descriptor, methods[i]->name); + //} + + /* + * Run through the array and count up how many elements there are. + */ + unsigned int idx; + for (idx = kSkip; (int) idx < methodCount && size < maxSize; idx++) { + const Method* meth = methods[idx]; + + if (dvmIsReflectionMethod(meth)) + continue; + + if (stopAtPrivileged && dvmIsPrivilegedMethod(meth)) { + /* + * We want the last element of the array to be the caller of + * the privileged method, so we want to include the privileged + * method and the next one. + */ + if (maxSize > size + 2) + maxSize = size + 2; + } + + size++; + } + + /* + * Create an array object to hold the classes. + * TODO: can use gDvm.classJavaLangClassArray here? + */ + ClassObject* classArrayClass = NULL; + ArrayObject* classes = NULL; + classArrayClass = dvmFindArrayClass("[Ljava/lang/Class;", NULL); + if (classArrayClass == NULL) { + LOGW("Unable to find java.lang.Class array class\n"); + goto bail; + } + classes = dvmAllocArray(classArrayClass, size, kObjectArrayRefWidth, + ALLOC_DEFAULT); + if (classes == NULL) { + LOGW("Unable to allocate class array (%d elems)\n", size); + goto bail; + } + + /* + * Fill in the array. + */ + ClassObject** objects = (ClassObject**) classes->contents; + + unsigned int sidx = 0; + for (idx = kSkip; (int) idx < methodCount && sidx < size; idx++) { + const Method* meth = methods[idx]; + + if (dvmIsReflectionMethod(meth)) + continue; + + *objects++ = meth->clazz; + sidx++; + } + +bail: + free(methods); + dvmReleaseTrackedAlloc((Object*) classes, NULL); + RETURN_PTR(classes); +} + +/* + * public static StackTraceElement[] getThreadStackTrace(Thread t) + * + * Retrieve the stack trace of the specified thread and return it as an + * array of StackTraceElement. Returns NULL on failure. + */ +static void Dalvik_dalvik_system_VMStack_getThreadStackTrace(const u4* args, + JValue* pResult) +{ + Object* targetThreadObj = (Object*) args[0]; + Thread* self = dvmThreadSelf(); + Thread* thread; + int* traceBuf; + + assert(targetThreadObj != NULL); + + dvmLockThreadList(self); + + /* + * Make sure the thread is still alive and in the list. + */ + for (thread = gDvm.threadList; thread != NULL; thread = thread->next) { + if (thread->threadObj == targetThreadObj) + break; + } + if (thread == NULL) { + LOGI("VMStack.getThreadStackTrace: threadObj %p not active\n", + targetThreadObj); + dvmUnlockThreadList(); + RETURN_PTR(NULL); + } + + /* + * Suspend the thread, pull out the stack trace, then resume the thread + * and release the thread list lock. If we're being asked to examine + * our own stack trace, skip the suspend/resume. + */ + int stackDepth = -1; + if (thread != self) + dvmSuspendThread(thread); + traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth); + if (thread != self) + dvmResumeThread(thread); + dvmUnlockThreadList(); + + /* + * Convert the raw buffer into an array of StackTraceElement. + */ + ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth); + free(traceBuf); + RETURN_PTR(trace); +} + +const DalvikNativeMethod dvm_dalvik_system_VMStack[] = { + { "getCallingClassLoader", "()Ljava/lang/ClassLoader;", + Dalvik_dalvik_system_VMStack_getCallingClassLoader }, + { "getCallingClassLoader2", "()Ljava/lang/ClassLoader;", + Dalvik_dalvik_system_VMStack_getCallingClassLoader2 }, + { "getClasses", "(IZ)[Ljava/lang/Class;", + Dalvik_dalvik_system_VMStack_getClasses }, + { "getThreadStackTrace", "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;", + Dalvik_dalvik_system_VMStack_getThreadStackTrace }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/dalvik_system_Zygote.c b/vm/native/dalvik_system_Zygote.c new file mode 100644 index 000000000..fe2b5c074 --- /dev/null +++ b/vm/native/dalvik_system_Zygote.c @@ -0,0 +1,432 @@ +/* + * 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. + */ + +/* + * dalvik.system.Zygote + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <grp.h> +#include <errno.h> + +#if defined(HAVE_PRCTL) +# include <sys/prctl.h> +#endif + +#define ZYGOTE_LOG_TAG "Zygote" + +/* must match values in dalvik.system.Zygote */ +enum { + DEBUG_ENABLE_DEBUGGER = 1, + DEBUG_ENABLE_CHECKJNI = 1 << 1, + DEBUG_ENABLE_ASSERT = 1 << 2, +}; + +/* + * This signal handler is for zygote mode, since the zygote + * must reap its children + */ +static void sigchldHandler(int s) +{ + pid_t pid; + int status; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + /* Log process-death status that we care about. In general it is not + safe to call LOG(...) from a signal handler because of possible + reentrancy. However, we know a priori that the current implementation + of LOG() is safe to call from a SIGCHLD handler in the zygote process. + If the LOG() implementation changes its locking strategy or its use + of syscalls within the lazy-init critical section, its use here may + become unsafe. */ + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + LOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)\n", + (int) pid, WEXITSTATUS(status)); + } else { + IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) { + LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG, + "Process %d exited cleanly (%d)\n", + (int) pid, WEXITSTATUS(status)); + } + } + } else if (WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGKILL) { + LOG(LOG_DEBUG, ZYGOTE_LOG_TAG, + "Process %d terminated by signal (%d)\n", + (int) pid, WTERMSIG(status)); + } else { + IF_LOGV(/*should use ZYGOTE_LOG_TAG*/) { + LOG(LOG_VERBOSE, ZYGOTE_LOG_TAG, + "Process %d terminated by signal (%d)\n", + (int) pid, WTERMSIG(status)); + } + } +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + LOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core\n", + (int) pid); + } +#endif /* ifdef WCOREDUMP */ + } + + /* + * If the just-crashed process is the system_server, bring down zygote + * so that it is restarted by init and system server will be restarted + * from there. + */ + if (pid == gDvm.systemServerPid) { + LOG(LOG_INFO, ZYGOTE_LOG_TAG, + "Exit zygote because system server (%d) has terminated\n", + (int) pid); + kill(getpid(), SIGKILL); + } + } + + if (pid < 0) { + LOG(LOG_WARN, ZYGOTE_LOG_TAG, + "Zygote SIGCHLD error (%d) in waitpid\n",errno); + } +} + +/* + * configure sigchld handler for the zygote process + * This is configured very late, because earlier in the dalvik lifecycle + * we can fork() and exec() for the verifier/optimizer, and we + * want to waitpid() for those rather than have them be harvested immediately. + * + * This ends up being called repeatedly before each fork(), but there's + * no real harm in that. + */ +static void setSignalHandler() +{ + int err; + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = sigchldHandler; + + err = sigaction (SIGCHLD, &sa, NULL); + + if (err < 0) { + LOGW("Error setting SIGCHLD handler errno: %d", errno); + } +} + +/* + * Set the SIGCHLD handler back to default behavior in zygote children + */ +static void unsetSignalHandler() +{ + int err; + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = SIG_DFL; + + err = sigaction (SIGCHLD, &sa, NULL); + + if (err < 0) { + LOGW("Error unsetting SIGCHLD handler errno: %d", errno); + } +} + +/* + * Calls POSIX setgroups() using the int[] object as an argument. + * A NULL argument is tolerated. + */ + +static int setgroupsIntarray(ArrayObject* gidArray) +{ + gid_t *gids; + u4 i; + s4 *contents; + + if (gidArray == NULL) { + return 0; + } + + /* just in case gid_t and u4 are different... */ + gids = alloca(sizeof(gid_t) * gidArray->length); + contents = (s4 *)gidArray->contents; + + for (i = 0 ; i < gidArray->length ; i++) { + gids[i] = (gid_t) contents[i]; + } + + return setgroups((size_t) gidArray->length, gids); +} + +/* + * Sets the resource limits via setrlimit(2) for the values in the + * two-dimensional array of integers that's passed in. The second dimension + * contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is + * treated as an empty array. + * + * -1 is returned on error. + */ +static int setrlimitsFromArray(ArrayObject* rlimits) +{ + u4 i; + struct rlimit rlim; + + if (rlimits == NULL) { + return 0; + } + + memset (&rlim, 0, sizeof(rlim)); + + ArrayObject** tuples = (ArrayObject **)(rlimits->contents); + + for (i = 0; i < rlimits->length; i++) { + ArrayObject * rlimit_tuple = tuples[i]; + s4* contents = (s4 *)rlimit_tuple->contents; + int err; + + if (rlimit_tuple->length != 3) { + LOGE("rlimits array must have a second dimension of size 3"); + return -1; + } + + rlim.rlim_cur = contents[1]; + rlim.rlim_max = contents[2]; + + err = setrlimit(contents[0], &rlim); + + if (err < 0) { + return -1; + } + } + + return 0; +} + +/* native public static int fork(); */ +static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult) +{ + pid_t pid; + int err; + + if (!gDvm.zygote) { + dvmThrowException("Ljava/lang/IllegalStateException;", + "VM instance not started with -Xzygote"); + + RETURN_VOID(); + } + + if (!dvmGcPreZygoteFork()) { + LOGE("pre-fork heap failed\n"); + dvmAbort(); + } + + setSignalHandler(); + + dvmDumpLoaderStats("zygote"); + pid = fork(); + +#ifdef HAVE_ANDROID_OS + if (pid == 0) { + /* child process */ + extern int gMallocLeakZygoteChild; + gMallocLeakZygoteChild = 1; + } +#endif + + RETURN_INT(pid); +} + +/* + * Enable/disable debug features requested by the caller. + * + * debugger + * If set, enable debugging; if not set, disable debugging. This is + * easy to handle, because the JDWP thread isn't started until we call + * dvmInitAfterZygote(). + * checkjni + * If set, make sure "check JNI" is eabled. This is a little weird, + * because we already have the JNIEnv for the main thread set up. However, + * since we only have one thread at this point, it's easy to patch up. + * assert + * If set, make sure assertions are enabled. This gets fairly weird, + * because it affects the result of a method called by class initializers, + * and hence can't affect pre-loaded/initialized classes. + */ +static void enableDebugFeatures(u4 debugFlags) +{ + LOGV("debugFlags is 0x%02x\n", debugFlags); + + gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0); + + if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) { + /* turn it on if it's not already enabled */ + dvmLateEnableCheckedJni(); + } + + if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) { + /* turn it on if it's not already enabled */ + dvmLateEnableAssertions(); + } +} + +/* + * Utility routine to fork zygote and specialize the child process. + */ +static pid_t forkAndSpecializeCommon(const u4* args) +{ + pid_t pid; + + uid_t uid = (uid_t) args[0]; + gid_t gid = (gid_t) args[1]; + ArrayObject* gids = (ArrayObject *)args[2]; + u4 debugFlags = args[3]; + ArrayObject *rlimits = (ArrayObject *)args[4]; + + if (!gDvm.zygote) { + dvmThrowException("Ljava/lang/IllegalStateException;", + "VM instance not started with -Xzygote"); + + return -1; + } + + if (!dvmGcPreZygoteFork()) { + LOGE("pre-fork heap failed\n"); + dvmAbort(); + } + + setSignalHandler(); + + dvmDumpLoaderStats("zygote"); + pid = fork(); + + if (pid == 0) { + int err; + /* The child process */ + +#ifdef HAVE_ANDROID_OS + extern int gMallocLeakZygoteChild; + gMallocLeakZygoteChild = 1; + + /* keep caps across UID change, unless we're staying root */ + if (uid != 0) { + err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + + if (err < 0) { + LOGW("cannot PR_SET_KEEPCAPS errno: %d", errno); + } + } + +#endif /* HAVE_ANDROID_OS */ + + err = setgroupsIntarray(gids); + + if (err < 0) { + LOGW("cannot setgroups() errno: %d", errno); + } + + err = setrlimitsFromArray(rlimits); + + if (err < 0) { + LOGW("cannot setrlimit() errno: %d", errno); + } + + err = setgid(gid); + if (err < 0) { + LOGW("cannot setgid(%d) errno: %d", gid, errno); + } + + err = setuid(uid); + if (err < 0) { + LOGW("cannot setuid(%d) errno: %d", uid, errno); + } + + /* + * Our system thread ID has changed. Get the new one. + */ + Thread* thread = dvmThreadSelf(); + thread->systemTid = dvmGetSysThreadId(); + + /* configure additional debug options */ + enableDebugFeatures(debugFlags); + + unsetSignalHandler(); + gDvm.zygote = false; + if (!dvmInitAfterZygote()) { + LOGE("error in post-zygote initialization\n"); + dvmAbort(); + } + } else if (pid > 0) { + /* the parent process */ + } + + return pid; +} + +/* native public static int forkAndSpecialize(int uid, int gid, + * int[] gids, int debugFlags); + */ +static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args, + JValue* pResult) +{ + pid_t pid; + + pid = forkAndSpecializeCommon(args); + + RETURN_INT(pid); +} + +/* native public static int forkSystemServer(int uid, int gid, + * int[] gids, int debugFlags); + */ +static void Dalvik_dalvik_system_Zygote_forkSystemServer( + const u4* args, JValue* pResult) +{ + pid_t pid; + pid = forkAndSpecializeCommon(args); + + /* The zygote process checks whether the child process has died or not. */ + if (pid > 0) { + int status; + + LOGI("System server process %d has been created", pid); + gDvm.systemServerPid = pid; + /* There is a slight window that the system server process has crashed + * but it went unnoticed because we haven't published its pid yet. So + * we recheck here just to make sure that all is well. + */ + if (waitpid(pid, &status, WNOHANG) == pid) { + LOGE("System server process %d has died. Restarting Zygote!", pid); + kill(getpid(), SIGKILL); + } + } + RETURN_INT(pid); +} + +const DalvikNativeMethod dvm_dalvik_system_Zygote[] = { + { "fork", "()I", + Dalvik_dalvik_system_Zygote_fork }, + { "forkAndSpecialize", "(II[II[[I)I", + Dalvik_dalvik_system_Zygote_forkAndSpecialize }, + { "forkSystemServer", "(II[II[[I)I", + Dalvik_dalvik_system_Zygote_forkSystemServer }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_Class.c b/vm/native/java_lang_Class.c new file mode 100644 index 000000000..09b440ff1 --- /dev/null +++ b/vm/native/java_lang_Class.c @@ -0,0 +1,813 @@ +/* + * 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. + */ + +/* + * java.lang.Class + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * native public boolean desiredAssertionStatus() + * + * Determine the class-init-time assertion status of a class. This is + * called from <clinit> in javac-generated classes that use the Java + * programming language "assert" keyword. + */ +static void Dalvik_java_lang_Class_desiredAssertionStatus(const u4* args, + JValue* pResult) +{ + ClassObject* thisPtr = (ClassObject*) args[0]; + char* className = dvmDescriptorToName(thisPtr->descriptor); + int i; + bool enable = false; + + /* + * Run through the list of arguments specified on the command line. The + * last matching argument takes precedence. + */ + for (i = 0; i < gDvm.assertionCtrlCount; i++) { + const AssertionControl* pCtrl = &gDvm.assertionCtrl[i]; + + if (pCtrl->isPackage) { + /* + * Given "dalvik/system/Debug" or "MyStuff", compute the + * length of the package portion of the class name string. + * + * Unlike most package operations, we allow matching on + * "sub-packages", so "dalvik..." will match "dalvik.Foo" + * and "dalvik.system.Foo". + * + * The pkgOrClass string looks like "dalvik/system/", i.e. it still + * has the terminating slash, so we can be sure we're comparing + * against full package component names. + */ + const char* lastSlash; + int pkgLen; + + lastSlash = strrchr(className, '/'); + if (lastSlash == NULL) { + pkgLen = 0; + } else { + pkgLen = lastSlash - className +1; + } + + if (pCtrl->pkgOrClassLen > pkgLen || + memcmp(pCtrl->pkgOrClass, className, pCtrl->pkgOrClassLen) != 0) + { + LOGV("ASRT: pkg no match: '%s'(%d) vs '%s'\n", + className, pkgLen, pCtrl->pkgOrClass); + } else { + LOGV("ASRT: pkg match: '%s'(%d) vs '%s' --> %d\n", + className, pkgLen, pCtrl->pkgOrClass, pCtrl->enable); + enable = pCtrl->enable; + } + } else { + /* + * "pkgOrClass" holds a fully-qualified class name, converted from + * dot-form to slash-form. An empty string means all classes. + */ + if (pCtrl->pkgOrClass == NULL) { + /* -esa/-dsa; see if class is a "system" class */ + if (strncmp(className, "java/", 5) != 0) { + LOGV("ASRT: sys no match: '%s'\n", className); + } else { + LOGV("ASRT: sys match: '%s' --> %d\n", + className, pCtrl->enable); + enable = pCtrl->enable; + } + } else if (*pCtrl->pkgOrClass == '\0') { + LOGV("ASRT: class all: '%s' --> %d\n", + className, pCtrl->enable); + enable = pCtrl->enable; + } else { + if (strcmp(pCtrl->pkgOrClass, className) != 0) { + LOGV("ASRT: cls no match: '%s' vs '%s'\n", + className, pCtrl->pkgOrClass); + } else { + LOGV("ASRT: cls match: '%s' vs '%s' --> %d\n", + className, pCtrl->pkgOrClass, pCtrl->enable); + enable = pCtrl->enable; + } + } + } + } + + free(className); + RETURN_INT(enable); +} + +/* + * static public Class<?> classForName(String name, boolean initialize, + * ClassLoader loader) + * + * Return the Class object associated with the class or interface with + * the specified name. + * + * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1". + */ +static void Dalvik_java_lang_Class_classForName(const u4* args, JValue* pResult) +{ + StringObject* nameObj = (StringObject*) args[0]; + bool initialize = (args[1] != 0); + Object* loader = (Object*) args[2]; + + RETURN_PTR(dvmFindClassByName(nameObj, loader, initialize)); +} + +/* + * static private ClassLoader getClassLoader(Class clazz) + * + * Return the class' defining class loader. + */ +static void Dalvik_java_lang_Class_getClassLoader(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + + RETURN_PTR(clazz->classLoader); +} + +/* + * public Class<?> getComponentType() + * + * If this is an array type, return the class of the elements; otherwise + * return NULL. + */ +static void Dalvik_java_lang_Class_getComponentType(const u4* args, + JValue* pResult) +{ + ClassObject* thisPtr = (ClassObject*) args[0]; + + if (!dvmIsArrayClass(thisPtr)) + RETURN_PTR(NULL); + + /* + * We can't just return thisPtr->elementClass, because that gives + * us the base type (e.g. X[][][] returns X). If this is a multi- + * dimensional array, we have to do the lookup by name. + */ + if (thisPtr->descriptor[1] == '[') + RETURN_PTR(dvmFindArrayClass(&thisPtr->descriptor[1], + thisPtr->classLoader)); + else + RETURN_PTR(thisPtr->elementClass); +} + +/* + * private static Class<?>[] getDeclaredClasses(Class<?> clazz, + * boolean publicOnly) + * + * Return an array with the classes that are declared by the specified class. + * If "publicOnly" is set, we strip out any classes that don't have "public" + * access. + */ +static void Dalvik_java_lang_Class_getDeclaredClasses(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + bool publicOnly = (args[1] != 0); + ArrayObject* classes; + + classes = dvmGetDeclaredClasses(clazz); + if (classes == NULL) { + if (!dvmCheckException(dvmThreadSelf())) { + /* empty list, so create a zero-length array */ + classes = dvmAllocArrayByClass(gDvm.classJavaLangClassArray, + 0, ALLOC_DEFAULT); + } + } else if (publicOnly) { + int i, newIdx, publicCount = 0; + ClassObject** pSource = (ClassObject**) classes->contents; + + /* count up public classes */ + for (i = 0; i < (int)classes->length; i++) { + if (dvmIsPublicClass(pSource[i])) + publicCount++; + } + + /* create a new array to hold them */ + ArrayObject* newClasses; + newClasses = dvmAllocArrayByClass(gDvm.classJavaLangClassArray, + publicCount, ALLOC_DEFAULT); + + /* copy them over */ + ClassObject** pDest = (ClassObject**) newClasses->contents; + for (i = newIdx = 0; i < (int)classes->length; i++) { + if (dvmIsPublicClass(pSource[i])) + pDest[newIdx++] = pSource[i]; + } + + assert(newIdx == publicCount); + dvmReleaseTrackedAlloc((Object*) classes, NULL); + classes = newClasses; + } + + dvmReleaseTrackedAlloc((Object*) classes, NULL); + RETURN_PTR(classes); +} + +/* + * static Constructor[] getDeclaredConstructors(Class clazz, boolean publicOnly) + * throws SecurityException + */ +static void Dalvik_java_lang_Class_getDeclaredConstructors(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + bool publicOnly = (args[1] != 0); + ArrayObject* constructors; + + constructors = dvmGetDeclaredConstructors(clazz, publicOnly); + dvmReleaseTrackedAlloc((Object*) constructors, NULL); + + RETURN_PTR(constructors); +} + +/* + * static Field[] getDeclaredFields(Class klass, boolean publicOnly) + * throws SecurityException + */ +static void Dalvik_java_lang_Class_getDeclaredFields(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + bool publicOnly = (args[1] != 0); + ArrayObject* fields; + + fields = dvmGetDeclaredFields(clazz, publicOnly); + dvmReleaseTrackedAlloc((Object*) fields, NULL); + + RETURN_PTR(fields); +} + +/* + * static Method[] getDeclaredMethods(Class clazz, boolean publicOnly) + * throws SecurityException + */ +static void Dalvik_java_lang_Class_getDeclaredMethods(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + bool publicOnly = (args[1] != 0); + ArrayObject* methods; + + methods = dvmGetDeclaredMethods(clazz, publicOnly); + dvmReleaseTrackedAlloc((Object*) methods, NULL); + + RETURN_PTR(methods); +} + +/* + * Class[] getInterfaces() + */ +static void Dalvik_java_lang_Class_getInterfaces(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + ArrayObject* interfaces; + + interfaces = dvmGetInterfaces(clazz); + dvmReleaseTrackedAlloc((Object*) interfaces, NULL); + + RETURN_PTR(interfaces); +} + +/* + * private static int getModifiers(Class klass, boolean + * ignoreInnerClassesAttrib) + * + * Return the class' modifier flags. If "ignoreInnerClassesAttrib" is false, + * and this is an inner class, we return the access flags from the inner class + * attribute. + */ +static void Dalvik_java_lang_Class_getModifiers(const u4* args, JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + bool ignoreInner = args[1]; + u4 accessFlags; + + accessFlags = clazz->accessFlags & JAVA_FLAGS_MASK; + + if (!ignoreInner) { + /* see if we have an InnerClass annotation with flags in it */ + StringObject* className = NULL; + int innerFlags; + + if (dvmGetInnerClass(clazz, &className, &innerFlags)) + accessFlags = innerFlags & JAVA_FLAGS_MASK; + + dvmReleaseTrackedAlloc((Object*) className, NULL); + } + + RETURN_INT(accessFlags); +} + +/* + * public String getName() + * + * Return the class' name. + */ +static void Dalvik_java_lang_Class_getName(const u4* args, JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + const char* descriptor = clazz->descriptor; + StringObject* nameObj; + + if ((descriptor[0] != 'L') && (descriptor[0] != '[')) { + /* + * The descriptor indicates that this is the class for + * a primitive type; special-case the return value. + */ + const char* name; + switch (descriptor[0]) { + case 'Z': name = "boolean"; break; + case 'B': name = "byte"; break; + case 'C': name = "char"; break; + case 'S': name = "short"; break; + case 'I': name = "int"; break; + case 'J': name = "long"; break; + case 'F': name = "float"; break; + case 'D': name = "double"; break; + case 'V': name = "void"; break; + default: { + LOGE("Unknown primitive type '%c'\n", descriptor[0]); + assert(false); + RETURN_PTR(NULL); + } + } + + nameObj = dvmCreateStringFromCstr(name, ALLOC_DEFAULT); + } else { + /* + * Convert the UTF-8 name to a java.lang.String. The + * name must use '.' to separate package components. + * + * TODO: this could be more efficient. Consider a custom + * conversion function here that walks the string once and + * avoids the allocation for the common case (name less than, + * say, 128 bytes). + */ + char* dotName = dvmDescriptorToDot(clazz->descriptor); + nameObj = dvmCreateStringFromCstr(dotName, ALLOC_DEFAULT); + free(dotName); + } + + dvmReleaseTrackedAlloc((Object*) nameObj, NULL); + +#if 0 + /* doesn't work -- need "java.lang.String" not "java/lang/String" */ + { + /* + * Find the string in the DEX file and use the copy in the intern + * table if it already exists (else put one there). Only works + * for strings in the DEX file, e.g. not arrays. + * + * We have to do the class lookup by name in the DEX file because + * we don't have a DexClassDef pointer in the ClassObject, and it's + * not worth adding one there just for this. Should be cheaper + * to do this than the string-creation above. + */ + const DexFile* pDexFile = clazz->pDexFile; + const DexClassDef* pClassDef; + const DexClassId* pClassId; + + pDexFile = clazz->pDexFile; + pClassDef = dvmDexFindClass(pDexFile, clazz->descriptor); + pClassId = dvmDexGetClassId(pDexFile, pClassDef->classIdx); + nameObj = dvmDexGetResolvedString(pDexFile, pClassId->nameIdx); + if (nameObj == NULL) { + nameObj = dvmResolveString(clazz, pClassId->nameIdx); + if (nameObj == NULL) + LOGW("WARNING: couldn't find string %u for '%s'\n", + pClassId->nameIdx, clazz->name); + } + } +#endif + + RETURN_PTR(nameObj); +} + +/* + * Return the superclass for instances of this class. + * + * If the class represents a java/lang/Object, an interface, a primitive + * type, or void (which *is* a primitive type??), return NULL. + * + * For an array, return the java/lang/Object ClassObject. + */ +static void Dalvik_java_lang_Class_getSuperclass(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + + if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz)) + RETURN_PTR(NULL); + else + RETURN_PTR(clazz->super); +} + +/* + * public boolean isAssignableFrom(Class<?> cls) + * + * Determine if this class is either the same as, or is a superclass or + * superinterface of, the class specified in the "cls" parameter. + */ +static void Dalvik_java_lang_Class_isAssignableFrom(const u4* args, + JValue* pResult) +{ + ClassObject* thisPtr = (ClassObject*) args[0]; + ClassObject* testClass = (ClassObject*) args[1]; + + if (testClass == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + RETURN_INT(false); + } + RETURN_INT(dvmInstanceof(testClass, thisPtr)); +} + +/* + * public boolean isInstance(Object o) + * + * Dynamic equivalent of Java programming language "instanceof". + */ +static void Dalvik_java_lang_Class_isInstance(const u4* args, + JValue* pResult) +{ + ClassObject* thisPtr = (ClassObject*) args[0]; + Object* testObj = (Object*) args[1]; + + if (testObj == NULL) + RETURN_INT(false); + RETURN_INT(dvmInstanceof(testObj->clazz, thisPtr)); +} + +/* + * public boolean isInterface() + */ +static void Dalvik_java_lang_Class_isInterface(const u4* args, + JValue* pResult) +{ + ClassObject* thisPtr = (ClassObject*) args[0]; + + RETURN_INT(dvmIsInterfaceClass(thisPtr)); +} + +/* + * public boolean isPrimitive() + */ +static void Dalvik_java_lang_Class_isPrimitive(const u4* args, + JValue* pResult) +{ + ClassObject* thisPtr = (ClassObject*) args[0]; + + RETURN_INT(dvmIsPrimitiveClass(thisPtr)); +} + +/* + * public T newInstance() throws InstantiationException, IllegalAccessException + * + * Create a new instance of this class. + */ +static void Dalvik_java_lang_Class_newInstance(const u4* args, JValue* pResult) +{ + Thread* self = dvmThreadSelf(); + ClassObject* clazz = (ClassObject*) args[0]; + Method* init; + Object* newObj; + + /* can't instantiate these */ + if (dvmIsPrimitiveClass(clazz) || dvmIsInterfaceClass(clazz) + || dvmIsArrayClass(clazz) || dvmIsAbstractClass(clazz)) + { + LOGD("newInstance failed: p%d i%d [%d a%d\n", + dvmIsPrimitiveClass(clazz), dvmIsInterfaceClass(clazz), + dvmIsArrayClass(clazz), dvmIsAbstractClass(clazz)); + dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;", + clazz->descriptor); + RETURN_VOID(); + } + + /* initialize the class if it hasn't been already */ + if (!dvmIsClassInitialized(clazz)) { + if (!dvmInitClass(clazz)) { + LOGW("Class init failed in newInstance call (%s)\n", + clazz->descriptor); + assert(dvmCheckException(self)); + RETURN_VOID(); + } + } + + /* find the "nullary" constructor */ + init = dvmFindDirectMethodByDescriptor(clazz, "<init>", "()V"); + if (init == NULL) { + /* common cause: secret "this" arg on non-static inner class ctor */ + LOGD("newInstance failed: no <init>()\n"); + dvmThrowExceptionWithClassMessage("Ljava/lang/InstantiationException;", + clazz->descriptor); + RETURN_VOID(); + } + + /* + * Verify access from the call site. + * + * First, make sure the method invoking Class.newInstance() has permission + * to access the class. + * + * Second, make sure it has permission to invoke the constructor. The + * constructor must be public or, if the caller is in the same package, + * have package scope. + */ + ClassObject* callerClass = dvmGetCaller2Class(self->curFrame); + + if (!dvmCheckClassAccess(callerClass, clazz)) { + LOGD("newInstance failed: %s not accessible to %s\n", + clazz->descriptor, callerClass->descriptor); + dvmThrowException("Ljava/lang/IllegalAccessException;", + "access to class not allowed"); + RETURN_VOID(); + } + if (!dvmCheckMethodAccess(callerClass, init)) { + LOGD("newInstance failed: %s.<init>() not accessible to %s\n", + clazz->descriptor, callerClass->descriptor); + dvmThrowException("Ljava/lang/IllegalAccessException;", + "access to constructor not allowed"); + RETURN_VOID(); + } + + newObj = dvmAllocObject(clazz, ALLOC_DEFAULT); + JValue unused; + + /* invoke constructor; unlike reflection calls, we don't wrap exceptions */ + dvmCallMethod(self, init, newObj, &unused); + dvmReleaseTrackedAlloc(newObj, NULL); + + RETURN_PTR(newObj); +} + +/* + * private Object[] getSignatureAnnotation() + * + * Returns the signature annotation array. + */ +static void Dalvik_java_lang_Class_getSignatureAnnotation(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + ArrayObject* arr = dvmGetClassSignatureAnnotation(clazz); + + dvmReleaseTrackedAlloc((Object*) arr, NULL); + RETURN_PTR(arr); +} + +/* + * public Class getDeclaringClass() + * + * Get the class that encloses this class (if any). + */ +static void Dalvik_java_lang_Class_getDeclaringClass(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + + ClassObject* enclosing = dvmGetDeclaringClass(clazz); + dvmReleaseTrackedAlloc((Object*) enclosing, NULL); + RETURN_PTR(enclosing); +} + +/* + * public Class getEnclosingClass() + * + * Get the class that encloses this class (if any). + */ +static void Dalvik_java_lang_Class_getEnclosingClass(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + + ClassObject* enclosing = dvmGetEnclosingClass(clazz); + dvmReleaseTrackedAlloc((Object*) enclosing, NULL); + RETURN_PTR(enclosing); +} + +/* + * public Constructor getEnclosingConstructor() + * + * Get the constructor that encloses this class (if any). + */ +static void Dalvik_java_lang_Class_getEnclosingConstructor(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + + Object* enclosing = dvmGetEnclosingMethod(clazz); + if (enclosing != NULL) { + dvmReleaseTrackedAlloc(enclosing, NULL); + if (enclosing->clazz == gDvm.classJavaLangReflectConstructor) { + RETURN_PTR(enclosing); + } + assert(enclosing->clazz == gDvm.classJavaLangReflectMethod); + } + RETURN_PTR(NULL); +} + +/* + * public Method getEnclosingMethod() + * + * Get the method that encloses this class (if any). + */ +static void Dalvik_java_lang_Class_getEnclosingMethod(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + + Object* enclosing = dvmGetEnclosingMethod(clazz); + if (enclosing != NULL) { + dvmReleaseTrackedAlloc(enclosing, NULL); + if (enclosing->clazz == gDvm.classJavaLangReflectMethod) { + RETURN_PTR(enclosing); + } + assert(enclosing->clazz == gDvm.classJavaLangReflectConstructor); + } + RETURN_PTR(NULL); +} + +#if 0 +static void Dalvik_java_lang_Class_getGenericInterfaces(const u4* args, + JValue* pResult) +{ + dvmThrowException("Ljava/lang/UnsupportedOperationException;", + "native method not implemented"); + + RETURN_PTR(NULL); +} + +static void Dalvik_java_lang_Class_getGenericSuperclass(const u4* args, + JValue* pResult) +{ + dvmThrowException("Ljava/lang/UnsupportedOperationException;", + "native method not implemented"); + + RETURN_PTR(NULL); +} + +static void Dalvik_java_lang_Class_getTypeParameters(const u4* args, + JValue* pResult) +{ + dvmThrowException("Ljava/lang/UnsupportedOperationException;", + "native method not implemented"); + + RETURN_PTR(NULL); +} +#endif + +/* + * public boolean isAnonymousClass() + * + * Returns true if this is an "anonymous" class. + */ +static void Dalvik_java_lang_Class_isAnonymousClass(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + StringObject* className = NULL; + int accessFlags; + + /* + * If this has an InnerClass annotation, pull it out. Lack of the + * annotation, or an annotation with a NULL class name, indicates + * that this is an anonymous inner class. + */ + if (!dvmGetInnerClass(clazz, &className, &accessFlags)) + RETURN_BOOLEAN(false); + + dvmReleaseTrackedAlloc((Object*) className, NULL); + RETURN_BOOLEAN(className == NULL); +} + +/* + * private Annotation[] getDeclaredAnnotations() + * + * Return the annotations declared on this class. + */ +static void Dalvik_java_lang_Class_getDeclaredAnnotations(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + + ArrayObject* annos = dvmGetClassAnnotations(clazz); + dvmReleaseTrackedAlloc((Object*) annos, NULL); + RETURN_PTR(annos); +} + +/* + * public String getInnerClassName() + * + * Returns the simple name of a member class or local class, or null otherwise. + */ +static void Dalvik_java_lang_Class_getInnerClassName(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + StringObject* nameObj; + int flags; + + if (dvmGetInnerClass(clazz, &nameObj, &flags)) { + dvmReleaseTrackedAlloc((Object*) nameObj, NULL); + RETURN_PTR(nameObj); + } else { + RETURN_PTR(NULL); + } +} + +/* + * static native void setAccessibleNoCheck(AccessibleObject ao, boolean flag); + */ +static void Dalvik_java_lang_Class_setAccessibleNoCheck(const u4* args, + JValue* pResult) +{ + Object* target = (Object*) args[0]; + u4 flag = (u4) args[1]; + + dvmSetFieldBoolean(target, gDvm.offJavaLangReflectAccessibleObject_flag, + flag); +} + +const DalvikNativeMethod dvm_java_lang_Class[] = { + { "desiredAssertionStatus", "()Z", + Dalvik_java_lang_Class_desiredAssertionStatus }, + { "classForName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;", + Dalvik_java_lang_Class_classForName }, + { "getClassLoader", "(Ljava/lang/Class;)Ljava/lang/ClassLoader;", + Dalvik_java_lang_Class_getClassLoader }, + { "getComponentType", "()Ljava/lang/Class;", + Dalvik_java_lang_Class_getComponentType }, + { "getSignatureAnnotation", "()[Ljava/lang/Object;", + Dalvik_java_lang_Class_getSignatureAnnotation }, + { "getDeclaredClasses", "(Ljava/lang/Class;Z)[Ljava/lang/Class;", + Dalvik_java_lang_Class_getDeclaredClasses }, + { "getDeclaredConstructors", "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Constructor;", + Dalvik_java_lang_Class_getDeclaredConstructors }, + { "getDeclaredFields", "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;", + Dalvik_java_lang_Class_getDeclaredFields }, + { "getDeclaredMethods", "(Ljava/lang/Class;Z)[Ljava/lang/reflect/Method;", + Dalvik_java_lang_Class_getDeclaredMethods }, + { "getInterfaces", "()[Ljava/lang/Class;", + Dalvik_java_lang_Class_getInterfaces }, + { "getModifiers", "(Ljava/lang/Class;Z)I", + Dalvik_java_lang_Class_getModifiers }, + { "getName", "()Ljava/lang/String;", + Dalvik_java_lang_Class_getName }, + { "getSuperclass", "()Ljava/lang/Class;", + Dalvik_java_lang_Class_getSuperclass }, + { "isAssignableFrom", "(Ljava/lang/Class;)Z", + Dalvik_java_lang_Class_isAssignableFrom }, + { "isInstance", "(Ljava/lang/Object;)Z", + Dalvik_java_lang_Class_isInstance }, + { "isInterface", "()Z", + Dalvik_java_lang_Class_isInterface }, + { "isPrimitive", "()Z", + Dalvik_java_lang_Class_isPrimitive }, + { "newInstanceImpl", "()Ljava/lang/Object;", + Dalvik_java_lang_Class_newInstance }, + { "getDeclaringClass", "()Ljava/lang/Class;", + Dalvik_java_lang_Class_getDeclaringClass }, + { "getEnclosingClass", "()Ljava/lang/Class;", + Dalvik_java_lang_Class_getEnclosingClass }, + { "getEnclosingConstructor", "()Ljava/lang/reflect/Constructor;", + Dalvik_java_lang_Class_getEnclosingConstructor }, + { "getEnclosingMethod", "()Ljava/lang/reflect/Method;", + Dalvik_java_lang_Class_getEnclosingMethod }, +#if 0 + { "getGenericInterfaces", "()[Ljava/lang/reflect/Type;", + Dalvik_java_lang_Class_getGenericInterfaces }, + { "getGenericSuperclass", "()Ljava/lang/reflect/Type;", + Dalvik_java_lang_Class_getGenericSuperclass }, + { "getTypeParameters", "()Ljava/lang/reflect/TypeVariable;", + Dalvik_java_lang_Class_getTypeParameters }, +#endif + { "isAnonymousClass", "()Z", + Dalvik_java_lang_Class_isAnonymousClass }, + { "getDeclaredAnnotations", "()[Ljava/lang/annotation/Annotation;", + Dalvik_java_lang_Class_getDeclaredAnnotations }, + { "getInnerClassName", "()Ljava/lang/String;", + Dalvik_java_lang_Class_getInnerClassName }, + { "setAccessibleNoCheck", "(Ljava/lang/reflect/AccessibleObject;Z)V", + Dalvik_java_lang_Class_setAccessibleNoCheck }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_Object.c b/vm/native/java_lang_Object.c new file mode 100644 index 000000000..89e219fea --- /dev/null +++ b/vm/native/java_lang_Object.c @@ -0,0 +1,112 @@ +/* + * 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. + */ + +/* + * java.lang.Object + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private Object internalClone() + * + * Implements most of Object.clone(). + */ +static void Dalvik_java_lang_Object_internalClone(const u4* args, + JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + Object* clone = dvmCloneObject(thisPtr); + + dvmReleaseTrackedAlloc(clone, NULL); + RETURN_PTR(clone); +} + +/* + * public int hashCode() + */ +static void Dalvik_java_lang_Object_hashCode(const u4* args, JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + RETURN_INT(dvmGetObjectHashCode(thisPtr)); +} + +/* + * public Class getClass() + */ +static void Dalvik_java_lang_Object_getClass(const u4* args, JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + + RETURN_PTR(thisPtr->clazz); +} + +/* + * public void notify() + * + * NOTE: we declare this as a full DalvikBridgeFunc, rather than a + * DalvikNativeFunc, because we really want to avoid the "self" lookup. + */ +static void Dalvik_java_lang_Object_notify(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + Object* thisPtr = (Object*) args[0]; + + dvmObjectNotify(self, thisPtr); + RETURN_VOID(); +} + +/* + * public void notifyAll() + */ +static void Dalvik_java_lang_Object_notifyAll(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + Object* thisPtr = (Object*) args[0]; + + dvmObjectNotifyAll(self, thisPtr); + RETURN_VOID(); +} + +/* + * public void wait(long ms, int ns) throws InterruptedException + */ +static void Dalvik_java_lang_Object_wait(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + Object* thisPtr = (Object*) args[0]; + + dvmObjectWait(self, thisPtr, GET_ARG_LONG(args,1), (s4)args[3], true); + RETURN_VOID(); +} + +const DalvikNativeMethod dvm_java_lang_Object[] = { + { "internalClone", "(Ljava/lang/Cloneable;)Ljava/lang/Object;", + Dalvik_java_lang_Object_internalClone }, + { "hashCode", "()I", + Dalvik_java_lang_Object_hashCode }, + { "notify", "()V", + (DalvikNativeFunc) Dalvik_java_lang_Object_notify }, + { "notifyAll", "()V", + (DalvikNativeFunc) Dalvik_java_lang_Object_notifyAll }, + { "wait", "(JI)V", + (DalvikNativeFunc) Dalvik_java_lang_Object_wait }, + { "getClass", "()Ljava/lang/Class;", + Dalvik_java_lang_Object_getClass }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_Runtime.c b/vm/native/java_lang_Runtime.c new file mode 100644 index 000000000..1278f032b --- /dev/null +++ b/vm/native/java_lang_Runtime.c @@ -0,0 +1,164 @@ +/* + * 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. + */ + +/* + * java.lang.Runtime + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * public void gc() + * + * Initiate a gc. + */ +static void Dalvik_java_lang_Runtime_gc(const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + + dvmCollectGarbage(false); + RETURN_VOID(); +} + +/* + * private static void nativeExit(int code, boolean isExit) + * + * Runtime.exit() calls this after doing shutdown processing. Runtime.halt() + * uses this as well. + */ +static void Dalvik_java_lang_Runtime_nativeExit(const u4* args, + JValue* pResult) +{ + int status = args[0]; + bool isExit = (args[1] != 0); + + if (isExit && gDvm.exitHook != NULL) { + dvmChangeStatus(NULL, THREAD_NATIVE); + (*gDvm.exitHook)(status); // not expected to return + dvmChangeStatus(NULL, THREAD_RUNNING); + LOGW("JNI exit hook returned\n"); + } + LOGD("Calling exit(%d)\n", status); + exit(status); +} + +/* + * static boolean nativeLoad(String filename, ClassLoader loader) + * + * Load the specified full path as a dynamic library filled with + * JNI-compatible methods. + */ +static void Dalvik_java_lang_Runtime_nativeLoad(const u4* args, + JValue* pResult) +{ + StringObject* fileNameObj = (StringObject*) args[0]; + Object* classLoader = (Object*) args[1]; + char* fileName; + int result; + + if (fileNameObj == NULL) + RETURN_INT(false); + fileName = dvmCreateCstrFromString(fileNameObj); + + result = dvmLoadNativeCode(fileName, classLoader); + + free(fileName); + RETURN_INT(result); +} + +/* + * public void runFinalization(boolean forced) + * + * Requests that the VM runs finalizers for objects on the heap. If the + * parameter forced is true, then the VM needs to ensure finalization. + * Otherwise this only inspires the VM to make a best-effort attempt to + * run finalizers before returning, but it's not guaranteed to actually + * do anything. + */ +static void Dalvik_java_lang_Runtime_runFinalization(const u4* args, + JValue* pResult) +{ + bool forced = (args[0] != 0); + + dvmWaitForHeapWorkerIdle(); + if (forced) { + // TODO(Google) Need to explicitly implement this, + // although dvmWaitForHeapWorkerIdle() + // should usually provide the "forced" + // behavior already. + } + + RETURN_VOID(); +} + +/* + * public void maxMemory() + * + * Returns GC heap max memory in bytes. + */ +static void Dalvik_java_lang_Runtime_maxMemory(const u4* args, JValue* pResult) +{ + unsigned int result = gDvm.heapSizeMax; + RETURN_LONG(result); +} + +/* + * public void totalMemory() + * + * Returns GC heap total memory in bytes. + */ +static void Dalvik_java_lang_Runtime_totalMemory(const u4* args, + JValue* pResult) +{ + int result = dvmGetHeapDebugInfo(kVirtualHeapSize); + RETURN_LONG(result); +} + +/* + * public void freeMemory() + * + * Returns GC heap free memory in bytes. + */ +static void Dalvik_java_lang_Runtime_freeMemory(const u4* args, + JValue* pResult) +{ + int result = dvmGetHeapDebugInfo(kVirtualHeapSize) + - dvmGetHeapDebugInfo(kVirtualHeapAllocated); + if (result < 0) { + result = 0; + } + RETURN_LONG(result); +} + +const DalvikNativeMethod dvm_java_lang_Runtime[] = { + { "freeMemory", "()J", + Dalvik_java_lang_Runtime_freeMemory }, + { "gc", "()V", + Dalvik_java_lang_Runtime_gc }, + { "maxMemory", "()J", + Dalvik_java_lang_Runtime_maxMemory }, + { "nativeExit", "(IZ)V", + Dalvik_java_lang_Runtime_nativeExit }, + { "nativeLoad", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Z", + Dalvik_java_lang_Runtime_nativeLoad }, + { "runFinalization", "(Z)V", + Dalvik_java_lang_Runtime_runFinalization }, + { "totalMemory", "()J", + Dalvik_java_lang_Runtime_totalMemory }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_String.c b/vm/native/java_lang_String.c new file mode 100644 index 000000000..48a8f83d4 --- /dev/null +++ b/vm/native/java_lang_String.c @@ -0,0 +1,43 @@ +/* + * 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. + */ + +/* + * java.lang.String + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * public String intern() + * + * Intern a string in the VM string table. + */ +static void Dalvik_java_lang_String_intern(const u4* args, JValue* pResult) +{ + StringObject* str = (StringObject*) args[0]; + StringObject* interned; + + interned = dvmLookupInternedString(str); + RETURN_PTR(interned); +} + +const DalvikNativeMethod dvm_java_lang_String[] = { + { "intern", "()Ljava/lang/String;", + Dalvik_java_lang_String_intern }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_System.c b/vm/native/java_lang_System.c new file mode 100644 index 000000000..b26a36897 --- /dev/null +++ b/vm/native/java_lang_System.c @@ -0,0 +1,292 @@ +/* + * 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. + */ + +/* + * java.lang.Class + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * public static void arraycopy(Object src, int srcPos, Object dest, + * int destPos, int length) + * + * The description of this function is long, and describes a multitude + * of checks and exceptions. + */ +static void Dalvik_java_lang_System_arraycopy(const u4* args, JValue* pResult) +{ + void* (*copyFunc)(void *dest, const void *src, size_t n); + ArrayObject* srcArray; + ArrayObject* dstArray; + ClassObject* srcClass; + ClassObject* dstClass; + int srcPos, dstPos, length; + char srcType, dstType; + bool srcPrim, dstPrim; + + srcArray = (ArrayObject*) args[0]; + srcPos = args[1]; + dstArray = (ArrayObject*) args[2]; + dstPos = args[3]; + length = args[4]; + + if (srcArray == dstArray) + copyFunc = memmove; /* might overlap */ + else + copyFunc = memcpy; /* can't overlap, use faster func */ + + /* check for null or bad pointer */ + if (!dvmValidateObject((Object*)srcArray) || + !dvmValidateObject((Object*)dstArray)) + { + assert(dvmCheckException(dvmThreadSelf())); + RETURN_VOID(); + } + /* make sure it's an array */ + if (!dvmIsArray(srcArray) || !dvmIsArray(dstArray)) { + dvmThrowException("Ljava/lang/ArrayStoreException;", NULL); + RETURN_VOID(); + } + + // avoid int overflow + if (srcPos < 0 || dstPos < 0 || length < 0 || + srcPos > (int) srcArray->length - length || + dstPos > (int) dstArray->length - length) + { + dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", NULL); + RETURN_VOID(); + } + + srcClass = srcArray->obj.clazz; + dstClass = dstArray->obj.clazz; + srcType = srcClass->descriptor[1]; + dstType = dstClass->descriptor[1]; + + /* + * If one of the arrays holds a primitive type, the other array must + * hold the same type. + */ + srcPrim = (srcType != '[' && srcType != 'L'); + dstPrim = (dstType != '[' && dstType != 'L'); + if (srcPrim || dstPrim) { + int width; + + if (srcPrim != dstPrim || srcType != dstType) { + dvmThrowException("Ljava/lang/ArrayStoreException;", NULL); + RETURN_VOID(); + } + + switch (srcClass->descriptor[1]) { + case 'B': + case 'Z': + width = 1; + break; + case 'C': + case 'S': + width = 2; + break; + case 'F': + case 'I': + width = 4; + break; + case 'D': + case 'J': + width = 8; + break; + default: /* 'V' or something weird */ + LOGE("Weird array type '%s'\n", srcClass->descriptor); + assert(false); + width = 0; + break; + } + + if (false) LOGVV("arraycopy prim dst=%p %d src=%p %d len=%d\n", + dstArray->contents, dstPos * width, + srcArray->contents, srcPos * width, + length * width); + (*copyFunc)((u1*)dstArray->contents + dstPos * width, + (const u1*)srcArray->contents + srcPos * width, + length * width); + } else { + /* + * Neither class is primitive. See if elements in "src" are instances + * of elements in "dst" (e.g. copy String to String or String to + * Object). + */ + int width = sizeof(Object*); + + if (srcClass->arrayDim == dstClass->arrayDim && + dvmInstanceof(srcClass, dstClass)) + { + /* + * "dst" can hold "src"; copy the whole thing. + */ + if (false) LOGVV("arraycopy ref dst=%p %d src=%p %d len=%d\n", + dstArray->contents, dstPos * width, + srcArray->contents, srcPos * width, + length * width); + (*copyFunc)((u1*)dstArray->contents + dstPos * width, + (const u1*)srcArray->contents + srcPos * width, + length * width); + } else { + /* + * The arrays are not fundamentally compatible. However, we may + * still be able to do this if the destination object is compatible + * (e.g. copy Object to String, but the Object being copied is + * actually a String). We need to copy elements one by one until + * something goes wrong. + * + * Because of overlapping moves, what we really want to do is + * compare the types and count up how many we can move, then call + * memmove() to shift the actual data. If we just start from the + * front we could do a smear rather than a move. + */ + Object** srcObj; + Object** dstObj; + int copyCount; + ClassObject* clazz = NULL; + + srcObj = ((Object**) srcArray->contents) + srcPos; + dstObj = ((Object**) dstArray->contents) + dstPos; + + if (length > 0 && srcObj[0] != NULL) + { + clazz = srcObj[0]->clazz; + if (!dvmCanPutArrayElement(clazz, dstClass)) + clazz = NULL; + } + + for (copyCount = 0; copyCount < length; copyCount++) + { + if (srcObj[copyCount] != NULL && + srcObj[copyCount]->clazz != clazz && + !dvmCanPutArrayElement(srcObj[copyCount]->clazz, dstClass)) + { + /* can't put this element into the array */ + break; + } + } + + if (false) LOGVV("arraycopy iref dst=%p %d src=%p %d count=%d of %d\n", + dstArray->contents, dstPos * width, + srcArray->contents, srcPos * width, + copyCount, length); + (*copyFunc)((u1*)dstArray->contents + dstPos * width, + (const u1*)srcArray->contents + srcPos * width, + copyCount * width); + + if (copyCount != length) { + dvmThrowException("Ljava/lang/ArrayStoreException;", NULL); + RETURN_VOID(); + } + } + } + + RETURN_VOID(); +} + +/* + * static long currentTimeMillis() + * + * Current time, in miliseconds. This doesn't need to be internal to the + * VM, but we're already handling java.lang.System here. + */ +static void Dalvik_java_lang_System_currentTimeMillis(const u4* args, + JValue* pResult) +{ + struct timeval tv; + + UNUSED_PARAMETER(args); + + gettimeofday(&tv, (struct timezone *) NULL); + long long when = tv.tv_sec * 1000LL + tv.tv_usec / 1000; + + RETURN_LONG(when); +} + +/* + * static long nanoTime() + * + * Current monotonically-increasing time, in nanoseconds. This doesn't + * need to be internal to the VM, but we're already handling + * java.lang.System here. + */ +static void Dalvik_java_lang_System_nanoTime(const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + + u8 when = dvmGetRelativeTimeNsec(); + RETURN_LONG(when); +} + +/* + * static int identityHashCode(Object x) + * + * Returns that hash code that the default hashCode() + * method would return for "x", even if "x"s class + * overrides hashCode(). + */ +static void Dalvik_java_lang_System_identityHashCode(const u4* args, + JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + RETURN_INT(dvmGetObjectHashCode(thisPtr)); +} + +/* + * public static String mapLibraryName(String libname) + */ +static void Dalvik_java_lang_System_mapLibraryName(const u4* args, + JValue* pResult) +{ + StringObject* nameObj = (StringObject*) args[0]; + StringObject* result = NULL; + char* name; + char* mappedName; + + if (nameObj == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + RETURN_VOID(); + } + + name = dvmCreateCstrFromString(nameObj); + mappedName = dvmCreateSystemLibraryName(name); + if (mappedName != NULL) { + result = dvmCreateStringFromCstr(mappedName, ALLOC_DEFAULT); + dvmReleaseTrackedAlloc((Object*) result, NULL); + } + + free(name); + free(mappedName); + RETURN_PTR(result); +} + +const DalvikNativeMethod dvm_java_lang_System[] = { + { "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", + Dalvik_java_lang_System_arraycopy }, + { "currentTimeMillis", "()J", + Dalvik_java_lang_System_currentTimeMillis }, + { "nanoTime", "()J", + Dalvik_java_lang_System_nanoTime }, + { "identityHashCode", "(Ljava/lang/Object;)I", + Dalvik_java_lang_System_identityHashCode }, + { "mapLibraryName", "(Ljava/lang/String;)Ljava/lang/String;", + Dalvik_java_lang_System_mapLibraryName }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_SystemProperties.c b/vm/native/java_lang_SystemProperties.c new file mode 100644 index 000000000..d3501f4ab --- /dev/null +++ b/vm/native/java_lang_SystemProperties.c @@ -0,0 +1,65 @@ +/* + * 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. + */ + +/* + * java.lang.SystemProperties + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * Expected call sequence: + * (1) call SystemProperties.preInit() to get VM defaults + * (2) set any higher-level defaults + * (3) call SystemProperties.postInit() to get command-line overrides + * This currently happens the first time somebody tries to access a property. + * + * SystemProperties is a Dalvik-specific package-scope class. + */ + +/* + * void preInit() + * + * Tells the VM to populate the properties table with VM defaults. + */ +static void Dalvik_java_lang_SystemProperties_preInit(const u4* args, + JValue* pResult) +{ + dvmCreateDefaultProperties((Object*) args[0]); + RETURN_VOID(); +} + +/* + * void postInit() + * + * Tells the VM to update properties with values from the command line. + */ +static void Dalvik_java_lang_SystemProperties_postInit(const u4* args, + JValue* pResult) +{ + dvmSetCommandLineProperties((Object*) args[0]); + RETURN_VOID(); +} + +const DalvikNativeMethod dvm_java_lang_SystemProperties[] = { + { "preInit", "()V", + Dalvik_java_lang_SystemProperties_preInit }, + { "postInit", "()V", + Dalvik_java_lang_SystemProperties_postInit }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_Throwable.c b/vm/native/java_lang_Throwable.c new file mode 100644 index 000000000..3cd0e2ea3 --- /dev/null +++ b/vm/native/java_lang_Throwable.c @@ -0,0 +1,61 @@ +/* + * 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. + */ + +/* + * java.lang.Throwable + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private static Object nativeFillInStackTrace() + */ +static void Dalvik_java_lang_Throwable_nativeFillInStackTrace(const u4* args, + JValue* pResult) +{ + Object* stackState = NULL; + + UNUSED_PARAMETER(args); + + stackState = dvmFillInStackTrace(dvmThreadSelf()); + RETURN_PTR(stackState); +} + +/* + * private static StackTraceElement[] nativeGetStackTrace(Object stackState) + * + * The "stackState" argument must be the value returned by an earlier call to + * nativeFillInStackTrace(). + */ +static void Dalvik_java_lang_Throwable_nativeGetStackTrace(const u4* args, + JValue* pResult) +{ + Object* stackState = (Object*) args[0]; + ArrayObject* elements = NULL; + + elements = dvmGetStackTrace(stackState); + RETURN_PTR(elements); +} + +const DalvikNativeMethod dvm_java_lang_Throwable[] = { + { "nativeFillInStackTrace", "()Ljava/lang/Object;", + Dalvik_java_lang_Throwable_nativeFillInStackTrace }, + { "nativeGetStackTrace", "(Ljava/lang/Object;)[Ljava/lang/StackTraceElement;", + Dalvik_java_lang_Throwable_nativeGetStackTrace }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_VMClassLoader.c b/vm/native/java_lang_VMClassLoader.c new file mode 100644 index 000000000..2d4e4b3a4 --- /dev/null +++ b/vm/native/java_lang_VMClassLoader.c @@ -0,0 +1,203 @@ +/* + * 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. + */ + +/* + * java.lang.VMClassLoader + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * static Class defineClass(ClassLoader cl, String name, + * byte[] data, int offset, int len, ProtectionDomain pd) + * throws ClassFormatError + * + * Convert an array of bytes to a Class object. + */ +static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args, + JValue* pResult) +{ + Object* loader = (Object*) args[0]; + StringObject* nameObj = (StringObject*) args[1]; + const u1* data = (const u1*) args[2]; + int offset = args[3]; + int len = args[4]; + Object* pd = (Object*) args[5]; + char* name = NULL; + + name = dvmCreateCstrFromString(nameObj); + LOGE("ERROR: defineClass(%p, %s, %p, %d, %d, %p)\n", + loader, name, data, offset, len, pd); + dvmThrowException("Ljava/lang/UnsupportedOperationException;", + "can't load this type of class file"); + + free(name); + RETURN_VOID(); +} + +/* + * static Class defineClass(ClassLoader cl, byte[] data, int offset, + * int len, ProtectionDomain pd) + * throws ClassFormatError + * + * Convert an array of bytes to a Class object. Deprecated version of + * previous method, lacks name parameter. + */ +static void Dalvik_java_lang_VMClassLoader_defineClass2(const u4* args, + JValue* pResult) +{ + Object* loader = (Object*) args[0]; + const u1* data = (const u1*) args[1]; + int offset = args[2]; + int len = args[3]; + Object* pd = (Object*) args[4]; + + LOGE("ERROR: defineClass(%p, %p, %d, %d, %p)\n", + loader, data, offset, len, pd); + dvmThrowException("Ljava/lang/UnsupportedOperationException;", + "can't load this type of class file"); + + RETURN_VOID(); +} + +/* + * static Class findLoadedClass(ClassLoader cl, String name) + */ +static void Dalvik_java_lang_VMClassLoader_findLoadedClass(const u4* args, + JValue* pResult) +{ + Object* loader = (Object*) args[0]; + StringObject* nameObj = (StringObject*) args[1]; + ClassObject* clazz = NULL; + char* name = NULL; + char* descriptor = NULL; + char* cp; + + if (nameObj == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + goto bail; + } + + /* + * Get a UTF-8 copy of the string, and convert dots to slashes. + */ + name = dvmCreateCstrFromString(nameObj); + if (name == NULL) + goto bail; + + descriptor = dvmDotToDescriptor(name); + if (descriptor == NULL) + goto bail; + + clazz = dvmLookupClass(descriptor, loader, false); + LOGVV("look: %s ldr=%p --> %p\n", descriptor, loader, clazz); + +bail: + free(name); + free(descriptor); + RETURN_PTR(clazz); +} + +/* + * private static int getBootClassPathSize() + * + * Get the number of entries in the boot class path. + */ +static void Dalvik_java_lang_VMClassLoader_getBootClassPathSize(const u4* args, + JValue* pResult) +{ + int count = dvmGetBootPathSize(); + RETURN_INT(count); +} + +/* + * private static String getBootClassPathResource(String name, int index) + * + * Find a resource with a matching name in a boot class path entry. + * + * This mimics the previous VM interface, since we're sharing class libraries. + */ +static void Dalvik_java_lang_VMClassLoader_getBootClassPathResource( + const u4* args, JValue* pResult) +{ + StringObject* nameObj = (StringObject*) args[0]; + StringObject* result; + int idx = args[1]; + char* name; + + name = dvmCreateCstrFromString(nameObj); + if (name == NULL) + RETURN_PTR(NULL); + + result = dvmGetBootPathResource(name, idx); + free(name); + dvmReleaseTrackedAlloc((Object*)result, NULL); + RETURN_PTR(result); +} + +/* + * static final Class getPrimitiveClass(char prim_type) + */ +static void Dalvik_java_lang_VMClassLoader_getPrimitiveClass(const u4* args, + JValue* pResult) +{ + int primType = args[0]; + + pResult->l = dvmFindPrimitiveClass(primType); +} + +/* + * static Class loadClass(String name, boolean resolve) + * throws ClassNotFoundException + * + * Load class using bootstrap class loader. + * + * Return the Class object associated with the class or interface with + * the specified name. + * + * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1". + */ +static void Dalvik_java_lang_VMClassLoader_loadClass(const u4* args, + JValue* pResult) +{ + StringObject* nameObj = (StringObject*) args[0]; + bool resolve = (args[1] != 0); + ClassObject* clazz; + + clazz = dvmFindClassByName(nameObj, NULL, resolve); + assert(clazz == NULL || dvmIsClassLinked(clazz)); + RETURN_PTR(clazz); +} + +const DalvikNativeMethod dvm_java_lang_VMClassLoader[] = { + { "defineClass", "(Ljava/lang/ClassLoader;Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;", + Dalvik_java_lang_VMClassLoader_defineClass }, + { "defineClass", "(Ljava/lang/ClassLoader;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;", + Dalvik_java_lang_VMClassLoader_defineClass2 }, + { "findLoadedClass", "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;", + Dalvik_java_lang_VMClassLoader_findLoadedClass }, + { "getBootClassPathSize", "()I", + Dalvik_java_lang_VMClassLoader_getBootClassPathSize }, + { "getBootClassPathResource", "(Ljava/lang/String;I)Ljava/lang/String;", + Dalvik_java_lang_VMClassLoader_getBootClassPathResource }, + { "getPrimitiveClass", "(C)Ljava/lang/Class;", + Dalvik_java_lang_VMClassLoader_getPrimitiveClass }, + { "loadClass", "(Ljava/lang/String;Z)Ljava/lang/Class;", + Dalvik_java_lang_VMClassLoader_loadClass }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_VMThread.c b/vm/native/java_lang_VMThread.c new file mode 100644 index 000000000..459fd35e4 --- /dev/null +++ b/vm/native/java_lang_VMThread.c @@ -0,0 +1,263 @@ +/* + * 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. + */ + +/* + * java.lang.VMThread + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * static void create(Thread t, long stacksize) + * + * This is eventually called as a result of Thread.start(). + * + * Throws an exception on failure. + */ +static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult) +{ + Object* threadObj = (Object*) args[0]; + s8 stackSize = GET_ARG_LONG(args, 1); + + dvmCreateInterpThread(threadObj, (int) stackSize); + RETURN_VOID(); +} + +/* + * static Thread currentThread() + */ +static void Dalvik_java_lang_VMThread_currentThread(const u4* args, + JValue* pResult) +{ + UNUSED_PARAMETER(args); + + RETURN_PTR(dvmThreadSelf()->threadObj); +} + +/* + * void getStatus() + * + * Gets the Thread status. Result is in VM terms, has to be mapped to + * Thread.State by interpreted code. + */ +static void Dalvik_java_lang_VMThread_getStatus(const u4* args, JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + Thread* thread; + int result; + + dvmLockThreadList(NULL); + thread = dvmGetThreadFromThreadObject(thisPtr); + if (thread != NULL) + result = thread->status; + else + result = THREAD_ZOMBIE; // assume it used to exist and is now gone + dvmUnlockThreadList(); + + RETURN_INT(result); +} + +/* + * boolean holdsLock(Object object) + * + * Returns whether the current thread has a monitor lock on the specific + * object. + */ +static void Dalvik_java_lang_VMThread_holdsLock(const u4* args, JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + Object* object = (Object*) args[1]; + Thread* thread; + + if (object == NULL) { + dvmThrowException("Ljava/lang/NullPointerException;", NULL); + RETURN_VOID(); + } + + dvmLockThreadList(NULL); + thread = dvmGetThreadFromThreadObject(thisPtr); + int result = dvmHoldsLock(thread, object); + dvmUnlockThreadList(); + + RETURN_BOOLEAN(result); +} + +/* + * void interrupt() + * + * Interrupt a thread that is waiting (or is about to wait) on a monitor. + */ +static void Dalvik_java_lang_VMThread_interrupt(const u4* args, JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + Thread* thread; + + dvmLockThreadList(NULL); + thread = dvmGetThreadFromThreadObject(thisPtr); + if (thread != NULL) + dvmThreadInterrupt(thread); + dvmUnlockThreadList(); + RETURN_VOID(); +} + +/* + * static boolean interrupted() + * + * Determine if the current thread has been interrupted. Clears the flag. + */ +static void Dalvik_java_lang_VMThread_interrupted(const u4* args, + JValue* pResult) +{ + Thread* self = dvmThreadSelf(); + bool interrupted; + + UNUSED_PARAMETER(args); + + interrupted = self->interrupted; + self->interrupted = false; + RETURN_BOOLEAN(interrupted); +} + +/* + * boolean isInterrupted() + * + * Determine if the specified thread has been interrupted. Does not clear + * the flag. + */ +static void Dalvik_java_lang_VMThread_isInterrupted(const u4* args, + JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + Thread* thread; + bool interrupted; + + dvmLockThreadList(NULL); + thread = dvmGetThreadFromThreadObject(thisPtr); + if (thread != NULL) + interrupted = thread->interrupted; + else + interrupted = false; + dvmUnlockThreadList(); + + RETURN_BOOLEAN(interrupted); +} + +/* + * void nameChanged(String newName) + * + * The name of the target thread has changed. We may need to alert DDMS. + */ +static void Dalvik_java_lang_VMThread_nameChanged(const u4* args, + JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + StringObject* nameStr = (StringObject*) args[1]; + Thread* thread; + int threadId = -1; + + /* get the thread's ID */ + dvmLockThreadList(NULL); + thread = dvmGetThreadFromThreadObject(thisPtr); + if (thread != NULL) + threadId = thread->threadId; + dvmUnlockThreadList(); + + dvmDdmSendThreadNameChange(threadId, nameStr); + //char* str = dvmCreateCstrFromString(nameStr); + //LOGI("UPDATE: threadid=%d now '%s'\n", threadId, str); + //free(str); + + RETURN_VOID(); +} + +/* + * void setPriority(int newPriority) + * + * Alter the priority of the specified thread. "newPriority" will range + * from Thread.MIN_PRIORITY to Thread.MAX_PRIORITY (1-10), with "normal" + * threads at Thread.NORM_PRIORITY (5). + */ +static void Dalvik_java_lang_VMThread_setPriority(const u4* args, + JValue* pResult) +{ + Object* thisPtr = (Object*) args[0]; + int newPriority = args[1]; + Thread* thread; + + dvmLockThreadList(NULL); + thread = dvmGetThreadFromThreadObject(thisPtr); + if (thread != NULL) + dvmChangeThreadPriority(thread, newPriority); + //dvmDumpAllThreads(false); + dvmUnlockThreadList(); + + RETURN_VOID(); +} + +/* + * static void sleep(long msec, int nsec) + */ +static void Dalvik_java_lang_VMThread_sleep(const u4* args, JValue* pResult) +{ + Thread* self = dvmThreadSelf(); + dvmThreadSleep(GET_ARG_LONG(args,0), args[2]); + RETURN_VOID(); +} + +/* + * public void yield() + * + * Causes the thread to temporarily pause and allow other threads to execute. + * + * The exact behavior is poorly defined. Some discussion here: + * http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0944.html + */ +static void Dalvik_java_lang_VMThread_yield(const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + + sched_yield(); + + RETURN_VOID(); +} + +const DalvikNativeMethod dvm_java_lang_VMThread[] = { + { "create", "(Ljava/lang/Thread;J)V", + Dalvik_java_lang_VMThread_create }, + { "currentThread", "()Ljava/lang/Thread;", + Dalvik_java_lang_VMThread_currentThread }, + { "getStatus", "()I", + Dalvik_java_lang_VMThread_getStatus }, + { "holdsLock", "(Ljava/lang/Object;)Z", + Dalvik_java_lang_VMThread_holdsLock }, + { "interrupt", "()V", + Dalvik_java_lang_VMThread_interrupt }, + { "interrupted", "()Z", + Dalvik_java_lang_VMThread_interrupted }, + { "isInterrupted", "()Z", + Dalvik_java_lang_VMThread_isInterrupted }, + { "nameChanged", "(Ljava/lang/String;)V", + Dalvik_java_lang_VMThread_nameChanged }, + { "setPriority", "(I)V", + Dalvik_java_lang_VMThread_setPriority }, + { "sleep", "(JI)V", + Dalvik_java_lang_VMThread_sleep }, + { "yield", "()V", + Dalvik_java_lang_VMThread_yield }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_reflect_AccessibleObject.c b/vm/native/java_lang_reflect_AccessibleObject.c new file mode 100644 index 000000000..3843f26d9 --- /dev/null +++ b/vm/native/java_lang_reflect_AccessibleObject.c @@ -0,0 +1,45 @@ +/* + * 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. + */ + +/* + * java.lang.reflect.AccessibleObject + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private static Object[] getClassSignatureAnnotation(Class clazz) + * + * Return the Signature annotation for the specified class. Equivalent to + * Class.getSignatureAnnotation(), but available to java.lang.reflect. + */ +static void Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation( + const u4* args, JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + ArrayObject* arr = dvmGetClassSignatureAnnotation(clazz); + + dvmReleaseTrackedAlloc((Object*) arr, NULL); + RETURN_PTR(arr); +} + +const DalvikNativeMethod dvm_java_lang_reflect_AccessibleObject[] = { + { "getClassSignatureAnnotation", "(Ljava/lang/Class;)[Ljava/lang/Object;", + Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_reflect_Array.c b/vm/native/java_lang_reflect_Array.c new file mode 100644 index 000000000..ddd608eb0 --- /dev/null +++ b/vm/native/java_lang_reflect_Array.c @@ -0,0 +1,147 @@ +/* + * 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. + */ + +/* + * java.lang.reflect.Array + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private static Object createObjectArray(Class<?> componentType, + * int length) throws NegativeArraySizeException; + * + * Create a one-dimensional array of Objects. + */ +static void Dalvik_java_lang_reflect_Array_createObjectArray(const u4* args, + JValue* pResult) +{ + ClassObject* elementClass = (ClassObject*) args[0]; + int length = args[1]; + ArrayObject* newArray; + + assert(elementClass != NULL); // tested by caller + if (length < 0) { + dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL); + RETURN_VOID(); + } + + newArray = dvmAllocObjectArray(elementClass, length, ALLOC_DEFAULT); + if (newArray == NULL) { + assert(dvmCheckException(dvmThreadSelf())); + RETURN_VOID(); + } + dvmReleaseTrackedAlloc((Object*) newArray, NULL); + + RETURN_PTR(newArray); +} + +/* + * private static Object createMultiArray(Class<?> componentType, + * int[] dimensions) throws NegativeArraySizeException; + * + * Create a multi-dimensional array of Objects or primitive types. + * + * We have to generate the names for X[], X[][], X[][][], and so on. The + * easiest way to deal with that is to create the full name once and then + * subtract pieces off. Besides, we want to start with the outermost + * piece and work our way in. + */ +static void Dalvik_java_lang_reflect_Array_createMultiArray(const u4* args, + JValue* pResult) +{ + static const char kPrimLetter[] = PRIM_TYPE_TO_LETTER; + ClassObject* elementClass = (ClassObject*) args[0]; + ArrayObject* dimArray = (ArrayObject*) args[1]; + ClassObject* arrayClass; + ArrayObject* newArray; + char* acDescriptor; + int numDim, i; + int* dimensions; + + LOGV("createMultiArray: '%s' [%d]\n", + elementClass->descriptor, dimArray->length); + + assert(elementClass != NULL); // verified by caller + + /* + * Verify dimensions. + * + * The caller is responsible for verifying that "dimArray" is non-null + * and has a length > 0 and <= 255. + */ + assert(dimArray != NULL); // verified by caller + numDim = dimArray->length; + assert(numDim > 0 && numDim <= 255); + + dimensions = (int*) dimArray->contents; + for (i = 0; i < numDim; i++) { + if (dimensions[i] < 0) { + dvmThrowException("Ljava/lang/NegativeArraySizeException;", NULL); + RETURN_VOID(); + } + LOGVV("DIM %d: %d\n", i, dimensions[i]); + } + + /* + * Generate the full name of the array class. + */ + acDescriptor = + (char*) malloc(strlen(elementClass->descriptor) + numDim + 1); + memset(acDescriptor, '[', numDim); + + LOGVV("#### element name = '%s'\n", elementClass->descriptor); + if (dvmIsPrimitiveClass(elementClass)) { + assert(elementClass->primitiveType >= 0); + acDescriptor[numDim] = kPrimLetter[elementClass->primitiveType]; + acDescriptor[numDim+1] = '\0'; + } else { + strcpy(acDescriptor+numDim, elementClass->descriptor); + } + LOGVV("#### array name = '%s'\n", acDescriptor); + + /* + * Find/generate the array class. + */ + arrayClass = dvmFindArrayClass(acDescriptor, elementClass->classLoader); + if (arrayClass == NULL) { + LOGW("Unable to find or generate array class '%s'\n", acDescriptor); + assert(dvmCheckException(dvmThreadSelf())); + free(acDescriptor); + RETURN_VOID(); + } + free(acDescriptor); + + /* create the array */ + newArray = dvmAllocMultiArray(arrayClass, numDim-1, dimensions); + if (newArray == NULL) { + assert(dvmCheckException(dvmThreadSelf())); + RETURN_VOID(); + } + + dvmReleaseTrackedAlloc((Object*) newArray, NULL); + RETURN_PTR(newArray); +} + +const DalvikNativeMethod dvm_java_lang_reflect_Array[] = { + { "createObjectArray", "(Ljava/lang/Class;I)Ljava/lang/Object;", + Dalvik_java_lang_reflect_Array_createObjectArray }, + { "createMultiArray", "(Ljava/lang/Class;[I)Ljava/lang/Object;", + Dalvik_java_lang_reflect_Array_createMultiArray }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_reflect_Constructor.c b/vm/native/java_lang_reflect_Constructor.c new file mode 100644 index 000000000..82b72eee3 --- /dev/null +++ b/vm/native/java_lang_reflect_Constructor.c @@ -0,0 +1,143 @@ +/* + * 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. + */ + +/* + * java.lang.reflect.Constructor + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * public int getConstructorModifiers(Class declaringClass, int slot) + */ +static void Dalvik_java_lang_reflect_Constructor_getConstructorModifiers( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + RETURN_INT(dvmFixMethodFlags(meth->accessFlags)); +} + +/* + * public int constructNative(Object[] args, Class declaringClass, + * Class[] parameterTypes, int slot, boolean noAccessCheck) + */ +static void Dalvik_java_lang_reflect_Constructor_constructNative( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ArrayObject* argList = (ArrayObject*) args[1]; + ClassObject* declaringClass = (ClassObject*) args[2]; + ArrayObject* params = (ArrayObject*) args[3]; + int slot = args[4]; + bool noAccessCheck = (args[5] != 0); + Object* newObj; + Method* meth; + + newObj = dvmAllocObject(declaringClass, ALLOC_DEFAULT); + if (newObj == NULL) + RETURN_PTR(NULL); + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + (void) dvmInvokeMethod(newObj, meth, argList, params, NULL, noAccessCheck); + dvmReleaseTrackedAlloc(newObj, NULL); + RETURN_PTR(newObj); +} + +/* + * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot) + * + * Return the annotations declared for this constructor. + */ +static void Dalvik_java_lang_reflect_Constructor_getDeclaredAnnotations( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + ArrayObject* annos = dvmGetMethodAnnotations(meth); + dvmReleaseTrackedAlloc((Object*)annos, NULL); + RETURN_PTR(annos); +} + +/* + * public Annotation[][] getParameterAnnotations(Class declaringClass, int slot) + * + * Return the annotations declared for this constructor's parameters. + */ +static void Dalvik_java_lang_reflect_Constructor_getParameterAnnotations( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + ArrayObject* annos = dvmGetParameterAnnotations(meth); + dvmReleaseTrackedAlloc((Object*)annos, NULL); + RETURN_PTR(annos); +} + +/* + * private Object[] getSignatureAnnotation() + * + * Returns the signature annotation. + */ +static void Dalvik_java_lang_reflect_Constructor_getSignatureAnnotation( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + ArrayObject* arr = dvmGetMethodSignatureAnnotation(meth); + dvmReleaseTrackedAlloc((Object*) arr, NULL); + RETURN_PTR(arr); +} + +const DalvikNativeMethod dvm_java_lang_reflect_Constructor[] = { + { "constructNative", "([Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;IZ)Ljava/lang/Object;", + Dalvik_java_lang_reflect_Constructor_constructNative }, + { "getConstructorModifiers", "(Ljava/lang/Class;I)I", + Dalvik_java_lang_reflect_Constructor_getConstructorModifiers }, + { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;", + Dalvik_java_lang_reflect_Constructor_getDeclaredAnnotations }, + { "getParameterAnnotations", "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;", + Dalvik_java_lang_reflect_Constructor_getParameterAnnotations }, + { "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;", + Dalvik_java_lang_reflect_Constructor_getSignatureAnnotation }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_reflect_Field.c b/vm/native/java_lang_reflect_Field.c new file mode 100644 index 000000000..72f4c651b --- /dev/null +++ b/vm/native/java_lang_reflect_Field.c @@ -0,0 +1,448 @@ +/* + * 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. + */ + +/* + * java.lang.reflect.Field + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * Get the address of a field from an object. This can be used with "get" + * or "set". + * + * "declaringClass" is the class in which the field was declared. For an + * instance field, "obj" is the object that holds the field data; for a + * static field its value is ignored. + * + * "If the underlying field is static, the class that declared the + * field is initialized if it has not already been initialized." + * + * On failure, throws an exception and returns NULL. + * + * The documentation lists exceptional conditions and the exceptions that + * should be thrown, but doesn't say which exception previals when two or + * more exceptional conditions exist at the same time. For example, + * attempting to set a protected field from an unrelated class causes an + * IllegalAccessException, while passing in a data type that doesn't match + * the field causes an IllegalArgumentException. If code does both at the + * same time, we have to choose one or othe other. + * + * The expected order is: + * (1) Check for illegal access. Throw IllegalAccessException. + * (2) Make sure the object actually has the field. Throw + * IllegalArgumentException. + * (3) Make sure the field matches the expected type, e.g. if we issued + * a "getInteger" call make sure the field is an integer or can be + * converted to an int with a widening conversion. Throw + * IllegalArgumentException. + * (4) Make sure "obj" is not null. Throw NullPointerException. + * + * TODO: we're currently handling #3 after #4, because we don't check the + * widening conversion until we're actually extracting the value from the + * object (which won't work well if it's a null reference). + */ +static JValue* getFieldDataAddr(Object* obj, ClassObject* declaringClass, + int slot, bool isSetOperation, bool noAccessCheck) +{ + Field* field; + JValue* result; + + field = dvmSlotToField(declaringClass, slot); + assert(field != NULL); + + /* verify access */ + if (!noAccessCheck) { + if (isSetOperation && dvmIsFinalField(field)) { + dvmThrowException("Ljava/lang/IllegalAccessException;", + "field is marked 'final'"); + return NULL; + } + + ClassObject* callerClass = + dvmGetCaller2Class(dvmThreadSelf()->curFrame); + + /* + * We need to check two things: + * (1) Would an instance of the calling class have access to the field? + * (2) If the field is "protected", is the object an instance of the + * calling class, or is the field's declaring class in the same + * package as the calling class? + * + * #1 is basic access control. #2 ensures that, just because + * you're a subclass of Foo, you can't mess with protected fields + * in arbitrary Foo objects from other packages. + */ + if (!dvmCheckFieldAccess(callerClass, field)) { + dvmThrowException("Ljava/lang/IllegalAccessException;", + "access to field not allowed"); + return NULL; + } + if (dvmIsProtectedField(field)) { + bool isInstance, samePackage; + + if (obj != NULL) + isInstance = dvmInstanceof(obj->clazz, callerClass); + else + isInstance = false; + samePackage = dvmInSamePackage(declaringClass, callerClass); + + if (!isInstance && !samePackage) { + dvmThrowException("Ljava/lang/IllegalAccessException;", + "access to protected field not allowed"); + return NULL; + } + } + } + + if (dvmIsStaticField(field)) { + /* init class if necessary, then return ptr to storage in "field" */ + if (!dvmIsClassInitialized(declaringClass)) { + if (!dvmInitClass(declaringClass)) { + assert(dvmCheckException(dvmThreadSelf())); + return NULL; + } + } + + result = dvmStaticFieldPtr((StaticField*) field); + } else { + /* + * Verify object is of correct type (i.e. it actually has the + * expected field in it), then grab a pointer to obj storage. + * The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL. + */ + if (!dvmVerifyObjectInClass(obj, declaringClass)) { + assert(dvmCheckException(dvmThreadSelf())); + if (obj != NULL) { + LOGD("Wrong type of object for field lookup: %s %s\n", + obj->clazz->descriptor, declaringClass->descriptor); + } + return NULL; + } + result = dvmFieldPtr(obj, ((InstField*) field)->byteOffset); + } + + return result; +} + +/* + * public int getFieldModifiers(Class declaringClass, int slot) + */ +static void Dalvik_java_lang_reflect_Field_getFieldModifiers( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Field* field; + + field = dvmSlotToField(declaringClass, slot); + RETURN_INT(field->accessFlags & JAVA_FLAGS_MASK); +} + +/* + * private Object getField(Object o, Class declaringClass, Class type, + * int slot, boolean noAccessCheck) + * + * Primitive types need to be boxed. + */ +static void Dalvik_java_lang_reflect_Field_getField(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + Object* obj = (Object*) args[1]; + ClassObject* declaringClass = (ClassObject*) args[2]; + ClassObject* fieldType = (ClassObject*) args[3]; + int slot = args[4]; + bool noAccessCheck = (args[5] != 0); + JValue value; + const JValue* fieldPtr; + DataObject* result; + + //dvmDumpClass(obj->clazz, kDumpClassFullDetail); + + /* get a pointer to the field's data; performs access checks */ + fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck); + if (fieldPtr == NULL) + RETURN_VOID(); + + /* copy 4 or 8 bytes out */ + if (fieldType->primitiveType == PRIM_LONG || + fieldType->primitiveType == PRIM_DOUBLE) + { + value.j = fieldPtr->j; + } else { + value.i = fieldPtr->i; + } + + result = dvmWrapPrimitive(value, fieldType); + dvmReleaseTrackedAlloc((Object*) result, NULL); + RETURN_PTR(result); +} + +/* + * private void setField(Object o, Class declaringClass, Class type, + * int slot, boolean noAccessCheck, Object value) + * + * When assigning into a primitive field we will automatically extract + * the value from box types. + */ +static void Dalvik_java_lang_reflect_Field_setField(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + Object* obj = (Object*) args[1]; + ClassObject* declaringClass = (ClassObject*) args[2]; + ClassObject* fieldType = (ClassObject*) args[3]; + int slot = args[4]; + bool noAccessCheck = (args[5] != 0); + Object* valueObj = (Object*) args[6]; + JValue* fieldPtr; + JValue value; + + /* unwrap primitive, or verify object type */ + if (!dvmUnwrapPrimitive(valueObj, fieldType, &value)) { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "invalid value for field"); + RETURN_VOID(); + } + + /* get a pointer to the field's data; performs access checks */ + fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck); + if (fieldPtr == NULL) + RETURN_VOID(); + + /* store 4 or 8 bytes */ + if (fieldType->primitiveType == PRIM_LONG || + fieldType->primitiveType == PRIM_DOUBLE) + { + fieldPtr->j = value.j; + } else { + fieldPtr->i = value.i; + } + + RETURN_VOID(); +} + +/* + * Convert a reflection primitive type ordinal (inherited from the previous + * VM's reflection classes) to our value. + */ +static PrimitiveType convPrimType(int typeNum) +{ + static const PrimitiveType conv[PRIM_MAX] = { + PRIM_NOT, PRIM_BOOLEAN, PRIM_BYTE, PRIM_CHAR, PRIM_SHORT, + PRIM_INT, PRIM_FLOAT, PRIM_LONG, PRIM_DOUBLE + }; + if (typeNum <= 0 || typeNum > 8) + return PRIM_NOT; + return conv[typeNum]; +} + +/* + * Primitive field getters, e.g.: + * private double getIField(Object o, Class declaringClass, + * Class type, int slot, boolean noAccessCheck, int type_no) + * + * The "type_no" is defined by the java.lang.reflect.Field class. + */ +static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + Object* obj = (Object*) args[1]; + ClassObject* declaringClass = (ClassObject*) args[2]; + ClassObject* fieldType = (ClassObject*) args[3]; + int slot = args[4]; + bool noAccessCheck = (args[5] != 0); + int typeNum = args[6]; + PrimitiveType targetType = convPrimType(typeNum); + const JValue* fieldPtr; + JValue value; + + if (!dvmIsPrimitiveClass(fieldType)) { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "not a primitive field"); + RETURN_VOID(); + } + + /* get a pointer to the field's data; performs access checks */ + fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck); + if (fieldPtr == NULL) + RETURN_VOID(); + + /* copy 4 or 8 bytes out */ + if (fieldType->primitiveType == PRIM_LONG || + fieldType->primitiveType == PRIM_DOUBLE) + { + value.j = fieldPtr->j; + } else { + value.i = fieldPtr->i; + } + + /* retrieve value, performing a widening conversion if necessary */ + if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType, + &(value.i), &(pResult->i)) < 0) + { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "invalid primitive conversion"); + RETURN_VOID(); + } +} + +/* + * Primitive field setters, e.g.: + * private void setIField(Object o, Class declaringClass, + * Class type, int slot, boolean noAccessCheck, int type_no, int value) + * + * The "type_no" is defined by the java.lang.reflect.Field class. + */ +static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + Object* obj = (Object*) args[1]; + ClassObject* declaringClass = (ClassObject*) args[2]; + ClassObject* fieldType = (ClassObject*) args[3]; + int slot = args[4]; + bool noAccessCheck = (args[5] != 0); + int typeNum = args[6]; + const s4* valuePtr = (s4*) &args[7]; + PrimitiveType srcType = convPrimType(typeNum); + JValue* fieldPtr; + JValue value; + + if (!dvmIsPrimitiveClass(fieldType)) { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "not a primitive field"); + RETURN_VOID(); + } + + /* convert the 32/64-bit arg to a JValue matching the field type */ + if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType, + valuePtr, &(value.i)) < 0) + { + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "invalid primitive conversion"); + RETURN_VOID(); + } + + /* get a pointer to the field's data; performs access checks */ + fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck); + if (fieldPtr == NULL) + RETURN_VOID(); + + /* store 4 or 8 bytes */ + if (fieldType->primitiveType == PRIM_LONG || + fieldType->primitiveType == PRIM_DOUBLE) + { + fieldPtr->j = value.j; + } else { + fieldPtr->i = value.i; + } + + RETURN_VOID(); +} + +/* + * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot) + * + * Return the annotations declared for this field. + */ +static void Dalvik_java_lang_reflect_Field_getDeclaredAnnotations( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Field* field; + + field = dvmSlotToField(declaringClass, slot); + assert(field != NULL); + + ArrayObject* annos = dvmGetFieldAnnotations(field); + dvmReleaseTrackedAlloc((Object*) annos, NULL); + RETURN_PTR(annos); +} + +/* + * private Object[] getSignatureAnnotation() + * + * Returns the signature annotation. + */ +static void Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Field* field; + + field = dvmSlotToField(declaringClass, slot); + assert(field != NULL); + + ArrayObject* arr = dvmGetFieldSignatureAnnotation(field); + dvmReleaseTrackedAlloc((Object*) arr, NULL); + RETURN_PTR(arr); +} + +const DalvikNativeMethod dvm_java_lang_reflect_Field[] = { + { "getFieldModifiers", "(Ljava/lang/Class;I)I", + Dalvik_java_lang_reflect_Field_getFieldModifiers }, + { "getField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;", + Dalvik_java_lang_reflect_Field_getField }, + { "getBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)B", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "getCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)C", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "getDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)D", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "getFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)F", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "getIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)I", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "getJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)J", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "getSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)S", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "getZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)Z", + Dalvik_java_lang_reflect_Field_getPrimitiveField }, + { "setField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V", + Dalvik_java_lang_reflect_Field_setField }, + { "setBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIB)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "setCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIC)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "setDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZID)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "setFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIF)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "setIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZII)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "setJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIJ)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "setSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIS)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "setZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIZ)V", + Dalvik_java_lang_reflect_Field_setPrimitiveField }, + { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;", + Dalvik_java_lang_reflect_Field_getDeclaredAnnotations }, + { "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;", + Dalvik_java_lang_reflect_Field_getSignatureAnnotation }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_reflect_Method.c b/vm/native/java_lang_reflect_Method.c new file mode 100644 index 000000000..760a09004 --- /dev/null +++ b/vm/native/java_lang_reflect_Method.c @@ -0,0 +1,220 @@ +/* + * 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. + */ + +/* + * java.lang.reflect.Method + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private int getMethodModifiers(Class decl_class, int slot) + * + * (Not sure why the access flags weren't stored in the class along with + * everything else. Not sure why this isn't static.) + */ +static void Dalvik_java_lang_reflect_Method_getMethodModifiers(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + RETURN_INT(dvmFixMethodFlags(meth->accessFlags)); +} + +/* + * private Object invokeNative(Object obj, Object[] args, Class declaringClass, + * Class[] parameterTypes, Class returnType, int slot, boolean noAccessCheck) + * + * Invoke a static or virtual method via reflection. + */ +static void Dalvik_java_lang_reflect_Method_invokeNative(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + Object* methObj = (Object*) args[1]; // null for static methods + ArrayObject* argList = (ArrayObject*) args[2]; + ClassObject* declaringClass = (ClassObject*) args[3]; + ArrayObject* params = (ArrayObject*) args[4]; + ClassObject* returnType = (ClassObject*) args[5]; + int slot = args[6]; + bool noAccessCheck = (args[7] != 0); + const Method* meth; + Object* result; + + /* + * "If the underlying method is static, the class that declared the + * method is initialized if it has not already been initialized." + */ + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + if (dvmIsStaticMethod(meth)) { + if (!dvmIsClassInitialized(declaringClass)) { + if (!dvmInitClass(declaringClass)) + goto init_failed; + } + } else { + /* looks like interfaces need this too? */ + if (dvmIsInterfaceClass(declaringClass) && + !dvmIsClassInitialized(declaringClass)) + { + if (!dvmInitClass(declaringClass)) + goto init_failed; + } + + /* make sure the object is an instance of the expected class */ + if (!dvmVerifyObjectInClass(methObj, declaringClass)) { + assert(dvmCheckException(dvmThreadSelf())); + RETURN_VOID(); + } + + /* do the virtual table lookup for the method */ + meth = dvmGetVirtualizedMethod(methObj->clazz, meth); + if (meth == NULL) { + assert(dvmCheckException(dvmThreadSelf())); + RETURN_VOID(); + } + } + + /* + * If the method has a return value, "result" will be an object or + * a boxed primitive. + */ + result = dvmInvokeMethod(methObj, meth, argList, params, returnType, + noAccessCheck); + + RETURN_PTR(result); + +init_failed: + /* + * If initialization failed, an exception will be raised. + */ + LOGD("Method.invoke() on bad class %s failed\n", + declaringClass->descriptor); + assert(dvmCheckException(dvmThreadSelf())); + RETURN_VOID(); +} + +/* + * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot) + * + * Return the annotations declared for this method. + */ +static void Dalvik_java_lang_reflect_Method_getDeclaredAnnotations( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + ArrayObject* annos = dvmGetMethodAnnotations(meth); + dvmReleaseTrackedAlloc((Object*)annos, NULL); + RETURN_PTR(annos); +} + +/* + * public Annotation[] getParameterAnnotations(Class declaringClass, int slot) + * + * Return the annotations declared for this method's parameters. + */ +static void Dalvik_java_lang_reflect_Method_getParameterAnnotations( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + ArrayObject* annos = dvmGetParameterAnnotations(meth); + dvmReleaseTrackedAlloc((Object*)annos, NULL); + RETURN_PTR(annos); +} + +/* + * private Object getDefaultValue(Class declaringClass, int slot) + * + * Return the default value for the annotation member represented by + * this Method instance. Returns NULL if none is defined. + */ +static void Dalvik_java_lang_reflect_Method_getDefaultValue(const u4* args, + JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + /* make sure this is an annotation class member */ + if (!dvmIsAnnotationClass(declaringClass)) + RETURN_PTR(NULL); + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + Object* def = dvmGetAnnotationDefaultValue(meth); + dvmReleaseTrackedAlloc(def, NULL); + RETURN_PTR(def); +} + +/* + * private Object[] getSignatureAnnotation() + * + * Returns the signature annotation. + */ +static void Dalvik_java_lang_reflect_Method_getSignatureAnnotation( + const u4* args, JValue* pResult) +{ + // ignore thisPtr in args[0] + ClassObject* declaringClass = (ClassObject*) args[1]; + int slot = args[2]; + Method* meth; + + meth = dvmSlotToMethod(declaringClass, slot); + assert(meth != NULL); + + ArrayObject* arr = dvmGetMethodSignatureAnnotation(meth); + dvmReleaseTrackedAlloc((Object*) arr, NULL); + RETURN_PTR(arr); +} + +const DalvikNativeMethod dvm_java_lang_reflect_Method[] = { + { "getMethodModifiers", "(Ljava/lang/Class;I)I", + Dalvik_java_lang_reflect_Method_getMethodModifiers }, + { "invokeNative", "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;", + Dalvik_java_lang_reflect_Method_invokeNative }, + { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;", + Dalvik_java_lang_reflect_Method_getDeclaredAnnotations }, + { "getParameterAnnotations", "(Ljava/lang/Class;I)[[Ljava/lang/annotation/Annotation;", + Dalvik_java_lang_reflect_Method_getParameterAnnotations }, + { "getDefaultValue", "(Ljava/lang/Class;I)Ljava/lang/Object;", + Dalvik_java_lang_reflect_Method_getDefaultValue }, + { "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;", + Dalvik_java_lang_reflect_Method_getSignatureAnnotation }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_lang_reflect_Proxy.c b/vm/native/java_lang_reflect_Proxy.c new file mode 100644 index 000000000..e71e11d07 --- /dev/null +++ b/vm/native/java_lang_reflect_Proxy.c @@ -0,0 +1,48 @@ +/* + * 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. + */ + +/* + * java.lang.reflect.Proxy + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * static Class generateProxy(String name, Class[] interfaces, + * ClassLoader loader) + * + * Generate a proxy class with the specified characteristics. Throws an + * exception on error. + */ +static void Dalvik_java_lang_reflect_Proxy_generateProxy(const u4* args, + JValue* pResult) +{ + StringObject* str = (StringObject*) args[0]; + ArrayObject* interfaces = (ArrayObject*) args[1]; + Object* loader = (Object*) args[2]; + ClassObject* result; + + result = dvmGenerateProxyClass(str, interfaces, loader); + RETURN_PTR(result); +} + +const DalvikNativeMethod dvm_java_lang_reflect_Proxy[] = { + { "generateProxy", "(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/lang/Class;", + Dalvik_java_lang_reflect_Proxy_generateProxy }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_security_AccessController.c b/vm/native/java_security_AccessController.c new file mode 100644 index 000000000..e0699f58c --- /dev/null +++ b/vm/native/java_security_AccessController.c @@ -0,0 +1,140 @@ +/* + * 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. + */ + +/* + * java.security.AccessController + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private static ProtectionDomain[] getStackDomains() + * + * Return an array of ProtectionDomain objects from the classes of the + * methods on the stack. Ignore reflection frames. Stop at the first + * privileged frame we see. + */ +static void Dalvik_java_security_AccessController_getStackDomains( + const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + + const Method** methods = NULL; + int length; + + /* + * Get an array with the stack trace in it. + */ + if (!dvmCreateStackTraceArray(dvmThreadSelf()->curFrame, &methods, &length)) + { + LOGE("Failed to create stack trace array\n"); + dvmThrowException("Ljava/lang/InternalError;", NULL); + RETURN_VOID(); + } + + //int i; + //LOGI("dvmCreateStackTraceArray results:\n"); + //for (i = 0; i < length; i++) + // LOGI(" %2d: %s.%s\n", i, methods[i]->clazz->name, methods[i]->name); + + /* + * Generate a list of ProtectionDomain objects from the frames that + * we're interested in. Skip the first two methods (this method, and + * the one that called us), and ignore reflection frames. Stop on the + * frame *after* the first privileged frame we see as we walk up. + * + * We create a new array, probably over-allocated, and fill in the + * stuff we want. We could also just run the list twice, but the + * costs of the per-frame tests could be more expensive than the + * second alloc. (We could also allocate it on the stack using C99 + * array creation, but it's not guaranteed to fit.) + * + * The array we return doesn't include null ProtectionDomain objects, + * so we skip those here. + */ + Object** subSet = (Object**) malloc((length-2) * sizeof(Object*)); + if (subSet == NULL) { + LOGE("Failed to allocate subSet (length=%d)\n", length); + free(methods); + dvmThrowException("Ljava/lang/InternalError;", NULL); + RETURN_VOID(); + } + int idx, subIdx = 0; + for (idx = 2; idx < length; idx++) { + const Method* meth = methods[idx]; + Object* pd; + + if (dvmIsReflectionMethod(meth)) + continue; + + if (dvmIsPrivilegedMethod(meth)) { + /* find nearest non-reflection frame; note we skip priv frame */ + //LOGI("GSD priv frame at %s.%s\n", meth->clazz->name, meth->name); + while (++idx < length && dvmIsReflectionMethod(methods[idx])) + ; + length = idx; // stomp length to end loop + meth = methods[idx]; + } + + /* get the pd object from the method's class */ + assert(gDvm.offJavaLangClass_pd != 0); + pd = dvmGetFieldObject((Object*) meth->clazz, + gDvm.offJavaLangClass_pd); + //LOGI("FOUND '%s' pd=%p\n", meth->clazz->name, pd); + if (pd != NULL) + subSet[subIdx++] = pd; + } + + //LOGI("subSet:\n"); + //for (i = 0; i < subIdx; i++) + // LOGI(" %2d: %s\n", i, subSet[i]->clazz->name); + + /* + * Create an array object to contain "subSet". + */ + ClassObject* pdArrayClass = NULL; + ArrayObject* domains = NULL; + pdArrayClass = dvmFindArrayClass("[Ljava/security/ProtectionDomain;", NULL); + if (pdArrayClass == NULL) { + LOGW("Unable to find ProtectionDomain class for array\n"); + goto bail; + } + domains = dvmAllocArray(pdArrayClass, subIdx, kObjectArrayRefWidth, + ALLOC_DEFAULT); + if (domains == NULL) { + LOGW("Unable to allocate pd array (%d elems)\n", subIdx); + goto bail; + } + + /* copy the ProtectionDomain objects out */ + Object** objects = (Object**) domains->contents; + for (idx = 0; idx < subIdx; idx++) + *objects++ = subSet[idx]; + +bail: + free(subSet); + free(methods); + dvmReleaseTrackedAlloc((Object*) domains, NULL); + RETURN_PTR(domains); +} + +const DalvikNativeMethod dvm_java_security_AccessController[] = { + { "getStackDomains", "()[Ljava/security/ProtectionDomain;", + Dalvik_java_security_AccessController_getStackDomains }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/java_util_concurrent_atomic_AtomicLong.c b/vm/native/java_util_concurrent_atomic_AtomicLong.c new file mode 100644 index 000000000..c46631fda --- /dev/null +++ b/vm/native/java_util_concurrent_atomic_AtomicLong.c @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/* + * java.util.concurrent.atomic.AtomicLong + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private static native boolean VMSupportsCS8(); + */ +static void Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8( + const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + RETURN_BOOLEAN(1); +} + +const DalvikNativeMethod dvm_java_util_concurrent_atomic_AtomicLong[] = { + { "VMSupportsCS8", "()Z", + Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8 }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c new file mode 100644 index 000000000..131acd372 --- /dev/null +++ b/vm/native/org_apache_harmony_dalvik_NativeTestTarget.c @@ -0,0 +1,43 @@ +/* + * 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. + */ + +/* + * org.apache.harmony.dalvik.NativeTestTarget + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * public static void emptyInternalStaticMethod() + * + * For benchmarks, a do-nothing internal method with no arguments. + */ +static void Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod( + const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + + RETURN_VOID(); +} + +const DalvikNativeMethod dvm_org_apache_harmony_dalvik_NativeTestTarget[] = +{ + { "emptyInternalStaticMethod", "()V", + Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c new file mode 100644 index 000000000..9c940d46b --- /dev/null +++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmServer.c @@ -0,0 +1,49 @@ +/* + * 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. + */ + +/* + * org.apache.harmony.dalvik.ddmc.DdmServer + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private static void nativeSendChunk(int type, byte[] data, + * int offset, int length) + * + * Send a DDM chunk to the server. + */ +static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk( + const u4* args, JValue* pResult) +{ + int type = args[0]; + ArrayObject* data = (ArrayObject*) args[1]; + int offset = args[2]; + int length = args[3]; + + assert(offset+length <= (int)data->length); + + dvmDbgDdmSendChunk(type, length, (const u1*)data->contents + offset); + RETURN_VOID(); +} + +const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmServer[] = { + { "nativeSendChunk", "(I[BII)V", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c new file mode 100644 index 000000000..5868a8bd6 --- /dev/null +++ b/vm/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c @@ -0,0 +1,170 @@ +/* + * 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. + */ + +/* + * org.apache.harmony.dalvik.ddmc.DdmVmInternal + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * public static void threadNotify(boolean enable) + * + * Enable DDM thread notifications. + */ +static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify( + const u4* args, JValue* pResult) +{ + bool enable = (args[0] != 0); + + //LOGI("ddmThreadNotification: %d\n", enable); + dvmDdmSetThreadNotification(enable); + RETURN_VOID(); +} + +/* + * public static byte[] getThreadStats() + * + * Get a buffer full of thread info. + */ +static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats( + const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + + ArrayObject* result = dvmDdmGenerateThreadStats(); + dvmReleaseTrackedAlloc((Object*) result, NULL); + RETURN_PTR(result); +} + +/* + * public static int heapInfoNotify(int what) + * + * Enable DDM heap notifications. + */ +static void Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify( + const u4* args, JValue* pResult) +{ + int when = args[0]; + bool ret; + + ret = dvmDdmHandleHpifChunk(when); + RETURN_BOOLEAN(ret); +} + +/* + * public static boolean heapSegmentNotify(int when, int what, bool native) + * + * Enable DDM heap notifications. + */ +static void + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify( + const u4* args, JValue* pResult) +{ + int when = args[0]; // 0=never (off), 1=during GC + int what = args[1]; // 0=merged objects, 1=distinct objects + bool native = (args[2] != 0); // false=virtual heap, true=native heap + bool ret; + + ret = dvmDdmHandleHpsgNhsgChunk(when, what, native); + RETURN_BOOLEAN(ret); +} + +/* + * public static StackTraceElement[] getStackTraceById(int threadId) + * + * Get a stack trace as an array of StackTraceElement objects. Returns + * NULL on failure, e.g. if the threadId couldn't be found. + */ +static void + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById( + const u4* args, JValue* pResult) +{ + u4 threadId = args[0]; + ArrayObject* trace; + + trace = dvmDdmGetStackTraceById(threadId); + RETURN_PTR(trace); +} + +/* + * public static void enableRecentAllocations(boolean enable) + * + * Enable or disable recent allocation tracking. + */ +static void + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations( + const u4* args, JValue* pResult) +{ + bool enable = (args[0] != 0); + + if (enable) + (void) dvmEnableAllocTracker(); + else + (void) dvmDisableAllocTracker(); + RETURN_VOID(); +} + +/* + * public static boolean getRecentAllocationStatus() + * + * Returns "true" if allocation tracking is enabled. + */ +static void + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus( + const u4* args, JValue* pResult) +{ + UNUSED_PARAMETER(args); + RETURN_BOOLEAN(gDvm.allocRecords != NULL); +} + +/* + * public static byte[] getRecentAllocations() + * + * Fill a buffer with data on recent heap allocations. + */ +static void + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations( + const u4* args, JValue* pResult) +{ + ArrayObject* data; + + data = dvmDdmGetRecentAllocations(); + dvmReleaseTrackedAlloc((Object*) data, NULL); + RETURN_PTR(data); +} + +const DalvikNativeMethod dvm_org_apache_harmony_dalvik_ddmc_DdmVmInternal[] = { + { "threadNotify", "(Z)V", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_threadNotify }, + { "getThreadStats", "()[B", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getThreadStats }, + { "heapInfoNotify", "(I)Z", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapInfoNotify }, + { "heapSegmentNotify", "(IIZ)Z", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_heapSegmentNotify }, + { "getStackTraceById", "(I)[Ljava/lang/StackTraceElement;", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getStackTraceById }, + { "enableRecentAllocations", "(Z)V", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_enableRecentAllocations }, + { "getRecentAllocationStatus", "()Z", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocationStatus }, + { "getRecentAllocations", "()[B", + Dalvik_org_apache_harmony_dalvik_ddmc_DdmVmInternal_getRecentAllocations }, + { NULL, NULL, NULL }, +}; + diff --git a/vm/native/sun_misc_Unsafe.c b/vm/native/sun_misc_Unsafe.c new file mode 100644 index 000000000..9adebc847 --- /dev/null +++ b/vm/native/sun_misc_Unsafe.c @@ -0,0 +1,350 @@ +/* + * 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. + */ + +/* + * sun.misc.Unsafe + */ +#include "Dalvik.h" +#include "native/InternalNativePriv.h" + + +/* + * private static native long objectFieldOffset0(Field field); + */ +static void Dalvik_sun_misc_Unsafe_objectFieldOffset0(const u4* args, + JValue* pResult) +{ + Object* fieldObject = (Object*) args[0]; + InstField* field = (InstField*) dvmGetFieldFromReflectObj(fieldObject); + s8 result = ((s8) field->byteOffset); + + RETURN_LONG(result); +} + +/* + * private static native int arrayBaseOffset0(Class clazz); + */ +static void Dalvik_sun_misc_Unsafe_arrayBaseOffset0(const u4* args, + JValue* pResult) +{ + // The base offset is not type-dependent in this vm. + UNUSED_PARAMETER(args); + RETURN_INT(offsetof(ArrayObject, contents)); +} + +/* + * private static native int arrayIndexScale0(Class clazz); + */ +static void Dalvik_sun_misc_Unsafe_arrayIndexScale0(const u4* args, + JValue* pResult) +{ + ClassObject* clazz = (ClassObject*) args[0]; + int result; + + if ((clazz == gDvm.classArrayBoolean) || + (clazz == gDvm.classArrayByte)) { + result = sizeof(u1); + } else if ((clazz == gDvm.classArrayChar) || + (clazz == gDvm.classArrayShort)) { + result = sizeof(u2); + } else if ((clazz == gDvm.classArrayLong) || + (clazz == gDvm.classArrayDouble)) { + result = sizeof(u8); + } else { + result = sizeof(u4); + } + + RETURN_INT(result); +} + +/* + * public native boolean compareAndSwapInt(Object obj, long offset, + * int expectedValue, int newValue); + */ +static void Dalvik_sun_misc_Unsafe_compareAndSwapInt(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s4 expectedValue = args[4]; + s4 newValue = args[5]; + volatile int32_t* address = (volatile int32_t*) (((u1*) obj) + offset); + + // Note: android_atomic_cmpxchg() returns 0 on success, not failure. + int result = android_atomic_cmpxchg(expectedValue, newValue, address); + + RETURN_BOOLEAN(result == 0); +} + +/* + * public native boolean compareAndSwapLong(Object obj, long offset, + * long expectedValue, long newValue); + */ +static void Dalvik_sun_misc_Unsafe_compareAndSwapLong(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s8 expectedValue = GET_ARG_LONG(args, 4); + s8 newValue = GET_ARG_LONG(args, 6); + volatile int64_t* address = (volatile int64_t*) (((u1*) obj) + offset); + + // Note: android_atomic_cmpxchg() returns 0 on success, not failure. + int result = + android_quasiatomic_cmpxchg_64(expectedValue, newValue, address); + + RETURN_BOOLEAN(result == 0); +} + +/* + * public native boolean compareAndSwapObject(Object obj, long offset, + * Object expectedValue, Object newValue); + */ +static void Dalvik_sun_misc_Unsafe_compareAndSwapObject(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + Object* expectedValue = (Object*) args[4]; + Object* newValue = (Object*) args[5]; + int32_t* address = (int32_t*) (((u1*) obj) + offset); + + // Note: android_atomic_cmpxchg() returns 0 on success, not failure. + int result = android_atomic_cmpxchg((int32_t) expectedValue, + (int32_t) newValue, address); + + RETURN_BOOLEAN(result == 0); +} + +/* + * public native int getIntVolatile(Object obj, long offset); + */ +static void Dalvik_sun_misc_Unsafe_getIntVolatile(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + volatile s4* address = (volatile s4*) (((u1*) obj) + offset); + + RETURN_INT(*address); +} + +/* + * public native void putIntVolatile(Object obj, long offset, int newValue); + */ +static void Dalvik_sun_misc_Unsafe_putIntVolatile(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s4 value = (s4) args[4]; + volatile s4* address = (volatile s4*) (((u1*) obj) + offset); + + *address = value; + RETURN_VOID(); +} + +/* + * public native long getLongVolatile(Object obj, long offset); + */ +static void Dalvik_sun_misc_Unsafe_getLongVolatile(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + volatile s8* address = (volatile s8*) (((u1*) obj) + offset); + + RETURN_LONG(android_quasiatomic_read_64(address)); +} + +/* + * public native void putLongVolatile(Object obj, long offset, long newValue); + */ +static void Dalvik_sun_misc_Unsafe_putLongVolatile(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s8 value = GET_ARG_LONG(args, 4); + volatile s8* address = (volatile s8*) (((u1*) obj) + offset); + + android_quasiatomic_swap_64(value, address); + RETURN_VOID(); +} + +/* + * public native Object getObjectVolatile(Object obj, long offset); + */ +static void Dalvik_sun_misc_Unsafe_getObjectVolatile(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + volatile Object** address = (volatile Object**) (((u1*) obj) + offset); + + RETURN_PTR((void*) *address); +} + +/* + * public native void putObjectVolatile(Object obj, long offset, + * Object newValue); + */ +static void Dalvik_sun_misc_Unsafe_putObjectVolatile(const u4* args, + JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + Object* value = (Object*) args[4]; + volatile Object** address = (volatile Object**) (((u1*) obj) + offset); + + *address = value; + RETURN_VOID(); +} + +/* + * public native int getInt(Object obj, long offset); + */ +static void Dalvik_sun_misc_Unsafe_getInt(const u4* args, JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s4* address = (s4*) (((u1*) obj) + offset); + + RETURN_INT(*address); +} + +/* + * public native void putInt(Object obj, long offset, int newValue); + */ +static void Dalvik_sun_misc_Unsafe_putInt(const u4* args, JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s4 value = (s4) args[4]; + s4* address = (s4*) (((u1*) obj) + offset); + + *address = value; + RETURN_VOID(); +} + +/* + * public native long getLong(Object obj, long offset); + */ +static void Dalvik_sun_misc_Unsafe_getLong(const u4* args, JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s8* address = (s8*) (((u1*) obj) + offset); + + RETURN_LONG(*address); +} + +/* + * public native void putLong(Object obj, long offset, long newValue); + */ +static void Dalvik_sun_misc_Unsafe_putLong(const u4* args, JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + s8 value = GET_ARG_LONG(args, 4); + s8* address = (s8*) (((u1*) obj) + offset); + + *address = value; + RETURN_VOID(); +} + +/* + * public native Object getObject(Object obj, long offset); + */ +static void Dalvik_sun_misc_Unsafe_getObject(const u4* args, JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + Object** address = (Object**) (((u1*) obj) + offset); + + RETURN_PTR(*address); +} + +/* + * public native void putObject(Object obj, long offset, Object newValue); + */ +static void Dalvik_sun_misc_Unsafe_putObject(const u4* args, JValue* pResult) +{ + // We ignore the this pointer in args[0]. + Object* obj = (Object*) args[1]; + s8 offset = GET_ARG_LONG(args, 2); + Object* value = (Object*) args[4]; + Object** address = (Object**) (((u1*) obj) + offset); + + *address = value; + RETURN_VOID(); +} + +const DalvikNativeMethod dvm_sun_misc_Unsafe[] = { + { "objectFieldOffset0", "(Ljava/lang/reflect/Field;)J", + Dalvik_sun_misc_Unsafe_objectFieldOffset0 }, + { "arrayBaseOffset0", "(Ljava/lang/Class;)I", + Dalvik_sun_misc_Unsafe_arrayBaseOffset0 }, + { "arrayIndexScale0", "(Ljava/lang/Class;)I", + Dalvik_sun_misc_Unsafe_arrayIndexScale0 }, + { "compareAndSwapInt", "(Ljava/lang/Object;JII)Z", + Dalvik_sun_misc_Unsafe_compareAndSwapInt }, + { "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z", + Dalvik_sun_misc_Unsafe_compareAndSwapLong }, + { "compareAndSwapObject", + "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z", + Dalvik_sun_misc_Unsafe_compareAndSwapObject }, + { "getIntVolatile", "(Ljava/lang/Object;J)I", + Dalvik_sun_misc_Unsafe_getIntVolatile }, + { "putIntVolatile", "(Ljava/lang/Object;JI)V", + Dalvik_sun_misc_Unsafe_putIntVolatile }, + { "getLongVolatile", "(Ljava/lang/Object;J)J", + Dalvik_sun_misc_Unsafe_getLongVolatile }, + { "putLongVolatile", "(Ljava/lang/Object;JJ)V", + Dalvik_sun_misc_Unsafe_putLongVolatile }, + { "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;", + Dalvik_sun_misc_Unsafe_getObjectVolatile }, + { "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V", + Dalvik_sun_misc_Unsafe_putObjectVolatile }, + { "getInt", "(Ljava/lang/Object;J)I", + Dalvik_sun_misc_Unsafe_getInt }, + { "putInt", "(Ljava/lang/Object;JI)V", + Dalvik_sun_misc_Unsafe_putInt }, + { "getLong", "(Ljava/lang/Object;J)J", + Dalvik_sun_misc_Unsafe_getLong }, + { "putLong", "(Ljava/lang/Object;JJ)V", + Dalvik_sun_misc_Unsafe_putLong }, + { "getObject", "(Ljava/lang/Object;J)Ljava/lang/Object;", + Dalvik_sun_misc_Unsafe_getObject }, + { "putObject", "(Ljava/lang/Object;JLjava/lang/Object;)V", + Dalvik_sun_misc_Unsafe_putObject }, + { NULL, NULL, NULL }, +}; + |
