Home | History | Annotate | Download | only in oo
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 /*
     17  * Array objects.
     18  */
     19 #include "Dalvik.h"
     20 
     21 #include <stdlib.h>
     22 #include <stddef.h>
     23 #include <limits.h>
     24 
     25 /* width of an object reference, for arrays of objects */
     26 static size_t kObjectArrayRefWidth = sizeof(Object*);
     27 
     28 static ClassObject* createArrayClass(const char* descriptor, Object* loader);
     29 
     30 /*
     31  * Allocate space for a new array object.  This is the lowest-level array
     32  * allocation function.
     33  *
     34  * Pass in the array class and the width of each element.
     35  *
     36  * On failure, returns NULL with an exception raised.
     37  */
     38 static ArrayObject* allocArray(ClassObject* arrayClass, size_t length,
     39     size_t elemWidth, int allocFlags)
     40 {
     41     assert(arrayClass != NULL);
     42     assert(arrayClass->descriptor != NULL);
     43     assert(arrayClass->descriptor[0] == '[');
     44     assert(length <= 0x7fffffff);
     45     assert(elemWidth > 0);
     46     assert(elemWidth <= 8);
     47     assert((elemWidth & (elemWidth - 1)) == 0);
     48     size_t elementShift = sizeof(size_t) * CHAR_BIT - 1 - CLZ(elemWidth);
     49     size_t elementSize = length << elementShift;
     50     size_t headerSize = OFFSETOF_MEMBER(ArrayObject, contents);
     51     size_t totalSize = elementSize + headerSize;
     52     if (elementSize >> elementShift != length || totalSize < elementSize) {
     53         std::string descriptor(dvmHumanReadableDescriptor(arrayClass->descriptor));
     54         dvmThrowExceptionFmt(gDvm.exOutOfMemoryError,
     55                 "%s of length %zd exceeds the VM limit", descriptor.c_str(), length);
     56         return NULL;
     57     }
     58     ArrayObject* newArray = (ArrayObject*)dvmMalloc(totalSize, allocFlags);
     59     if (newArray != NULL) {
     60         DVM_OBJECT_INIT(newArray, arrayClass);
     61         newArray->length = length;
     62         dvmTrackAllocation(arrayClass, totalSize);
     63     }
     64     return newArray;
     65 }
     66 
     67 /*
     68  * Create a new array, given an array class.  The class may represent an
     69  * array of references or primitives.
     70  */
     71 ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
     72     size_t length, int allocFlags)
     73 {
     74     const char* descriptor = arrayClass->descriptor;
     75 
     76     assert(descriptor[0] == '[');       /* must be array class */
     77     if (descriptor[1] != '[' && descriptor[1] != 'L') {
     78         /* primitive array */
     79         assert(descriptor[2] == '\0');
     80         return dvmAllocPrimitiveArray(descriptor[1], length, allocFlags);
     81     } else {
     82         return allocArray(arrayClass, length, kObjectArrayRefWidth,
     83             allocFlags);
     84     }
     85 }
     86 
     87 /*
     88  * Find the array class for "elemClassObj", which could itself be an
     89  * array class.
     90  */
     91 ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj)
     92 {
     93     ClassObject* arrayClass;
     94 
     95     assert(elemClassObj != NULL);
     96 
     97     /* Simply prepend "[" to the descriptor. */
     98     int nameLen = strlen(elemClassObj->descriptor);
     99     char className[nameLen + 2];
    100 
    101     className[0] = '[';
    102     memcpy(className+1, elemClassObj->descriptor, nameLen+1);
    103     arrayClass = dvmFindArrayClass(className, elemClassObj->classLoader);
    104 
    105     return arrayClass;
    106 }
    107 
    108 /*
    109  * Create a new array that holds primitive types.
    110  *
    111  * "type" is the primitive type letter, e.g. 'I' for int or 'J' for long.
    112  */
    113 ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags)
    114 {
    115     ArrayObject* newArray;
    116     ClassObject* arrayClass;
    117     int width;
    118 
    119     switch (type) {
    120     case 'I':
    121         arrayClass = gDvm.classArrayInt;
    122         width = 4;
    123         break;
    124     case 'C':
    125         arrayClass = gDvm.classArrayChar;
    126         width = 2;
    127         break;
    128     case 'B':
    129         arrayClass = gDvm.classArrayByte;
    130         width = 1;
    131         break;
    132     case 'Z':
    133         arrayClass = gDvm.classArrayBoolean;
    134         width = 1; /* special-case this? */
    135         break;
    136     case 'F':
    137         arrayClass = gDvm.classArrayFloat;
    138         width = 4;
    139         break;
    140     case 'D':
    141         arrayClass = gDvm.classArrayDouble;
    142         width = 8;
    143         break;
    144     case 'S':
    145         arrayClass = gDvm.classArrayShort;
    146         width = 2;
    147         break;
    148     case 'J':
    149         arrayClass = gDvm.classArrayLong;
    150         width = 8;
    151         break;
    152     default:
    153         ALOGE("Unknown primitive type '%c'", type);
    154         dvmAbort();
    155         return NULL; // Keeps the compiler happy.
    156     }
    157 
    158     newArray = allocArray(arrayClass, length, width, allocFlags);
    159 
    160     /* the caller must dvmReleaseTrackedAlloc if allocFlags==ALLOC_DEFAULT */
    161     return newArray;
    162 }
    163 
    164 /*
    165  * Recursively create an array with multiple dimensions.  Elements may be
    166  * Objects or primitive types.
    167  *
    168  * The dimension we're creating is in dimensions[0], so when we recurse
    169  * we advance the pointer.
    170  */
    171 ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
    172     const int* dimensions)
    173 {
    174     ArrayObject* newArray;
    175     const char* elemName = arrayClass->descriptor + 1; // Advance past one '['.
    176 
    177     LOGVV("dvmAllocMultiArray: class='%s' curDim=%d *dimensions=%d",
    178         arrayClass->descriptor, curDim, *dimensions);
    179 
    180     if (curDim == 0) {
    181         if (*elemName == 'L' || *elemName == '[') {
    182             LOGVV("  end: array class (obj) is '%s'",
    183                 arrayClass->descriptor);
    184             newArray = allocArray(arrayClass, *dimensions,
    185                         kObjectArrayRefWidth, ALLOC_DEFAULT);
    186         } else {
    187             LOGVV("  end: array class (prim) is '%s'",
    188                 arrayClass->descriptor);
    189             newArray = dvmAllocPrimitiveArray(
    190                     dexGetPrimitiveTypeDescriptorChar(arrayClass->elementClass->primitiveType),
    191                     *dimensions, ALLOC_DEFAULT);
    192         }
    193     } else {
    194         ClassObject* subArrayClass;
    195         int i;
    196 
    197         /* if we have X[][], find X[] */
    198         subArrayClass = dvmFindArrayClass(elemName, arrayClass->classLoader);
    199         if (subArrayClass == NULL) {
    200             /* not enough '['s on the initial class? */
    201             assert(dvmCheckException(dvmThreadSelf()));
    202             return NULL;
    203         }
    204         assert(dvmIsArrayClass(subArrayClass));
    205 
    206         /* allocate the array that holds the sub-arrays */
    207         newArray = allocArray(arrayClass, *dimensions, kObjectArrayRefWidth,
    208                         ALLOC_DEFAULT);
    209         if (newArray == NULL) {
    210             assert(dvmCheckException(dvmThreadSelf()));
    211             return NULL;
    212         }
    213 
    214         /*
    215          * Create a new sub-array in every element of the array.
    216          */
    217         for (i = 0; i < *dimensions; i++) {
    218           ArrayObject* newSubArray;
    219           newSubArray = dvmAllocMultiArray(subArrayClass, curDim-1,
    220                           dimensions+1);
    221             if (newSubArray == NULL) {
    222                 dvmReleaseTrackedAlloc((Object*) newArray, NULL);
    223                 assert(dvmCheckException(dvmThreadSelf()));
    224                 return NULL;
    225             }
    226             dvmSetObjectArrayElement(newArray, i, (Object *)newSubArray);
    227             dvmReleaseTrackedAlloc((Object*) newSubArray, NULL);
    228         }
    229     }
    230 
    231     /* caller must call dvmReleaseTrackedAlloc */
    232     return newArray;
    233 }
    234 
    235 
    236 /*
    237  * Find an array class, by name (e.g. "[I").
    238  *
    239  * If the array class doesn't exist, we generate it.
    240  *
    241  * If the element class doesn't exist, we return NULL (no exception raised).
    242  */
    243 ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader)
    244 {
    245     ClassObject* clazz;
    246 
    247     assert(descriptor[0] == '[');
    248     //ALOGV("dvmFindArrayClass: '%s' %p", descriptor, loader);
    249 
    250     clazz = dvmLookupClass(descriptor, loader, false);
    251     if (clazz == NULL) {
    252         ALOGV("Array class '%s' %p not found; creating", descriptor, loader);
    253         clazz = createArrayClass(descriptor, loader);
    254         if (clazz != NULL)
    255             dvmAddInitiatingLoader(clazz, loader);
    256     }
    257 
    258     return clazz;
    259 }
    260 
    261 /*
    262  * Create an array class (i.e. the class object for the array, not the
    263  * array itself).  "descriptor" looks like "[C" or "[Ljava/lang/String;".
    264  *
    265  * If "descriptor" refers to an array of primitives, look up the
    266  * primitive type's internally-generated class object.
    267  *
    268  * "loader" is the class loader of the class that's referring to us.  It's
    269  * used to ensure that we're looking for the element type in the right
    270  * context.  It does NOT become the class loader for the array class; that
    271  * always comes from the base element class.
    272  *
    273  * Returns NULL with an exception raised on failure.
    274  */
    275 static ClassObject* createArrayClass(const char* descriptor, Object* loader)
    276 {
    277     ClassObject* newClass = NULL;
    278     ClassObject* elementClass = NULL;
    279     int arrayDim;
    280     u4 extraFlags;
    281 
    282     assert(descriptor[0] == '[');
    283     assert(gDvm.classJavaLangClass != NULL);
    284     assert(gDvm.classJavaLangObject != NULL);
    285 
    286     /*
    287      * Identify the underlying element class and the array dimension depth.
    288      */
    289     extraFlags = CLASS_ISARRAY;
    290     if (descriptor[1] == '[') {
    291         /* array of arrays; keep descriptor and grab stuff from parent */
    292         ClassObject* outer;
    293 
    294         outer = dvmFindClassNoInit(&descriptor[1], loader);
    295         if (outer != NULL) {
    296             /* want the base class, not "outer", in our elementClass */
    297             elementClass = outer->elementClass;
    298             arrayDim = outer->arrayDim + 1;
    299             extraFlags |= CLASS_ISOBJECTARRAY;
    300         } else {
    301             assert(elementClass == NULL);     /* make sure we fail */
    302         }
    303     } else {
    304         arrayDim = 1;
    305         if (descriptor[1] == 'L') {
    306             /* array of objects; strip off "[" and look up descriptor. */
    307             const char* subDescriptor = &descriptor[1];
    308             LOGVV("searching for element class '%s'", subDescriptor);
    309             elementClass = dvmFindClassNoInit(subDescriptor, loader);
    310             extraFlags |= CLASS_ISOBJECTARRAY;
    311         } else {
    312             /* array of a primitive type */
    313             elementClass = dvmFindPrimitiveClass(descriptor[1]);
    314         }
    315     }
    316 
    317     if (elementClass == NULL) {
    318         /* failed */
    319         assert(dvmCheckException(dvmThreadSelf()));
    320         dvmFreeClassInnards(newClass);
    321         dvmReleaseTrackedAlloc((Object*) newClass, NULL);
    322         return NULL;
    323     }
    324 
    325     /*
    326      * See if it's already loaded.  Array classes are always associated
    327      * with the class loader of their underlying element type -- an array
    328      * of Strings goes with the loader for java/lang/String -- so we need
    329      * to look for it there.  (The caller should have checked for the
    330      * existence of the class before calling here, but they did so with
    331      * *their* class loader, not the element class' loader.)
    332      *
    333      * If we find it, the caller adds "loader" to the class' initiating
    334      * loader list, which should prevent us from going through this again.
    335      *
    336      * This call is unnecessary if "loader" and "elementClass->classLoader"
    337      * are the same, because our caller (dvmFindArrayClass) just did the
    338      * lookup.  (Even if we get this wrong we still have correct behavior,
    339      * because we effectively do this lookup again when we add the new
    340      * class to the hash table -- necessary because of possible races with
    341      * other threads.)
    342      */
    343     if (loader != elementClass->classLoader) {
    344         LOGVV("--- checking for '%s' in %p vs. elem %p",
    345             descriptor, loader, elementClass->classLoader);
    346         newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
    347         if (newClass != NULL) {
    348             ALOGV("--- we already have %s in %p, don't need in %p",
    349                 descriptor, elementClass->classLoader, loader);
    350             return newClass;
    351         }
    352     }
    353 
    354 
    355     /*
    356      * Fill out the fields in the ClassObject.
    357      *
    358      * It is possible to execute some methods against arrays, because all
    359      * arrays are instances of Object, so we need to set up a vtable.  We
    360      * can just point at the one in Object.
    361      *
    362      * Array classes are simple enough that we don't need to do a full
    363      * link step.
    364      */
    365     newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_NON_MOVING);
    366     if (newClass == NULL)
    367         return NULL;
    368     DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
    369     dvmSetClassSerialNumber(newClass);
    370     newClass->descriptorAlloc = strdup(descriptor);
    371     newClass->descriptor = newClass->descriptorAlloc;
    372     dvmSetFieldObject((Object *)newClass,
    373                       OFFSETOF_MEMBER(ClassObject, super),
    374                       (Object *)gDvm.classJavaLangObject);
    375     newClass->vtableCount = gDvm.classJavaLangObject->vtableCount;
    376     newClass->vtable = gDvm.classJavaLangObject->vtable;
    377     newClass->primitiveType = PRIM_NOT;
    378     dvmSetFieldObject((Object *)newClass,
    379                       OFFSETOF_MEMBER(ClassObject, elementClass),
    380                       (Object *)elementClass);
    381     dvmSetFieldObject((Object *)newClass,
    382                       OFFSETOF_MEMBER(ClassObject, classLoader),
    383                       (Object *)elementClass->classLoader);
    384     newClass->arrayDim = arrayDim;
    385     newClass->status = CLASS_INITIALIZED;
    386 
    387     /* don't need to set newClass->objectSize */
    388 
    389     /*
    390      * All arrays have java/lang/Cloneable and java/io/Serializable as
    391      * interfaces.  We need to set that up here, so that stuff like
    392      * "instanceof" works right.
    393      *
    394      * Note: The GC could run during the call to dvmFindSystemClassNoInit(),
    395      * so we need to make sure the class object is GC-valid while we're in
    396      * there.  Do this by clearing the interface list so the GC will just
    397      * think that the entries are null.
    398      *
    399      * TODO?
    400      * We may want to cache these two classes to avoid the lookup, though
    401      * it's not vital -- we only do it when creating an array class, not
    402      * every time we create an array.  Better yet, create a single, global
    403      * copy of "interfaces" and "iftable" somewhere near the start and
    404      * just point to those (and remember not to free them for arrays).
    405      */
    406     newClass->interfaceCount = 2;
    407     newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader,
    408                                 sizeof(ClassObject*) * 2);
    409     memset(newClass->interfaces, 0, sizeof(ClassObject*) * 2);
    410     newClass->interfaces[0] =
    411         dvmFindSystemClassNoInit("Ljava/lang/Cloneable;");
    412     newClass->interfaces[1] =
    413         dvmFindSystemClassNoInit("Ljava/io/Serializable;");
    414     dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
    415     if (newClass->interfaces[0] == NULL || newClass->interfaces[1] == NULL) {
    416         ALOGE("Unable to create array class '%s': missing interfaces",
    417             descriptor);
    418         dvmFreeClassInnards(newClass);
    419         dvmThrowInternalError("missing array ifaces");
    420         dvmReleaseTrackedAlloc((Object*) newClass, NULL);
    421         return NULL;
    422     }
    423     /*
    424      * We assume that Cloneable/Serializable don't have superinterfaces --
    425      * normally we'd have to crawl up and explicitly list all of the
    426      * supers as well.  These interfaces don't have any methods, so we
    427      * don't have to worry about the ifviPool either.
    428      */
    429     newClass->iftableCount = 2;
    430     newClass->iftable = (InterfaceEntry*) dvmLinearAlloc(newClass->classLoader,
    431                                 sizeof(InterfaceEntry) * 2);
    432     memset(newClass->iftable, 0, sizeof(InterfaceEntry) * 2);
    433     newClass->iftable[0].clazz = newClass->interfaces[0];
    434     newClass->iftable[1].clazz = newClass->interfaces[1];
    435     dvmLinearReadOnly(newClass->classLoader, newClass->iftable);
    436 
    437     /*
    438      * Inherit access flags from the element.  Arrays can't be used as a
    439      * superclass or interface, so we want to add "abstract final" and remove
    440      * "interface".
    441      */
    442     int accessFlags = elementClass->accessFlags;
    443     if (!gDvm.optimizing) {
    444         // If the element class is an inner class, make sure we get the correct access flags.
    445         StringObject* className = NULL;
    446         dvmGetInnerClass(elementClass, &className, &accessFlags);
    447         dvmReleaseTrackedAlloc((Object*) className, NULL);
    448     }
    449     accessFlags &= JAVA_FLAGS_MASK;
    450     accessFlags &= ~ACC_INTERFACE;
    451     accessFlags |= ACC_ABSTRACT | ACC_FINAL;
    452 
    453     // Set the flags we determined above.
    454     SET_CLASS_FLAG(newClass, accessFlags | extraFlags);
    455 
    456     if (!dvmAddClassToHash(newClass)) {
    457         /*
    458          * Another thread must have loaded the class after we
    459          * started but before we finished.  Discard what we've
    460          * done and leave some hints for the GC.
    461          *
    462          * (Yes, this happens.)
    463          */
    464 
    465         /* Clean up the class before letting the
    466          * GC get its hands on it.
    467          */
    468         dvmFreeClassInnards(newClass);
    469 
    470         /* Let the GC free the class.
    471          */
    472         dvmReleaseTrackedAlloc((Object*) newClass, NULL);
    473 
    474         /* Grab the winning class.
    475          */
    476         newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
    477         assert(newClass != NULL);
    478         return newClass;
    479     }
    480     dvmReleaseTrackedAlloc((Object*) newClass, NULL);
    481 
    482     ALOGV("Created array class '%s' %p (access=0x%04x.%04x)",
    483         descriptor, newClass->classLoader,
    484         newClass->accessFlags >> 16,
    485         newClass->accessFlags & JAVA_FLAGS_MASK);
    486 
    487     return newClass;
    488 }
    489 
    490 /*
    491  * Copy the entire contents of one array of objects to another.  If the copy
    492  * is impossible because of a type clash, we fail and return "false".
    493  */
    494 bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
    495     ClassObject* dstElemClass)
    496 {
    497     Object** src = (Object**)(void*)srcArray->contents;
    498     u4 length, count;
    499 
    500     assert(srcArray->length == dstArray->length);
    501     assert(dstArray->clazz->elementClass == dstElemClass ||
    502         (dstArray->clazz->elementClass == dstElemClass->elementClass &&
    503          dstArray->clazz->arrayDim == dstElemClass->arrayDim+1));
    504 
    505     length = dstArray->length;
    506     for (count = 0; count < length; count++) {
    507         if (!dvmInstanceof(src[count]->clazz, dstElemClass)) {
    508             ALOGW("dvmCopyObjectArray: can't store %s in %s",
    509                 src[count]->clazz->descriptor, dstElemClass->descriptor);
    510             return false;
    511         }
    512         dvmSetObjectArrayElement(dstArray, count, src[count]);
    513     }
    514 
    515     return true;
    516 }
    517 
    518 /*
    519  * Copy the entire contents of an array of boxed primitives into an
    520  * array of primitives.  The boxed value must fit in the primitive (i.e.
    521  * narrowing conversions are not allowed).
    522  */
    523 bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
    524     ClassObject* dstElemClass)
    525 {
    526     Object** src = (Object**)(void*)srcArray->contents;
    527     void* dst = (void*)dstArray->contents;
    528     u4 count = dstArray->length;
    529     PrimitiveType typeIndex = dstElemClass->primitiveType;
    530 
    531     assert(typeIndex != PRIM_NOT);
    532     assert(srcArray->length == dstArray->length);
    533 
    534     while (count--) {
    535         JValue result;
    536 
    537         /*
    538          * This will perform widening conversions as appropriate.  It
    539          * might make sense to be more restrictive and require that the
    540          * primitive type exactly matches the box class, but it's not
    541          * necessary for correctness.
    542          */
    543         if (!dvmUnboxPrimitive(*src, dstElemClass, &result)) {
    544             ALOGW("dvmCopyObjectArray: can't store %s in %s",
    545                 (*src)->clazz->descriptor, dstElemClass->descriptor);
    546             return false;
    547         }
    548 
    549         /* would be faster with 4 loops, but speed not crucial here */
    550         switch (typeIndex) {
    551         case PRIM_BOOLEAN:
    552         case PRIM_BYTE:
    553             {
    554                 u1* tmp = (u1*)dst;
    555                 *tmp++ = result.b;
    556                 dst = tmp;
    557             }
    558             break;
    559         case PRIM_CHAR:
    560         case PRIM_SHORT:
    561             {
    562                 u2* tmp = (u2*)dst;
    563                 *tmp++ = result.s;
    564                 dst = tmp;
    565             }
    566             break;
    567         case PRIM_FLOAT:
    568         case PRIM_INT:
    569             {
    570                 u4* tmp = (u4*)dst;
    571                 *tmp++ = result.i;
    572                 dst = tmp;
    573             }
    574             break;
    575         case PRIM_DOUBLE:
    576         case PRIM_LONG:
    577             {
    578                 u8* tmp = (u8*)dst;
    579                 *tmp++ = result.j;
    580                 dst = tmp;
    581             }
    582             break;
    583         default:
    584             /* should not be possible to get here */
    585             dvmAbort();
    586         }
    587 
    588         src++;
    589     }
    590 
    591     return true;
    592 }
    593 
    594 /*
    595  * Returns the width, in bytes, required by elements in instances of
    596  * the array class.
    597  */
    598 size_t dvmArrayClassElementWidth(const ClassObject* arrayClass)
    599 {
    600     const char *descriptor;
    601 
    602     assert(dvmIsArrayClass(arrayClass));
    603 
    604     if (dvmIsObjectArrayClass(arrayClass)) {
    605         return sizeof(Object *);
    606     } else {
    607         descriptor = arrayClass->descriptor;
    608         switch (descriptor[1]) {
    609         case 'B': return 1;  /* byte */
    610         case 'C': return 2;  /* char */
    611         case 'D': return 8;  /* double */
    612         case 'F': return 4;  /* float */
    613         case 'I': return 4;  /* int */
    614         case 'J': return 8;  /* long */
    615         case 'S': return 2;  /* short */
    616         case 'Z': return 1;  /* boolean */
    617         }
    618     }
    619     ALOGE("class %p has an unhandled descriptor '%s'", arrayClass, descriptor);
    620     dvmDumpThread(dvmThreadSelf(), false);
    621     dvmAbort();
    622     return 0;  /* Quiet the compiler. */
    623 }
    624 
    625 size_t dvmArrayObjectSize(const ArrayObject *array)
    626 {
    627     assert(array != NULL);
    628     size_t size = OFFSETOF_MEMBER(ArrayObject, contents);
    629     size += array->length * dvmArrayClassElementWidth(array->clazz);
    630     return size;
    631 }
    632