Array.c revision 4c1a2915e40eceeb68dbc323d28b8bf8763af83b
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
24#if WITH_HPROF && WITH_HPROF_STACK
25#include "hprof/Hprof.h"
26#endif
27
28static ClassObject* createArrayClass(const char* descriptor, Object* loader);
29static ClassObject* createPrimitiveClass(int idx);
30
31static const char gPrimLetter[] = PRIM_TYPE_TO_LETTER;
32
33/*
34 * Allocate space for a new array object.  This is the lowest-level array
35 * allocation function.
36 *
37 * Pass in the array class and the width of each element.
38 *
39 * On failure, returns NULL with an exception raised.
40 */
41ArrayObject* dvmAllocArray(ClassObject* arrayClass, size_t length,
42    size_t elemWidth, int allocFlags)
43{
44    ArrayObject* newArray;
45    size_t size;
46
47    assert(arrayClass->descriptor[0] == '[');
48
49    if (length > 0x0fffffff) {
50        /* too large and (length * elemWidth) will overflow 32 bits */
51        LOGE("Rejecting allocation of %u-element array\n", length);
52        dvmThrowBadAllocException("array size too large");
53        return NULL;
54    }
55
56    size = offsetof(ArrayObject, contents);
57    size += length * elemWidth;
58
59    /* Note that we assume that the Array class does not
60     * override finalize().
61     */
62    newArray = dvmMalloc(size, allocFlags);
63    if (newArray != NULL) {
64        DVM_OBJECT_INIT(&newArray->obj, arrayClass);
65        newArray->length = length;
66        LOGVV("AllocArray: %s [%d] (%d)\n",
67            arrayClass->descriptor, (int) length, (int) size);
68#if WITH_HPROF && WITH_HPROF_STACK
69        hprofFillInStackTrace(&newArray->obj);
70#endif
71        dvmTrackAllocation(arrayClass, size);
72    }
73    /* the caller must call dvmReleaseTrackedAlloc */
74    return newArray;
75}
76
77/*
78 * Create a new array, given an array class.  The class may represent an
79 * array of references or primitives.
80 */
81ArrayObject* dvmAllocArrayByClass(ClassObject* arrayClass,
82    size_t length, int allocFlags)
83{
84    const char* descriptor = arrayClass->descriptor;
85
86    assert(descriptor[0] == '[');       /* must be array class */
87    if (descriptor[1] != '[' && descriptor[1] != 'L') {
88        /* primitive array */
89        assert(descriptor[2] == '\0');
90        return dvmAllocPrimitiveArray(descriptor[1], length, allocFlags);
91    } else {
92        return dvmAllocArray(arrayClass, length, kObjectArrayRefWidth,
93            allocFlags);
94    }
95}
96
97/*
98 * Find the array class for "elemClassObj", which could itself be an
99 * array class.
100 */
101ClassObject* dvmFindArrayClassForElement(ClassObject* elemClassObj)
102{
103    ClassObject* arrayClass;
104
105    assert(elemClassObj != NULL);
106
107    if (elemClassObj->arrayClass != NULL) {
108        arrayClass = elemClassObj->arrayClass;
109        LOGVV("using cached '%s' class for '%s'\n",
110            arrayClass->descriptor, elemClassObj->descriptor);
111    } else {
112        /* Simply prepend "[" to the descriptor. */
113        int nameLen = strlen(elemClassObj->descriptor);
114        char className[nameLen + 2];
115
116        className[0] = '[';
117        memcpy(className+1, elemClassObj->descriptor, nameLen+1);
118        arrayClass = dvmFindArrayClass(className, elemClassObj->classLoader);
119        if (arrayClass != NULL)
120            elemClassObj->arrayClass = arrayClass;
121    }
122
123    return arrayClass;
124}
125
126/*
127 * Create a new array that holds references to members of the specified class.
128 *
129 * "elemClassObj" is the element type, and may itself be an array class.  It
130 * may not be a primitive class.
131 *
132 * "allocFlags" determines whether the new object will be added to the
133 * "tracked alloc" table.
134 *
135 * This is less efficient than dvmAllocArray(), but occasionally convenient.
136 */
137ArrayObject* dvmAllocObjectArray(ClassObject* elemClassObj, size_t length,
138    int allocFlags)
139{
140    ClassObject* arrayClass;
141    ArrayObject* newArray = NULL;
142
143    LOGVV("dvmAllocObjectArray: '%s' len=%d\n",
144        elemClassObj->descriptor, (int)length);
145
146    arrayClass = dvmFindArrayClassForElement(elemClassObj);
147    if (arrayClass != NULL) {
148        newArray = dvmAllocArray(arrayClass, length, kObjectArrayRefWidth,
149            allocFlags);
150    }
151
152    /* the caller must call dvmReleaseTrackedAlloc */
153    return newArray;
154}
155
156/*
157 * Create a new array that holds primitive types.
158 *
159 * "type" is the primitive type letter, e.g. 'I' for int or 'J' for long.
160 * If the array class doesn't exist, it will be created.
161 */
162ArrayObject* dvmAllocPrimitiveArray(char type, size_t length, int allocFlags)
163{
164    ArrayObject* newArray;
165    ClassObject** pTypeClass;
166    int width;
167
168    switch (type) {
169    case 'I':
170        pTypeClass = &gDvm.classArrayInt;
171        width = 4;
172        break;
173    case 'C':
174        pTypeClass = &gDvm.classArrayChar;
175        width = 2;
176        break;
177    case 'B':
178        pTypeClass = &gDvm.classArrayByte;
179        width = 1;
180        break;
181    case 'Z':
182        pTypeClass = &gDvm.classArrayBoolean;
183        width = 1; /* special-case this? */
184        break;
185    case 'F':
186        pTypeClass = &gDvm.classArrayFloat;
187        width = 4;
188        break;
189    case 'D':
190        pTypeClass = &gDvm.classArrayDouble;
191        width = 8;
192        break;
193    case 'S':
194        pTypeClass = &gDvm.classArrayShort;
195        width = 2;
196        break;
197    case 'J':
198        pTypeClass = &gDvm.classArrayLong;
199        width = 8;
200        break;
201    default:
202        LOGE("Unknown type '%c'\n", type);
203        assert(false);
204        return NULL;
205    }
206
207    if (*pTypeClass == NULL) {
208        char typeClassName[3] = "[x";
209
210        typeClassName[1] = type;
211
212        *pTypeClass = dvmFindArrayClass(typeClassName, NULL);
213        if (*pTypeClass == NULL) {
214            LOGE("ERROR: failed to generate array class for '%s'\n",
215                typeClassName);
216            return NULL;
217        }
218    }
219
220    newArray = dvmAllocArray(*pTypeClass, length, width, allocFlags);
221
222    /* the caller must dvmReleaseTrackedAlloc if allocFlags==ALLOC_DEFAULT */
223    return newArray;
224}
225
226/*
227 * Recursively create an array with multiple dimensions.  Elements may be
228 * Objects or primitive types.
229 *
230 * The dimension we're creating is in dimensions[0], so when we recurse
231 * we advance the pointer.
232 */
233ArrayObject* dvmAllocMultiArray(ClassObject* arrayClass, int curDim,
234    const int* dimensions)
235{
236    ArrayObject* newArray;
237    const char* elemName = arrayClass->descriptor + 1; // Advance past one '['.
238
239    LOGVV("dvmAllocMultiArray: class='%s' curDim=%d *dimensions=%d\n",
240        arrayClass->descriptor, curDim, *dimensions);
241
242    if (curDim == 0) {
243        if (*elemName == 'L' || *elemName == '[') {
244            LOGVV("  end: array class (obj) is '%s'\n",
245                arrayClass->descriptor);
246            newArray = dvmAllocArray(arrayClass, *dimensions,
247                        kObjectArrayRefWidth, ALLOC_DEFAULT);
248        } else {
249            LOGVV("  end: array class (prim) is '%s'\n",
250                arrayClass->descriptor);
251            newArray = dvmAllocPrimitiveArray(
252                    gPrimLetter[arrayClass->elementClass->primitiveType],
253                    *dimensions, ALLOC_DEFAULT);
254        }
255    } else {
256        ClassObject* subArrayClass;
257        Object** contents;
258        int i;
259
260        /* if we have X[][], find X[] */
261        subArrayClass = dvmFindArrayClass(elemName, arrayClass->classLoader);
262        if (subArrayClass == NULL) {
263            /* not enough '['s on the initial class? */
264            assert(dvmCheckException(dvmThreadSelf()));
265            return NULL;
266        }
267        assert(dvmIsArrayClass(subArrayClass));
268
269        /* allocate the array that holds the sub-arrays */
270        newArray = dvmAllocArray(arrayClass, *dimensions, kObjectArrayRefWidth,
271                        ALLOC_DEFAULT);
272        if (newArray == NULL) {
273            assert(dvmCheckException(dvmThreadSelf()));
274            return NULL;
275        }
276
277        /*
278         * Create a new sub-array in every element of the array.
279         */
280        contents = (Object**) newArray->contents;
281        for (i = 0; i < *dimensions; i++) {
282            ArrayObject* newSubArray;
283
284            newSubArray = dvmAllocMultiArray(subArrayClass, curDim-1,
285                            dimensions+1);
286            if (newSubArray == NULL) {
287                dvmReleaseTrackedAlloc((Object*) newArray, NULL);
288                assert(dvmCheckException(dvmThreadSelf()));
289                return NULL;
290            }
291
292            *contents++ = (Object*) newSubArray;
293            dvmReleaseTrackedAlloc((Object*) newSubArray, NULL);
294        }
295    }
296
297    /* caller must call dvmReleaseTrackedAlloc */
298    return newArray;
299}
300
301
302/*
303 * Find an array class, by name (e.g. "[I").
304 *
305 * If the array class doesn't exist, we generate it.
306 *
307 * If the element class doesn't exist, we return NULL (no exception raised).
308 */
309ClassObject* dvmFindArrayClass(const char* descriptor, Object* loader)
310{
311    ClassObject* clazz;
312
313    assert(descriptor[0] == '[');
314    //LOGV("dvmFindArrayClass: '%s' %p\n", descriptor, loader);
315
316    clazz = dvmLookupClass(descriptor, loader, false);
317    if (clazz == NULL) {
318        LOGV("Array class '%s' %p not found; creating\n", descriptor, loader);
319        clazz = createArrayClass(descriptor, loader);
320        if (clazz != NULL)
321            dvmAddInitiatingLoader(clazz, loader);
322    }
323
324    return clazz;
325}
326
327/*
328 * Create an array class (i.e. the class object for the array, not the
329 * array itself).  "descriptor" looks like "[C" or "[Ljava/lang/String;".
330 *
331 * If "descriptor" refers to an array of primitives, look up the
332 * primitive type's internally-generated class object.
333 *
334 * "loader" is the class loader of the class that's referring to us.  It's
335 * used to ensure that we're looking for the element type in the right
336 * context.  It does NOT become the class loader for the array class; that
337 * always comes from the base element class.
338 *
339 * Returns NULL with an exception raised on failure.
340 */
341static ClassObject* createArrayClass(const char* descriptor, Object* loader)
342{
343    ClassObject* newClass = NULL;
344    ClassObject* elementClass = NULL;
345    int arrayDim;
346    u4 extraFlags;
347
348    assert(descriptor[0] == '[');
349    assert(gDvm.classJavaLangClass != NULL);
350    assert(gDvm.classJavaLangObject != NULL);
351
352    /*
353     * Identify the underlying element class and the array dimension depth.
354     */
355    extraFlags = CLASS_ISARRAY;
356    if (descriptor[1] == '[') {
357        /* array of arrays; keep descriptor and grab stuff from parent */
358        ClassObject* outer;
359
360        outer = dvmFindClassNoInit(&descriptor[1], loader);
361        if (outer != NULL) {
362            /* want the base class, not "outer", in our elementClass */
363            elementClass = outer->elementClass;
364            arrayDim = outer->arrayDim + 1;
365            extraFlags |= CLASS_ISOBJECTARRAY;
366        } else {
367            assert(elementClass == NULL);     /* make sure we fail */
368        }
369    } else {
370        arrayDim = 1;
371        if (descriptor[1] == 'L') {
372            /* array of objects; strip off "[" and look up descriptor. */
373            const char* subDescriptor = &descriptor[1];
374            LOGVV("searching for element class '%s'\n", subDescriptor);
375            elementClass = dvmFindClassNoInit(subDescriptor, loader);
376            extraFlags |= CLASS_ISOBJECTARRAY;
377        } else {
378            /* array of a primitive type */
379            elementClass = dvmFindPrimitiveClass(descriptor[1]);
380        }
381    }
382
383    if (elementClass == NULL) {
384        /* failed */
385        assert(dvmCheckException(dvmThreadSelf()));
386        dvmFreeClassInnards(newClass);
387        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
388        return NULL;
389    }
390
391    /*
392     * See if it's already loaded.  Array classes are always associated
393     * with the class loader of their underlying element type -- an array
394     * of Strings goes with the loader for java/lang/String -- so we need
395     * to look for it there.  (The caller should have checked for the
396     * existence of the class before calling here, but they did so with
397     * *their* class loader, not the element class' loader.)
398     *
399     * If we find it, the caller adds "loader" to the class' initiating
400     * loader list, which should prevent us from going through this again.
401     *
402     * This call is unnecessary if "loader" and "elementClass->classLoader"
403     * are the same, because our caller (dvmFindArrayClass) just did the
404     * lookup.  (Even if we get this wrong we still have correct behavior,
405     * because we effectively do this lookup again when we add the new
406     * class to the hash table -- necessary because of possible races with
407     * other threads.)
408     */
409    if (loader != elementClass->classLoader) {
410        LOGVV("--- checking for '%s' in %p vs. elem %p\n",
411            descriptor, loader, elementClass->classLoader);
412        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
413        if (newClass != NULL) {
414            LOGV("--- we already have %s in %p, don't need in %p\n",
415                descriptor, elementClass->classLoader, loader);
416            return newClass;
417        }
418    }
419
420
421    /*
422     * Fill out the fields in the ClassObject.
423     *
424     * It is possible to execute some methods against arrays, because all
425     * arrays are instances of Object, so we need to set up a vtable.  We
426     * can just point at the one in Object.
427     *
428     * Array classes are simple enough that we don't need to do a full
429     * link step.
430     */
431    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
432    if (newClass == NULL)
433        return NULL;
434    DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass);
435    newClass->descriptorAlloc = strdup(descriptor);
436    newClass->descriptor = newClass->descriptorAlloc;
437    newClass->super = gDvm.classJavaLangObject;
438    newClass->vtableCount = gDvm.classJavaLangObject->vtableCount;
439    newClass->vtable = gDvm.classJavaLangObject->vtable;
440    newClass->primitiveType = PRIM_NOT;
441    newClass->elementClass = elementClass;
442    newClass->classLoader = elementClass->classLoader;
443    newClass->arrayDim = arrayDim;
444    newClass->status = CLASS_INITIALIZED;
445#if WITH_HPROF && WITH_HPROF_STACK
446    newClass->hprofSerialNumber = 0;
447    hprofFillInStackTrace(newClass);
448#endif
449
450    /* don't need to set newClass->objectSize */
451
452    /*
453     * All arrays have java/lang/Cloneable and java/io/Serializable as
454     * interfaces.  We need to set that up here, so that stuff like
455     * "instanceof" works right.
456     *
457     * Note: The GC could run during the call to dvmFindSystemClassNoInit(),
458     * so we need to make sure the class object is GC-valid while we're in
459     * there.  Do this by clearing the interface list so the GC will just
460     * think that the entries are null.
461     *
462     * TODO?
463     * We may want to cache these two classes to avoid the lookup, though
464     * it's not vital -- we only do it when creating an array class, not
465     * every time we create an array.  Better yet, create a single, global
466     * copy of "interfaces" and "iftable" somewhere near the start and
467     * just point to those (and remember not to free them for arrays).
468     */
469    newClass->interfaceCount = 2;
470    newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader,
471                                sizeof(ClassObject*) * 2);
472    memset(newClass->interfaces, 0, sizeof(ClassObject*) * 2);
473    newClass->interfaces[0] =
474        dvmFindSystemClassNoInit("Ljava/lang/Cloneable;");
475    newClass->interfaces[1] =
476        dvmFindSystemClassNoInit("Ljava/io/Serializable;");
477    dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
478    if (newClass->interfaces[0] == NULL || newClass->interfaces[1] == NULL) {
479        LOGE("Unable to create array class '%s': missing interfaces\n",
480            descriptor);
481        dvmFreeClassInnards(newClass);
482        dvmThrowException("Ljava/lang/InternalError;", "missing array ifaces");
483        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
484        return NULL;
485    }
486    /*
487     * We assume that Cloneable/Serializable don't have superinterfaces --
488     * normally we'd have to crawl up and explicitly list all of the
489     * supers as well.  These interfaces don't have any methods, so we
490     * don't have to worry about the ifviPool either.
491     */
492    newClass->iftableCount = 2;
493    newClass->iftable = (InterfaceEntry*) dvmLinearAlloc(newClass->classLoader,
494                                sizeof(InterfaceEntry) * 2);
495    memset(newClass->iftable, 0, sizeof(InterfaceEntry) * 2);
496    newClass->iftable[0].clazz = newClass->interfaces[0];
497    newClass->iftable[1].clazz = newClass->interfaces[1];
498    dvmLinearReadOnly(newClass->classLoader, newClass->iftable);
499
500    /*
501     * Inherit access flags from the element.  Arrays can't be used as a
502     * superclass or interface, so we want to add "final" and remove
503     * "interface".
504     *
505     * Don't inherit any non-standard flags (e.g., CLASS_FINALIZABLE)
506     * from elementClass.  We assume that the array class does not
507     * override finalize().
508     */
509    newClass->accessFlags = ((newClass->elementClass->accessFlags &
510                             ~ACC_INTERFACE) | ACC_FINAL) & JAVA_FLAGS_MASK;
511
512    /* Set the flags we determined above.
513     * This must happen after accessFlags is set.
514     */
515    SET_CLASS_FLAG(newClass, extraFlags);
516
517    if (!dvmAddClassToHash(newClass)) {
518        /*
519         * Another thread must have loaded the class after we
520         * started but before we finished.  Discard what we've
521         * done and leave some hints for the GC.
522         */
523        LOGI("WOW: somebody generated %s simultaneously\n",
524            newClass->descriptor);
525
526        /* Clean up the class before letting the
527         * GC get its hands on it.
528         */
529        assert(newClass->obj.clazz == gDvm.unlinkedJavaLangClass);
530        dvmFreeClassInnards(newClass);
531
532        /* Let the GC free the class.
533         */
534        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
535
536        /* Grab the winning class.
537         */
538        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
539        assert(newClass != NULL);
540        return newClass;
541    }
542
543    /* make it available to the GC */
544    newClass->obj.clazz = gDvm.classJavaLangClass;
545    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
546
547    LOGV("Created array class '%s' %p (access=0x%04x.%04x)\n",
548        descriptor, newClass->classLoader,
549        newClass->accessFlags >> 16,
550        newClass->accessFlags & JAVA_FLAGS_MASK);
551
552    return newClass;
553}
554
555/*
556 * Get a class we generated for the primitive types.
557 *
558 * These correspond to e.g. Integer.TYPE, and are used as the element
559 * class in arrays of primitives.
560 *
561 * "type" should be 'I', 'J', 'Z', etc.
562 *
563 * Returns NULL if the type doesn't correspond to a known primitive type.
564 */
565ClassObject* dvmFindPrimitiveClass(char type)
566{
567    int idx;
568
569    switch (type) {
570    case 'Z':
571        idx = PRIM_BOOLEAN;
572        break;
573    case 'C':
574        idx = PRIM_CHAR;
575        break;
576    case 'F':
577        idx = PRIM_FLOAT;
578        break;
579    case 'D':
580        idx = PRIM_DOUBLE;
581        break;
582    case 'B':
583        idx = PRIM_BYTE;
584        break;
585    case 'S':
586        idx = PRIM_SHORT;
587        break;
588    case 'I':
589        idx = PRIM_INT;
590        break;
591    case 'J':
592        idx = PRIM_LONG;
593        break;
594    case 'V':
595        idx = PRIM_VOID;
596        break;
597    default:
598        LOGW("Unknown primitive type '%c'\n", type);
599        return NULL;
600    }
601
602    /*
603     * Create the primitive class if it hasn't already been, and add it
604     * to the table.
605     */
606    if (gDvm.primitiveClass[idx] == NULL) {
607        ClassObject* primClass = createPrimitiveClass(idx);
608        dvmReleaseTrackedAlloc((Object*) primClass, NULL);
609
610        if (!ATOMIC_CMP_SWAP((int*) &gDvm.primitiveClass[idx],
611            0, (int) primClass))
612        {
613            /*
614             * Looks like somebody beat us to it.  Free up the one we
615             * just created and use the other one.
616             */
617            dvmFreeClassInnards(primClass);
618        }
619    }
620
621    return gDvm.primitiveClass[idx];
622}
623
624/*
625 * Synthesize a primitive class.
626 *
627 * The spec for java.lang.Class.isPrimitive describes the names to
628 * be used for these classes.
629 *
630 * Just creates the class and returns it (does not add it to the class list).
631 */
632static ClassObject* createPrimitiveClass(int idx)
633{
634    ClassObject* newClass;
635    static const char* kClassDescriptors[PRIM_MAX] = {
636        "Z", "C", "F", "D", "B", "S", "I", "J", "V"
637    };
638
639    assert(gDvm.classJavaLangClass != NULL);
640    assert(idx >= 0 && idx < PRIM_MAX);
641
642    /*
643     * Fill out a few fields in the ClassObject.
644     *
645     * Note that primitive classes do not sub-class java/lang/Object.  This
646     * matters for "instanceof" checks.  Also, we assume that the primitive
647     * class does not override finalize().
648     */
649    newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
650    if (newClass == NULL)
651        return NULL;
652    DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
653    newClass->accessFlags = ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT;
654    newClass->primitiveType = idx;
655    newClass->descriptorAlloc = NULL;
656    newClass->descriptor = kClassDescriptors[idx];
657    //newClass->super = gDvm.classJavaLangObject;
658    newClass->status = CLASS_INITIALIZED;
659#if WITH_HPROF && WITH_HPROF_STACK
660    newClass->hprofSerialNumber = 0;
661    hprofFillInStackTrace(newClass);
662#endif
663
664    /* don't need to set newClass->objectSize */
665
666    LOGVV("Created primitive class '%s'\n", kClassDescriptors[idx]);
667
668    return newClass;
669}
670
671/*
672 * Copy the entire contents of one array of objects to another.  If the copy
673 * is impossible because of a type clash, we fail and return "false".
674 */
675bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
676    ClassObject* dstElemClass)
677{
678    Object** src = (Object**)srcArray->contents;
679    Object** dst = (Object**)dstArray->contents;
680    u4 count = dstArray->length;
681
682    assert(srcArray->length == dstArray->length);
683    assert(dstArray->obj.clazz->elementClass == dstElemClass ||
684        (dstArray->obj.clazz->elementClass == dstElemClass->elementClass &&
685         dstArray->obj.clazz->arrayDim == dstElemClass->arrayDim+1));
686
687    while (count--) {
688        if (!dvmInstanceof((*src)->clazz, dstElemClass)) {
689            LOGW("dvmCopyObjectArray: can't store %s in %s\n",
690                (*src)->clazz->descriptor, dstElemClass->descriptor);
691            return false;
692        }
693        *dst++ = *src++;
694    }
695
696    return true;
697}
698
699/*
700 * Add all primitive classes to the root set of objects.
701TODO: do these belong to the root class loader?
702 */
703void dvmGcScanPrimitiveClasses()
704{
705    int i;
706
707    for (i = 0; i < PRIM_MAX; i++) {
708        dvmMarkObject((Object *)gDvm.primitiveClass[i]);    // may be NULL
709    }
710}
711
712