/* * Returns "true" if this is a "checked" exception, i.e. it's a subclass * of Throwable (assumed) but not a subclass of RuntimeException or Error. */ bool dvmIsCheckedException(const Object* exception) { if (dvmInstanceof(exception->clazz, gDvm.classJavaLangError) || dvmInstanceof(exception->clazz, gDvm.classJavaLangRuntimeException)) { return false; } else { return true; } }
/* * Unwrap a primitive data type, if necessary. * * If "returnType" is not primitive, we just tuck "value" into JValue and * return it after verifying that it's the right type of object. * * Fails if the field is primitive and "value" is either not a boxed * primitive or is of a type that cannot be converted. * * Returns "true" on success, "false" on failure. */ bool dvmUnboxPrimitive(Object* value, ClassObject* returnType, JValue* pResult) { PrimitiveType typeIndex = returnType->primitiveType; PrimitiveType valueIndex; if (typeIndex == PRIM_NOT) { if (value != NULL && !dvmInstanceof(value->clazz, returnType)) { ALOGD("wrong object type: %s %s", value->clazz->descriptor, returnType->descriptor); return false; } pResult->l = value; return true; } else if (typeIndex == PRIM_VOID) { /* can't put anything into a void */ return false; } valueIndex = getBoxedType((DataObject*)value); if (valueIndex == PRIM_NOT) return false; /* assumes value is stored in first instance field of "value" */ /* (see dvmValidateBoxClasses) */ if (dvmConvertPrimitiveValue(valueIndex, typeIndex, (s4*) ((DataObject*)value)->instanceData, (s4*)pResult) < 0) { ALOGV("Prim conversion failed"); return false; } return true; }
/* * Convert types and widen primitives. Puts the value of "arg" into * "destPtr". * * Returns the width of the argument in 32-bit words (1 or 2), or -1 on error. */ int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* destPtr) { int retVal; if (dvmIsPrimitiveClass(type)) { /* e.g.: "arg" is java/lang/Float instance, "type" is VM float class */ PrimitiveType srcType; s4* valuePtr; srcType = getBoxedType(arg); if (srcType == PRIM_NOT) { // didn't pass a boxed primitive in LOGVV("conv arg: type '%s' not boxed primitive", arg->clazz->descriptor); return -1; } /* assumes value is stored in first instance field */ valuePtr = (s4*) arg->instanceData; retVal = dvmConvertPrimitiveValue(srcType, type->primitiveType, valuePtr, destPtr); } else { /* verify object is compatible */ if ((arg == NULL) || dvmInstanceof(arg->clazz, type)) { *destPtr = (s4) arg; retVal = 1; } else { LOGVV("Arg %p (%s) not compatible with %s", arg, arg->clazz->descriptor, type->descriptor); retVal = -1; } } return retVal; }
static void countAssignableInstancesOfClassCallback(Object *obj, void *arg) { CountContext *ctx = (CountContext *)arg; assert(ctx != NULL); if (obj->clazz != NULL && dvmInstanceof(obj->clazz, ctx->clazz)) { ctx->count += 1; } }
static void countAssignableInstancesOfClassCallback(void *ptr, void *arg) { CountContext *ctx = arg; const Object *obj = ptr; assert(ctx != NULL); if (dvmInstanceof(obj->clazz, ctx->clazz)) { ctx->count += 1; } }
/* * 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; }
/* * Get the "cause" field from an exception. * * The Throwable class initializes the "cause" field to "this" to * differentiate between being initialized to null and never being * initialized. We check for that here and convert it to NULL. */ Object* dvmGetExceptionCause(const Object* exception) { if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) { LOGE("Tried to get cause from object of type '%s'\n", exception->clazz->descriptor); dvmAbort(); } Object* cause = dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause); if (cause == exception) return NULL; else return cause; }
/* * Determine whether "sub" is an instance of "clazz", where both of these * are array classes. * * Consider an array class, e.g. Y[][], where Y is a subclass of X. * Y[][] instanceof Y[][] --> true (identity) * Y[][] instanceof X[][] --> true (element superclass) * Y[][] instanceof Y --> false * Y[][] instanceof Y[] --> false * Y[][] instanceof Object --> true (everything is an object) * Y[][] instanceof Object[] --> true * Y[][] instanceof Object[][] --> true * Y[][] instanceof Object[][][] --> false (too many []s) * Y[][] instanceof Serializable --> true (all arrays are Serializable) * Y[][] instanceof Serializable[] --> true * Y[][] instanceof Serializable[][] --> false (unless Y is Serializable) * * Don't forget about primitive types. * int[] instanceof Object[] --> false * * "subElemClass" is sub->elementClass. * * "subDim" is usually just sub->dim, but for some kinds of checks we want * to pass in a non-array class and pretend that it's an array. */ static int isArrayInstanceOfArray(const ClassObject* subElemClass, int subDim, const ClassObject* clazz) { //assert(dvmIsArrayClass(sub)); assert(dvmIsArrayClass(clazz)); /* "If T is an array type TC[]... one of the following must be true: * TC and SC are the same primitive type. * TC and SC are reference types and type SC can be cast to TC [...]." * * We need the class objects for the array elements. For speed we * tucked them into the class object. */ assert(subDim > 0 && clazz->arrayDim > 0); if (subDim == clazz->arrayDim) { /* * See if "sub" is an instance of "clazz". This handles the * interfaces, java.lang.Object, superclassing, etc. */ return dvmInstanceof(subElemClass, clazz->elementClass); } else if (subDim > clazz->arrayDim) { /* * The thing we might be an instance of has fewer dimensions. It * must be an Object or array of Object, or a standard array * interface or array of standard array interfaces (the standard * interfaces being java/lang/Cloneable and java/io/Serializable). */ if (dvmIsInterfaceClass(clazz->elementClass)) { /* * See if the class implements its base element. We know the * base element is an interface; if the array class implements * it, we know it's a standard array interface. */ return dvmImplements(clazz, clazz->elementClass); } else { /* * See if this is an array of Object, Object[], etc. We know * that the superclass of an array is always Object, so we * just compare the element type to that. */ return (clazz->elementClass == clazz->super); } } else { /* * Too many []s. */ return false; } }
/* * Verify that "obj" is non-null and is an instance of "clazz". * Used to implement reflection on fields and methods. * * Returns "false" and throws an exception if not. */ bool dvmVerifyObjectInClass(Object* obj, ClassObject* clazz) { ClassObject* exceptionClass = NULL; if (obj == NULL) { exceptionClass = gDvm.exNullPointerException; } else if (!dvmInstanceof(obj->clazz, clazz)) { exceptionClass = gDvm.exIllegalArgumentException; } if (exceptionClass == NULL) { return true; } std::string expectedClassName(dvmHumanReadableDescriptor(clazz->descriptor)); std::string actualClassName(dvmHumanReadableType(obj)); dvmThrowExceptionFmt(exceptionClass, "expected receiver of type %s, but got %s", expectedClassName.c_str(), actualClassName.c_str()); return false; }
/* * 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)) { dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;", "source and destination must be arrays, but were %s and %s", ((Object*)srcArray)->clazz->descriptor, ((Object*)dstArray)->clazz->descriptor); RETURN_VOID(); } // avoid int overflow if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > (int) srcArray->length - length || dstPos > (int) dstArray->length - length) { dvmThrowExceptionFmt("Ljava/lang/ArrayIndexOutOfBoundsException;", "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d", srcArray->length, srcPos, dstArray->length, dstPos, length); 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) { dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;", "source and destination arrays are incompatible: %s and %s", srcClass->descriptor, dstClass->descriptor); 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); dvmWriteBarrierArray(dstArray, dstPos, dstPos+length); } 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); dvmWriteBarrierArray(dstArray, 0, copyCount); if (copyCount != length) { dvmThrowExceptionFmt("Ljava/lang/ArrayStoreException;", "source[%d] of type %s cannot be stored in destination array of type %s", copyCount, srcObj[copyCount]->clazz->descriptor, dstClass->descriptor); RETURN_VOID(); } } } RETURN_VOID(); }
/* * 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; }
/* * We have to carry the exception's stack trace around, but in many cases * it will never be examined. It makes sense to keep it in a compact, * VM-specific object, rather than an array of Objects with strings. * * Pass in the thread whose stack we're interested in. If "thread" is * not self, the thread must be suspended. This implies that the thread * list lock is held, which means we can't allocate objects or we risk * jamming the GC. So, we allow this function to return different formats. * (This shouldn't be called directly -- see the inline functions in the * header file.) * * If "wantObject" is true, this returns a newly-allocated Object, which is * presently an array of integers, but could become something else in the * future. If "wantObject" is false, return plain malloc data. * * NOTE: if we support class unloading, we will need to scan the class * object references out of these arrays. */ void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount) { ArrayObject* stackData = NULL; int* simpleData = NULL; void* fp; void* startFp; int stackDepth; int* intPtr; if (pCount != NULL) *pCount = 0; fp = thread->curFrame; assert(thread == dvmThreadSelf() || dvmIsSuspended(thread)); /* * We're looking at a stack frame for code running below a Throwable * constructor. We want to remove the Throwable methods and the * superclass initializations so the user doesn't see them when they * read the stack dump. * * TODO: this just scrapes off the top layers of Throwable. Might not do * the right thing if we create an exception object or cause a VM * exception while in a Throwable method. */ while (fp != NULL) { const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); const Method* method = saveArea->method; if (dvmIsBreakFrame(fp)) break; if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable)) break; //LOGD("EXCEP: ignoring %s.%s\n", // method->clazz->descriptor, method->name); fp = saveArea->prevFrame; } startFp = fp; /* * Compute the stack depth. */ stackDepth = 0; while (fp != NULL) { const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); if (!dvmIsBreakFrame(fp)) stackDepth++; assert(fp != saveArea->prevFrame); fp = saveArea->prevFrame; } //LOGD("EXCEP: stack depth is %d\n", stackDepth); if (!stackDepth) goto bail; /* * We need to store a pointer to the Method and the program counter. * We have 4-byte pointers, so we use '[I'. */ if (wantObject) { assert(sizeof(Method*) == 4); stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT); if (stackData == NULL) { assert(dvmCheckException(dvmThreadSelf())); goto bail; } intPtr = (int*) stackData->contents; } else { /* array of ints; first entry is stack depth */ assert(sizeof(Method*) == sizeof(int)); simpleData = (int*) malloc(sizeof(int) * stackDepth*2); if (simpleData == NULL) goto bail; assert(pCount != NULL); intPtr = simpleData; } if (pCount != NULL) *pCount = stackDepth; fp = startFp; while (fp != NULL) { const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); const Method* method = saveArea->method; if (!dvmIsBreakFrame(fp)) { //LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor, // method->name); *intPtr++ = (int) method; if (dvmIsNativeMethod(method)) { *intPtr++ = 0; /* no saved PC for native methods */ } else { assert(saveArea->xtra.currentPc >= method->insns && saveArea->xtra.currentPc < method->insns + dvmGetMethodInsnsSize(method)); *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns); } stackDepth--; // for verification } assert(fp != saveArea->prevFrame); fp = saveArea->prevFrame; } assert(stackDepth == 0); bail: if (wantObject) { dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf()); return stackData; } else { return simpleData; } }
/* * Search the method's list of exceptions for a match. * * Returns the offset of the catch block on success, or -1 on failure. */ static int findCatchInMethod(Thread* self, const Method* method, int relPc, ClassObject* excepClass) { /* * Need to clear the exception before entry. Otherwise, dvmResolveClass * might think somebody threw an exception while it was loading a class. */ assert(!dvmCheckException(self)); assert(!dvmIsNativeMethod(method)); LOGVV("findCatchInMethod %s.%s excep=%s depth=%d\n", method->clazz->descriptor, method->name, excepClass->descriptor, dvmComputeExactFrameDepth(self->curFrame)); DvmDex* pDvmDex = method->clazz->pDvmDex; const DexCode* pCode = dvmGetMethodCode(method); DexCatchIterator iterator; if (dexFindCatchHandler(&iterator, pCode, relPc)) { for (;;) { DexCatchHandler* handler = dexCatchIteratorNext(&iterator); if (handler == NULL) { break; } if (handler->typeIdx == kDexNoIndex) { /* catch-all */ LOGV("Match on catch-all block at 0x%02x in %s.%s for %s\n", relPc, method->clazz->descriptor, method->name, excepClass->descriptor); return handler->address; } ClassObject* throwable = dvmDexGetResolvedClass(pDvmDex, handler->typeIdx); if (throwable == NULL) { /* * TODO: this behaves badly if we run off the stack * while trying to throw an exception. The problem is * that, if we're in a class loaded by a class loader, * the call to dvmResolveClass has to ask the class * loader for help resolving any previously-unresolved * classes. If this particular class loader hasn't * resolved StackOverflowError, it will call into * interpreted code, and blow up. * * We currently replace the previous exception with * the StackOverflowError, which means they won't be * catching it *unless* they explicitly catch * StackOverflowError, in which case we'll be unable * to resolve the class referred to by the "catch" * block. * * We end up getting a huge pile of warnings if we do * a simple synthetic test, because this method gets * called on every stack frame up the tree, and it * fails every time. * * This eventually bails out, effectively becoming an * uncatchable exception, so other than the flurry of * warnings it's not really a problem. Still, we could * probably handle this better. */ throwable = dvmResolveClass(method->clazz, handler->typeIdx, true); if (throwable == NULL) { /* * We couldn't find the exception they wanted in * our class files (or, perhaps, the stack blew up * while we were querying a class loader). Cough * up a warning, then move on to the next entry. * Keep the exception status clear. */ LOGW("Could not resolve class ref'ed in exception " "catch list (class index %d, exception %s)\n", handler->typeIdx, (self->exception != NULL) ? self->exception->clazz->descriptor : "(none)"); dvmClearException(self); continue; } } //LOGD("ADDR MATCH, check %s instanceof %s\n", // excepClass->descriptor, pEntry->excepClass->descriptor); if (dvmInstanceof(excepClass, throwable)) { LOGV("Match on catch block at 0x%02x in %s.%s for %s\n", relPc, method->clazz->descriptor, method->name, excepClass->descriptor); return handler->address; } } } LOGV("No matching catch block at 0x%02x in %s for %s\n", relPc, method->name, excepClass->descriptor); return -1; }
/* * Initialize an exception with an appropriate constructor. * * "exception" is the exception object to initialize. * Either or both of "msg" and "cause" may be null. * "self" is dvmThreadSelf(), passed in so we don't have to look it up again. * * If the process of initializing the exception causes another * exception (e.g., OutOfMemoryError) to be thrown, return an error * and leave self->exception intact. */ static bool initException(Object* exception, const char* msg, Object* cause, Thread* self) { enum { kInitUnknown, kInitNoarg, kInitMsg, kInitMsgThrow, kInitThrow } initKind = kInitUnknown; Method* initMethod = NULL; ClassObject* excepClass = exception->clazz; StringObject* msgStr = NULL; bool result = false; bool needInitCause = false; assert(self != NULL); assert(self->exception == NULL); /* if we have a message, create a String */ if (msg == NULL) msgStr = NULL; else { msgStr = dvmCreateStringFromCstr(msg); if (msgStr == NULL) { LOGW("Could not allocate message string \"%s\" while " "throwing internal exception (%s)\n", msg, excepClass->descriptor); goto bail; } } if (cause != NULL) { if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) { LOGE("Tried to init exception with cause '%s'\n", cause->clazz->descriptor); dvmAbort(); } } /* * The Throwable class has four public constructors: * (1) Throwable() * (2) Throwable(String message) * (3) Throwable(String message, Throwable cause) (added in 1.4) * (4) Throwable(Throwable cause) (added in 1.4) * * The first two are part of the original design, and most exception * classes should support them. The third prototype was used by * individual exceptions. e.g. ClassNotFoundException added it in 1.2. * The general "cause" mechanism was added in 1.4. Some classes, * such as IllegalArgumentException, initially supported the first * two, but added the second two in a later release. * * Exceptions may be picky about how their "cause" field is initialized. * If you call ClassNotFoundException(String), it may choose to * initialize its "cause" field to null. Doing so prevents future * calls to Throwable.initCause(). * * So, if "cause" is not NULL, we need to look for a constructor that * takes a throwable. If we can't find one, we fall back on calling * #1/#2 and making a separate call to initCause(). Passing a null ref * for "message" into Throwable(String, Throwable) is allowed, but we * prefer to use the Throwable-only version because it has different * behavior. * * java.lang.TypeNotPresentException is a strange case -- it has #3 but * not #2. (Some might argue that the constructor is actually not #3, * because it doesn't take the message string as an argument, but it * has the same effect and we can work with it here.) * * java.lang.AssertionError is also a strange case -- it has a * constructor that takes an Object, but not one that takes a String. * There may be other cases like this, as well, so we generally look * for an Object-taking constructor if we can't find one that takes * a String. */ if (cause == NULL) { if (msgStr == NULL) { initMethod = findExceptionInitMethod(excepClass, false, false); initKind = kInitNoarg; } else { initMethod = findExceptionInitMethod(excepClass, true, false); if (initMethod != NULL) { initKind = kInitMsg; } else { /* no #2, try #3 */ initMethod = findExceptionInitMethod(excepClass, true, true); if (initMethod != NULL) { initKind = kInitMsgThrow; } } } } else { if (msgStr == NULL) { initMethod = findExceptionInitMethod(excepClass, false, true); if (initMethod != NULL) { initKind = kInitThrow; } else { initMethod = findExceptionInitMethod(excepClass, false, false); initKind = kInitNoarg; needInitCause = true; } } else { initMethod = findExceptionInitMethod(excepClass, true, true); if (initMethod != NULL) { initKind = kInitMsgThrow; } else { initMethod = findExceptionInitMethod(excepClass, true, false); initKind = kInitMsg; needInitCause = true; } } } if (initMethod == NULL) { /* * We can't find the desired constructor. This can happen if a * subclass of java/lang/Throwable doesn't define an expected * constructor, e.g. it doesn't provide one that takes a string * when a message has been provided. */ LOGW("WARNING: exception class '%s' missing constructor " "(msg='%s' kind=%d)\n", excepClass->descriptor, msg, initKind); assert(strcmp(excepClass->descriptor, "Ljava/lang/RuntimeException;") != 0); dvmThrowChainedException("Ljava/lang/RuntimeException;", "re-throw on exception class missing constructor", NULL); goto bail; } /* * Call the constructor with the appropriate arguments. */ JValue unused; switch (initKind) { case kInitNoarg: LOGVV("+++ exc noarg (ic=%d)\n", needInitCause); dvmCallMethod(self, initMethod, exception, &unused); break; case kInitMsg: LOGVV("+++ exc msg (ic=%d)\n", needInitCause); dvmCallMethod(self, initMethod, exception, &unused, msgStr); break; case kInitThrow: LOGVV("+++ exc throw"); assert(!needInitCause); dvmCallMethod(self, initMethod, exception, &unused, cause); break; case kInitMsgThrow: LOGVV("+++ exc msg+throw"); assert(!needInitCause); dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause); break; default: assert(false); goto bail; } /* * It's possible the constructor has thrown an exception. If so, we * return an error and let our caller deal with it. */ if (self->exception != NULL) { LOGW("Exception thrown (%s) while throwing internal exception (%s)\n", self->exception->clazz->descriptor, exception->clazz->descriptor); goto bail; } /* * If this exception was caused by another exception, and we weren't * able to find a cause-setting constructor, set the "cause" field * with an explicit call. */ if (needInitCause) { Method* initCause; initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); if (initCause != NULL) { dvmCallMethod(self, initCause, exception, &unused, cause); if (self->exception != NULL) { /* initCause() threw an exception; return an error and * let the caller deal with it. */ LOGW("Exception thrown (%s) during initCause() " "of internal exception (%s)\n", self->exception->clazz->descriptor, exception->clazz->descriptor); goto bail; } } else { LOGW("WARNING: couldn't find initCause in '%s'\n", excepClass->descriptor); } } result = true; bail: dvmReleaseTrackedAlloc((Object*) msgStr, self); // NULL is ok return result; }
void fastiva_Dalvik_java_lang_System_arraycopy(java_lang_Object_p arg0, jint srcPos, java_lang_Object_p arg2, jint dstPos, jint length) { ArrayObject* srcArray = (ArrayObject*) arg0; ArrayObject* dstArray = (ArrayObject*) arg2; #endif /* Check for null pointers. */ if (srcArray == NULL) { dvmThrowNullPointerException("src == null"); THROW_VOID(); } if (dstArray == NULL) { dvmThrowNullPointerException("dst == null"); THROW_VOID(); } /* Make sure source and destination are arrays. */ if (!dvmIsArray(srcArray)) { dvmThrowArrayStoreExceptionNotArray(((Object*)srcArray)->clazz, "source"); THROW_VOID(); } if (!dvmIsArray(dstArray)) { dvmThrowArrayStoreExceptionNotArray(((Object*)dstArray)->clazz, "destination"); THROW_VOID(); } /* avoid int overflow */ if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > (int) srcArray->length - length || dstPos > (int) dstArray->length - length) { dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException, "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d", srcArray->length, srcPos, dstArray->length, dstPos, length); THROW_VOID(); } ClassObject* srcClass = srcArray->clazz; ClassObject* dstClass = dstArray->clazz; char srcType = srcClass->descriptor[1]; char dstType = dstClass->descriptor[1]; /* * If one of the arrays holds a primitive type, the other array must * hold the same type. */ bool srcPrim = (srcType != '[' && srcType != 'L'); bool dstPrim = (dstType != '[' && dstType != 'L'); if (srcPrim || dstPrim) { if (srcPrim != dstPrim || srcType != dstType) { dvmThrowArrayStoreExceptionIncompatibleArrays(srcClass, dstClass); THROW_VOID(); } if (false) ALOGD("arraycopy prim[%c] dst=%p %d src=%p %d len=%d", srcType, dstArray->contents, dstPos, srcArray->contents, srcPos, length); switch (srcType) { case 'B': case 'Z': /* 1 byte per element */ memmove((u1*) dstArray->contents + dstPos, (const u1*) srcArray->contents + srcPos, length); break; case 'C': case 'S': /* 2 bytes per element */ move16((u1*) dstArray->contents + dstPos * 2, (const u1*) srcArray->contents + srcPos * 2, length * 2); break; case 'F': case 'I': /* 4 bytes per element */ move32((u1*) dstArray->contents + dstPos * 4, (const u1*) srcArray->contents + srcPos * 4, length * 4); break; case 'D': case 'J': /* * 8 bytes per element. We don't need to guarantee atomicity * of the entire 64-bit word, so we can use the 32-bit copier. */ move32((u1*) dstArray->contents + dstPos * 8, (const u1*) srcArray->contents + srcPos * 8, length * 8); break; default: /* illegal array type */ ALOGE("Weird array type '%s'", srcClass->descriptor); dvmAbort(); } } 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). */ const int width = sizeof(Object*); if (srcClass->arrayDim == dstClass->arrayDim && dvmInstanceof(srcClass, dstClass)) { /* * "dst" can hold "src"; copy the whole thing. */ if (false) ALOGD("arraycopy ref dst=%p %d src=%p %d len=%d", dstArray->contents, dstPos * width, srcArray->contents, srcPos * width, length * width); move32((u1*)dstArray->contents + dstPos * width, (const u1*)srcArray->contents + srcPos * width, length * width); dvmWriteBarrierArray(dstArray, dstPos, dstPos+length); } 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 move32() to shift the actual data. If we just * start from the front we could do a smear rather than a move. */ Object** srcObj; int copyCount; ClassObject* clazz = NULL; srcObj = ((Object**)(void*)srcArray->contents) + srcPos; 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) ALOGD("arraycopy iref dst=%p %d src=%p %d count=%d of %d", dstArray->contents, dstPos * width, srcArray->contents, srcPos * width, copyCount, length); move32((u1*)dstArray->contents + dstPos * width, (const u1*)srcArray->contents + srcPos * width, copyCount * width); dvmWriteBarrierArray(dstArray, 0, copyCount); if (copyCount != length) { dvmThrowArrayStoreExceptionIncompatibleArrayElement(srcPos + copyCount, srcObj[copyCount]->clazz, dstClass); THROW_VOID(); } } } RETURN_VOID(); }