/* * 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; }
/* * 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); }
/* * Find the method corresponding to "methodRef". * * We use "referrer" to find the DexFile with the constant pool that * "methodRef" is an index into. We also use its class loader. The method * being resolved may very well be in a different DEX file. * * If this is a static method, we ensure that the method's class is * initialized. */ Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx, MethodType methodType) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexMethodId* pMethodId; Method* resMethod; assert(methodType != METHOD_INTERFACE); LOGVV("--- resolving method %u (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)) { /* method is part of an interface */ dvmThrowExceptionWithClassMessage( "Ljava/lang/IncompatibleClassChangeError;", resClass->descriptor); return NULL; } const char* name = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx); DexProto proto; dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); /* * We need to chase up the class hierarchy to find methods defined * in super-classes. (We only want to check the current class * if we're looking for a constructor; since DIRECT calls are only * for constructors and private methods, we don't want to walk up.) */ if (methodType == METHOD_DIRECT) { resMethod = dvmFindDirectMethod(resClass, name, &proto); } else if (methodType == METHOD_STATIC) { resMethod = dvmFindDirectMethodHier(resClass, name, &proto); } else { resMethod = dvmFindVirtualMethodHier(resClass, name, &proto); } if (resMethod == NULL) { dvmThrowException("Ljava/lang/NoSuchMethodError;", name); return NULL; } LOGVV("--- found method %d (%s.%s)\n", methodIdx, resClass->descriptor, resMethod->name); /* see if this is a pure-abstract method */ if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) { dvmThrowException("Ljava/lang/AbstractMethodError;", name); return NULL; } /* * If we're the first to resolve this class, we need to initialize * it now. Only necessary for METHOD_STATIC. */ if (methodType == METHOD_STATIC) { if (!dvmIsClassInitialized(resMethod->clazz) && !dvmInitClass(resMethod->clazz)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } else { assert(!dvmCheckException(dvmThreadSelf())); } } else { /* * Edge case: if the <clinit> for a class creates an instance * of itself, we will call <init> on a class that is still being * initialized by us. */ assert(dvmIsClassInitialized(resMethod->clazz) || dvmIsClassInitializing(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; }