/* * 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. */ /* * Operations on an Object. */ #include "Dalvik.h" /* * Find a matching field, in the current class only. * * Returns NULL if the field can't be found. (Does not throw an exception.) */ InstField* dvmFindInstanceField(const ClassObject* clazz, const char* fieldName, const char* signature) { InstField* pField; int i; assert(clazz != NULL); /* * Find a field with a matching name and signature. The Java programming * language does not allow you to have two fields with the same name * and different types, but the Java VM spec does allow it, so we can't * bail out early when the name matches. */ pField = clazz->ifields; for (i = 0; i < clazz->ifieldCount; i++, pField++) { if (strcmp(fieldName, pField->field.name) == 0 && strcmp(signature, pField->field.signature) == 0) { return pField; } } return NULL; } /* * Find a matching field, in this class or a superclass. * * Searching through interfaces isn't necessary, because interface fields * are inherently public/static/final. * * Returns NULL if the field can't be found. (Does not throw an exception.) */ InstField* dvmFindInstanceFieldHier(const ClassObject* clazz, const char* fieldName, const char* signature) { InstField* pField; /* * Search for a match in the current class. */ pField = dvmFindInstanceField(clazz, fieldName, signature); if (pField != NULL) return pField; if (clazz->super != NULL) return dvmFindInstanceFieldHier(clazz->super, fieldName, signature); else return NULL; } /* * Find a matching field, in this class or an interface. * * Returns NULL if the field can't be found. (Does not throw an exception.) */ StaticField* dvmFindStaticField(const ClassObject* clazz, const char* fieldName, const char* signature) { StaticField* pField; int i; assert(clazz != NULL); /* * Find a field with a matching name and signature. As with instance * fields, the VM allows you to have two fields with the same name so * long as they have different types. */ pField = clazz->sfields; for (i = 0; i < clazz->sfieldCount; i++, pField++) { if (strcmp(fieldName, pField->field.name) == 0 && strcmp(signature, pField->field.signature) == 0) { return pField; } } return NULL; } /* * Find a matching field, in this class or a superclass. * * Returns NULL if the field can't be found. (Does not throw an exception.) */ StaticField* dvmFindStaticFieldHier(const ClassObject* clazz, const char* fieldName, const char* signature) { StaticField* pField; /* * Search for a match in the current class. */ pField = dvmFindStaticField(clazz, fieldName, signature); if (pField != NULL) return pField; /* * See if it's in any of our interfaces. We don't check interfaces * inherited from the superclass yet. * * (Note the set may have been stripped down because of redundancy with * the superclass; see notes in createIftable.) */ int i = 0; if (clazz->super != NULL) { assert(clazz->iftableCount >= clazz->super->iftableCount); i = clazz->super->iftableCount; } for ( ; i < clazz->iftableCount; i++) { ClassObject* iface = clazz->iftable[i].clazz; pField = dvmFindStaticField(iface, fieldName, signature); if (pField != NULL) return pField; } if (clazz->super != NULL) return dvmFindStaticFieldHier(clazz->super, fieldName, signature); else return NULL; } /* * Find a matching field, in this class or a superclass. * * We scan both the static and instance field lists in the class. If it's * not found there, we check the direct interfaces, and then recursively * scan the superclasses. This is the order prescribed in the VM spec * (v2 5.4.3.2). * * In most cases we know that we're looking for either a static or an * instance field and there's no value in searching through both types. * During verification we need to recognize and reject certain unusual * situations, and we won't see them unless we walk the lists this way. */ Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName, const char* signature) { Field* pField; /* * Search for a match in the current class. Which set we scan first * doesn't really matter. */ pField = (Field*) dvmFindStaticField(clazz, fieldName, signature); if (pField != NULL) return pField; pField = (Field*) dvmFindInstanceField(clazz, fieldName, signature); if (pField != NULL) return pField; /* * See if it's in any of our interfaces. We don't check interfaces * inherited from the superclass yet. */ int i = 0; if (clazz->super != NULL) { assert(clazz->iftableCount >= clazz->super->iftableCount); i = clazz->super->iftableCount; } for ( ; i < clazz->iftableCount; i++) { ClassObject* iface = clazz->iftable[i].clazz; pField = (Field*) dvmFindStaticField(iface, fieldName, signature); if (pField != NULL) return pField; } if (clazz->super != NULL) return dvmFindFieldHier(clazz->super, fieldName, signature); else return NULL; } /* * Compare the given name, return type, and argument types with the contents * of the given method. This returns 0 if they are equal and non-zero if not. */ static inline int compareMethodHelper(Method* method, const char* methodName, const char* returnType, size_t argCount, const char** argTypes) { DexParameterIterator iterator; const DexProto* proto; if (strcmp(methodName, method->name) != 0) { return 1; } proto = &method->prototype; if (strcmp(returnType, dexProtoGetReturnType(proto)) != 0) { return 1; } if (dexProtoGetParameterCount(proto) != argCount) { return 1; } dexParameterIteratorInit(&iterator, proto); for (/*argCount*/; argCount != 0; argCount--, argTypes++) { const char* argType = *argTypes; const char* paramType = dexParameterIteratorNextDescriptor(&iterator); if (paramType == NULL) { /* Param list ended early; no match */ break; } else if (strcmp(argType, paramType) != 0) { /* Types aren't the same; no match. */ break; } } if (argCount == 0) { /* We ran through all the given arguments... */ if (dexParameterIteratorNextDescriptor(&iterator) == NULL) { /* ...and through all the method's arguments; success! */ return 0; } } return 1; } /* * Get the count of arguments in the given method descriptor string, * and also find a pointer to the return type. */ static inline size_t countArgsAndFindReturnType(const char* descriptor, const char** pReturnType) { size_t count = 0; bool bogus = false; bool done = false; assert(*descriptor == '('); descriptor++; while (!done) { switch (*descriptor) { case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': { count++; break; } case '[': { do { descriptor++; } while (*descriptor == '['); /* * Don't increment count, as it will be taken care of * by the next iteration. Also, decrement descriptor * to compensate for the increment below the switch. */ descriptor--; break; } case 'L': { do { descriptor++; } while ((*descriptor != ';') && (*descriptor != '\0')); count++; if (*descriptor == '\0') { /* Bogus descriptor. */ done = true; bogus = true; } break; } case ')': { /* * Note: The loop will exit after incrementing descriptor * one more time, so it then points at the return type. */ done = true; break; } default: { /* Bogus descriptor. */ done = true; bogus = true; break; } } descriptor++; } if (bogus) { *pReturnType = NULL; return 0; } *pReturnType = descriptor; return count; } /* * Copy the argument types into the given array using the given buffer * for the contents. */ static inline void copyTypes(char* buffer, const char** argTypes, size_t argCount, const char* descriptor) { size_t i; char c; /* Skip the '('. */ descriptor++; for (i = 0; i < argCount; i++) { argTypes[i] = buffer; /* Copy all the array markers and one extra character. */ do { c = *(descriptor++); *(buffer++) = c; } while (c == '['); if (c == 'L') { /* Copy the rest of a class name. */ do { c = *(descriptor++); *(buffer++) = c; } while (c != ';'); } *(buffer++) = '\0'; } } /* * Look for a match in the given class. Returns the match if found * or NULL if not. */ static Method* findMethodInListByDescriptor(const ClassObject* clazz, bool findVirtual, bool isHier, const char* name, const char* descriptor) { const char* returnType; size_t argCount = countArgsAndFindReturnType(descriptor, &returnType); if (returnType == NULL) { LOGW("Bogus method descriptor: %s\n", descriptor); return NULL; } /* * Make buffer big enough for all the argument type characters and * one '\0' per argument. The "- 2" is because "returnType - * descriptor" includes two parens. */ char buffer[argCount + (returnType - descriptor) - 2]; const char* argTypes[argCount]; copyTypes(buffer, argTypes, argCount, descriptor); while (clazz != NULL) { Method* methods; size_t methodCount; size_t i; if (findVirtual) { methods = clazz->virtualMethods; methodCount = clazz->virtualMethodCount; } else { methods = clazz->directMethods; methodCount = clazz->directMethodCount; } for (i = 0; i < methodCount; i++) { Method* method = &methods[i]; if (compareMethodHelper(method, name, returnType, argCount, argTypes) == 0) { return method; } } if (! isHier) { break; } clazz = clazz->super; } return NULL; } /* * Look for a match in the given clazz. Returns the match if found * or NULL if not. * * "wantedType" should be METHOD_VIRTUAL or METHOD_DIRECT to indicate the * list to search through. If the match can come from either list, use * MATCH_UNKNOWN to scan both. */ static Method* findMethodInListByProto(const ClassObject* clazz, MethodType wantedType, bool isHier, const char* name, const DexProto* proto) { while (clazz != NULL) { int i; /* * Check the virtual and/or direct method lists. */ if (wantedType == METHOD_VIRTUAL || wantedType == METHOD_UNKNOWN) { for (i = 0; i < clazz->virtualMethodCount; i++) { Method* method = &clazz->virtualMethods[i]; if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) { return method; } } } if (wantedType == METHOD_DIRECT || wantedType == METHOD_UNKNOWN) { for (i = 0; i < clazz->directMethodCount; i++) { Method* method = &clazz->directMethods[i]; if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) { return method; } } } if (! isHier) { break; } clazz = clazz->super; } return NULL; } /* * Find a "virtual" method in a class. * * Does not chase into the superclass. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz, const char* methodName, const char* descriptor) { return findMethodInListByDescriptor(clazz, true, false, methodName, descriptor); // TODO? - throw IncompatibleClassChangeError if a match is // found in the directMethods list, rather than NotFoundError. // Note we could have been called by dvmFindVirtualMethodHier though. } /* * Find a "virtual" method in a class, knowing only the name. This is * only useful in limited circumstances, e.g. when searching for a member * of an annotation class. * * Does not chase into the superclass. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindVirtualMethodByName(const ClassObject* clazz, const char* methodName) { Method* methods = clazz->virtualMethods; int methodCount = clazz->virtualMethodCount; int i; for (i = 0; i < methodCount; i++) { if (strcmp(methods[i].name, methodName) == 0) return &methods[i]; } return NULL; } /* * Find a "virtual" method in a class. * * Does not chase into the superclass. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName, const DexProto* proto) { return findMethodInListByProto(clazz, METHOD_VIRTUAL, false, methodName, proto); } /* * Find a "virtual" method in a class. If we don't find it, try the * superclass. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz, const char* methodName, const char* descriptor) { return findMethodInListByDescriptor(clazz, true, true, methodName, descriptor); } /* * Find a "virtual" method in a class. If we don't find it, try the * superclass. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindVirtualMethodHier(const ClassObject* clazz, const char* methodName, const DexProto* proto) { return findMethodInListByProto(clazz, METHOD_VIRTUAL, true, methodName, proto); } /* * Find a "direct" method (static, private, or "<*init>"). * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz, const char* methodName, const char* descriptor) { return findMethodInListByDescriptor(clazz, false, false, methodName, descriptor); } /* * Find a "direct" method. If we don't find it, try the superclass. This * is only appropriate for static methods, but will work for all direct * methods. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz, const char* methodName, const char* descriptor) { return findMethodInListByDescriptor(clazz, false, true, methodName, descriptor); } /* * Find a "direct" method (static or "<*init>"). * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName, const DexProto* proto) { return findMethodInListByProto(clazz, METHOD_DIRECT, false, methodName, proto); } /* * Find a "direct" method in a class. If we don't find it, try the * superclass. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindDirectMethodHier(const ClassObject* clazz, const char* methodName, const DexProto* proto) { return findMethodInListByProto(clazz, METHOD_DIRECT, true, methodName, proto); } /* * Find a virtual or static method in a class. If we don't find it, try the * superclass. This is compatible with the VM spec (v2 5.4.3.3) method * search order, but it stops short of scanning through interfaces (which * should be done after this function completes). * * In most cases we know that we're looking for either a static or an * instance field and there's no value in searching through both types. * During verification we need to recognize and reject certain unusual * situations, and we won't see them unless we walk the lists this way. * * Returns NULL if the method can't be found. (Does not throw an exception.) */ Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName, const DexProto* proto) { return findMethodInListByProto(clazz, METHOD_UNKNOWN, true, methodName, proto); } /* * 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; } /* * Get the source file for a method. */ const char* dvmGetMethodSourceFile(const Method* meth) { /* * TODO: A method's debug info can override the default source * file for a class, so we should account for that possibility * here. */ return meth->clazz->sourceFile; } /* * Dump some information about an object. */ void dvmDumpObject(const Object* obj) { ClassObject* clazz; int i; if (obj == NULL || obj->clazz == NULL) { LOGW("Null or malformed object not dumped\n"); return; } clazz = obj->clazz; LOGD("----- Object dump: %p (%s, %d bytes) -----\n", obj, clazz->descriptor, (int) clazz->objectSize); //printHexDump(obj, clazz->objectSize); LOGD(" Fields:\n"); while (clazz != NULL) { LOGD(" -- %s\n", clazz->descriptor); for (i = 0; i < clazz->ifieldCount; i++) { const InstField* pField = &clazz->ifields[i]; char type = pField->field.signature[0]; if (type == 'F' || type == 'D') { double dval; if (type == 'F') dval = dvmGetFieldFloat(obj, pField->byteOffset); else dval = dvmGetFieldDouble(obj, pField->byteOffset); LOGD(" %2d: '%s' '%s' af=%04x off=%d %.3f\n", i, pField->field.name, pField->field.signature, pField->field.accessFlags, pField->byteOffset, dval); } else { u8 lval; if (type == 'J') lval = dvmGetFieldLong(obj, pField->byteOffset); else if (type == 'Z') lval = dvmGetFieldBoolean(obj, pField->byteOffset); else lval = dvmGetFieldInt(obj, pField->byteOffset); LOGD(" %2d: '%s' '%s' af=%04x off=%d 0x%08llx\n", i, pField->field.name, pField->field.signature, pField->field.accessFlags, pField->byteOffset, lval); } } clazz = clazz->super; } }