/* * Find a class by name, initializing it if requested. */ ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader, bool doInit) { ClassObject* clazz = NULL; char* name = NULL; char* descriptor = NULL; if (nameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); goto bail; } name = dvmCreateCstrFromString(nameObj); /* * We need to validate and convert the name (from x.y.z to x/y/z). This * is especially handy for array types, since we want to avoid * auto-generating bogus array classes. */ if (!validateClassName(name)) { LOGW("dvmFindClassByName rejecting '%s'\n", name); dvmThrowException("Ljava/lang/ClassNotFoundException;", name); goto bail; } descriptor = dvmDotToDescriptor(name); if (descriptor == NULL) { goto bail; } if (doInit) clazz = dvmFindClass(descriptor, loader); else clazz = dvmFindClassNoInit(descriptor, loader); if (clazz == NULL) { LOGVV("FAIL: load %s (%d)\n", descriptor, doInit); Thread* self = dvmThreadSelf(); Object* oldExcep = dvmGetException(self); dvmAddTrackedAlloc(oldExcep, self); /* don't let this be GCed */ dvmClearException(self); dvmThrowChainedException("Ljava/lang/ClassNotFoundException;", name, oldExcep); dvmReleaseTrackedAlloc(oldExcep, self); } else { LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n", descriptor, doInit, clazz, clazz->classLoader); } bail: free(name); free(descriptor); return clazz; }
/* * Verify that "obj" is non-null and is an instance of "clazz". * * Returns "false" and throws an exception if not. */ bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz) { if (obj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); return false; } if (!dvmInstanceof(obj->clazz, clazz)) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "object is not an instance of the class"); return false; } return true; }
/* * static void dumpHprofData(String fileName, FileDescriptor fd) * * Cause "hprof" data to be dumped. We can throw an IOException if an * error occurs during file handling. */ static void Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args, JValue* pResult) { #ifdef WITH_HPROF StringObject* fileNameStr = (StringObject*) args[0]; Object* fileDescriptor = (Object*) args[1]; char* fileName; int result; /* * Only one of these may be NULL. */ if (fileNameStr == NULL && fileDescriptor == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } if (fileNameStr != NULL) { fileName = dvmCreateCstrFromString(fileNameStr); if (fileName == NULL) { /* unexpected -- malloc failure? */ dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?"); RETURN_VOID(); } } else { fileName = strdup("[fd]"); } int fd = -1; if (fileDescriptor != NULL) { fd = getFileDescriptor(fileDescriptor); if (fd < 0) RETURN_VOID(); } result = hprofDumpHeap(fileName, fd, false); free(fileName); if (result != 0) { /* ideally we'd throw something more specific based on actual failure */ dvmThrowException("Ljava/lang/RuntimeException;", "Failure during heap dump -- check log output for details"); RETURN_VOID(); } #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif RETURN_VOID(); }
/* * We have a method pointer for a method in "clazz", but it might be * pointing to a method in a derived class. We want to find the actual entry * from the class' vtable. If "clazz" is an interface, we have to do a * little more digging. * * (This is used for reflection and JNI "call method" calls.) */ const Method* dvmGetVirtualizedMethod(const ClassObject* clazz, const Method* meth) { Method* actualMeth; int methodIndex; assert(!dvmIsStaticMethod(meth)); if (dvmIsPrivateMethod(meth)) // no vtable entry for these return meth; /* * If the method was declared in an interface, we need to scan through * the class' list of interfaces for it, and find the vtable index * from that. * * TODO: use the interface cache. */ if (dvmIsInterfaceClass(meth->clazz)) { int i; for (i = 0; i < clazz->iftableCount; i++) { if (clazz->iftable[i].clazz == meth->clazz) break; } if (i == clazz->iftableCount) { dvmThrowException("Ljava/lang/IncompatibleClassChangeError;", "invoking method from interface not implemented by class"); return NULL; } methodIndex = clazz->iftable[i].methodIndexArray[meth->methodIndex]; } else { methodIndex = meth->methodIndex; } assert(methodIndex >= 0 && methodIndex < clazz->vtableCount); actualMeth = clazz->vtable[methodIndex]; /* * Make sure there's code to execute. */ if (dvmIsAbstractMethod(actualMeth)) { dvmThrowException("Ljava/lang/AbstractMethodError;", NULL); return NULL; } assert(!dvmIsMirandaMethod(actualMeth)); return actualMeth; }
/* * 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; 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); }
/* * 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(); } }
/* * 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)); }
/* * 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(); }
/* * 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(); } }
/* * static void dumpHprofDataDdms() * * Cause "hprof" data to be computed and sent directly to DDMS. */ static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args, JValue* pResult) { #ifdef WITH_HPROF int result; result = hprofDumpHeap("[DDMS]", -1, true); if (result != 0) { /* ideally we'd throw something more specific based on actual failure */ dvmThrowException("Ljava/lang/RuntimeException;", "Failure during heap dump -- check log output for details"); RETURN_VOID(); } #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif RETURN_VOID(); }
/* * Resolve an instance field reference. * * Returns NULL and throws an exception on error (no such field, illegal * access). */ InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; InstField* resField; LOGVV("--- resolving field %u (referrer=%s cl=%p)\n", ifieldIdx, referrer->descriptor, referrer->classLoader); pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindInstanceFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldError;", dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * Class must be initialized by now (unless verifier is buggy). We * could still be in the process of initializing it if the field * access is from a static initializer. */ assert(dvmIsClassInitialized(resField->field.clazz) || dvmIsClassInitializing(resField->field.clazz)); /* * The class is initialized, the method has been found. Add a pointer * to our data structure so we don't have to jump through the hoops again. */ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField); LOGVV(" field %u is %s.%s\n", ifieldIdx, resField->field.clazz->descriptor, resField->field.name); return resField; }
/* * 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(); }
/* * Gather up the allocation data and copy it into a byte[]. * * Returns NULL on failure with an exception raised. */ ArrayObject* dvmDdmGetRecentAllocations(void) { u1* data; size_t len; if (!dvmGenerateTrackedAllocationReport(&data, &len)) { /* assume OOM */ dvmThrowException("Ljava/lang/OutOfMemoryError;","recent alloc native"); return NULL; } ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', len, ALLOC_DEFAULT); if (arrayObj != NULL) memcpy(arrayObj->contents, data, len); return arrayObj; }
/* * 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 if (fieldType->primitiveType == PRIM_NOT) { if (slot < 0) { StaticField *sfield; sfield = (StaticField *)dvmSlotToField(declaringClass, slot); assert(fieldPtr == &sfield->value); dvmSetStaticFieldObject(sfield, value.l); } else { int offset = declaringClass->ifields[slot].byteOffset; assert(fieldPtr == (JValue *)BYTE_OFFSET(obj, offset)); dvmSetFieldObject(obj, offset, value.l); } } else { fieldPtr->i = value.i; } RETURN_VOID(); }
/* * Resolve a static field reference. The DexFile format doesn't distinguish * between static and instance field references, so the "resolved" pointer * in the Dex struct will have the wrong type. We trivially cast it here. * * Causes the field's class to be initialized. */ StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; StaticField* resField; pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindStaticFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldError;", dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * If we're the first to resolve the field in which this class resides, * we need to do it now. Note that, if the field was inherited from * a superclass, it is not necessarily the same as "resClass". */ if (!dvmIsClassInitialized(resField->field.clazz) && !dvmInitClass(resField->field.clazz)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } /* * The class is initialized, the method has been found. Add a pointer * to our data structure so we don't have to jump through the hoops again. */ dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); return resField; }
/* * 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 void startMethodTracingNative(String traceFileName, * FileDescriptor fd, int bufferSize, int flags) * * Start method trace profiling. * * If both "traceFileName" and "fd" are null, the result will be sent * directly to DDMS. (The non-DDMS versions of the calls are expected * to enforce non-NULL filenames.) */ static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args, JValue* pResult) { StringObject* traceFileStr = (StringObject*) args[0]; Object* traceFd = (Object*) args[1]; int bufferSize = args[2]; int flags = args[3]; if (bufferSize == 0) { // Default to 8MB per the documentation. bufferSize = 8 * 1024 * 1024; } if (bufferSize < 1024) { dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL); RETURN_VOID(); } char* traceFileName = NULL; if (traceFileStr != NULL) traceFileName = dvmCreateCstrFromString(traceFileStr); int fd = -1; if (traceFd != NULL) { int origFd = getFileDescriptor(traceFd); if (origFd < 0) RETURN_VOID(); fd = dup(origFd); if (fd < 0) { dvmThrowExceptionFmt("Ljava/lang/RuntimeException;", "dup(%d) failed: %s", origFd, strerror(errno)); RETURN_VOID(); } } dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]", fd, bufferSize, flags, (traceFileName == NULL && fd == -1)); free(traceFileName); RETURN_VOID(); }
/* * Check to see if "obj" is NULL. If so, throw an exception. Assumes the * pc has already been exported to the stack. * * Perform additional checks on debug builds. * * Use this to check for NULL when the instruction handler calls into * something that could throw an exception (so we have already called * EXPORT_PC at the top). */ static inline bool checkForNull(Object* obj) { if (obj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); return false; } #ifdef WITH_EXTRA_OBJECT_VALIDATION if (!dvmIsValidObject(obj)) { LOGE("Invalid object %p\n", obj); dvmAbort(); } #endif #ifndef NDEBUG if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) { /* probable heap corruption */ LOGE("Invalid object class %p (in %p)\n", obj->clazz, obj); dvmAbort(); } #endif return true; }
/* * Extracts the fd from a FileDescriptor object. * * If an error is encountered, or the extracted descriptor is numerically * invalid, this returns -1 with an exception raised. */ static int getFileDescriptor(Object* obj) { assert(obj != NULL); assert(strcmp(obj->clazz->descriptor, "Ljava/io/FileDescriptor;") == 0); InstField* field = dvmFindInstanceField(obj->clazz, "descriptor", "I"); if (field == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldException;", "No FileDescriptor.descriptor field"); return -1; } int fd = dvmGetFieldInt(obj, field->byteOffset); if (fd < 0) { dvmThrowExceptionFmt("Ljava/lang/RuntimeException;", "Invalid file descriptor"); return -1; } return fd; }
/* * Verify that the "cookie" is a DEX file we opened. * * Expects that the hash table will be *unlocked* here. * * If the cookie is invalid, we throw an exception and return "false". */ static bool validateCookie(int cookie) { DexOrJar* pDexOrJar = (DexOrJar*) cookie; LOGVV("+++ dex verifying cookie %p\n", pDexOrJar); if (pDexOrJar == NULL) return false; u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName); dvmHashTableLock(gDvm.userDexFiles); void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, false); dvmHashTableUnlock(gDvm.userDexFiles); if (result == NULL) { dvmThrowException("Ljava/lang/RuntimeException;", "invalid DexFile cookie"); return false; } return true; }
/* * 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); dvmReleaseTrackedAlloc((Object*) result, NULL); } free(name); free(mappedName); RETURN_PTR(result); }
/* * Magic "internal native" code stub, inserted into abstract method * definitions when a class is first loaded. This throws the expected * exception so we don't have to explicitly check for it in the interpreter. */ void dvmAbstractMethodStub(const u4* args, JValue* pResult) { LOGD("--- called into dvmAbstractMethodStub\n"); dvmThrowException("Ljava/lang/AbstractMethodError;", "abstract method not implemented"); }
GOTO_TARGET_END GOTO_TARGET(invokeSuper, bool methodCallRange) { Method* baseMethod; u2 thisReg; EXPORT_PC(); vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ ref = FETCH(1); /* method ref */ vdst = FETCH(2); /* 4 regs -or- first reg */ if (methodCallRange) { ILOGV("|invoke-super-range args=%d @0x%04x {regs=v%d-v%d}", vsrc1, ref, vdst, vdst+vsrc1-1); thisReg = vdst; } else { ILOGV("|invoke-super args=%d @0x%04x {regs=0x%04x %x}", vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); thisReg = vdst & 0x0f; } /* impossible in well-formed code, but we must check nevertheless */ if (!checkForNull((Object*) GET_REGISTER(thisReg))) GOTO_exceptionThrown(); /* * Resolve the method. This is the correct method for the static * type of the object. We also verify access permissions here. * The first arg to dvmResolveMethod() is just the referring class * (used for class loaders and such), so we don't want to pass * the superclass into the resolution call. */ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref); if (baseMethod == NULL) { baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL); if (baseMethod == NULL) { ILOGV("+ unknown method or access denied\n"); GOTO_exceptionThrown(); } } /* * Combine the object we found with the vtable offset in the * method's class. * * We're using the current method's class' superclass, not the * superclass of "this". This is because we might be executing * in a method inherited from a superclass, and we want to run * in that class' superclass. */ if (baseMethod->methodIndex >= curMethod->clazz->super->vtableCount) { /* * Method does not exist in the superclass. Could happen if * superclass gets updated. */ dvmThrowException("Ljava/lang/NoSuchMethodError;", baseMethod->name); GOTO_exceptionThrown(); } methodToCall = curMethod->clazz->super->vtable[baseMethod->methodIndex]; #if 0 if (dvmIsAbstractMethod(methodToCall)) { dvmThrowException("Ljava/lang/AbstractMethodError;", "abstract method not implemented"); GOTO_exceptionThrown(); } #else assert(!dvmIsAbstractMethod(methodToCall) || methodToCall->nativeFunc != NULL); #endif LOGVV("+++ base=%s.%s super-virtual=%s.%s\n", baseMethod->clazz->descriptor, baseMethod->name, methodToCall->clazz->descriptor, methodToCall->name); assert(methodToCall != NULL); GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); }
GOTO_TARGET_END GOTO_TARGET(invokeVirtual, bool methodCallRange) { Method* baseMethod; Object* thisPtr; EXPORT_PC(); vsrc1 = INST_AA(inst); /* AA (count) or BA (count + arg 5) */ ref = FETCH(1); /* method ref */ vdst = FETCH(2); /* 4 regs -or- first reg */ /* * The object against which we are executing a method is always * in the first argument. */ if (methodCallRange) { assert(vsrc1 > 0); ILOGV("|invoke-virtual-range args=%d @0x%04x {regs=v%d-v%d}", vsrc1, ref, vdst, vdst+vsrc1-1); thisPtr = (Object*) GET_REGISTER(vdst); } else { assert((vsrc1>>4) > 0); ILOGV("|invoke-virtual args=%d @0x%04x {regs=0x%04x %x}", vsrc1 >> 4, ref, vdst, vsrc1 & 0x0f); thisPtr = (Object*) GET_REGISTER(vdst & 0x0f); } if (!checkForNull(thisPtr)) GOTO_exceptionThrown(); /* * Resolve the method. This is the correct method for the static * type of the object. We also verify access permissions here. */ baseMethod = dvmDexGetResolvedMethod(methodClassDex, ref); if (baseMethod == NULL) { baseMethod = dvmResolveMethod(curMethod->clazz, ref,METHOD_VIRTUAL); if (baseMethod == NULL) { ILOGV("+ unknown method or access denied\n"); GOTO_exceptionThrown(); } } /* * Combine the object we found with the vtable offset in the * method. */ assert(baseMethod->methodIndex < thisPtr->clazz->vtableCount); methodToCall = thisPtr->clazz->vtable[baseMethod->methodIndex]; #if 0 if (dvmIsAbstractMethod(methodToCall)) { /* * This can happen if you create two classes, Base and Sub, where * Sub is a sub-class of Base. Declare a protected abstract * method foo() in Base, and invoke foo() from a method in Base. * Base is an "abstract base class" and is never instantiated * directly. Now, Override foo() in Sub, and use Sub. This * Works fine unless Sub stops providing an implementation of * the method. */ dvmThrowException("Ljava/lang/AbstractMethodError;", "abstract method not implemented"); GOTO_exceptionThrown(); } #else assert(!dvmIsAbstractMethod(methodToCall) || methodToCall->nativeFunc != NULL); #endif LOGVV("+++ base=%s.%s virtual[%d]=%s.%s\n", baseMethod->clazz->descriptor, baseMethod->name, (u4) baseMethod->methodIndex, methodToCall->clazz->descriptor, methodToCall->name); assert(methodToCall != NULL); #if 0 if (vsrc1 != methodToCall->insSize) { LOGW("WRONG METHOD: base=%s.%s virtual[%d]=%s.%s\n", baseMethod->clazz->descriptor, baseMethod->name, (u4) baseMethod->methodIndex, methodToCall->clazz->descriptor, methodToCall->name); //dvmDumpClass(baseMethod->clazz); //dvmDumpClass(methodToCall->clazz); dvmDumpAllClasses(0); } #endif GOTO_invokeMethod(methodCallRange, methodToCall, vsrc1, vdst); }
GOTO_TARGET(filledNewArray, bool methodCallRange) { ClassObject* arrayClass; ArrayObject* newArray; u4* contents; char typeCh; int i; u4 arg5; EXPORT_PC(); ref = FETCH(1); /* class ref */ vdst = FETCH(2); /* first 4 regs -or- range base */ if (methodCallRange) { vsrc1 = INST_AA(inst); /* #of elements */ arg5 = -1; /* silence compiler warning */ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}", vsrc1, ref, vdst, vdst+vsrc1-1); } else { arg5 = INST_A(inst); vsrc1 = INST_B(inst); /* #of elements */ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}", vsrc1, ref, vdst, arg5); } /* * Resolve the array class. */ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref); if (arrayClass == NULL) { arrayClass = dvmResolveClass(curMethod->clazz, ref, false); if (arrayClass == NULL) GOTO_exceptionThrown(); } /* if (!dvmIsArrayClass(arrayClass)) { dvmThrowException("Ljava/lang/RuntimeError;", "filled-new-array needs array class"); GOTO_exceptionThrown(); } */ /* verifier guarantees this is an array class */ assert(dvmIsArrayClass(arrayClass)); assert(dvmIsClassInitialized(arrayClass)); /* * Create an array of the specified type. */ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor); typeCh = arrayClass->descriptor[1]; if (typeCh == 'D' || typeCh == 'J') { /* category 2 primitives not allowed */ dvmThrowException("Ljava/lang/RuntimeError;", "bad filled array req"); GOTO_exceptionThrown(); } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') { /* TODO: requires multiple "fill in" loops with different widths */ LOGE("non-int primitives not implemented\n"); dvmThrowException("Ljava/lang/InternalError;", "filled-new-array not implemented for anything but 'int'"); GOTO_exceptionThrown(); } newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK); if (newArray == NULL) GOTO_exceptionThrown(); /* * Fill in the elements. It's legal for vsrc1 to be zero. */ contents = (u4*) newArray->contents; if (methodCallRange) { for (i = 0; i < vsrc1; i++) contents[i] = GET_REGISTER(vdst+i); } else { assert(vsrc1 <= 5); if (vsrc1 == 5) { contents[4] = GET_REGISTER(arg5); vsrc1--; } for (i = 0; i < vsrc1; i++) { contents[i] = GET_REGISTER(vdst & 0x0f); vdst >>= 4; } } retval.l = newArray; }
/* * Get the address of a field from an object. This can be used with "get" * or "set". * * "declaringClass" is the class in which the field was declared. For an * instance field, "obj" is the object that holds the field data; for a * static field its value is ignored. * * "If the underlying field is static, the class that declared the * field is initialized if it has not already been initialized." * * On failure, throws an exception and returns NULL. * * The documentation lists exceptional conditions and the exceptions that * should be thrown, but doesn't say which exception previals when two or * more exceptional conditions exist at the same time. For example, * attempting to set a protected field from an unrelated class causes an * IllegalAccessException, while passing in a data type that doesn't match * the field causes an IllegalArgumentException. If code does both at the * same time, we have to choose one or othe other. * * The expected order is: * (1) Check for illegal access. Throw IllegalAccessException. * (2) Make sure the object actually has the field. Throw * IllegalArgumentException. * (3) Make sure the field matches the expected type, e.g. if we issued * a "getInteger" call make sure the field is an integer or can be * converted to an int with a widening conversion. Throw * IllegalArgumentException. * (4) Make sure "obj" is not null. Throw NullPointerException. * * TODO: we're currently handling #3 after #4, because we don't check the * widening conversion until we're actually extracting the value from the * object (which won't work well if it's a null reference). */ static JValue* getFieldDataAddr(Object* obj, ClassObject* declaringClass, int slot, bool isSetOperation, bool noAccessCheck) { Field* field; JValue* result; field = dvmSlotToField(declaringClass, slot); assert(field != NULL); /* verify access */ if (!noAccessCheck) { if (isSetOperation && dvmIsFinalField(field)) { dvmThrowException("Ljava/lang/IllegalAccessException;", "field is marked 'final'"); return NULL; } ClassObject* callerClass = dvmGetCaller2Class(dvmThreadSelf()->curFrame); /* * We need to check two things: * (1) Would an instance of the calling class have access to the field? * (2) If the field is "protected", is the object an instance of the * calling class, or is the field's declaring class in the same * package as the calling class? * * #1 is basic access control. #2 ensures that, just because * you're a subclass of Foo, you can't mess with protected fields * in arbitrary Foo objects from other packages. */ if (!dvmCheckFieldAccess(callerClass, field)) { dvmThrowException("Ljava/lang/IllegalAccessException;", "access to field not allowed"); return NULL; } if (dvmIsProtectedField(field)) { bool isInstance, samePackage; if (obj != NULL) isInstance = dvmInstanceof(obj->clazz, callerClass); else isInstance = false; samePackage = dvmInSamePackage(declaringClass, callerClass); if (!isInstance && !samePackage) { dvmThrowException("Ljava/lang/IllegalAccessException;", "access to protected field not allowed"); return NULL; } } } if (dvmIsStaticField(field)) { /* init class if necessary, then return ptr to storage in "field" */ if (!dvmIsClassInitialized(declaringClass)) { if (!dvmInitClass(declaringClass)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } } result = dvmStaticFieldPtr((StaticField*) field); } else { /* * Verify object is of correct type (i.e. it actually has the * expected field in it), then grab a pointer to obj storage. * The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL. */ if (!dvmVerifyObjectInClass(obj, declaringClass)) { assert(dvmCheckException(dvmThreadSelf())); if (obj != NULL) { LOGD("Wrong type of object for field lookup: %s %s\n", obj->clazz->descriptor, declaringClass->descriptor); } return NULL; } result = dvmFieldPtr(obj, ((InstField*) field)->byteOffset); } return result; }
/* * Find the class corresponding to "classIdx", which maps to a class name * string. It might be in the same DEX file as "referrer", in a different * DEX file, generated by a class loader, or generated by the VM (e.g. * array classes). * * Because the DexTypeId is associated with the referring class' DEX file, * we may have to resolve the same class more than once if it's referred * to from classes in multiple DEX files. This is a necessary property for * DEX files associated with different class loaders. * * We cache a copy of the lookup in the DexFile's "resolved class" table, * so future references to "classIdx" are faster. * * Note that "referrer" may be in the process of being linked. * * Traditional VMs might do access checks here, but in Dalvik the class * "constant pool" is shared between all classes in the DEX file. We rely * on the verifier to do the checks for us. * * Does not initialize the class. * * "fromUnverifiedConstant" should only be set if this call is the direct * result of executing a "const-class" or "instance-of" instruction, which * use class constants not resolved by the bytecode verifier. * * Returns NULL with an exception raised on failure. */ ClassObject* dvmResolveClass(const ClassObject* referrer, u4 classIdx, bool fromUnverifiedConstant) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const char* className; /* * Check the table first -- this gets called from the other "resolve" * methods. */ resClass = dvmDexGetResolvedClass(pDvmDex, classIdx); if (resClass != NULL) return resClass; LOGVV("--- resolving class %u (referrer=%s cl=%p)\n", classIdx, referrer->descriptor, referrer->classLoader); /* * Class hasn't been loaded yet, or is in the process of being loaded * and initialized now. Try to get a copy. If we find one, put the * pointer in the DexTypeId. There isn't a race condition here -- * 32-bit writes are guaranteed atomic on all target platforms. Worst * case we have two threads storing the same value. * * If this is an array class, we'll generate it here. */ className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx); if (className[0] != '\0' && className[1] == '\0') { /* primitive type */ resClass = dvmFindPrimitiveClass(className[0]); } else { resClass = dvmFindClassNoInit(className, referrer->classLoader); } if (resClass != NULL) { /* * If the referrer was pre-verified, the resolved class must come * from the same DEX or from a bootstrap class. The pre-verifier * makes assumptions that could be invalidated by a wacky class * loader. (See the notes at the top of oo/Class.c.) * * The verifier does *not* fail a class for using a const-class * or instance-of instruction referring to an unresolveable class, * because the result of the instruction is simply a Class object * or boolean -- there's no need to resolve the class object during * verification. Instance field and virtual method accesses can * break dangerously if we get the wrong class, but const-class and * instance-of are only interesting at execution time. So, if we * we got here as part of executing one of the "unverified class" * instructions, we skip the additional check. * * Ditto for class references from annotations and exception * handler lists. */ if (!fromUnverifiedConstant && IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED)) { ClassObject* resClassCheck = resClass; if (dvmIsArrayClass(resClassCheck)) resClassCheck = resClassCheck->elementClass; if (referrer->pDvmDex != resClassCheck->pDvmDex && resClassCheck->classLoader != NULL) { LOGW("Class resolved by unexpected DEX:" " %s(%p):%p ref [%s] %s(%p):%p\n", referrer->descriptor, referrer->classLoader, referrer->pDvmDex, resClass->descriptor, resClassCheck->descriptor, resClassCheck->classLoader, resClassCheck->pDvmDex); LOGW("(%s had used a different %s during pre-verification)\n", referrer->descriptor, resClass->descriptor); dvmThrowException("Ljava/lang/IllegalAccessError;", "Class ref in pre-verified class resolved to unexpected " "implementation"); return NULL; } } LOGVV("##### +ResolveClass(%s): referrer=%s dex=%p ldr=%p ref=%d\n", resClass->descriptor, referrer->descriptor, referrer->pDvmDex, referrer->classLoader, classIdx); /* * Add what we found to the list so we can skip the class search * next time through. * * TODO: should we be doing this when fromUnverifiedConstant==true? * (see comments at top of oo/Class.c) */ dvmDexSetResolvedClass(pDvmDex, classIdx, resClass); } else { /* not found, exception should be raised */ LOGVV("Class not found: %s\n", dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); assert(dvmCheckException(dvmThreadSelf())); } return resClass; }
/* * Generate a proxy class with the specified name, interfaces, and loader. * "interfaces" is an array of class objects. * * The interpreted code has done all of the necessary checks, e.g. we know * that "interfaces" contains only interface classes. * * On failure we leave a partially-created class object sitting around, * but the garbage collector will take care of it. */ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, Object* loader) { int result = -1; char* nameStr = NULL; Method** methods = NULL; ClassObject* newClass = NULL; int i; nameStr = dvmCreateCstrFromString(str); if (nameStr == NULL) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "missing name"); goto bail; } LOGV("+++ Generate proxy class '%s' %p from %d interface classes\n", nameStr, loader, interfaces->length); /* * Characteristics of a Proxy class: * - concrete class, public and final * - superclass is java.lang.reflect.Proxy * - implements all listed interfaces (req'd for instanceof) * - has one method for each method in the interfaces (barring duplicates) * - has one constructor (takes an InvocationHandler arg) * - has overrides for hashCode, equals, and toString (these come first) * - has one field, a reference to the InvocationHandler object * * The idea here is to create a class object and fill in the details * as we would in loadClassFromDex(), and then call dvmLinkClass() to do * all the heavy lifting (notably populating the virtual and interface * method tables). */ /* * Generate a temporary list of virtual methods. */ int methodCount; if (!gatherMethods(interfaces, &methods, &methodCount)) goto bail; /* * Allocate storage for the class object and set some basic fields. */ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT); if (newClass == NULL) return NULL; DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass); newClass->descriptorAlloc = dvmNameToDescriptor(nameStr); newClass->descriptor = newClass->descriptorAlloc; newClass->accessFlags = ACC_PUBLIC | ACC_FINAL; newClass->super = gDvm.classJavaLangReflectProxy; newClass->primitiveType = PRIM_NOT; newClass->classLoader = loader; #if WITH_HPROF && WITH_HPROF_STACK newClass->hprofSerialNumber = 0; hprofFillInStackTrace(newClass); #endif /* * Add direct method definitions. We have one (the constructor). */ newClass->directMethodCount = 1; newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader, 1 * sizeof(Method)); createConstructor(newClass, &newClass->directMethods[0]); dvmLinearReadOnly(newClass->classLoader, newClass->directMethods); /* * Add virtual method definitions. */ newClass->virtualMethodCount = methodCount; newClass->virtualMethods = (Method*) dvmLinearAlloc(newClass->classLoader, newClass->virtualMethodCount * sizeof(Method)); for (i = 0; i < newClass->virtualMethodCount; i++) { createHandlerMethod(newClass, &newClass->virtualMethods[i],methods[i]); } dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods); /* * Add interface list. */ int interfaceCount = interfaces->length; ClassObject** ifArray = (ClassObject**) interfaces->contents; newClass->interfaceCount = interfaceCount; newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader, sizeof(ClassObject*) * interfaceCount); for (i = 0; i < interfaceCount; i++) newClass->interfaces[i] = ifArray[i]; dvmLinearReadOnly(newClass->classLoader, newClass->interfaces); /* * The class has one instance field, "protected InvocationHandler h", * which is filled in by the constructor. */ newClass->ifieldCount = 1; newClass->ifields = (InstField*) dvmLinearAlloc(newClass->classLoader, 1 * sizeof(InstField)); InstField* ifield = &newClass->ifields[0]; ifield->field.clazz = newClass; ifield->field.name = "h"; ifield->field.signature = "Ljava/lang/reflect/InvocationHandler;"; ifield->field.accessFlags = ACC_PROTECTED; ifield->byteOffset = -1; /* set later */ dvmLinearReadOnly(newClass->classLoader, newClass->ifields); /* * Everything is ready. See if the linker will lap it up. */ newClass->status = CLASS_LOADED; if (!dvmLinkClass(newClass, true)) { LOGI("Proxy class link failed\n"); goto bail; } /* * All good. Add it to the hash table. We should NOT see a collision * here; if we do, it means the caller has screwed up and provided us * with a duplicate name. */ if (!dvmAddClassToHash(newClass)) { LOGE("ERROR: attempted to generate %s more than once\n", newClass->descriptor); goto bail; } result = 0; bail: free(nameStr); free(methods); if (result != 0) { /* must free innards explicitly if we didn't finish linking */ dvmFreeClassInnards(newClass); newClass = NULL; dvmThrowException("Ljava/lang/RuntimeException;", NULL); } /* this allows the GC to free it */ dvmReleaseTrackedAlloc((Object*) newClass, NULL); return newClass; }
/* * This is the common message body for proxy methods. * * The method we're calling looks like: * public Object invoke(Object proxy, Method method, Object[] args) * * This means we have to create a Method object, box our arguments into * a new Object[] array, make the call, and unbox the return value if * necessary. */ static void proxyInvoker(const u4* args, JValue* pResult, const Method* method, Thread* self) { Object* thisObj = (Object*) args[0]; Object* methodObj = NULL; ArrayObject* argArray = NULL; Object* handler; Method* invoke; ClassObject* returnType; int hOffset; JValue invokeResult; /* * Retrieve handler object for this proxy instance. */ hOffset = dvmFindFieldOffset(thisObj->clazz, "h", "Ljava/lang/reflect/InvocationHandler;"); if (hOffset < 0) { LOGE("Unable to find 'h' in Proxy object\n"); dvmAbort(); } handler = dvmGetFieldObject(thisObj, hOffset); /* * Find the invoke() method, looking in "this"s class. (Because we * start here we don't have to convert it to a vtable index and then * index into this' vtable.) */ invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); if (invoke == NULL) { LOGE("Unable to find invoke()\n"); dvmAbort(); } LOGV("invoke: %s.%s, this=%p, handler=%s\n", method->clazz->descriptor, method->name, thisObj, handler->clazz->descriptor); /* * Create a java.lang.reflect.Method object for this method. * * We don't want to use "method", because that's the concrete * implementation in the proxy class. We want the abstract Method * from the declaring interface. We have a pointer to it tucked * away in the "insns" field. * * TODO: this could be cached for performance. */ methodObj = dvmCreateReflectMethodObject((Method*) method->insns); if (methodObj == NULL) { assert(dvmCheckException(self)); goto bail; } /* * Determine the return type from the signature. * * TODO: this could be cached for performance. */ returnType = dvmGetBoxedReturnType(method); if (returnType == NULL) { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGE("Could not determine return type for '%s'\n", desc); free(desc); assert(dvmCheckException(self)); goto bail; } LOGV(" return type will be %s\n", returnType->descriptor); /* * Convert "args" array into Object[] array, using the method * signature to determine types. If the method takes no arguments, * we must pass null. */ argArray = boxMethodArgs(method, args+1); if (dvmCheckException(self)) goto bail; /* * Call h.invoke(proxy, method, args). * * We don't need to repackage exceptions, so if one has been thrown * just jump to the end. */ dvmCallMethod(self, invoke, handler, &invokeResult, thisObj, methodObj, argArray); if (dvmCheckException(self)) goto bail; /* * Unbox the return value. If it's the wrong type, throw a * ClassCastException. If it's a null pointer and we need a * primitive type, throw a NullPointerException. */ if (returnType->primitiveType == PRIM_VOID) { LOGVV("+++ ignoring return to void\n"); } else if (invokeResult.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowException("Ljava/lang/NullPointerException;", "null result when primitive expected"); goto bail; } pResult->l = NULL; } else { if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) { dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;", ((Object*)invokeResult.l)->clazz->descriptor); goto bail; } } bail: dvmReleaseTrackedAlloc(methodObj, self); dvmReleaseTrackedAlloc((Object*)argArray, self); }
/* * Resolve an interface method reference. * * Returns NULL with an exception raised on failure. */ Method* dvmResolveInterfaceMethod(const ClassObject* referrer, u4 methodIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexMethodId* pMethodId; Method* resMethod; int i; LOGVV("--- resolving interface method %d (referrer=%s)\n", methodIdx, referrer->descriptor); pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); resClass = dvmResolveClass(referrer, pMethodId->classIdx, false); if (resClass == NULL) { /* can't find the class that the method is a part of */ assert(dvmCheckException(dvmThreadSelf())); return NULL; } if (!dvmIsInterfaceClass(resClass)) { /* whoops */ dvmThrowExceptionWithClassMessage( "Ljava/lang/IncompatibleClassChangeError;", resClass->descriptor); return NULL; } /* * This is the first time the method has been resolved. Set it in our * resolved-method structure. It always resolves to the same thing, * so looking it up and storing it doesn't create a race condition. * * If we scan into the interface's superclass -- which is always * java/lang/Object -- we will catch things like: * interface I ... * I myobj = (something that implements I) * myobj.hashCode() * However, the Method->methodIndex will be an offset into clazz->vtable, * rather than an offset into clazz->iftable. The invoke-interface * code can test to see if the method returned is abstract or concrete, * and use methodIndex accordingly. I'm not doing this yet because * (a) we waste time in an unusual case, and (b) we're probably going * to fix it in the DEX optimizer. * * We do need to scan the superinterfaces, in case we're invoking a * superinterface method on an interface reference. The class in the * DexTypeId is for the static type of the object, not the class in * which the method is first defined. We have the full, flattened * list in "iftable". */ const char* methodName = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx); DexProto proto; dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n", methodName, methodSig, resClass->descriptor); resMethod = dvmFindVirtualMethod(resClass, methodName, &proto); if (resMethod == NULL) { LOGVV("+++ did not resolve immediately\n"); for (i = 0; i < resClass->iftableCount; i++) { resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz, methodName, &proto); if (resMethod != NULL) break; } if (resMethod == NULL) { dvmThrowException("Ljava/lang/NoSuchMethodError;", methodName); return NULL; } } else { LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name, resMethod->clazz->descriptor, (u4) resMethod->methodIndex); } LOGVV("--- found interface method %d (%s.%s)\n", methodIdx, resClass->descriptor, resMethod->name); /* we're expecting this to be abstract */ assert(dvmIsAbstractMethod(resMethod)); /* interface methods are always public; no need to check access */ /* * The interface class *may* be initialized. According to VM spec * v2 2.17.4, the interfaces a class refers to "need not" be initialized * when the class is initialized. * * It isn't necessary for an interface class to be initialized before * we resolve methods on that interface. * * We choose not to do the initialization now. */ //assert(dvmIsClassInitialized(resMethod->clazz)); /* * The class is initialized, the method has been found. Add a pointer * to our data structure so we don't have to jump through the hoops again. */ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); return resMethod; }