Array.cpp revision e8e1ddccd616e8226b7cc1e4e9fdb327429249e8
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 */
26static size_t kObjectArrayRefWidth = sizeof(Object*);
27
28static 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 */
38static 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 */
71ArrayObject* 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 */
91ClassObject* 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 */
113ArrayObject* 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        LOGE("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 */
171ArrayObject* 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 */
243ClassObject* 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 */
275static 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        LOGE("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 "final" and remove
440     * "interface".
441     *
442     * Don't inherit any non-standard flags (e.g., CLASS_FINALIZABLE)
443     * from elementClass.  We assume that the array class does not
444     * override finalize().
445     */
446    newClass->accessFlags = ((newClass->elementClass->accessFlags &
447                             ~ACC_INTERFACE) | ACC_FINAL) & JAVA_FLAGS_MASK;
448
449    /* Set the flags we determined above.
450     * This must happen after accessFlags is set.
451     */
452    SET_CLASS_FLAG(newClass, extraFlags);
453
454    if (!dvmAddClassToHash(newClass)) {
455        /*
456         * Another thread must have loaded the class after we
457         * started but before we finished.  Discard what we've
458         * done and leave some hints for the GC.
459         *
460         * (Yes, this happens.)
461         */
462
463        /* Clean up the class before letting the
464         * GC get its hands on it.
465         */
466        dvmFreeClassInnards(newClass);
467
468        /* Let the GC free the class.
469         */
470        dvmReleaseTrackedAlloc((Object*) newClass, NULL);
471
472        /* Grab the winning class.
473         */
474        newClass = dvmLookupClass(descriptor, elementClass->classLoader, false);
475        assert(newClass != NULL);
476        return newClass;
477    }
478    dvmReleaseTrackedAlloc((Object*) newClass, NULL);
479
480    ALOGV("Created array class '%s' %p (access=0x%04x.%04x)",
481        descriptor, newClass->classLoader,
482        newClass->accessFlags >> 16,
483        newClass->accessFlags & JAVA_FLAGS_MASK);
484
485    return newClass;
486}
487
488/*
489 * Copy the entire contents of one array of objects to another.  If the copy
490 * is impossible because of a type clash, we fail and return "false".
491 */
492bool dvmCopyObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
493    ClassObject* dstElemClass)
494{
495    Object** src = (Object**)(void*)srcArray->contents;
496    u4 length, count;
497
498    assert(srcArray->length == dstArray->length);
499    assert(dstArray->clazz->elementClass == dstElemClass ||
500        (dstArray->clazz->elementClass == dstElemClass->elementClass &&
501         dstArray->clazz->arrayDim == dstElemClass->arrayDim+1));
502
503    length = dstArray->length;
504    for (count = 0; count < length; count++) {
505        if (!dvmInstanceof(src[count]->clazz, dstElemClass)) {
506            ALOGW("dvmCopyObjectArray: can't store %s in %s",
507                src[count]->clazz->descriptor, dstElemClass->descriptor);
508            return false;
509        }
510        dvmSetObjectArrayElement(dstArray, count, src[count]);
511    }
512
513    return true;
514}
515
516/*
517 * Copy the entire contents of an array of boxed primitives into an
518 * array of primitives.  The boxed value must fit in the primitive (i.e.
519 * narrowing conversions are not allowed).
520 */
521bool dvmUnboxObjectArray(ArrayObject* dstArray, const ArrayObject* srcArray,
522    ClassObject* dstElemClass)
523{
524    Object** src = (Object**)(void*)srcArray->contents;
525    void* dst = (void*)dstArray->contents;
526    u4 count = dstArray->length;
527    PrimitiveType typeIndex = dstElemClass->primitiveType;
528
529    assert(typeIndex != PRIM_NOT);
530    assert(srcArray->length == dstArray->length);
531
532    while (count--) {
533        JValue result;
534
535        /*
536         * This will perform widening conversions as appropriate.  It
537         * might make sense to be more restrictive and require that the
538         * primitive type exactly matches the box class, but it's not
539         * necessary for correctness.
540         */
541        if (!dvmUnboxPrimitive(*src, dstElemClass, &result)) {
542            ALOGW("dvmCopyObjectArray: can't store %s in %s",
543                (*src)->clazz->descriptor, dstElemClass->descriptor);
544            return false;
545        }
546
547        /* would be faster with 4 loops, but speed not crucial here */
548        switch (typeIndex) {
549        case PRIM_BOOLEAN:
550        case PRIM_BYTE:
551            {
552                u1* tmp = (u1*)dst;
553                *tmp++ = result.b;
554                dst = tmp;
555            }
556            break;
557        case PRIM_CHAR:
558        case PRIM_SHORT:
559            {
560                u2* tmp = (u2*)dst;
561                *tmp++ = result.s;
562                dst = tmp;
563            }
564            break;
565        case PRIM_FLOAT:
566        case PRIM_INT:
567            {
568                u4* tmp = (u4*)dst;
569                *tmp++ = result.i;
570                dst = tmp;
571            }
572            break;
573        case PRIM_DOUBLE:
574        case PRIM_LONG:
575            {
576                u8* tmp = (u8*)dst;
577                *tmp++ = result.j;
578                dst = tmp;
579            }
580            break;
581        default:
582            /* should not be possible to get here */
583            dvmAbort();
584        }
585
586        src++;
587    }
588
589    return true;
590}
591
592/*
593 * Returns the width, in bytes, required by elements in instances of
594 * the array class.
595 */
596size_t dvmArrayClassElementWidth(const ClassObject* arrayClass)
597{
598    const char *descriptor;
599
600    assert(dvmIsArrayClass(arrayClass));
601
602    if (dvmIsObjectArrayClass(arrayClass)) {
603        return sizeof(Object *);
604    } else {
605        descriptor = arrayClass->descriptor;
606        switch (descriptor[1]) {
607        case 'B': return 1;  /* byte */
608        case 'C': return 2;  /* char */
609        case 'D': return 8;  /* double */
610        case 'F': return 4;  /* float */
611        case 'I': return 4;  /* int */
612        case 'J': return 8;  /* long */
613        case 'S': return 2;  /* short */
614        case 'Z': return 1;  /* boolean */
615        }
616    }
617    LOGE("class %p has an unhandled descriptor '%s'", arrayClass, descriptor);
618    dvmDumpThread(dvmThreadSelf(), false);
619    dvmAbort();
620    return 0;  /* Quiet the compiler. */
621}
622
623size_t dvmArrayObjectSize(const ArrayObject *array)
624{
625    assert(array != NULL);
626    size_t size = OFFSETOF_MEMBER(ArrayObject, contents);
627    size += array->length * dvmArrayClassElementWidth(array->clazz);
628    return size;
629}
630