/*
* 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 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 shared library.
*
* 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 may not be declared "synchronized".
*
* 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.
*
* TODO: this is huge. Consider splitting this into one file per class.
*/
#include "Dalvik.h"
#include <stdlib.h>
#include <stddef.h>
#include <sys/time.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <grp.h>
#include <limits.h>
#if defined(HAVE_PRCTL)
#include <sys/prctl.h>
#endif
#include "alloc/HeapDebug.h"
/*
* 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.
*/
static bool verifyObjectInClass(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.
*/
static ClassObject* findClassByName(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("findClassByName rejecting '%s'\n", 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.
*/
static inline u4 fixMethodFlags(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;
}
/*
* ===========================================================================
* dalvik.system.VMDebug
* ===========================================================================
*/
#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 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);
}
static const DalvikNativeMethod 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 },
{ "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 },
};
/*
* ===========================================================================
* org.apache.harmony.dalvik.NativeTestTarget
* ===========================================================================
*/
/*
* 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();
}
static const DalvikNativeMethod org_apache_harmony_dalvik_NativeTestTarget[] =
{
{ "emptyInternalStaticMethod", "()V",
Dalvik_org_apache_harmony_dalvik_NativeTestTarget_emptyInternalMethod },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* dalvik.system.DexFile
* ===========================================================================
*/
/*
* Internal struct for managing DexFile.
*/
typedef struct DexOrJar {
char* fileName;
bool isDex;
bool okayToFree;
RawDexFile* pRawDexFile;
JarFile* pJarFile;
} DexOrJar;
/*
* (This is a dvmHashTableFree callback.)
*/
static void freeDexOrJar(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.
*/
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);
void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
hashcmpDexOrJar, false);
if (result == NULL)
return false;
return true;
}
/*
* private static int openDexFile(String fileName) throws IOException
*
* Open a DEX file, returning a pointer to our internal data structure.
*
* The filename should point to the "source" jar or DEX file. The DEX
* code will automatically find the "optimized" version in the cache
* directory, creating it if necessary.
*
* 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* nameObj = (StringObject*) args[0];
DexOrJar* pDexOrJar = NULL;
JarFile* pJarFile;
RawDexFile* pRawDexFile;
char* name;
name = dvmCreateCstrFromString(nameObj);
/*
* 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.
*/
if (dvmClassPathContains(gDvm.bootClassPath, name)) {
LOGW("Refusing to reopen boot DEX '%s'\n", name);
dvmThrowException("Ljava/io/IOException;",
"Re-opening BOOTCLASSPATH DEX files is not allowed");
free(name);
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(name, &pRawDexFile, false) == 0) {
LOGV("Opening DEX file '%s' (DEX)\n", name);
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = true;
pDexOrJar->pRawDexFile = pRawDexFile;
} else if (dvmJarFileOpen(name, &pJarFile, false) == 0) {
LOGV("Opening DEX file '%s' (Jar)\n", name);
pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));
pDexOrJar->isDex = false;
pDexOrJar->pJarFile = pJarFile;
} else {
LOGV("Unable to open DEX file '%s'\n", name);
dvmThrowException("Ljava/io/IOException;", "unable to open DEX file");
}
if (pDexOrJar != NULL) {
pDexOrJar->fileName = name;
/* add to hash table */
u4 hash = dvmComputeUtf8Hash(name);
void* result;
result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar,
hashcmpDexOrJar, true);
if (result != pDexOrJar) {
LOGE("Pointer has already been added?\n");
dvmAbort();
}
pDexOrJar->okayToFree = true;
} else
free(name);
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);
if (!dvmHashTableRemove(gDvm.userDexFiles, hash, pDexOrJar)) {
LOGW("WARNING: could not remove '%s' from DEX hash table\n",
pDexOrJar->fileName);
}
LOGV("+++ freeing DexFile '%s' resources\n", pDexOrJar->fileName);
freeDexOrJar(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();
}
}
static const DalvikNativeMethod dalvik_system_DexFile[] = {
{ "openDexFile", "(Ljava/lang/String;)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 },
};
/*
* ===========================================================================
* dalvik.system.VMRuntime
* ===========================================================================
*/
/*
* 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());
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* dalvik.system.Zygote
* ===========================================================================
*/
#define ZYGOTE_LOG_TAG "Zygote"
/*
* 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);
}
/*
* 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 enableDebugger = 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();
// jdwp not started until dvmInitAfterZygote()
gDvm.jdwpAllowed = (enableDebugger != 0);
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, boolean enableDebugger);
*/
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, boolean enableDebugger);
*/
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);
}
static const DalvikNativeMethod dalvik_system_Zygote[] = {
{ "fork", "()I",
Dalvik_dalvik_system_Zygote_fork },
{ "forkAndSpecialize", "(II[IZ[[I)I",
Dalvik_dalvik_system_Zygote_forkAndSpecialize },
{ "forkSystemServer", "(II[IZ[[I)I",
Dalvik_dalvik_system_Zygote_forkSystemServer },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* java.lang.VMClassLoader
* ===========================================================================
*/
/*
* 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 = findClassByName(nameObj, NULL, resolve);
assert(clazz == NULL || dvmIsClassLinked(clazz));
RETURN_PTR(clazz);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* dalvik.system.VMStack
* ===========================================================================
*/
#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.
*/
static 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;
}
/*
* 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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.VMThread
* ===========================================================================
*/
/*
* 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();
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.Throwable
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.Object
* ===========================================================================
*/
/*
* 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_PTR(thisPtr); /* use the pointer as the hash code */
}
/*
* 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();
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.Class
* ===========================================================================
*/
/*
* 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(findClassByName(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 = dvmGetCallerClass(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);
}
static const DalvikNativeMethod 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 },
{ "newInstance", "()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 },
};
/*
* ===========================================================================
* java.lang.System
* ===========================================================================
*/
/*
* 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();
}
if (srcPos < 0 || dstPos < 0 || length < 0 ||
srcPos + length > (int) srcArray->length ||
dstPos + length > (int) dstArray->length)
{
dvmThrowException("Ljava/lang/IndexOutOfBoundsException;", 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)
{
/* This is a static method, which means args[0] is the Object.
* Passing the same args to Object.hashCode will work because
* it treats the first arg as the "this" pointer.
*/
Dalvik_java_lang_Object_hashCode(args, pResult);
}
/*
* 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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.SystemProperties
* ===========================================================================
*/
/*
* 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();
}
static const DalvikNativeMethod java_lang_SystemProperties[] = {
{ "preInit", "()V",
Dalvik_java_lang_SystemProperties_preInit },
{ "postInit", "()V",
Dalvik_java_lang_SystemProperties_postInit },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* java.lang.Runtime
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.String
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod java_lang_String[] = {
{ "intern", "()Ljava/lang/String;",
Dalvik_java_lang_String_intern },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* java.lang.reflect.AccessibleObject
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod java_lang_reflect_AccessibleObject[] = {
{ "getClassSignatureAnnotation", "(Ljava/lang/Class;)[Ljava/lang/Object;",
Dalvik_java_lang_reflect_AccessibleObject_getClassSignatureAnnotation },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* java.lang.reflect.Array
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.reflect.Constructor
* ===========================================================================
*/
/*
* 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(fixMethodFlags(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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.reflect.Field
* ===========================================================================
*/
/*
* 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 verifyObjectInClass throws an NPE if "obj" is NULL.
*/
if (!verifyObjectInClass(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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.reflect.Method
* ===========================================================================
*/
/*
* 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(fixMethodFlags(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 (!verifyObjectInClass(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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.lang.reflect.Proxy
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* java.security.AccessController
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod java_security_AccessController[] = {
{ "getStackDomains", "()[Ljava/security/ProtectionDomain;",
Dalvik_java_security_AccessController_getStackDomains },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* java.util.concurrent.atomic.AtomicLong
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod java_util_concurrent_atomic_AtomicLong[] = {
{ "VMSupportsCS8", "()Z",
Dalvik_java_util_concurrent_atomic_AtomicLong_VMSupportsCS8 },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* org.apache.harmony.dalvik.ddmc.DdmServer
* ===========================================================================
*/
/*
* 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();
}
static const DalvikNativeMethod org_apache_harmony_dalvik_ddmc_DdmServer[] = {
{ "nativeSendChunk", "(I[BII)V",
Dalvik_org_apache_harmony_dalvik_ddmc_DdmServer_nativeSendChunk },
{ NULL, NULL, NULL },
};
/*
* ===========================================================================
* org.apache.harmony.dalvik.ddmc.DdmVmInternal
* ===========================================================================
*/
/*
* 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);
}
static const DalvikNativeMethod
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 },
};
/*
* ===========================================================================
* sun.misc.Unsafe
* ===========================================================================
*/
/*
* 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();
}
static const DalvikNativeMethod 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 },
};
/*
* ===========================================================================
* General
* ===========================================================================
*/
/*
* Set of classes for which we provide methods.
*
* The last field, classNameHash, is filled in at startup.
*/
static DalvikNativeClass gDvmNativeMethodSet[] = {
{ "Ljava/lang/Object;", java_lang_Object, 0 },
{ "Ljava/lang/Class;", java_lang_Class, 0 },
{ "Ljava/lang/Runtime;", java_lang_Runtime, 0 },
{ "Ljava/lang/String;", java_lang_String, 0 },
{ "Ljava/lang/System;", java_lang_System, 0 },
{ "Ljava/lang/SystemProperties;", java_lang_SystemProperties, 0 },
{ "Ljava/lang/Throwable;", java_lang_Throwable, 0 },
{ "Ljava/lang/VMClassLoader;", java_lang_VMClassLoader, 0 },
{ "Ljava/lang/VMThread;", java_lang_VMThread, 0 },
{ "Ljava/lang/reflect/AccessibleObject;",
java_lang_reflect_AccessibleObject, 0 },
{ "Ljava/lang/reflect/Array;", java_lang_reflect_Array, 0 },
{ "Ljava/lang/reflect/Constructor;", java_lang_reflect_Constructor, 0 },
{ "Ljava/lang/reflect/Field;", java_lang_reflect_Field, 0 },
{ "Ljava/lang/reflect/Method;", java_lang_reflect_Method, 0 },
{ "Ljava/lang/reflect/Proxy;", java_lang_reflect_Proxy, 0 },
{ "Ljava/security/AccessController;", java_security_AccessController, 0 },
{ "Ljava/util/concurrent/atomic/AtomicLong;",
java_util_concurrent_atomic_AtomicLong, 0 },
{ "Ldalvik/system/VMDebug;", dalvik_system_VMDebug, 0 },
{ "Ldalvik/system/DexFile;", dalvik_system_DexFile, 0 },
{ "Ldalvik/system/VMRuntime;", dalvik_system_VMRuntime, 0 },
{ "Ldalvik/system/Zygote;", dalvik_system_Zygote, 0 },
{ "Ldalvik/system/VMStack;", dalvik_system_VMStack, 0 },
{ "Lorg/apache/harmony/dalvik/ddmc/DdmServer;",
org_apache_harmony_dalvik_ddmc_DdmServer, 0 },
{ "Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;",
org_apache_harmony_dalvik_ddmc_DdmVmInternal, 0 },
{ "Lorg/apache/harmony/dalvik/NativeTestTarget;",
org_apache_harmony_dalvik_NativeTestTarget, 0 },
{ "Lsun/misc/Unsafe;", 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, freeDexOrJar);
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)
{
LOGI("--- called into dvmAbstractMethodStub\n");
dvmThrowException("Ljava/lang/AbstractMethodError;",
"abstract method not implemented");
}
syntax highlighted by Code2HTML, v. 0.9.1