• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   */
allocArray(ClassObject * arrayClass,size_t length,size_t elemWidth,int allocFlags)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   */
dvmAllocArrayByClass(ClassObject * arrayClass,size_t length,int allocFlags)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   */
dvmFindArrayClassForElement(ClassObject * elemClassObj)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   */
dvmAllocPrimitiveArray(char type,size_t length,int allocFlags)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   */
dvmAllocMultiArray(ClassObject * arrayClass,int curDim,const int * dimensions)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   */
dvmFindArrayClass(const char * descriptor,Object * loader)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   */
createArrayClass(const char * descriptor,Object * loader)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   */
dvmCopyObjectArray(ArrayObject * dstArray,const ArrayObject * srcArray,ClassObject * dstElemClass)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   */
dvmUnboxObjectArray(ArrayObject * dstArray,const ArrayObject * srcArray,ClassObject * dstElemClass)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   */
dvmArrayClassElementWidth(const ClassObject * arrayClass)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  
dvmArrayObjectSize(const ArrayObject * array)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