| /* |
| * 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. |
| */ |
| /* |
| * Array objects. |
| */ |
| #include "Dalvik.h" |
| |
| #include <stdlib.h> |
| #include <stddef.h> |
| |
| #if WITH_HPROF && WITH_HPROF_STACK |
| #include "hprof/Hprof.h" |
| #endif |
| |
| static ClassObject* createArrayClass(const char* descriptor, Object* loader); |
| static ClassObject* createPrimitiveClass(int idx); |
| |
| static const char gPrimLetter[] = PRIM_TYPE_TO_LETTER; |
| |
| /* |
| * Allocate space for a new array object. This is the lowest-level array |
| * allocation function. |
| * |
| * Pass in the array class and the width of each element. |
| * |
| * On failure, returns NULL with an exception raised. |
| */ |
| ArrayObject* dvmAllocArray(ClassObject* arrayClass, size_t length, |
| size_t elemWidth, int allocFlags) |
| { |
| ArrayObject* newArray; |
| size_t size; |
| |
| assert(arrayClass->descriptor[0] == '['); |
| |
| if (length > 0x0fffffff) { |
| /* too large and (length * elemWidth) will overflow 32 bits */ |
| LOGE("Rejecting allocation of %u-element array\n", length); |
| dvmThrowBadAllocException("array size too large"); |
| return NULL; |
| } |
| |
| size = offsetof(ArrayObject, contents); |
| size += length * elemWidth; |
| |
| /* Note that we assume that the Array class does not |
| * override finalize(). |
| */ |
| newArray = dvmMalloc(size, allocFlags); |
| if (newArray != NULL) { |
| DVM_OBJECT_INIT(&newArray->obj, arrayClass); |
| newArray->length = length; |
| LOGVV("AllocArray: %s [%d] (%d)\n", |
| arrayClass->descriptor, (int) length, (int) size); |
| #if WITH_HPROF && WITH_HPROF_STACK |
| hprofFillInStackTrace(&newArray->obj); |
| #endif |
| dvmTrackAllocation(arrayClass, size); |
| } |
| /* the caller must call dvmReleaseTrackedAlloc */ |
| return newArray; |
| } |
| |
| /* |
| * Create a new array, given an array class. The class may represent an |
| * array of references or primitives. |
| */ |
| ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass, |
| size_t length, int allocFlags) |
| { |
| const char* descriptor = arrayClass->descriptor; |
| |
| assert(descriptor[0] == '['); /* must be array class */ |
| if (descriptor[1] != '[' && descriptor[1] != 'L') { |
| /* primitive array */ |
| assert(descriptor[2] == '\0'); |
| return dvmAllocPrimitiveArray(descriptor[1], length, allocFlags); |
| } else { |
| return dvmAllocArray(arrayClass, length, kObjectArrayRefWidth, |
| allocFlags); |
| } |
| } |
| |
| /* |
| * Find the array class for "elemClassObj", which could itself be an |
| * array class. |
| */ |
| ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj) |
| { |
| ClassObject* arrayClass; |
| |
| assert(elemClassObj != NULL); |
| |
| if (elemClassObj->arrayClass != NULL) { |
| arrayClass = elemClassObj->arrayClass; |
| LOGVV("using cached '%s' class for '%s'\n", |
| arrayClass->descriptor, elemClassObj->descriptor); |
| } else { |
| /* Simply prepend "[" to the descriptor. */ |
| int nameLen = strlen(elemClassObj->descriptor); |
| char className[nameLen + 2]; |
| |
| className[0] = '['; |
| memcpy(className+1, elemClassObj->descriptor, nameLen+1); |
| arrayClass = dvmFindArrayClass(className, elemClassObj->classLoader); |
| if (arrayClass != NULL) |
| elemClassObj->arrayClass = arrayClass; |
| } |
| |
| return arrayClass; |
| } |
| |
| /* |
| * Create a new array that holds references to members of the specified class. |
| * |
| * "elemClassObj" is the element type, and may itself be an array class. It |
| * may not be a primitive class. |
| * |
| * "allocFlags" determines whether the new object will be added to the |
| * "tracked alloc" table. |
| * |
| * This is less efficient than dvmAllocArray(), but occasionally convenient. |
| */ |
| ArrayObject* dvmAllocObjectArray(ClassObject* elemClassObj, size_t length, |
| int allocFlags) |
| { |
| ClassObject* arrayClass; |
| ArrayObject* newArray = NULL; |
| |
| LOGVV("dvmAllocObjectArray: '%s' len=%d\n", |
| elemClassObj->descriptor, (int)length); |
| |
| arrayClass = dvmFindArrayClassForElement(elemClassObj); |
| if (arrayClass != NULL) { |
| newArray = dvmAllocArray(arrayClass, length, kObjectArrayRefWidth, |
| allocFlags); |
| } |
| |
| /* the caller must call dvmReleaseTrackedAlloc */ |
| return newArray; |
| } |
| |
| /* |
| * Create a new array that holds primitive types. |
| * |
| * "type" is the primitive type letter, e.g. 'I' for int or 'J' for long. |
| * If the array class doesn't exist, it will be created. |
| */ |
| ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags) |
| { |
| ArrayObject* newArray; |
| ClassObject** pTypeClass; |
| int width; |
| |
| switch (type) { |
| case 'I': |
| pTypeClass = &gDvm.classArrayInt; |
| width = 4; |
| break; |
| case 'C': |
| pTypeClass = &gDvm.classArrayChar; |
| width = 2; |
| break; |
| case 'B': |
| pTypeClass = &gDvm.classArrayByte; |
| width = 1; |
| break; |
| case 'Z': |
| pTypeClass = &gDvm.classArrayBoolean; |
| width = 1; /* special-case this? */ |
| break; |
| case 'F': |
| pTypeClass = &gDvm.classArrayFloat; |
| width = 4; |
| break; |
| case 'D': |
| pTypeClass = &gDvm.classArrayDouble; |
| width = 8; |
| break; |
| case 'S': |
| pTypeClass = &gDvm.classArrayShort; |
| width = 2; |
| break; |
| case 'J': |
| pTypeClass = &gDvm.classArrayLong; |
| width = 8; |
| break; |
| default: |
| LOGE("Unknown type '%c'\n", type); |
| assert(false); |
| return NULL; |
| } |
| |
| if (*pTypeClass == NULL) { |
| char typeClassName[3] = "[x"; |
| |
| typeClassName[1] = type; |
| |
| *pTypeClass = dvmFindArrayClass(typeClassName, NULL); |
| if (*pTypeClass == NULL) { |
| LOGE("ERROR: failed to generate array class for '%s'\n", |
| typeClassName); |
| return NULL; |
| } |
| } |
| |
| newArray = dvmAllocArray(*pTypeClass, length, width, allocFlags); |
| |
| /* the caller must dvmReleaseTrackedAlloc if allocFlags==ALLOC_DEFAULT */ |
| return newArray; |
| } |
| |
| /* |
| * Recursively create an array with multiple dimensions. Elements may be |
| * Objects or primitive types. |
| * |
| * The dimension we're creating is in dimensions[0], so when we recurse |
| * we advance the pointer. |
| */ |
| ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim, |
| const int* dimensions) |
| { |
| ArrayObject* newArray; |
| const char* elemName = arrayClass->descriptor + 1; // Advance past one '['. |
| |
| LOGVV("dvmAllocMultiArray: class='%s' curDim=%d *dimensions=%d\n", |
| arrayClass->descriptor, curDim, *dimensions); |
| |
| if (curDim == 0) { |
| if (*elemName == 'L' || *elemName == '[') { |
| LOGVV(" end: array class (obj) is '%s'\n", |
| arrayClass->descriptor); |
| newArray = dvmAllocArray(arrayClass, *dimensions, |
| kObjectArrayRefWidth, ALLOC_DEFAULT); |
| } else { |
| LOGVV(" end: array class (prim) is '%s'\n", |
| arrayClass->descriptor); |
| newArray = dvmAllocPrimitiveArray( |
| gPrimLetter[arrayClass->elementClass->primitiveType], |
| *dimensions, ALLOC_DEFAULT); |
| } |
| } else { |
| ClassObject* subArrayClass; |
| Object** contents; |
| int i; |
| |
| /* if we have X[][], find X[] */ |
| subArrayClass = dvmFindArrayClass(elemName, arrayClass->classLoader); |
| if (subArrayClass == NULL) { |
| /* not enough '['s on the initial class? */ |
| assert(dvmCheckException(dvmThreadSelf())); |
| return NULL; |
| } |
| assert(dvmIsArrayClass(subArrayClass)); |
| |
| /* allocate the array that holds the sub-arrays */ |
| newArray = dvmAllocArray(arrayClass, *dimensions, kObjectArrayRefWidth, |
| ALLOC_DEFAULT); |
| if (newArray == NULL) { |
| assert(dvmCheckException(dvmThreadSelf())); |
| return NULL; |
| } |
| |
| /* |
| * Create a new sub-array in every element of the array. |
| */ |
| contents = (Object**) newArray->contents; |
| for (i = 0; i < *dimensions; i++) { |
| ArrayObject* newSubArray; |
| |
| newSubArray = dvmAllocMultiArray(subArrayClass, curDim-1, |
| dimensions+1); |
| if (newSubArray == NULL) { |
| dvmReleaseTrackedAlloc((Object*) newArray, NULL); |
| assert(dvmCheckException(dvmThreadSelf())); |
| return NULL; |
| } |
| |
| *contents++ = (Object*) newSubArray; |
| dvmReleaseTrackedAlloc((Object*) newSubArray, NULL); |
| } |
| } |
| |
| /* caller must call dvmReleaseTrackedAlloc */ |
| return newArray; |
| } |
| |
| |
| /* |
| * Find an array class, by name (e.g. "[I"). |
| * |
| * If the array class doesn't exist, we generate it. |
| * |
| * If the element class doesn't exist, we return NULL (no exception raised). |
| */ |
| ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader) |
| { |
| ClassObject* clazz; |
| |
| assert(descriptor[0] == '['); |
| //LOGV("dvmFindArrayClass: '%s' %p\n", descriptor, loader); |
| |
| clazz = dvmLookupClass(descriptor, loader, false); |
| if (clazz == NULL) { |
| LOGV("Array class '%s' %p not found; creating\n", descriptor, loader); |
| clazz = createArrayClass(descriptor, loader); |
| if (clazz != NULL) |
| dvmAddInitiatingLoader(clazz, loader); |
| } |
| |
| return clazz; |
| } |
| |
| /* |
| * Create an array class (i.e. the class object for the array, not the |
| * array itself). "descriptor" looks like "[C" or "[Ljava/lang/String;". |
| * |
| * If "descriptor" refers to an array of primitives, look up the |
| * primitive type's internally-generated class object. |
| * |
| * "loader" is the class loader of the class that's referring to us. It's |
| * used to ensure that we're looking for the element type in the right |
| * context. It does NOT become the class loader for the array class; that |
| * always comes from the base element class. |
| * |
| * Returns NULL with an exception raised on failure. |
| */ |
| static ClassObject* createArrayClass(const char* descriptor, Object* loader) |
| { |
| ClassObject* newClass = NULL; |
| ClassObject* elementClass = NULL; |
| int arrayDim; |
| u4 extraFlags; |
| |
| assert(descriptor[0] == '['); |
| assert(gDvm.classJavaLangClass != NULL); |
| assert(gDvm.classJavaLangObject != NULL); |
| |
| /* |
| * Identify the underlying element class and the array dimension depth. |
| */ |
| extraFlags = CLASS_ISARRAY; |
| if (descriptor[1] == '[') { |
| /* array of arrays; keep descriptor and grab stuff from parent */ |
| ClassObject* outer; |
| |
| outer = dvmFindClassNoInit(&descriptor[1], loader); |
| if (outer != NULL) { |
| /* want the base class, not "outer", in our elementClass */ |
| elementClass = outer->elementClass; |
| arrayDim = outer->arrayDim + 1; |
| extraFlags |= CLASS_ISOBJECTARRAY; |
| } else { |
| assert(elementClass == NULL); /* make sure we fail */ |
| } |
| } else { |
| arrayDim = 1; |
| if (descriptor[1] == 'L') { |
| /* array of objects; strip off "[" and look up descriptor. */ |
| const char* subDescriptor = &descriptor[1]; |
| LOGVV("searching for element class '%s'\n", subDescriptor); |
| elementClass = dvmFindClassNoInit(subDescriptor, loader); |
| extraFlags |= CLASS_ISOBJECTARRAY; |
| } else { |
| /* array of a primitive type */ |
| elementClass = dvmFindPrimitiveClass(descriptor[1]); |
| } |
| } |
| |
| if (elementClass == NULL) { |
| /* failed */ |
| assert(dvmCheckException(dvmThreadSelf())); |
| dvmFreeClassInnards(newClass); |
| dvmReleaseTrackedAlloc((Object*) newClass, NULL); |
| return NULL; |
| } |
| |
| /* |
| * See if it's already loaded. Array classes are always associated |
| * with the class loader of their underlying element type -- an array |
| * of Strings goes with the loader for java/lang/String -- so we need |
| * to look for it there. (The caller should have checked for the |
| * existence of the class before calling here, but they did so with |
| * *their* class loader, not the element class' loader.) |
| * |
| * If we find it, the caller adds "loader" to the class' initiating |
| * loader list, which should prevent us from going through this again. |
| * |
| * This call is unnecessary if "loader" and "elementClass->classLoader" |
| * are the same, because our caller (dvmFindArrayClass) just did the |
| * lookup. (Even if we get this wrong we still have correct behavior, |
| * because we effectively do this lookup again when we add the new |
| * class to the hash table -- necessary because of possible races with |
| * other threads.) |
| */ |
| if (loader != elementClass->classLoader) { |
| LOGVV("--- checking for '%s' in %p vs. elem %p\n", |
| descriptor, loader, elementClass->classLoader); |
| newClass = dvmLookupClass(descriptor, elementClass->classLoader, false); |
| if (newClass != NULL) { |
| LOGV("--- we already have %s in %p, don't need in %p\n", |
| descriptor, elementClass->classLoader, loader); |
| return newClass; |
| } |
| } |
| |
| |
| /* |
| * Fill out the fields in the ClassObject. |
| * |
| * It is possible to execute some methods against arrays, because all |
| * arrays are instances of Object, so we need to set up a vtable. We |
| * can just point at the one in Object. |
| * |
| * Array classes are simple enough that we don't need to do a full |
| * link step. |
| */ |
| newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT); |
| if (newClass == NULL) |
| return NULL; |
| DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass); |
| dvmSetClassSerialNumber(newClass); |
| newClass->descriptorAlloc = strdup(descriptor); |
| newClass->descriptor = newClass->descriptorAlloc; |
| newClass->super = gDvm.classJavaLangObject; |
| newClass->vtableCount = gDvm.classJavaLangObject->vtableCount; |
| newClass->vtable = gDvm.classJavaLangObject->vtable; |
| newClass->primitiveType = PRIM_NOT; |
| newClass->elementClass = elementClass; |
| newClass->classLoader = elementClass->classLoader; |
| newClass->arrayDim = arrayDim; |
| newClass->status = CLASS_INITIALIZED; |
| #if WITH_HPROF && WITH_HPROF_STACK |
| hprofFillInStackTrace(newClass); |
| #endif |
| |
| /* don't need to set newClass->objectSize */ |
| |
| /* |
| * All arrays have java/lang/Cloneable and java/io/Serializable as |
| * interfaces. We need to set that up here, so that stuff like |
| * "instanceof" works right. |
| * |
| * Note: The GC could run during the call to dvmFindSystemClassNoInit(), |
| * so we need to make sure the class object is GC-valid while we're in |
| * there. Do this by clearing the interface list so the GC will just |
| * think that the entries are null. |
| * |
| * TODO? |
| * We may want to cache these two classes to avoid the lookup, though |
| * it's not vital -- we only do it when creating an array class, not |
| * every time we create an array. Better yet, create a single, global |
| * copy of "interfaces" and "iftable" somewhere near the start and |
| * just point to those (and remember not to free them for arrays). |
| */ |
| newClass->interfaceCount = 2; |
| newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader, |
| sizeof(ClassObject*) * 2); |
| memset(newClass->interfaces, 0, sizeof(ClassObject*) * 2); |
| newClass->interfaces[0] = |
| dvmFindSystemClassNoInit("Ljava/lang/Cloneable;"); |
| newClass->interfaces[1] = |
| dvmFindSystemClassNoInit("Ljava/io/Serializable;"); |
| dvmLinearReadOnly(newClass->classLoader, newClass->interfaces); |
| if (newClass->interfaces[0] == NULL || newClass->interfaces[1] == NULL) { |
| LOGE("Unable to create array class '%s': missing interfaces\n", |
| descriptor); |
| dvmFreeClassInnards(newClass); |
| dvmThrowException("Ljava/lang/InternalError;", "missing array ifaces"); |
| dvmReleaseTrackedAlloc((Object*) newClass, NULL); |
| return NULL; |
| } |
| /* |
| * We assume that Cloneable/Serializable don't have superinterfaces -- |
| * normally we'd have to crawl up and explicitly list all of the |
| * supers as well. These interfaces don't have any methods, so we |
| * don't have to worry about the ifviPool either. |
| */ |
| newClass->iftableCount = 2; |
| newClass->iftable = (InterfaceEntry*) dvmLinearAlloc(newClass->classLoader, |
| sizeof(InterfaceEntry) * 2); |
| memset(newClass->iftable, 0, sizeof(InterfaceEntry) * 2); |
| newClass->iftable[0].clazz = newClass->interfaces[0]; |
| newClass->iftable[1].clazz = newClass->interfaces[1]; |
| dvmLinearReadOnly(newClass->classLoader, newClass->iftable); |
| |
| /* |
| * Inherit access flags from the element. Arrays can't be used as a |
| * superclass or interface, so we want to add "final" and remove |
| * "interface". |
| * |
| * Don't inherit any non-standard flags (e.g., CLASS_FINALIZABLE) |
| * from elementClass. We assume that the array class does not |
| * override finalize(). |
| */ |
| newClass->accessFlags = ((newClass->elementClass->accessFlags & |
| ~ACC_INTERFACE) | ACC_FINAL) & JAVA_FLAGS_MASK; |
| |
| /* Set the flags we determined above. |
| * This must happen after accessFlags is set. |
| */ |
| SET_CLASS_FLAG(newClass, extraFlags); |
| |
| if (!dvmAddClassToHash(newClass)) { |
| /* |
| * Another thread must have loaded the class after we |
| * started but before we finished. Discard what we've |
| * done and leave some hints for the GC. |
| */ |
| LOGI("WOW: somebody generated %s simultaneously\n", |
| newClass->descriptor); |
| |
| /* Clean up the class before letting the |
| * GC get its hands on it. |
| */ |
| assert(newClass->obj.clazz == gDvm.unlinkedJavaLangClass); |
| dvmFreeClassInnards(newClass); |
| |
| /* Let the GC free the class. |
| */ |
| dvmReleaseTrackedAlloc((Object*) newClass, NULL); |
| |
| /* Grab the winning class. |
| */ |
| newClass = dvmLookupClass(descriptor, elementClass->classLoader, false); |
| assert(newClass != NULL); |
| return newClass; |
| } |
| |
| /* make it available to the GC */ |
| newClass->obj.clazz = gDvm.classJavaLangClass; |
| dvmReleaseTrackedAlloc((Object*) newClass, NULL); |
| |
| LOGV("Created array class '%s' %p (access=0x%04x.%04x)\n", |
| descriptor, newClass->classLoader, |
| newClass->accessFlags >> 16, |
| newClass->accessFlags & JAVA_FLAGS_MASK); |
| |
| return newClass; |
| } |
| |
| /* |
| * Get a class we generated for the primitive types. |
| * |
| * These correspond to e.g. Integer.TYPE, and are used as the element |
| * class in arrays of primitives. |
| * |
| * "type" should be 'I', 'J', 'Z', etc. |
| * |
| * Returns NULL if the type doesn't correspond to a known primitive type. |
| */ |
| ClassObject* dvmFindPrimitiveClass(char type) |
| { |
| int idx; |
| |
| switch (type) { |
| case 'Z': |
| idx = PRIM_BOOLEAN; |
| break; |
| case 'C': |
| idx = PRIM_CHAR; |
| break; |
| case 'F': |
| idx = PRIM_FLOAT; |
| break; |
| case 'D': |
| idx = PRIM_DOUBLE; |
| break; |
| case 'B': |
| idx = PRIM_BYTE; |
| break; |
| case 'S': |
| idx = PRIM_SHORT; |
| break; |
| case 'I': |
| idx = PRIM_INT; |
| break; |
| case 'J': |
| idx = PRIM_LONG; |
| break; |
| case 'V': |
| idx = PRIM_VOID; |
| break; |
| default: |
| LOGW("Unknown primitive type '%c'\n", type); |
| return NULL; |
| } |
| |
| /* |
| * Create the primitive class if it hasn't already been, and add it |
| * to the table. |
| */ |
| if (gDvm.primitiveClass[idx] == NULL) { |
| ClassObject* primClass = createPrimitiveClass(idx); |
| dvmReleaseTrackedAlloc((Object*) primClass, NULL); |
| |
| if (!ATOMIC_CMP_SWAP((int*) &gDvm.primitiveClass[idx], |
| 0, (int) primClass)) |
| { |
| /* |
| * Looks like somebody beat us to it. Free up the one we |
| * just created and use the other one. |
| */ |
| dvmFreeClassInnards(primClass); |
| } |
| } |
| |
| return gDvm.primitiveClass[idx]; |
| } |
| |
| /* |
| * Synthesize a primitive class. |
| * |
| * The spec for java.lang.Class.isPrimitive describes the names to |
| * be used for these classes. |
| * |
| * Just creates the class and returns it (does not add it to the class list). |
| */ |
| static ClassObject* createPrimitiveClass(int idx) |
| { |
| ClassObject* newClass; |
| static const char* kClassDescriptors[PRIM_MAX] = { |
| "Z", "C", "F", "D", "B", "S", "I", "J", "V" |
| }; |
| |
| assert(gDvm.classJavaLangClass != NULL); |
| assert(idx >= 0 && idx < PRIM_MAX); |
| |
| /* |
| * Fill out a few fields in the ClassObject. |
| * |
| * Note that primitive classes do not sub-class java/lang/Object. This |
| * matters for "instanceof" checks. Also, we assume that the primitive |
| * class does not override finalize(). |
| */ |
| newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT); |
| if (newClass == NULL) |
| return NULL; |
| DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass); |
| dvmSetClassSerialNumber(newClass); |
| newClass->accessFlags = ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT; |
| newClass->primitiveType = idx; |
| newClass->descriptorAlloc = NULL; |
| newClass->descriptor = kClassDescriptors[idx]; |
| //newClass->super = gDvm.classJavaLangObject; |
| newClass->status = CLASS_INITIALIZED; |
| #if WITH_HPROF && WITH_HPROF_STACK |
| hprofFillInStackTrace(newClass); |
| #endif |
| |
| /* don't need to set newClass->objectSize */ |
| |
| LOGVV("Created primitive class '%s'\n", kClassDescriptors[idx]); |
| |
| return newClass; |
| } |
| |
| /* |
| * Copy the entire contents of one array of objects to another. If the copy |
| * is impossible because of a type clash, we fail and return "false". |
| */ |
| bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray, |
| ClassObject* dstElemClass) |
| { |
| Object** src = (Object**)srcArray->contents; |
| Object** dst = (Object**)dstArray->contents; |
| u4 count = dstArray->length; |
| |
| assert(srcArray->length == dstArray->length); |
| assert(dstArray->obj.clazz->elementClass == dstElemClass || |
| (dstArray->obj.clazz->elementClass == dstElemClass->elementClass && |
| dstArray->obj.clazz->arrayDim == dstElemClass->arrayDim+1)); |
| |
| while (count--) { |
| if (!dvmInstanceof((*src)->clazz, dstElemClass)) { |
| LOGW("dvmCopyObjectArray: can't store %s in %s\n", |
| (*src)->clazz->descriptor, dstElemClass->descriptor); |
| return false; |
| } |
| *dst++ = *src++; |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Add all primitive classes to the root set of objects. |
| TODO: do these belong to the root class loader? |
| */ |
| void dvmGcScanPrimitiveClasses() |
| { |
| int i; |
| |
| for (i = 0; i < PRIM_MAX; i++) { |
| dvmMarkObject((Object *)gDvm.primitiveClass[i]); // may be NULL |
| } |
| } |
| |