auto import from //depot/cupcake/@135843
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
new file mode 100644
index 0000000..c7ab763
--- /dev/null
+++ b/vm/oo/Class.c
@@ -0,0 +1,4673 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Class loading, including bootstrap class loader, linking, and
+ * initialization.
+ */
+
+#define LOG_CLASS_LOADING 0
+
+#include "Dalvik.h"
+#include "libdex/DexClass.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/stat.h>
+
+#if LOG_CLASS_LOADING
+#include <unistd.h>
+#include <pthread.h>
+#include <cutils/process_name.h>
+#include <sys/types.h>
+#endif
+
+/*
+Notes on Linking and Verification
+
+The basic way to retrieve a class is to load it, make sure its superclass
+and interfaces are available, prepare its fields, and return it. This gets
+a little more complicated when multiple threads can be trying to retrieve
+the class simultaneously, requiring that we use the class object's monitor
+to keep things orderly.
+
+The linking (preparing, resolving) of a class can cause us to recursively
+load superclasses and interfaces. Barring circular references (e.g. two
+classes that are superclasses of each other), this will complete without
+the loader attempting to access the partially-linked class.
+
+With verification, the situation is different. If we try to verify
+every class as we load it, we quickly run into trouble. Even the lowly
+java.lang.Object requires CloneNotSupportedException; follow the list
+of referenced classes and you can head down quite a trail. The trail
+eventually leads back to Object, which is officially not fully-formed yet.
+
+The VM spec (specifically, v2 5.4.1) notes that classes pulled in during
+verification do not need to be prepared or verified. This means that we
+are allowed to have loaded but unverified classes. It further notes that
+the class must be verified before it is initialized, which allows us to
+defer verification for all classes until class init. You can't execute
+code or access fields in an uninitialized class, so this is safe.
+
+It also allows a more peaceful coexistence between verified and
+unverifiable code. If class A refers to B, and B has a method that
+refers to a bogus class C, should we allow class A to be verified?
+If A only exercises parts of B that don't use class C, then there is
+nothing wrong with running code in A. We can fully verify both A and B,
+and allow execution to continue until B causes initialization of C. The
+VerifyError is thrown close to the point of use.
+
+This gets a little weird with java.lang.Class, which is the only class
+that can be instantiated before it is initialized. We have to force
+initialization right after the class is created, because by definition we
+have instances of it on the heap, and somebody might get a class object and
+start making virtual calls on it. We can end up going recursive during
+verification of java.lang.Class, but we avoid that by checking to see if
+verification is already in progress before we try to initialize it.
+*/
+
+/*
+Notes on class loaders and interaction with optimization / verification
+
+In what follows, "pre-verification" and "optimization" are the steps
+performed by the dexopt command, which attempts to verify and optimize
+classes as part of unpacking jar files and storing the DEX data in the
+dalvik-cache directory. These steps are performed by loading the DEX
+files directly, without any assistance from ClassLoader instances.
+
+When we pre-verify and optimize a class in a DEX file, we make some
+assumptions about where the class loader will go to look for classes.
+If we can't guarantee those assumptions, e.g. because a class ("AppClass")
+references something not defined in the bootstrap jars or the AppClass jar,
+we can't pre-verify or optimize the class.
+
+The VM doesn't define the behavior of user-defined class loaders.
+For example, suppose application class AppClass, loaded by UserLoader,
+has a method that creates a java.lang.String. The first time
+AppClass.stringyMethod tries to do something with java.lang.String, it
+asks UserLoader to find it. UserLoader is expected to defer to its parent
+loader, but isn't required to. UserLoader might provide a replacement
+for String.
+
+We can run into trouble if we pre-verify AppClass with the assumption that
+java.lang.String will come from core.jar, and don't verify this assumption
+at runtime. There are two places that an alternate implementation of
+java.lang.String can come from: the AppClass jar, or from some other jar
+that UserLoader knows about. (Someday UserLoader will be able to generate
+some bytecode and call DefineClass, but not yet.)
+
+To handle the first situation, the pre-verifier will explicitly check for
+conflicts between the class being optimized/verified and the bootstrap
+classes. If an app jar contains a class that has the same package and
+class name as a class in a bootstrap jar, the verification resolver refuses
+to find either, which will block pre-verification and optimization on
+classes that reference ambiguity. The VM will postpone verification of
+the app class until first load.
+
+For the second situation, we need to ensure that all references from a
+pre-verified class are satisified by the class' jar or earlier bootstrap
+jars. In concrete terms: when resolving a reference to NewClass,
+which was caused by a reference in class AppClass, we check to see if
+AppClass was pre-verified. If so, we require that NewClass comes out
+of either the AppClass jar or one of the jars in the bootstrap path.
+(We may not control the class loaders, but we do manage the DEX files.
+We can verify that it's either (loader==null && dexFile==a_boot_dex)
+or (loader==UserLoader && dexFile==AppClass.dexFile). Classes from
+DefineClass can't be pre-verified, so this doesn't apply.)
+
+This should ensure that you can't "fake out" the pre-verifier by creating
+a user-defined class loader that replaces system classes. It should
+also ensure that you can write such a loader and have it work in the
+expected fashion; all you lose is some performance due to "just-in-time
+verification" and the lack of DEX optimizations.
+
+There is a "back door" of sorts in the class resolution check, due to
+the fact that the "class ref" entries are shared between the bytecode
+and meta-data references (e.g. annotations and exception handler lists).
+The class references in annotations have no bearing on class verification,
+so when a class does an annotation query that causes a class reference
+index to be resolved, we don't want to fail just because the calling
+class was pre-verified and the resolved class is in some random DEX file.
+The successful resolution adds the class to the "resolved classes" table,
+so when optimized bytecode references it we don't repeat the resolve-time
+check. We can avoid this by not updating the "resolved classes" table
+when the class reference doesn't come out of something that has been
+checked by the verifier, but that has a nonzero performance impact.
+Since the ultimate goal of this test is to catch an unusual situation
+(user-defined class loaders redefining core classes), the added caution
+may not be worth the performance hit.
+*/
+
+static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap);
+static void freeCpeArray(ClassPathEntry* cpe);
+
+static ClassObject* findClassFromLoaderNoInit(
+ const char* descriptor, Object* loader);
+static ClassObject* findClassNoInit(const char* descriptor, Object* loader,\
+ DvmDex* pDvmDex);
+static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
+ const DexClassDef* pClassDef, Object* loader);
+static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,\
+ Method* meth);
+static int computeJniArgInfo(const DexProto* proto);
+static void loadSFieldFromDex(ClassObject* clazz,
+ const DexField* pDexSField, StaticField* sfield);
+static void loadIFieldFromDex(ClassObject* clazz,
+ const DexField* pDexIField, InstField* field);
+static void freeMethodInnards(Method* meth);
+static bool createVtable(ClassObject* clazz);
+static bool createIftable(ClassObject* clazz);
+static bool insertMethodStubs(ClassObject* clazz);
+static bool computeFieldOffsets(ClassObject* clazz);
+static void throwEarlierClassFailure(ClassObject* clazz);
+
+#if LOG_CLASS_LOADING
+/*
+ * Logs information about a class loading with given timestamp.
+ *
+ * TODO: In the case where we fail in dvmLinkClass() and log the class as closing (type='<'),
+ * it would probably be better to use a new type code to indicate the failure. This change would
+ * require a matching change in the parser and analysis code in frameworks/base/tools/preload.
+ */
+static void logClassLoadWithTime(char type, ClassObject* clazz, u8 time) {
+ pid_t ppid = getppid();
+ pid_t pid = getpid();
+ unsigned int tid = (unsigned int) pthread_self();
+
+ LOG(LOG_INFO, "PRELOAD", "%c%d:%d:%d:%s:%d:%s:%lld\n", type, ppid, pid, tid,
+ get_process_name(), (int) clazz->classLoader, clazz->descriptor,
+ time);
+}
+
+/*
+ * Logs information about a class loading.
+ */
+static void logClassLoad(char type, ClassObject* clazz) {
+ logClassLoadWithTime(type, clazz, dvmGetThreadCpuTimeNsec());
+}
+#endif
+
+/*
+ * Some LinearAlloc unit tests.
+ */
+static void linearAllocTests()
+{
+ char* fiddle;
+ int try = 1;
+
+ switch (try) {
+ case 0:
+ fiddle = dvmLinearAlloc(NULL, 3200-28);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 1:
+ fiddle = dvmLinearAlloc(NULL, 3200-24);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 2:
+ fiddle = dvmLinearAlloc(NULL, 3200-20);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 3:
+ fiddle = dvmLinearAlloc(NULL, 3200-16);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ case 4:
+ fiddle = dvmLinearAlloc(NULL, 3200-12);
+ dvmLinearReadOnly(NULL, fiddle);
+ break;
+ }
+ fiddle = dvmLinearAlloc(NULL, 896);
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 20); // watch addr of this alloc
+ dvmLinearReadOnly(NULL, fiddle);
+
+ fiddle = dvmLinearAlloc(NULL, 1);
+ fiddle[0] = 'q';
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 4096);
+ fiddle[0] = 'x';
+ fiddle[4095] = 'y';
+ dvmLinearReadOnly(NULL, fiddle);
+ dvmLinearFree(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 0);
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearRealloc(NULL, fiddle, 12);
+ fiddle[11] = 'z';
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearRealloc(NULL, fiddle, 5);
+ dvmLinearReadOnly(NULL, fiddle);
+ fiddle = dvmLinearAlloc(NULL, 17001);
+ fiddle[0] = 'x';
+ fiddle[17000] = 'y';
+ dvmLinearReadOnly(NULL, fiddle);
+
+ char* str = dvmLinearStrdup(NULL, "This is a test!");
+ LOGI("GOT: '%s'\n", str);
+
+ dvmLinearAllocDump(NULL);
+ dvmLinearFree(NULL, str);
+}
+
+/*
+ * Initialize the bootstrap class loader.
+ *
+ * Call this after the bootclasspath string has been finalized.
+ */
+bool dvmClassStartup(void)
+{
+ ClassObject* unlinkedClass;
+
+ /* make this a requirement -- don't currently support dirs in path */
+ if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
+ LOGE("ERROR: must specify non-'.' bootclasspath\n");
+ return false;
+ }
+
+ gDvm.loadedClasses =
+ dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
+
+ gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
+ if (gDvm.pBootLoaderAlloc == NULL)
+ return false;
+
+ if (false) {
+ linearAllocTests();
+ exit(0);
+ }
+
+ /*
+ * Class serial number. We start with a high value to make it distinct
+ * in binary dumps (e.g. hprof).
+ */
+ gDvm.classSerialNumber = 0x50000000;
+
+
+ /* This placeholder class is used while a ClassObject is
+ * loading/linking so those not in the know can still say
+ * "obj->clazz->...".
+ */
+ unlinkedClass = &gDvm.unlinkedJavaLangClassObject;
+
+ memset(unlinkedClass, 0, sizeof(*unlinkedClass));
+
+ /* Set obj->clazz to NULL so anyone who gets too interested
+ * in the fake class will crash.
+ */
+ DVM_OBJECT_INIT(&unlinkedClass->obj, NULL);
+ unlinkedClass->descriptor = "!unlinkedClass";
+ dvmSetClassSerialNumber(unlinkedClass);
+
+ gDvm.unlinkedJavaLangClass = unlinkedClass;
+
+ /*
+ * Process the bootstrap class path. This means opening the specified
+ * DEX or Jar files and possibly running them through the optimizer.
+ */
+ assert(gDvm.bootClassPath == NULL);
+ processClassPath(gDvm.bootClassPathStr, true);
+
+ if (gDvm.bootClassPath == NULL)
+ return false;
+
+ return true;
+}
+
+/*
+ * Clean up.
+ */
+void dvmClassShutdown(void)
+{
+ int i;
+
+ /* discard all system-loaded classes */
+ dvmHashTableFree(gDvm.loadedClasses);
+ gDvm.loadedClasses = NULL;
+
+ /* discard primitive classes created for arrays */
+ for (i = 0; i < PRIM_MAX; i++)
+ dvmFreeClassInnards(gDvm.primitiveClass[i]);
+
+ /* this closes DEX files, JAR files, etc. */
+ freeCpeArray(gDvm.bootClassPath);
+ gDvm.bootClassPath = NULL;
+
+ dvmLinearAllocDestroy(NULL);
+}
+
+
+/*
+ * ===========================================================================
+ * Bootstrap class loader
+ * ===========================================================================
+ */
+
+/*
+ * Dump the contents of a ClassPathEntry array.
+ */
+static void dumpClassPath(const ClassPathEntry* cpe)
+{
+ int idx = 0;
+
+ while (cpe->kind != kCpeLastEntry) {
+ const char* kindStr;
+
+ switch (cpe->kind) {
+ case kCpeDir: kindStr = "dir"; break;
+ case kCpeJar: kindStr = "jar"; break;
+ case kCpeDex: kindStr = "dex"; break;
+ default: kindStr = "???"; break;
+ }
+
+ LOGI(" %2d: type=%s %s %p\n", idx, kindStr, cpe->fileName, cpe->ptr);
+ if (CALC_CACHE_STATS && cpe->kind == kCpeJar) {
+ JarFile* pJarFile = (JarFile*) cpe->ptr;
+ DvmDex* pDvmDex = dvmGetJarFileDex(pJarFile);
+ dvmDumpAtomicCacheStats(pDvmDex->pInterfaceCache);
+ }
+
+ cpe++;
+ idx++;
+ }
+}
+
+/*
+ * Dump the contents of the bootstrap class path.
+ */
+void dvmDumpBootClassPath(void)
+{
+ dumpClassPath(gDvm.bootClassPath);
+}
+
+/*
+ * Returns "true" if the class path contains the specified path.
+ */
+bool dvmClassPathContains(const ClassPathEntry* cpe, const char* path)
+{
+ while (cpe->kind != kCpeLastEntry) {
+ if (strcmp(cpe->fileName, path) == 0)
+ return true;
+
+ cpe++;
+ }
+ return false;
+}
+
+/*
+ * Free an array of ClassPathEntry structs.
+ *
+ * We release the contents of each entry, then free the array itself.
+ */
+static void freeCpeArray(ClassPathEntry* cpe)
+{
+ ClassPathEntry* cpeStart = cpe;
+
+ if (cpe == NULL)
+ return;
+
+ while (cpe->kind != kCpeLastEntry) {
+ switch (cpe->kind) {
+ case kCpeJar:
+ /* free JarFile */
+ dvmJarFileFree((JarFile*) cpe->ptr);
+ break;
+ case kCpeDex:
+ /* free RawDexFile */
+ dvmRawDexFileFree((RawDexFile*) cpe->ptr);
+ break;
+ default:
+ /* e.g. kCpeDir */
+ assert(cpe->ptr == NULL);
+ break;
+ }
+
+ free(cpe->fileName);
+ cpe++;
+ }
+
+ free(cpeStart);
+}
+
+/*
+ * Prepare a ClassPathEntry struct, which at this point only has a valid
+ * filename. We need to figure out what kind of file it is, and for
+ * everything other than directories we need to open it up and see
+ * what's inside.
+ */
+static bool prepareCpe(ClassPathEntry* cpe, bool isBootstrap)
+{
+ JarFile* pJarFile = NULL;
+ RawDexFile* pRawDexFile = NULL;
+ struct stat sb;
+ int cc;
+
+ cc = stat(cpe->fileName, &sb);
+ if (cc < 0) {
+ LOGW("Unable to stat classpath element '%s'\n", cpe->fileName);
+ return false;
+ }
+ if (S_ISDIR(sb.st_mode)) {
+ /*
+ * The directory will usually have .class files in subdirectories,
+ * which may be a few levels down. Doing a recursive scan and
+ * caching the results would help us avoid hitting the filesystem
+ * on misses. Whether or not this is of measureable benefit
+ * depends on a number of factors, but most likely it is not
+ * worth the effort (especially since most of our stuff will be
+ * in DEX or JAR).
+ */
+ cpe->kind = kCpeDir;
+ assert(cpe->ptr == NULL);
+ return true;
+ }
+
+ if (dvmJarFileOpen(cpe->fileName, NULL, &pJarFile, isBootstrap) == 0) {
+ cpe->kind = kCpeJar;
+ cpe->ptr = pJarFile;
+ return true;
+ }
+
+ // TODO: do we still want to support "raw" DEX files in the classpath?
+ if (dvmRawDexFileOpen(cpe->fileName, NULL, &pRawDexFile, isBootstrap) == 0)
+ {
+ cpe->kind = kCpeDex;
+ cpe->ptr = pRawDexFile;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Convert a colon-separated list of directories, Zip files, and DEX files
+ * into an array of ClassPathEntry structs.
+ *
+ * If we're unable to load a bootstrap class path entry, we fail. This
+ * is necessary to preserve the dependencies implied by optimized DEX files
+ * (e.g. if the same class appears in multiple places).
+ *
+ * During normal startup we fail if there are no entries, because we won't
+ * get very far without the basic language support classes, but if we're
+ * optimizing a DEX file we allow it.
+ */
+static ClassPathEntry* processClassPath(const char* pathStr, bool isBootstrap)
+{
+ ClassPathEntry* cpe = NULL;
+ char* mangle;
+ char* cp;
+ const char* end;
+ int idx, count;
+
+ assert(pathStr != NULL);
+
+ mangle = strdup(pathStr);
+
+ /*
+ * Run through and essentially strtok() the string. Get a count of
+ * the #of elements while we're at it.
+ *
+ * If the path was constructed strangely (e.g. ":foo::bar:") this will
+ * over-allocate, which isn't ideal but is mostly harmless.
+ */
+ count = 1;
+ for (cp = mangle; *cp != '\0'; cp++) {
+ if (*cp == ':') { /* separates two entries */
+ count++;
+ *cp = '\0';
+ }
+ }
+ end = cp;
+
+ /*
+ * Allocate storage. We over-alloc by one so we can set an "end" marker.
+ */
+ cpe = (ClassPathEntry*) calloc(count+1, sizeof(ClassPathEntry));
+
+ /*
+ * Set the global pointer so the DEX file dependency stuff can find it.
+ */
+ gDvm.bootClassPath = cpe;
+
+ /*
+ * Go through a second time, pulling stuff out.
+ */
+ cp = mangle;
+ idx = 0;
+ while (cp < end) {
+ if (*cp == '\0') {
+ /* leading, trailing, or doubled ':'; ignore it */
+ } else {
+ ClassPathEntry tmp;
+ tmp.kind = kCpeUnknown;
+ tmp.fileName = strdup(cp);
+ tmp.ptr = NULL;
+
+ /* drop an end marker here so DEX loader can walk unfinished list */
+ cpe[idx].kind = kCpeLastEntry;
+ cpe[idx].fileName = NULL;
+ cpe[idx].ptr = NULL;
+
+ if (!prepareCpe(&tmp, isBootstrap)) {
+ LOGD("Failed on '%s' (boot=%d)\n", tmp.fileName, isBootstrap);
+ /* drop from list and continue on */
+ free(tmp.fileName);
+
+ if (isBootstrap || gDvm.optimizing) {
+ /* if boot path entry or we're optimizing, this is fatal */
+ free(cpe);
+ cpe = NULL;
+ goto bail;
+ }
+ } else {
+ /* copy over, pointers and all */
+ if (tmp.fileName[0] != '/')
+ LOGW("Non-absolute bootclasspath entry '%s'\n",
+ tmp.fileName);
+ cpe[idx] = tmp;
+ idx++;
+ }
+ }
+
+ cp += strlen(cp) +1;
+ }
+ assert(idx <= count);
+ if (idx == 0 && !gDvm.optimizing) {
+ LOGE("ERROR: no valid entries found in bootclasspath '%s'\n", pathStr);
+ free(cpe);
+ cpe = NULL;
+ goto bail;
+ }
+
+ LOGVV(" (filled %d of %d slots)\n", idx, count);
+
+ /* put end marker in over-alloc slot */
+ cpe[idx].kind = kCpeLastEntry;
+ cpe[idx].fileName = NULL;
+ cpe[idx].ptr = NULL;
+
+ //dumpClassPath(cpe);
+
+bail:
+ free(mangle);
+ gDvm.bootClassPath = cpe;
+ return cpe;
+}
+
+/*
+ * Search the DEX files we loaded from the bootstrap class path for a DEX
+ * file that has the class with the matching descriptor.
+ *
+ * Returns the matching DEX file and DexClassDef entry if found, otherwise
+ * returns NULL.
+ */
+static DvmDex* searchBootPathForClass(const char* descriptor,
+ const DexClassDef** ppClassDef)
+{
+ const ClassPathEntry* cpe = gDvm.bootClassPath;
+ const DexClassDef* pFoundDef = NULL;
+ DvmDex* pFoundFile = NULL;
+
+ LOGVV("+++ class '%s' not yet loaded, scanning bootclasspath...\n",
+ descriptor);
+
+ while (cpe->kind != kCpeLastEntry) {
+ //LOGV("+++ checking '%s' (%d)\n", cpe->fileName, cpe->kind);
+
+ switch (cpe->kind) {
+ case kCpeDir:
+ LOGW("Directory entries ('%s') not supported in bootclasspath\n",
+ cpe->fileName);
+ break;
+ case kCpeJar:
+ {
+ JarFile* pJarFile = (JarFile*) cpe->ptr;
+ const DexClassDef* pClassDef;
+ DvmDex* pDvmDex;
+
+ pDvmDex = dvmGetJarFileDex(pJarFile);
+ pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+ if (pClassDef != NULL) {
+ /* found */
+ pFoundDef = pClassDef;
+ pFoundFile = pDvmDex;
+ goto found;
+ }
+ }
+ break;
+ case kCpeDex:
+ {
+ RawDexFile* pRawDexFile = (RawDexFile*) cpe->ptr;
+ const DexClassDef* pClassDef;
+ DvmDex* pDvmDex;
+
+ pDvmDex = dvmGetRawDexFileDex(pRawDexFile);
+ pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+ if (pClassDef != NULL) {
+ /* found */
+ pFoundDef = pClassDef;
+ pFoundFile = pDvmDex;
+ goto found;
+ }
+ }
+ break;
+ default:
+ LOGE("Unknown kind %d\n", cpe->kind);
+ assert(false);
+ break;
+ }
+
+ cpe++;
+ }
+
+ /*
+ * Special handling during verification + optimization.
+ *
+ * The DEX optimizer needs to load classes from the DEX file it's working
+ * on. Rather than trying to insert it into the bootstrap class path
+ * or synthesizing a class loader to manage it, we just make it available
+ * here. It logically comes after all existing entries in the bootstrap
+ * class path.
+ */
+ if (gDvm.bootClassPathOptExtra != NULL) {
+ const DexClassDef* pClassDef;
+
+ pClassDef =
+ dexFindClass(gDvm.bootClassPathOptExtra->pDexFile, descriptor);
+ if (pClassDef != NULL) {
+ /* found */
+ pFoundDef = pClassDef;
+ pFoundFile = gDvm.bootClassPathOptExtra;
+ }
+ }
+
+found:
+ *ppClassDef = pFoundDef;
+ return pFoundFile;
+}
+
+/*
+ * Set the "extra" DEX, which becomes a de facto member of the bootstrap
+ * class set.
+ */
+void dvmSetBootPathExtraDex(DvmDex* pDvmDex)
+{
+ gDvm.bootClassPathOptExtra = pDvmDex;
+}
+
+
+/*
+ * Return the #of entries in the bootstrap class path.
+ *
+ * (Used for ClassLoader.getResources().)
+ */
+int dvmGetBootPathSize(void)
+{
+ const ClassPathEntry* cpe = gDvm.bootClassPath;
+
+ while (cpe->kind != kCpeLastEntry)
+ cpe++;
+
+ return cpe - gDvm.bootClassPath;
+}
+
+/*
+ * Find a resource with the specified name in entry N of the boot class path.
+ *
+ * We return a newly-allocated String of one of these forms:
+ * file://path/name
+ * jar:file://path!/name
+ * Where "path" is the bootstrap class path entry and "name" is the string
+ * passed into this method. "path" needs to be an absolute path (starting
+ * with '/'); if it's not we'd need to "absolutify" it as part of forming
+ * the URL string.
+ */
+StringObject* dvmGetBootPathResource(const char* name, int idx)
+{
+ const int kUrlOverhead = 13; // worst case for Jar URL
+ const ClassPathEntry* cpe = gDvm.bootClassPath;
+ StringObject* urlObj = NULL;
+
+ LOGV("+++ searching for resource '%s' in %d(%s)\n",
+ name, idx, cpe[idx].fileName);
+
+ /* we could use direct array index, but I don't entirely trust "idx" */
+ while (idx-- && cpe->kind != kCpeLastEntry)
+ cpe++;
+ if (cpe->kind == kCpeLastEntry) {
+ assert(false);
+ return NULL;
+ }
+
+ char urlBuf[strlen(name) + strlen(cpe->fileName) + kUrlOverhead +1];
+
+ switch (cpe->kind) {
+ case kCpeDir:
+ sprintf(urlBuf, "file://%s/%s", cpe->fileName, name);
+ if (access(urlBuf+7, F_OK) != 0)
+ goto bail;
+ break;
+ case kCpeJar:
+ {
+ JarFile* pJarFile = (JarFile*) cpe->ptr;
+ if (dexZipFindEntry(&pJarFile->archive, name) == NULL)
+ goto bail;
+ sprintf(urlBuf, "jar:file://%s!/%s", cpe->fileName, name);
+ }
+ break;
+ case kCpeDex:
+ LOGV("No resources in DEX files\n");
+ goto bail;
+ default:
+ assert(false);
+ goto bail;
+ }
+
+ LOGV("+++ using URL='%s'\n", urlBuf);
+ urlObj = dvmCreateStringFromCstr(urlBuf, ALLOC_DEFAULT);
+
+bail:
+ return urlObj;
+}
+
+
+/*
+ * ===========================================================================
+ * Class list management
+ * ===========================================================================
+ */
+
+/* search for these criteria in the Class hash table */
+typedef struct ClassMatchCriteria {
+ const char* descriptor;
+ Object* loader;
+} ClassMatchCriteria;
+
+#define kInitLoaderInc 4 /* must be power of 2 */
+
+/*
+ * Determine if "loader" appears in clazz' initiating loader list.
+ *
+ * The class hash table lock must be held when calling here, since
+ * it's also used when updating a class' initiating loader list.
+ *
+ * TODO: switch to some sort of lock-free data structure so we don't have
+ * to grab the lock to do a lookup. Among other things, this would improve
+ * the speed of compareDescriptorClasses().
+ */
+bool dvmLoaderInInitiatingList(const ClassObject* clazz, const Object* loader)
+{
+ /*
+ * The bootstrap class loader can't be just an initiating loader for
+ * anything (it's always the defining loader if the class is visible
+ * to it). We don't put defining loaders in the initiating list.
+ */
+ if (loader == NULL)
+ return false;
+
+ /*
+ * Scan the list for a match. The list is expected to be short.
+ */
+ int i;
+ for (i = clazz->initiatingLoaderCount-1; i >= 0; --i) {
+ if (clazz->initiatingLoaders[i] == loader) {
+ //LOGI("+++ found initiating match %p in %s\n",
+ // loader, clazz->descriptor);
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Add "loader" to clazz's initiating loader set, unless it's the defining
+ * class loader.
+ *
+ * In the common case this will be a short list, so we don't need to do
+ * anything too fancy here.
+ *
+ * This locks gDvm.loadedClasses for synchronization, so don't hold it
+ * when calling here.
+ */
+void dvmAddInitiatingLoader(ClassObject* clazz, Object* loader)
+{
+ if (loader != clazz->classLoader) {
+ assert(loader != NULL);
+
+ LOGVV("Adding %p to '%s' init list\n", loader, clazz->descriptor);
+ dvmHashTableLock(gDvm.loadedClasses);
+
+ /*
+ * Make sure nobody snuck in. The penalty for adding twice is
+ * pretty minor, and probably outweighs the O(n^2) hit for
+ * checking before every add, so we may not want to do this.
+ */
+ if (false && dvmLoaderInInitiatingList(clazz, loader)) {
+ LOGW("WOW: simultaneous add of initiating class loader\n");
+ goto bail_unlock;
+ }
+
+ /*
+ * The list never shrinks, so we just keep a count of the
+ * number of elements in it, and reallocate the buffer when
+ * we run off the end.
+ *
+ * The pointer is initially NULL, so we *do* want to call realloc
+ * when count==0.
+ */
+ if ((clazz->initiatingLoaderCount & (kInitLoaderInc-1)) == 0) {
+ Object** newList;
+
+ newList = (Object**) realloc(clazz->initiatingLoaders,
+ (clazz->initiatingLoaderCount + kInitLoaderInc)
+ * sizeof(Object*));
+ if (newList == NULL) {
+ /* this is mainly a cache, so it's not the EotW */
+ assert(false);
+ goto bail_unlock;
+ }
+ clazz->initiatingLoaders = newList;
+
+ //LOGI("Expanded init list to %d (%s)\n",
+ // clazz->initiatingLoaderCount+kInitLoaderInc,
+ // clazz->descriptor);
+ }
+
+ clazz->initiatingLoaders[clazz->initiatingLoaderCount++] = loader;
+
+bail_unlock:
+ dvmHashTableUnlock(gDvm.loadedClasses);
+ }
+}
+
+/*
+ * (This is a dvmHashTableLookup callback.)
+ *
+ * Entries in the class hash table are stored as { descriptor, d-loader }
+ * tuples. If the hashed class descriptor matches the requested descriptor,
+ * and the hashed defining class loader matches the requested class
+ * loader, we're good. If only the descriptor matches, we check to see if the
+ * loader is in the hashed class' initiating loader list. If so, we
+ * can return "true" immediately and skip some of the loadClass melodrama.
+ *
+ * The caller must lock the hash table before calling here.
+ *
+ * Returns 0 if a matching entry is found, nonzero otherwise.
+ */
+static int hashcmpClassByCrit(const void* vclazz, const void* vcrit)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ const ClassMatchCriteria* pCrit = (const ClassMatchCriteria*) vcrit;
+ bool match;
+
+ match = (strcmp(clazz->descriptor, pCrit->descriptor) == 0 &&
+ (clazz->classLoader == pCrit->loader ||
+ (pCrit->loader != NULL &&
+ dvmLoaderInInitiatingList(clazz, pCrit->loader)) ));
+ //if (match)
+ // LOGI("+++ %s %p matches existing %s %p\n",
+ // pCrit->descriptor, pCrit->loader,
+ // clazz->descriptor, clazz->classLoader);
+ return !match;
+}
+
+/*
+ * Like hashcmpClassByCrit, but passing in a fully-formed ClassObject
+ * instead of a ClassMatchCriteria.
+ */
+static int hashcmpClassByClass(const void* vclazz, const void* vaddclazz)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ const ClassObject* addClazz = (const ClassObject*) vaddclazz;
+ bool match;
+
+ match = (strcmp(clazz->descriptor, addClazz->descriptor) == 0 &&
+ (clazz->classLoader == addClazz->classLoader ||
+ (addClazz->classLoader != NULL &&
+ dvmLoaderInInitiatingList(clazz, addClazz->classLoader)) ));
+ return !match;
+}
+
+/*
+ * Search through the hash table to find an entry with a matching descriptor
+ * and an initiating class loader that matches "loader".
+ *
+ * The table entries are hashed on descriptor only, because they're unique
+ * on *defining* class loader, not *initiating* class loader. This isn't
+ * great, because it guarantees we will have to probe when multiple
+ * class loaders are used.
+ *
+ * Note this does NOT try to load a class; it just finds a class that
+ * has already been loaded.
+ *
+ * If "unprepOkay" is set, this will return classes that have been added
+ * to the hash table but are not yet fully loaded and linked. Otherwise,
+ * such classes are ignored. (The only place that should set "unprepOkay"
+ * is findClassNoInit(), which will wait for the prep to finish.)
+ *
+ * Returns NULL if not found.
+ */
+ClassObject* dvmLookupClass(const char* descriptor, Object* loader,
+ bool unprepOkay)
+{
+ ClassMatchCriteria crit;
+ void* found;
+ u4 hash;
+
+ crit.descriptor = descriptor;
+ crit.loader = loader;
+ hash = dvmComputeUtf8Hash(descriptor);
+
+ LOGVV("threadid=%d: dvmLookupClass searching for '%s' %p\n",
+ dvmThreadSelf()->threadId, descriptor, loader);
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ found = dvmHashTableLookup(gDvm.loadedClasses, hash, &crit,
+ hashcmpClassByCrit, false);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ /*
+ * The class has been added to the hash table but isn't ready for use.
+ * We're going to act like we didn't see it, so that the caller will
+ * go through the full "find class" path, which includes locking the
+ * object and waiting until it's ready. We could do that lock/wait
+ * here, but this is an extremely rare case, and it's simpler to have
+ * the wait-for-class code centralized.
+ */
+ if (found != NULL && !unprepOkay && !dvmIsClassLinked(found)) {
+ LOGV("Ignoring not-yet-ready %s, using slow path\n",
+ ((ClassObject*)found)->descriptor);
+ found = NULL;
+ }
+
+ return (ClassObject*) found;
+}
+
+/*
+ * Add a new class to the hash table.
+ *
+ * The class is considered "new" if it doesn't match on both the class
+ * descriptor and the defining class loader.
+ *
+ * TODO: we should probably have separate hash tables for each
+ * ClassLoader. This could speed up dvmLookupClass and
+ * other common operations. It does imply a VM-visible data structure
+ * for each ClassLoader object with loaded classes, which we don't
+ * have yet.
+ */
+bool dvmAddClassToHash(ClassObject* clazz)
+{
+ void* found;
+ u4 hash;
+
+ hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ found = dvmHashTableLookup(gDvm.loadedClasses, hash, clazz,
+ hashcmpClassByClass, true);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ LOGV("+++ dvmAddClassToHash '%s' %p (isnew=%d) --> %p\n",
+ clazz->descriptor, clazz->classLoader,
+ (found == (void*) clazz), clazz);
+
+ //dvmCheckClassTablePerf();
+
+ /* can happen if two threads load the same class simultaneously */
+ return (found == (void*) clazz);
+}
+
+#if 0
+/*
+ * Compute hash value for a class.
+ */
+u4 hashcalcClass(const void* item)
+{
+ return dvmComputeUtf8Hash(((const ClassObject*) item)->descriptor);
+}
+
+/*
+ * Check the performance of the "loadedClasses" hash table.
+ */
+void dvmCheckClassTablePerf(void)
+{
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashTableProbeCount(gDvm.loadedClasses, hashcalcClass,
+ hashcmpClassByClass);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+#endif
+
+/*
+ * Remove a class object from the hash table.
+ */
+static void removeClassFromHash(ClassObject* clazz)
+{
+ LOGV("+++ removeClassFromHash '%s'\n", clazz->descriptor);
+
+ u4 hash = dvmComputeUtf8Hash(clazz->descriptor);
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ if (!dvmHashTableRemove(gDvm.loadedClasses, hash, clazz))
+ LOGW("Hash table remove failed on class '%s'\n", clazz->descriptor);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+
+/*
+ * ===========================================================================
+ * Class creation
+ * ===========================================================================
+ */
+
+/*
+ * Set clazz->serialNumber to the next available value.
+ *
+ * This usually happens *very* early in class creation, so don't expect
+ * anything else in the class to be ready.
+ */
+void dvmSetClassSerialNumber(ClassObject* clazz)
+{
+ u4 oldValue, newValue;
+
+ assert(clazz->serialNumber == 0);
+
+ do {
+ oldValue = gDvm.classSerialNumber;
+ newValue = oldValue + 1;
+ } while (!ATOMIC_CMP_SWAP(&gDvm.classSerialNumber, oldValue, newValue));
+
+ clazz->serialNumber = (u4) oldValue;
+}
+
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded and initialized if it has not already been.
+ * If necessary, the superclass will be loaded.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClass(const char* descriptor, Object* loader)
+{
+ ClassObject* clazz;
+
+ clazz = dvmFindClassNoInit(descriptor, loader);
+ if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+ /* initialize class */
+ if (!dvmInitClass(clazz)) {
+ /* init failed; leave it in the list, marked as bad */
+ assert(dvmCheckException(dvmThreadSelf()));
+ assert(clazz->status == CLASS_ERROR);
+ return NULL;
+ }
+ }
+
+ return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), using the specified
+ * initiating ClassLoader.
+ *
+ * The class will be loaded if it has not already been, as will its
+ * superclass. It will not be initialized.
+ *
+ * If the class can't be found, returns NULL with an appropriate exception
+ * raised.
+ */
+ClassObject* dvmFindClassNoInit(const char* descriptor,
+ Object* loader)
+{
+ assert(descriptor != NULL);
+ //assert(loader != NULL);
+
+ LOGVV("FindClassNoInit '%s' %p\n", descriptor, loader);
+
+ if (*descriptor == '[') {
+ /*
+ * Array class. Find in table, generate if not found.
+ */
+ return dvmFindArrayClass(descriptor, loader);
+ } else {
+ /*
+ * Regular class. Find in table, load if not found.
+ */
+ if (loader != NULL) {
+ return findClassFromLoaderNoInit(descriptor, loader);
+ } else {
+ return dvmFindSystemClassNoInit(descriptor);
+ }
+ }
+}
+
+/*
+ * Load the named class (by descriptor) from the specified class
+ * loader. This calls out to let the ClassLoader object do its thing.
+ *
+ * Returns with NULL and an exception raised on error.
+ */
+static ClassObject* findClassFromLoaderNoInit(const char* descriptor,
+ Object* loader)
+{
+ //LOGI("##### findClassFromLoaderNoInit (%s,%p)\n",
+ // descriptor, loader);
+
+ Thread* self = dvmThreadSelf();
+ ClassObject* clazz;
+
+ assert(loader != NULL);
+
+ /*
+ * Do we already have it?
+ *
+ * The class loader code does the "is it already loaded" check as
+ * well. However, this call is much faster than calling through
+ * interpreted code. Doing this does mean that in the common case
+ * (365 out of 420 calls booting the sim) we're doing the
+ * lookup-by-descriptor twice. It appears this is still a win, so
+ * I'm keeping it in.
+ */
+ clazz = dvmLookupClass(descriptor, loader, false);
+ if (clazz != NULL) {
+ LOGVV("Already loaded: %s %p\n", descriptor, loader);
+ return clazz;
+ } else {
+ LOGVV("Not already loaded: %s %p\n", descriptor, loader);
+ }
+
+ char* dotName = NULL;
+ StringObject* nameObj = NULL;
+ Object* excep;
+ Method* loadClass;
+
+ /* convert "Landroid/debug/Stuff;" to "android.debug.Stuff" */
+ dotName = dvmDescriptorToDot(descriptor);
+ if (dotName == NULL) {
+ dvmThrowException("Ljava/lang/OutOfMemoryError;", NULL);
+ goto bail;
+ }
+ nameObj = dvmCreateStringFromCstr(dotName, ALLOC_DEFAULT);
+ if (nameObj == NULL) {
+ assert(dvmCheckException(self));
+ goto bail;
+ }
+
+ // TODO: cache the vtable offset
+ loadClass = dvmFindVirtualMethodHierByDescriptor(loader->clazz, "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ if (loadClass == NULL) {
+ LOGW("Couldn't find loadClass in ClassLoader\n");
+ goto bail;
+ }
+
+#ifdef WITH_PROFILER
+ dvmMethodTraceClassPrepBegin();
+#endif
+
+ /*
+ * Invoke loadClass(). This will probably result in a couple of
+ * exceptions being thrown, because the ClassLoader.loadClass()
+ * implementation eventually calls VMClassLoader.loadClass to see if
+ * the bootstrap class loader can find it before doing its own load.
+ */
+ LOGVV("--- Invoking loadClass(%s, %p)\n", dotName, loader);
+ JValue result;
+ dvmCallMethod(self, loadClass, loader, &result, nameObj);
+ clazz = (ClassObject*) result.l;
+
+#ifdef WITH_PROFILER
+ dvmMethodTraceClassPrepEnd();
+#endif
+
+ excep = dvmGetException(self);
+ if (excep != NULL) {
+#if DVM_SHOW_EXCEPTION >= 2
+ LOGD("NOTE: loadClass '%s' %p threw exception %s\n",
+ dotName, loader, excep->clazz->descriptor);
+#endif
+ dvmAddTrackedAlloc(excep, self);
+ dvmClearException(self);
+ dvmThrowChainedExceptionWithClassMessage(
+ "Ljava/lang/NoClassDefFoundError;", descriptor, excep);
+ dvmReleaseTrackedAlloc(excep, self);
+ clazz = NULL;
+ goto bail;
+ } else {
+ assert(clazz != NULL);
+ }
+
+ dvmAddInitiatingLoader(clazz, loader);
+
+ LOGVV("--- Successfully loaded %s %p (thisldr=%p clazz=%p)\n",
+ descriptor, clazz->classLoader, loader, clazz);
+
+bail:
+ dvmReleaseTrackedAlloc((Object*)nameObj, NULL);
+ free(dotName);
+ return clazz;
+}
+
+/*
+ * Load the named class (by descriptor) from the specified DEX file.
+ * Used by class loaders to instantiate a class object from a
+ * VM-managed DEX.
+ */
+ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
+ Object* classLoader)
+{
+ assert(pDvmDex != NULL);
+
+ return findClassNoInit(descriptor, classLoader, pDvmDex);
+}
+
+
+/*
+ * Find the named class (by descriptor), scanning through the
+ * bootclasspath if it hasn't already been loaded.
+ *
+ * "descriptor" looks like "Landroid/debug/Stuff;".
+ *
+ * Uses NULL as the defining class loader.
+ */
+ClassObject* dvmFindSystemClass(const char* descriptor)
+{
+ ClassObject* clazz;
+
+ clazz = dvmFindSystemClassNoInit(descriptor);
+ if (clazz != NULL && clazz->status < CLASS_INITIALIZED) {
+ /* initialize class */
+ if (!dvmInitClass(clazz)) {
+ /* init failed; leave it in the list, marked as bad */
+ assert(dvmCheckException(dvmThreadSelf()));
+ assert(clazz->status == CLASS_ERROR);
+ return NULL;
+ }
+ }
+
+ return clazz;
+}
+
+/*
+ * Find the named class (by descriptor), searching for it in the
+ * bootclasspath.
+ *
+ * On failure, this returns NULL with an exception raised.
+ */
+ClassObject* dvmFindSystemClassNoInit(const char* descriptor)
+{
+ return findClassNoInit(descriptor, NULL, NULL);
+}
+
+/*
+ * Find the named class (by descriptor). If it's not already loaded,
+ * we load it and link it, but don't execute <clinit>. (The VM has
+ * specific limitations on which events can cause initialization.)
+ *
+ * If "pDexFile" is NULL, we will search the bootclasspath for an entry.
+ *
+ * On failure, this returns NULL with an exception raised.
+ *
+ * TODO: we need to return an indication of whether we loaded the class or
+ * used an existing definition. If somebody deliberately tries to load a
+ * class twice in the same class loader, they should get a LinkageError,
+ * but inadvertent simultaneous class references should "just work".
+ */
+static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
+ DvmDex* pDvmDex)
+{
+ Thread* self = dvmThreadSelf();
+ ClassObject* clazz;
+#ifdef WITH_PROFILER
+ bool profilerNotified = false;
+#endif
+
+ if (loader != NULL) {
+ LOGVV("#### findClassNoInit(%s,%p,%p)\n", descriptor, loader,
+ pDvmDex->pDexFile);
+ }
+
+ /*
+ * We don't expect an exception to be raised at this point. The
+ * exception handling code is good about managing this. This *can*
+ * happen if a JNI lookup fails and the JNI code doesn't do any
+ * error checking before doing another class lookup, so we may just
+ * want to clear this and restore it on exit. If we don't, some kinds
+ * of failures can't be detected without rearranging other stuff.
+ *
+ * Most often when we hit this situation it means that something is
+ * broken in the VM or in JNI code, so I'm keeping it in place (and
+ * making it an informative abort rather than an assert).
+ */
+ if (dvmCheckException(self)) {
+ LOGE("Class lookup %s attemped while exception %s pending\n",
+ descriptor, dvmGetException(self)->clazz->descriptor);
+ dvmDumpAllThreads(false);
+ dvmAbort();
+ }
+
+ clazz = dvmLookupClass(descriptor, loader, true);
+ if (clazz == NULL) {
+ const DexClassDef* pClassDef;
+
+#ifdef WITH_PROFILER
+ dvmMethodTraceClassPrepBegin();
+ profilerNotified = true;
+#endif
+
+#if LOG_CLASS_LOADING
+ u8 startTime = dvmGetThreadCpuTimeNsec();
+#endif
+
+ if (pDvmDex == NULL) {
+ assert(loader == NULL); /* shouldn't be here otherwise */
+ pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
+ } else {
+ pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
+ }
+
+ if (pDvmDex == NULL || pClassDef == NULL) {
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/NoClassDefFoundError;", descriptor);
+ goto bail;
+ }
+
+ /* found a match, try to load it */
+ clazz = loadClassFromDex(pDvmDex, pClassDef, loader);
+ if (dvmCheckException(self)) {
+ /* class was found but had issues */
+ dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+ goto bail;
+ }
+
+ /*
+ * Lock the class while we link it so other threads must wait for us
+ * to finish. Set the "initThreadId" so we can identify recursive
+ * invocation.
+ */
+ dvmLockObject(self, (Object*) clazz);
+ clazz->initThreadId = self->threadId;
+
+ /*
+ * Add to hash table so lookups succeed.
+ *
+ * [Are circular references possible when linking a class?]
+ */
+ assert(clazz->classLoader == loader);
+ if (!dvmAddClassToHash(clazz)) {
+ /*
+ * Another thread must have loaded the class after we
+ * started but before we finished. Discard what we've
+ * done and leave some hints for the GC.
+ *
+ * (Yes, this happens.)
+ */
+ //LOGW("WOW: somebody loaded %s simultaneously\n", descriptor);
+ clazz->initThreadId = 0;
+ dvmUnlockObject(self, (Object*) clazz);
+
+ /* Let the GC free the class.
+ */
+ assert(clazz->obj.clazz == gDvm.unlinkedJavaLangClass);
+ dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+ /* Grab the winning class.
+ */
+ clazz = dvmLookupClass(descriptor, loader, true);
+ assert(clazz != NULL);
+ goto got_class;
+ }
+ dvmReleaseTrackedAlloc((Object*) clazz, NULL);
+
+#if LOG_CLASS_LOADING
+ logClassLoadWithTime('>', clazz, startTime);
+#endif
+ /*
+ * Prepare and resolve.
+ */
+ if (!dvmLinkClass(clazz, false)) {
+ assert(dvmCheckException(self));
+
+ /* Make note of the error and clean up the class.
+ */
+ removeClassFromHash(clazz);
+ clazz->status = CLASS_ERROR;
+ dvmFreeClassInnards(clazz);
+
+ /* Let any waiters know.
+ */
+ clazz->initThreadId = 0;
+ dvmObjectNotifyAll(self, (Object*) clazz);
+ dvmUnlockObject(self, (Object*) clazz);
+
+#if LOG_CLASS_LOADING
+ LOG(LOG_INFO, "DVMLINK FAILED FOR CLASS ", "%s in %s\n",
+ clazz->descriptor, get_process_name());
+
+ /*
+ * TODO: It would probably be better to use a new type code here (instead of '<') to
+ * indicate the failure. This change would require a matching change in the parser
+ * and analysis code in frameworks/base/tools/preload.
+ */
+ logClassLoad('<', clazz);
+#endif
+ clazz = NULL;
+ if (gDvm.optimizing) {
+ /* happens with "external" libs */
+ LOGV("Link of class '%s' failed\n", descriptor);
+ } else {
+ LOGW("Link of class '%s' failed\n", descriptor);
+ }
+ goto bail;
+ }
+ dvmObjectNotifyAll(self, (Object*) clazz);
+ dvmUnlockObject(self, (Object*) clazz);
+
+ /*
+ * Add class stats to global counters.
+ *
+ * TODO: these should probably be atomic ops.
+ */
+ gDvm.numLoadedClasses++;
+ gDvm.numDeclaredMethods +=
+ clazz->virtualMethodCount + clazz->directMethodCount;
+ gDvm.numDeclaredInstFields += clazz->ifieldCount;
+ gDvm.numDeclaredStaticFields += clazz->sfieldCount;
+
+ /*
+ * Cache pointers to basic classes. We want to use these in
+ * various places, and it's easiest to initialize them on first
+ * use rather than trying to force them to initialize (startup
+ * ordering makes it weird).
+ */
+ if (gDvm.classJavaLangObject == NULL &&
+ strcmp(descriptor, "Ljava/lang/Object;") == 0)
+ {
+ /* It should be impossible to get here with anything
+ * but the bootclasspath loader.
+ */
+ assert(loader == NULL);
+ gDvm.classJavaLangObject = clazz;
+ }
+
+#if LOG_CLASS_LOADING
+ logClassLoad('<', clazz);
+#endif
+
+ } else {
+got_class:
+ if (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+ /*
+ * We can race with other threads for class linking. We should
+ * never get here recursively; doing so indicates that two
+ * classes have circular dependencies.
+ *
+ * One exception: we force discovery of java.lang.Class in
+ * dvmLinkClass(), and Class has Object as its superclass. So
+ * if the first thing we ever load is Object, we will init
+ * Object->Class->Object. The easiest way to avoid this is to
+ * ensure that Object is never the first thing we look up, so
+ * we get Foo->Class->Object instead.
+ */
+ dvmLockObject(self, (Object*) clazz);
+ if (!dvmIsClassLinked(clazz) &&
+ clazz->initThreadId == self->threadId)
+ {
+ LOGW("Recursive link on class %s\n", clazz->descriptor);
+ dvmUnlockObject(self, (Object*) clazz);
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/ClassCircularityError;", clazz->descriptor);
+ clazz = NULL;
+ goto bail;
+ }
+ //LOGI("WAITING for '%s' (owner=%d)\n",
+ // clazz->descriptor, clazz->initThreadId);
+ while (!dvmIsClassLinked(clazz) && clazz->status != CLASS_ERROR) {
+ dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+ }
+ dvmUnlockObject(self, (Object*) clazz);
+ }
+ if (clazz->status == CLASS_ERROR) {
+ /*
+ * Somebody else tried to load this and failed. We need to raise
+ * an exception and report failure.
+ */
+ throwEarlierClassFailure(clazz);
+ clazz = NULL;
+ goto bail;
+ }
+ }
+
+ /* check some invariants */
+ assert(dvmIsClassLinked(clazz));
+ assert(gDvm.classJavaLangClass != NULL);
+ assert(clazz->obj.clazz == gDvm.classJavaLangClass);
+ if (clazz != gDvm.classJavaLangObject) {
+ if (clazz->super == NULL) {
+ LOGE("Non-Object has no superclass (gDvm.classJavaLangObject=%p)\n",
+ gDvm.classJavaLangObject);
+ dvmAbort();
+ }
+ }
+ if (!dvmIsInterfaceClass(clazz)) {
+ //LOGI("class=%s vtableCount=%d, virtualMeth=%d\n",
+ // clazz->descriptor, clazz->vtableCount,
+ // clazz->virtualMethodCount);
+ assert(clazz->vtableCount >= clazz->virtualMethodCount);
+ }
+
+ /*
+ * Normally class objects are initialized before we instantiate them,
+ * but we can't do that with java.lang.Class (chicken, meet egg). We
+ * do it explicitly here.
+ *
+ * The verifier could call here to find Class while verifying Class,
+ * so we need to check for CLASS_VERIFYING as well as !initialized.
+ */
+ if (clazz == gDvm.classJavaLangClass && !dvmIsClassInitialized(clazz) &&
+ !(clazz->status == CLASS_VERIFYING))
+ {
+ LOGV("+++ explicitly initializing %s\n", clazz->descriptor);
+ dvmInitClass(clazz);
+ }
+
+bail:
+#ifdef WITH_PROFILER
+ if (profilerNotified)
+ dvmMethodTraceClassPrepEnd();
+#endif
+ assert(clazz != NULL || dvmCheckException(self));
+ return clazz;
+}
+
+/*
+ * Helper for loadClassFromDex, which takes a DexClassDataHeader and
+ * encoded data pointer in addition to the other arguments.
+ */
+static ClassObject* loadClassFromDex0(DvmDex* pDvmDex,
+ const DexClassDef* pClassDef, const DexClassDataHeader* pHeader,
+ const u1* pEncodedData, Object* classLoader)
+{
+ ClassObject* newClass = NULL;
+ const DexFile* pDexFile;
+ const char* descriptor;
+ int i;
+
+ pDexFile = pDvmDex->pDexFile;
+ descriptor = dexGetClassDescriptor(pDexFile, pClassDef);
+
+ /*
+ * Make sure the aren't any "bonus" flags set, since we use them for
+ * runtime state.
+ */
+ if ((pClassDef->accessFlags & ~EXPECTED_FILE_FLAGS) != 0) {
+ LOGW("Invalid file flags in class %s: %04x\n",
+ descriptor, pClassDef->accessFlags);
+ return NULL;
+ }
+
+ /*
+ * Allocate storage for the class object on the GC heap, so that other
+ * objects can have references to it. We bypass the usual mechanism
+ * (allocObject), because we don't have all the bits and pieces yet.
+ *
+ * Note that we assume that java.lang.Class does not override
+ * finalize().
+ */
+ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT);
+ if (newClass == NULL)
+ return NULL;
+
+ /* Until the class is loaded and linked, use a placeholder
+ * obj->clazz value as a hint to the GC. We don't want
+ * the GC trying to scan the object while it's full of Idx
+ * values. Also, the real java.lang.Class may not exist
+ * yet.
+ */
+ DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass);
+
+ dvmSetClassSerialNumber(newClass);
+ newClass->descriptor = descriptor;
+ assert(newClass->descriptorAlloc == NULL);
+ newClass->accessFlags = pClassDef->accessFlags;
+ newClass->classLoader = classLoader;
+ newClass->pDvmDex = pDvmDex;
+ newClass->primitiveType = PRIM_NOT;
+
+ /*
+ * Stuff the superclass index into the object pointer field. The linker
+ * pulls it out and replaces it with a resolved ClassObject pointer.
+ * I'm doing it this way (rather than having a dedicated superclassIdx
+ * field) to save a few bytes of overhead per class.
+ *
+ * newClass->super is not traversed or freed by dvmFreeClassInnards, so
+ * this is safe.
+ */
+ assert(sizeof(u4) == sizeof(ClassObject*));
+ newClass->super = (ClassObject*) pClassDef->superclassIdx;
+
+ /*
+ * Stuff class reference indices into the pointer fields.
+ *
+ * The elements of newClass->interfaces are not traversed or freed by
+ * dvmFreeClassInnards, so this is GC-safe.
+ */
+ const DexTypeList* pInterfacesList;
+ pInterfacesList = dexGetInterfacesList(pDexFile, pClassDef);
+ if (pInterfacesList != NULL) {
+ newClass->interfaceCount = pInterfacesList->size;
+ newClass->interfaces = (ClassObject**) dvmLinearAlloc(classLoader,
+ newClass->interfaceCount * sizeof(ClassObject*));
+
+ for (i = 0; i < newClass->interfaceCount; i++) {
+ const DexTypeItem* pType = dexGetTypeItem(pInterfacesList, i);
+ newClass->interfaces[i] = (ClassObject*)(u4) pType->typeIdx;
+ }
+ dvmLinearReadOnly(classLoader, newClass->interfaces);
+ }
+
+ /* load field definitions */
+
+ /*
+ * TODO: consider over-allocating the class object and appending the
+ * static field info onto the end. It's fixed-size and known at alloc
+ * time. This would save a couple of native heap allocations, but it
+ * would also make heap compaction more difficult because we pass Field
+ * pointers around internally.
+ */
+
+ if (pHeader->staticFieldsSize != 0) {
+ /* static fields stay on system heap; field data isn't "write once" */
+ int count = (int) pHeader->staticFieldsSize;
+ u4 lastIndex = 0;
+ DexField field;
+
+ newClass->sfieldCount = count;
+ newClass->sfields =
+ (StaticField*) calloc(count, sizeof(StaticField));
+ for (i = 0; i < count; i++) {
+ dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+ loadSFieldFromDex(newClass, &field, &newClass->sfields[i]);
+ }
+ }
+
+ if (pHeader->instanceFieldsSize != 0) {
+ int count = (int) pHeader->instanceFieldsSize;
+ u4 lastIndex = 0;
+ DexField field;
+
+ newClass->ifieldCount = count;
+ newClass->ifields = (InstField*) dvmLinearAlloc(classLoader,
+ count * sizeof(InstField));
+ for (i = 0; i < count; i++) {
+ dexReadClassDataField(&pEncodedData, &field, &lastIndex);
+ loadIFieldFromDex(newClass, &field, &newClass->ifields[i]);
+ }
+ dvmLinearReadOnly(classLoader, newClass->ifields);
+ }
+
+ /* load method definitions */
+
+ if (pHeader->directMethodsSize != 0) {
+ int count = (int) pHeader->directMethodsSize;
+ u4 lastIndex = 0;
+ DexMethod method;
+
+ newClass->directMethodCount = count;
+ newClass->directMethods = (Method*) dvmLinearAlloc(classLoader,
+ count * sizeof(Method));
+ for (i = 0; i < count; i++) {
+ dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+ loadMethodFromDex(newClass, &method, &newClass->directMethods[i]);
+ }
+ dvmLinearReadOnly(classLoader, newClass->directMethods);
+ }
+
+ if (pHeader->virtualMethodsSize != 0) {
+ int count = (int) pHeader->virtualMethodsSize;
+ u4 lastIndex = 0;
+ DexMethod method;
+
+ newClass->virtualMethodCount = count;
+ newClass->virtualMethods = (Method*) dvmLinearAlloc(classLoader,
+ count * sizeof(Method));
+ for (i = 0; i < count; i++) {
+ dexReadClassDataMethod(&pEncodedData, &method, &lastIndex);
+ loadMethodFromDex(newClass, &method, &newClass->virtualMethods[i]);
+ }
+ dvmLinearReadOnly(classLoader, newClass->virtualMethods);
+ }
+
+ newClass->sourceFile = dexGetSourceFile(pDexFile, pClassDef);
+ newClass->status = CLASS_LOADED;
+
+ /* caller must call dvmReleaseTrackedAlloc */
+ return newClass;
+}
+
+/*
+ * Try to load the indicated class from the specified DEX file.
+ *
+ * This is effectively loadClass()+defineClass() for a DexClassDef. The
+ * loading was largely done when we crunched through the DEX.
+ *
+ * Returns NULL on failure. If we locate the class but encounter an error
+ * while processing it, an appropriate exception is thrown.
+ */
+static ClassObject* loadClassFromDex(DvmDex* pDvmDex,
+ const DexClassDef* pClassDef, Object* classLoader)
+{
+ ClassObject* result;
+ DexClassDataHeader header;
+ const u1* pEncodedData;
+ const DexFile* pDexFile;
+
+ assert((pDvmDex != NULL) && (pClassDef != NULL));
+ pDexFile = pDvmDex->pDexFile;
+
+ if (gDvm.verboseClass) {
+ LOGV("CLASS: loading '%s'...\n",
+ dexGetClassDescriptor(pDexFile, pClassDef));
+ }
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+
+ if (pEncodedData != NULL) {
+ dexReadClassDataHeader(&pEncodedData, &header);
+ } else {
+ // Provide an all-zeroes header for the rest of the loading.
+ memset(&header, 0, sizeof(header));
+ }
+
+ result = loadClassFromDex0(pDvmDex, pClassDef, &header, pEncodedData,
+ classLoader);
+
+ if (gDvm.verboseClass && (result != NULL)) {
+ LOGI("[Loaded %s from DEX %p (cl=%p)]\n",
+ result->descriptor, pDvmDex, classLoader);
+ }
+
+ return result;
+}
+
+/*
+ * Free anything in a ClassObject that was allocated on the system heap.
+ *
+ * The ClassObject itself is allocated on the GC heap, so we leave it for
+ * the garbage collector.
+ *
+ * NOTE: this may be called with a partially-constructed object.
+ * NOTE: there is no particular ordering imposed, so don't go poking at
+ * superclasses.
+ */
+void dvmFreeClassInnards(ClassObject* clazz)
+{
+ void *tp;
+ int i;
+
+ if (clazz == NULL)
+ return;
+
+ assert(clazz->obj.clazz == gDvm.classJavaLangClass ||
+ clazz->obj.clazz == gDvm.unlinkedJavaLangClass);
+
+ /* Guarantee that dvmFreeClassInnards can be called on a given
+ * class multiple times by clearing things out as we free them.
+ * We don't make any attempt at real atomicity here; higher
+ * levels need to make sure that no two threads can free the
+ * same ClassObject at the same time.
+ *
+ * TODO: maybe just make it so the GC will never free the
+ * innards of an already-freed class.
+ *
+ * TODO: this #define isn't MT-safe -- the compiler could rearrange it.
+ */
+#define NULL_AND_FREE(p) \
+ do { \
+ if ((p) != NULL) { \
+ tp = (p); \
+ (p) = NULL; \
+ free(tp); \
+ } \
+ } while (0)
+#define NULL_AND_LINEAR_FREE(p) \
+ do { \
+ if ((p) != NULL) { \
+ tp = (p); \
+ (p) = NULL; \
+ dvmLinearFree(clazz->classLoader, tp); \
+ } \
+ } while (0)
+
+ /* arrays just point at Object's vtable; don't free vtable in this case.
+ * dvmIsArrayClass() checks clazz->descriptor, so we have to do this check
+ * before freeing the name.
+ */
+ clazz->vtableCount = -1;
+ if (dvmIsArrayClass(clazz)) {
+ clazz->vtable = NULL;
+ } else {
+ NULL_AND_LINEAR_FREE(clazz->vtable);
+ }
+
+ clazz->descriptor = NULL;
+ NULL_AND_FREE(clazz->descriptorAlloc);
+
+ if (clazz->directMethods != NULL) {
+ Method *directMethods = clazz->directMethods;
+ int directMethodCount = clazz->directMethodCount;
+ clazz->directMethods = NULL;
+ clazz->directMethodCount = -1;
+ for (i = 0; i < directMethodCount; i++) {
+ freeMethodInnards(&directMethods[i]);
+ }
+ dvmLinearFree(clazz->classLoader, directMethods);
+ }
+ if (clazz->virtualMethods != NULL) {
+ Method *virtualMethods = clazz->virtualMethods;
+ int virtualMethodCount = clazz->virtualMethodCount;
+ clazz->virtualMethodCount = -1;
+ clazz->virtualMethods = NULL;
+ for (i = 0; i < virtualMethodCount; i++) {
+ freeMethodInnards(&virtualMethods[i]);
+ }
+ dvmLinearFree(clazz->classLoader, virtualMethods);
+ }
+
+ clazz->initiatingLoaderCount = -1;
+ NULL_AND_FREE(clazz->initiatingLoaders);
+
+ clazz->interfaceCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->interfaces);
+
+ clazz->iftableCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->iftable);
+
+ clazz->ifviPoolCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->ifviPool);
+
+ clazz->sfieldCount = -1;
+ NULL_AND_FREE(clazz->sfields);
+
+ clazz->ifieldCount = -1;
+ NULL_AND_LINEAR_FREE(clazz->ifields);
+
+#undef NULL_AND_FREE
+#undef NULL_AND_LINEAR_FREE
+}
+
+/*
+ * Free anything in a Method that was allocated on the system heap.
+ */
+static void freeMethodInnards(Method* meth)
+{
+#if 0
+ free(meth->exceptions);
+ free(meth->lines);
+ free(meth->locals);
+#else
+ // TODO: call dvmFreeRegisterMap() if meth->registerMap was allocated
+ // on the system heap
+ UNUSED_PARAMETER(meth);
+#endif
+}
+
+/*
+ * Clone a Method, making new copies of anything that will be freed up
+ * by freeMethodInnards().
+ */
+static void cloneMethod(Method* dst, const Method* src)
+{
+ memcpy(dst, src, sizeof(Method));
+#if 0
+ /* for current usage, these are never set, so no need to implement copy */
+ assert(dst->exceptions == NULL);
+ assert(dst->lines == NULL);
+ assert(dst->locals == NULL);
+#endif
+}
+
+/*
+ * Pull the interesting pieces out of a DexMethod.
+ *
+ * The DEX file isn't going anywhere, so we don't need to make copies of
+ * the code area.
+ */
+static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod,
+ Method* meth)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexMethodId* pMethodId;
+ const DexCode* pDexCode;
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+
+ meth->name = dexStringById(pDexFile, pMethodId->nameIdx);
+ dexProtoSetFromMethodId(&meth->prototype, pDexFile, pMethodId);
+ meth->shorty = dexProtoGetShorty(&meth->prototype);
+ meth->accessFlags = pDexMethod->accessFlags;
+ meth->clazz = clazz;
+ meth->jniArgInfo = 0;
+
+ if (dvmCompareNameDescriptorAndMethod("finalize", "()V", meth) == 0) {
+ SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+ }
+
+ pDexCode = dexGetCode(pDexFile, pDexMethod);
+ if (pDexCode != NULL) {
+ /* integer constants, copy over for faster access */
+ meth->registersSize = pDexCode->registersSize;
+ meth->insSize = pDexCode->insSize;
+ meth->outsSize = pDexCode->outsSize;
+
+ /* pointer to code area */
+ meth->insns = pDexCode->insns;
+ } else {
+ /*
+ * We don't have a DexCode block, but we still want to know how
+ * much space is needed for the arguments (so we don't have to
+ * compute it later). We also take this opportunity to compute
+ * JNI argument info.
+ *
+ * We do this for abstract methods as well, because we want to
+ * be able to substitute our exception-throwing "stub" in.
+ */
+ int argsSize = dvmComputeMethodArgsSize(meth);
+ if (!dvmIsStaticMethod(meth))
+ argsSize++;
+ meth->registersSize = meth->insSize = argsSize;
+ assert(meth->outsSize == 0);
+ assert(meth->insns == NULL);
+
+ if (dvmIsNativeMethod(meth)) {
+ meth->nativeFunc = dvmResolveNativeMethod;
+ meth->jniArgInfo = computeJniArgInfo(&meth->prototype);
+ }
+ }
+}
+
+/*
+ * jniArgInfo (32-bit int) layout:
+ * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH
+ *
+ * S - if set, do things the hard way (scan the signature)
+ * R - return-type enumeration
+ * H - target-specific hints
+ *
+ * This info is used at invocation time by dvmPlatformInvoke. In most
+ * cases, the target-specific hints allow dvmPlatformInvoke to avoid
+ * having to fully parse the signature.
+ *
+ * The return-type bits are always set, even if target-specific hint bits
+ * are unavailable.
+ */
+static int computeJniArgInfo(const DexProto* proto)
+{
+ const char* sig = dexProtoGetShorty(proto);
+ int returnType, padFlags, jniArgInfo;
+ char sigByte;
+ int stackOffset, padMask;
+ u4 hints;
+
+ /* The first shorty character is the return type. */
+ switch (*(sig++)) {
+ case 'V':
+ returnType = DALVIK_JNI_RETURN_VOID;
+ break;
+ case 'F':
+ returnType = DALVIK_JNI_RETURN_FLOAT;
+ break;
+ case 'D':
+ returnType = DALVIK_JNI_RETURN_DOUBLE;
+ break;
+ case 'J':
+ returnType = DALVIK_JNI_RETURN_S8;
+ break;
+ default:
+ returnType = DALVIK_JNI_RETURN_S4;
+ break;
+ }
+
+ jniArgInfo = returnType << DALVIK_JNI_RETURN_SHIFT;
+
+ hints = dvmPlatformInvokeHints(proto);
+
+ if (hints & DALVIK_JNI_NO_ARG_INFO) {
+ jniArgInfo |= DALVIK_JNI_NO_ARG_INFO;
+ } else {
+ assert((hints & DALVIK_JNI_RETURN_MASK) == 0);
+ jniArgInfo |= hints;
+ }
+
+ return jniArgInfo;
+}
+
+/*
+ * Load information about a static field.
+ *
+ * This also "prepares" static fields by initializing them
+ * to their "standard default values".
+ */
+static void loadSFieldFromDex(ClassObject* clazz,
+ const DexField* pDexSField, StaticField* sfield)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexFieldId* pFieldId;
+
+ pFieldId = dexGetFieldId(pDexFile, pDexSField->fieldIdx);
+
+ sfield->field.clazz = clazz;
+ sfield->field.name = dexStringById(pDexFile, pFieldId->nameIdx);
+ sfield->field.signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ sfield->field.accessFlags = pDexSField->accessFlags;
+
+ /* Static object field values are set to "standard default values"
+ * (null or 0) until the class is initialized. We delay loading
+ * constant values from the class until that time.
+ */
+ //sfield->value.j = 0;
+ assert(sfield->value.j == 0LL); // cleared earlier with calloc
+
+#ifdef PROFILE_FIELD_ACCESS
+ sfield->field.gets = sfield->field.puts = 0;
+#endif
+}
+
+/*
+ * Load information about an instance field.
+ */
+static void loadIFieldFromDex(ClassObject* clazz,
+ const DexField* pDexIField, InstField* ifield)
+{
+ DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+ const DexFieldId* pFieldId;
+
+ pFieldId = dexGetFieldId(pDexFile, pDexIField->fieldIdx);
+
+ ifield->field.clazz = clazz;
+ ifield->field.name = dexStringById(pDexFile, pFieldId->nameIdx);
+ ifield->field.signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ ifield->field.accessFlags = pDexIField->accessFlags;
+#ifndef NDEBUG
+ assert(ifield->byteOffset == 0); // cleared earlier with calloc
+ ifield->byteOffset = -1; // make it obvious if we fail to set later
+#endif
+
+#ifdef PROFILE_FIELD_ACCESS
+ ifield->field.gets = ifield->field.puts = 0;
+#endif
+}
+
+/*
+ * Cache java.lang.ref.Reference fields and methods.
+ */
+static bool precacheReferenceOffsets(ClassObject *clazz)
+{
+ Method *meth;
+ int i;
+
+ /* We trick the GC object scanner by not counting
+ * java.lang.ref.Reference.referent as an object
+ * field. It will get explicitly scanned as part
+ * of the reference-walking process.
+ *
+ * Find the object field named "referent" and put it
+ * just after the list of object reference fields.
+ */
+ dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+ for (i = 0; i < clazz->ifieldRefCount; i++) {
+ InstField *pField = &clazz->ifields[i];
+ if (strcmp(pField->field.name, "referent") == 0) {
+ int targetIndex;
+
+ /* Swap this field with the last object field.
+ */
+ targetIndex = clazz->ifieldRefCount - 1;
+ if (i != targetIndex) {
+ InstField *swapField = &clazz->ifields[targetIndex];
+ InstField tmpField;
+ int tmpByteOffset;
+
+ /* It's not currently strictly necessary
+ * for the fields to be in byteOffset order,
+ * but it's more predictable that way.
+ */
+ tmpByteOffset = swapField->byteOffset;
+ swapField->byteOffset = pField->byteOffset;
+ pField->byteOffset = tmpByteOffset;
+
+ tmpField = *swapField;
+ *swapField = *pField;
+ *pField = tmpField;
+ }
+
+ /* One fewer object field (wink wink).
+ */
+ clazz->ifieldRefCount--;
+ i--; /* don't trip "didn't find it" test if field was last */
+ break;
+ }
+ }
+ dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+ if (i == clazz->ifieldRefCount) {
+ LOGE("Unable to reorder 'referent' in %s\n", clazz->descriptor);
+ return false;
+ }
+
+ /* Cache pretty much everything about Reference so that
+ * we don't need to call interpreted code when clearing/enqueueing
+ * references. This is fragile, so we'll be paranoid.
+ */
+ gDvm.classJavaLangRefReference = clazz;
+
+ gDvm.offJavaLangRefReference_referent =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "referent", "Ljava/lang/Object;");
+ assert(gDvm.offJavaLangRefReference_referent >= 0);
+
+ gDvm.offJavaLangRefReference_queue =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "queue", "Ljava/lang/ref/ReferenceQueue;");
+ assert(gDvm.offJavaLangRefReference_queue >= 0);
+
+ gDvm.offJavaLangRefReference_queueNext =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "queueNext", "Ljava/lang/ref/Reference;");
+ assert(gDvm.offJavaLangRefReference_queueNext >= 0);
+
+ gDvm.offJavaLangRefReference_vmData =
+ dvmFindFieldOffset(gDvm.classJavaLangRefReference,
+ "vmData", "I");
+ assert(gDvm.offJavaLangRefReference_vmData >= 0);
+
+#if FANCY_REFERENCE_SUBCLASS
+ meth = dvmFindVirtualMethodByDescriptor(clazz, "clear", "()V");
+ assert(meth != NULL);
+ gDvm.voffJavaLangRefReference_clear = meth->methodIndex;
+
+ meth = dvmFindVirtualMethodByDescriptor(clazz, "enqueue", "()Z");
+ assert(meth != NULL);
+ gDvm.voffJavaLangRefReference_enqueue = meth->methodIndex;
+#else
+ /* enqueueInternal() is private and thus a direct method. */
+ meth = dvmFindDirectMethodByDescriptor(clazz, "enqueueInternal", "()Z");
+ assert(meth != NULL);
+ gDvm.methJavaLangRefReference_enqueueInternal = meth;
+#endif
+
+ return true;
+}
+
+
+/*
+ * Link (prepare and resolve). Verification is deferred until later.
+ *
+ * This converts symbolic references into pointers. It's independent of
+ * the source file format.
+ *
+ * If "classesResolved" is false, we assume that superclassIdx and
+ * interfaces[] are holding class reference indices rather than pointers.
+ * The class references will be resolved during link. (This is done when
+ * loading from DEX to avoid having to create additional storage to pass
+ * the indices around.)
+ *
+ * Returns "false" with an exception pending on failure.
+ */
+bool dvmLinkClass(ClassObject* clazz, bool classesResolved)
+{
+ u4 superclassIdx = 0;
+ bool okay = false;
+ bool resolve_okay;
+ int numInterfacesResolved = 0;
+ int i;
+
+ if (gDvm.verboseClass)
+ LOGV("CLASS: linking '%s'...\n", clazz->descriptor);
+
+ /* "Resolve" the class.
+ *
+ * At this point, clazz's reference fields contain Dex
+ * file indices instead of direct object references.
+ * We need to translate those indices into real references,
+ * while making sure that the GC doesn't sweep any of
+ * the referenced objects.
+ *
+ * The GC will avoid scanning this object as long as
+ * clazz->obj.clazz is gDvm.unlinkedJavaLangClass.
+ * Once clazz is ready, we'll replace clazz->obj.clazz
+ * with gDvm.classJavaLangClass to let the GC know
+ * to look at it.
+ */
+ assert(clazz->obj.clazz == gDvm.unlinkedJavaLangClass);
+
+ /* It's important that we take care of java.lang.Class
+ * first. If we were to do this after looking up the
+ * superclass (below), Class wouldn't be ready when
+ * java.lang.Object needed it.
+ *
+ * Note that we don't set clazz->obj.clazz yet.
+ */
+ if (gDvm.classJavaLangClass == NULL) {
+ if (clazz->classLoader == NULL &&
+ strcmp(clazz->descriptor, "Ljava/lang/Class;") == 0)
+ {
+ gDvm.classJavaLangClass = clazz;
+ } else {
+ gDvm.classJavaLangClass =
+ dvmFindSystemClassNoInit("Ljava/lang/Class;");
+ if (gDvm.classJavaLangClass == NULL) {
+ /* should have thrown one */
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto bail;
+ }
+ }
+ }
+ assert(gDvm.classJavaLangClass != NULL);
+
+ /*
+ * Resolve all Dex indices so we can hand the ClassObject
+ * over to the GC. If we fail at any point, we need to remove
+ * any tracked references to avoid leaking memory.
+ */
+
+ /*
+ * All classes have a direct superclass, except for java/lang/Object.
+ */
+ if (!classesResolved) {
+ superclassIdx = (u4) clazz->super; /* unpack temp store */
+ clazz->super = NULL;
+ }
+ if (strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0) {
+ assert(!classesResolved);
+ if (superclassIdx != kDexNoIndex) {
+ /* TODO: is this invariant true for all java/lang/Objects,
+ * regardless of the class loader? For now, assume it is.
+ */
+ dvmThrowException("Ljava/lang/ClassFormatError;",
+ "java.lang.Object has a superclass");
+ goto bail;
+ }
+
+ /* Don't finalize objects whose classes use the
+ * default (empty) Object.finalize().
+ */
+ CLEAR_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+ } else {
+ if (!classesResolved) {
+ if (superclassIdx == kDexNoIndex) {
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "no superclass defined");
+ goto bail;
+ }
+ clazz->super = dvmResolveClass(clazz, superclassIdx, false);
+ if (clazz->super == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ if (gDvm.optimizing) {
+ /* happens with "external" libs */
+ LOGV("Unable to resolve superclass of %s (%d)\n",
+ clazz->descriptor, superclassIdx);
+ } else {
+ LOGW("Unable to resolve superclass of %s (%d)\n",
+ clazz->descriptor, superclassIdx);
+ }
+ goto bail;
+ }
+ }
+ /* verify */
+ if (dvmIsFinalClass(clazz->super)) {
+ LOGW("Superclass of '%s' is final '%s'\n",
+ clazz->descriptor, clazz->super->descriptor);
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+ "superclass is final");
+ goto bail;
+ } else if (dvmIsInterfaceClass(clazz->super)) {
+ LOGW("Superclass of '%s' is interface '%s'\n",
+ clazz->descriptor, clazz->super->descriptor);
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
+ "superclass is an interface");
+ goto bail;
+ } else if (!dvmCheckClassAccess(clazz, clazz->super)) {
+ LOGW("Superclass of '%s' (%s) is not accessible\n",
+ clazz->descriptor, clazz->super->descriptor);
+ dvmThrowException("Ljava/lang/IllegalAccessError;",
+ "superclass not accessible");
+ goto bail;
+ }
+
+ /* Don't let the GC reclaim the superclass.
+ * TODO: shouldn't be needed; remove when things stabilize
+ */
+ dvmAddTrackedAlloc((Object *)clazz->super, NULL);
+
+ /* Inherit finalizability from the superclass. If this
+ * class also overrides finalize(), its CLASS_ISFINALIZABLE
+ * bit will already be set.
+ */
+ if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISFINALIZABLE)) {
+ SET_CLASS_FLAG(clazz, CLASS_ISFINALIZABLE);
+ }
+
+ /* See if this class descends from java.lang.Reference
+ * and set the class flags appropriately.
+ */
+ if (IS_CLASS_FLAG_SET(clazz->super, CLASS_ISREFERENCE)) {
+ u4 superRefFlags;
+
+ /* We've already determined the reference type of this
+ * inheritance chain. Inherit reference-ness from the superclass.
+ */
+ superRefFlags = GET_CLASS_FLAG_GROUP(clazz->super,
+ CLASS_ISREFERENCE |
+ CLASS_ISWEAKREFERENCE |
+ CLASS_ISPHANTOMREFERENCE);
+ SET_CLASS_FLAG(clazz, superRefFlags);
+ } else if (clazz->classLoader == NULL &&
+ clazz->super->classLoader == NULL &&
+ strcmp(clazz->super->descriptor,
+ "Ljava/lang/ref/Reference;") == 0)
+ {
+ u4 refFlags;
+
+ /* This class extends Reference, which means it should
+ * be one of the magic Soft/Weak/PhantomReference classes.
+ */
+ refFlags = CLASS_ISREFERENCE;
+ if (strcmp(clazz->descriptor,
+ "Ljava/lang/ref/SoftReference;") == 0)
+ {
+ /* Only CLASS_ISREFERENCE is set for soft references.
+ */
+ } else if (strcmp(clazz->descriptor,
+ "Ljava/lang/ref/WeakReference;") == 0)
+ {
+ refFlags |= CLASS_ISWEAKREFERENCE;
+ } else if (strcmp(clazz->descriptor,
+ "Ljava/lang/ref/PhantomReference;") == 0)
+ {
+ refFlags |= CLASS_ISPHANTOMREFERENCE;
+ } else {
+ /* No-one else is allowed to inherit directly
+ * from Reference.
+ */
+//xxx is this the right exception? better than an assertion.
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "illegal inheritance from Reference");
+ goto bail;
+ }
+
+ /* The class should not have any reference bits set yet.
+ */
+ assert(GET_CLASS_FLAG_GROUP(clazz,
+ CLASS_ISREFERENCE |
+ CLASS_ISWEAKREFERENCE |
+ CLASS_ISPHANTOMREFERENCE) == 0);
+
+ SET_CLASS_FLAG(clazz, refFlags);
+ }
+ }
+
+ if (!classesResolved && clazz->interfaceCount > 0) {
+ /*
+ * Resolve the interfaces implemented directly by this class. We
+ * stuffed the class index into the interface pointer slot.
+ */
+ dvmLinearReadWrite(clazz->classLoader, clazz->interfaces);
+ for (i = 0; i < clazz->interfaceCount; i++) {
+ u4 interfaceIdx;
+
+ interfaceIdx = (u4) clazz->interfaces[i]; /* unpack temp store */
+ assert(interfaceIdx != kDexNoIndex);
+
+ clazz->interfaces[i] = dvmResolveClass(clazz, interfaceIdx, false);
+ if (clazz->interfaces[i] == NULL) {
+ const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
+
+ assert(dvmCheckException(dvmThreadSelf()));
+ dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+
+ const char* classDescriptor;
+ classDescriptor = dexStringByTypeIdx(pDexFile, interfaceIdx);
+ if (gDvm.optimizing) {
+ /* happens with "external" libs */
+ LOGV("Failed resolving %s interface %d '%s'\n",
+ clazz->descriptor, interfaceIdx, classDescriptor);
+ } else {
+ LOGI("Failed resolving %s interface %d '%s'\n",
+ clazz->descriptor, interfaceIdx, classDescriptor);
+ }
+ goto bail_during_resolve;
+ }
+
+ /* are we allowed to implement this interface? */
+ if (!dvmCheckClassAccess(clazz, clazz->interfaces[i])) {
+ dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+ LOGW("Interface '%s' is not accessible to '%s'\n",
+ clazz->interfaces[i]->descriptor, clazz->descriptor);
+ dvmThrowException("Ljava/lang/IllegalAccessError;",
+ "interface not accessible");
+ goto bail_during_resolve;
+ }
+
+ /* Don't let the GC reclaim the interface class.
+ * TODO: shouldn't be needed; remove when things stabilize
+ */
+ dvmAddTrackedAlloc((Object *)clazz->interfaces[i], NULL);
+ numInterfacesResolved++;
+
+ LOGVV("+++ found interface '%s'\n",
+ clazz->interfaces[i]->descriptor);
+ }
+ dvmLinearReadOnly(clazz->classLoader, clazz->interfaces);
+ }
+
+ /*
+ * The ClassObject is now in a GC-able state. We let the GC
+ * realize this by punching in the real class type, which is
+ * always java.lang.Class.
+ *
+ * After this line, clazz will be fair game for the GC.
+ * Every field that the GC will look at must now be valid:
+ * - clazz->super
+ * - class->classLoader
+ * - clazz->sfields
+ * - clazz->interfaces
+ */
+ clazz->obj.clazz = gDvm.classJavaLangClass;
+
+ if (false) {
+bail_during_resolve:
+ resolve_okay = false;
+ } else {
+ resolve_okay = true;
+ }
+
+ /*
+ * Now that the GC can scan the ClassObject, we can let
+ * go of the explicit references we were holding onto.
+ *
+ * Either that or we failed, in which case we need to
+ * release the references so we don't leak memory.
+ */
+ if (clazz->super != NULL) {
+ dvmReleaseTrackedAlloc((Object *)clazz->super, NULL);
+ }
+ for (i = 0; i < numInterfacesResolved; i++) {
+ dvmReleaseTrackedAlloc((Object *)clazz->interfaces[i], NULL);
+ }
+
+ if (!resolve_okay) {
+ //LOGW("resolve_okay is false\n");
+ goto bail;
+ }
+
+ /*
+ * Populate vtable.
+ */
+ if (dvmIsInterfaceClass(clazz)) {
+ /* no vtable; just set the method indices */
+ int count = clazz->virtualMethodCount;
+
+ if (count != (u2) count) {
+ LOGE("Too many methods (%d) in interface '%s'\n", count,
+ clazz->descriptor);
+ goto bail;
+ }
+
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+ for (i = 0; i < count; i++)
+ clazz->virtualMethods[i].methodIndex = (u2) i;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ } else {
+ if (!createVtable(clazz)) {
+ LOGW("failed creating vtable\n");
+ goto bail;
+ }
+ }
+
+ /*
+ * Populate interface method tables. Can alter the vtable.
+ */
+ if (!createIftable(clazz))
+ goto bail;
+
+ /*
+ * Insert special-purpose "stub" method implementations.
+ */
+ if (!insertMethodStubs(clazz))
+ goto bail;
+
+ /*
+ * Compute instance field offsets and, hence, the size of the object.
+ */
+ if (!computeFieldOffsets(clazz))
+ goto bail;
+
+ /*
+ * Cache fields and methods from java/lang/ref/Reference and
+ * java/lang/Class. This has to happen after computeFieldOffsets().
+ */
+ if (clazz->classLoader == NULL) {
+ if (strcmp(clazz->descriptor, "Ljava/lang/ref/Reference;") == 0) {
+ if (!precacheReferenceOffsets(clazz)) {
+ LOGE("failed pre-caching Reference offsets\n");
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ goto bail;
+ }
+ } else if (clazz == gDvm.classJavaLangClass) {
+ gDvm.offJavaLangClass_pd = dvmFindFieldOffset(clazz, "pd",
+ "Ljava/security/ProtectionDomain;");
+ if (gDvm.offJavaLangClass_pd <= 0) {
+ LOGE("ERROR: unable to find 'pd' field in Class\n");
+ dvmAbort(); /* we're not going to get much farther */
+ //goto bail;
+ }
+ }
+ }
+
+ /*
+ * Done!
+ */
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED))
+ clazz->status = CLASS_VERIFIED;
+ else
+ clazz->status = CLASS_RESOLVED;
+ okay = true;
+ if (gDvm.verboseClass)
+ LOGV("CLASS: linked '%s'\n", clazz->descriptor);
+
+ /*
+ * We send CLASS_PREPARE events to the debugger from here. The
+ * definition of "preparation" is creating the static fields for a
+ * class and initializing them to the standard default values, but not
+ * executing any code (that comes later, during "initialization").
+ *
+ * We did the static prep in loadSFieldFromDex() while loading the class.
+ *
+ * The class has been prepared and resolved but possibly not yet verified
+ * at this point.
+ */
+ if (gDvm.debuggerActive) {
+ dvmDbgPostClassPrepare(clazz);
+ }
+
+bail:
+ if (!okay) {
+ clazz->status = CLASS_ERROR;
+ if (!dvmCheckException(dvmThreadSelf())) {
+ dvmThrowException("Ljava/lang/VirtualMachineError;", NULL);
+ }
+ }
+ return okay;
+}
+
+/*
+ * Create the virtual method table.
+ *
+ * The top part of the table is a copy of the table from our superclass,
+ * with our local methods overriding theirs. The bottom part of the table
+ * has any new methods we defined.
+ */
+static bool createVtable(ClassObject* clazz)
+{
+ bool result = false;
+ int maxCount;
+ int i;
+
+ if (clazz->super != NULL) {
+ //LOGI("SUPER METHODS %d %s->%s\n", clazz->super->vtableCount,
+ // clazz->descriptor, clazz->super->descriptor);
+ }
+
+ /* the virtual methods we define, plus the superclass vtable size */
+ maxCount = clazz->virtualMethodCount;
+ if (clazz->super != NULL) {
+ maxCount += clazz->super->vtableCount;
+ } else {
+ /* TODO: is this invariant true for all java/lang/Objects,
+ * regardless of the class loader? For now, assume it is.
+ */
+ assert(strcmp(clazz->descriptor, "Ljava/lang/Object;") == 0);
+ }
+ //LOGD("+++ max vmethods for '%s' is %d\n", clazz->descriptor, maxCount);
+
+ /*
+ * Over-allocate the table, then realloc it down if necessary. So
+ * long as we don't allocate anything in between we won't cause
+ * fragmentation, and reducing the size should be unlikely to cause
+ * a buffer copy.
+ */
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ clazz->vtable = (Method**) dvmLinearAlloc(clazz->classLoader,
+ sizeof(Method*) * maxCount);
+ if (clazz->vtable == NULL)
+ goto bail;
+
+ if (clazz->super != NULL) {
+ int actualCount;
+
+ memcpy(clazz->vtable, clazz->super->vtable,
+ sizeof(*(clazz->vtable)) * clazz->super->vtableCount);
+ actualCount = clazz->super->vtableCount;
+
+ /*
+ * See if any of our virtual methods override the superclass.
+ */
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ Method* localMeth = &clazz->virtualMethods[i];
+ int si;
+
+ for (si = 0; si < clazz->super->vtableCount; si++) {
+ Method* superMeth = clazz->vtable[si];
+
+ if (dvmCompareMethodNamesAndProtos(localMeth, superMeth) == 0)
+ {
+ /* verify */
+ if (dvmIsFinalMethod(superMeth)) {
+ LOGW("Method %s.%s overrides final %s.%s\n",
+ localMeth->clazz->descriptor, localMeth->name,
+ superMeth->clazz->descriptor, superMeth->name);
+ goto bail;
+ }
+ clazz->vtable[si] = localMeth;
+ localMeth->methodIndex = (u2) si;
+ //LOGV("+++ override %s.%s (slot %d)\n",
+ // clazz->descriptor, localMeth->name, si);
+ break;
+ }
+ }
+
+ if (si == clazz->super->vtableCount) {
+ /* not an override, add to end */
+ clazz->vtable[actualCount] = localMeth;
+ localMeth->methodIndex = (u2) actualCount;
+ actualCount++;
+
+ //LOGV("+++ add method %s.%s\n",
+ // clazz->descriptor, localMeth->name);
+ }
+ }
+
+ if (actualCount != (u2) actualCount) {
+ LOGE("Too many methods (%d) in class '%s'\n", actualCount,
+ clazz->descriptor);
+ goto bail;
+ }
+
+ assert(actualCount <= maxCount);
+
+ if (actualCount < maxCount) {
+ assert(clazz->vtable != NULL);
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ clazz->vtable = dvmLinearRealloc(clazz->classLoader, clazz->vtable,
+ sizeof(*(clazz->vtable)) * actualCount);
+ if (clazz->vtable == NULL) {
+ LOGE("vtable realloc failed\n");
+ goto bail;
+ } else {
+ LOGVV("+++ reduced vtable from %d to %d\n",
+ maxCount, actualCount);
+ }
+ }
+
+ clazz->vtableCount = actualCount;
+ } else {
+ /* java/lang/Object case */
+ int count = clazz->virtualMethodCount;
+ if (count != (u2) count) {
+ LOGE("Too many methods (%d) in base class '%s'\n", count,
+ clazz->descriptor);
+ goto bail;
+ }
+
+ for (i = 0; i < count; i++) {
+ clazz->vtable[i] = &clazz->virtualMethods[i];
+ clazz->virtualMethods[i].methodIndex = (u2) i;
+ }
+ clazz->vtableCount = clazz->virtualMethodCount;
+ }
+
+ result = true;
+
+bail:
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ return result;
+}
+
+/*
+ * Create and populate "iftable".
+ *
+ * The set of interfaces we support is the combination of the interfaces
+ * we implement directly and those implemented by our superclass. Each
+ * interface can have one or more "superinterfaces", which we must also
+ * support. For speed we flatten the tree out.
+ *
+ * We might be able to speed this up when there are lots of interfaces
+ * by merge-sorting the class pointers and binary-searching when removing
+ * duplicates. We could also drop the duplicate removal -- it's only
+ * there to reduce the memory footprint.
+ *
+ * Because of "Miranda methods", this may reallocate clazz->virtualMethods.
+ *
+ * Returns "true" on success.
+ */
+static bool createIftable(ClassObject* clazz)
+{
+ bool result = false;
+ bool zapIftable = false;
+ bool zapVtable = false;
+ bool zapIfvipool = false;
+ int ifCount, superIfCount, idx;
+ int i;
+
+ if (clazz->super != NULL)
+ superIfCount = clazz->super->iftableCount;
+ else
+ superIfCount = 0;
+
+ ifCount = superIfCount;
+ ifCount += clazz->interfaceCount;
+ for (i = 0; i < clazz->interfaceCount; i++)
+ ifCount += clazz->interfaces[i]->iftableCount;
+
+ LOGVV("INTF: class '%s' direct w/supra=%d super=%d total=%d\n",
+ clazz->descriptor, ifCount - superIfCount, superIfCount, ifCount);
+
+ if (ifCount == 0) {
+ assert(clazz->iftableCount == 0);
+ assert(clazz->iftable == NULL);
+ result = true;
+ goto bail;
+ }
+
+ /*
+ * Create a table with enough space for all interfaces, and copy the
+ * superclass' table in.
+ */
+ clazz->iftable = (InterfaceEntry*) dvmLinearAlloc(clazz->classLoader,
+ sizeof(InterfaceEntry) * ifCount);
+ zapIftable = true;
+ memset(clazz->iftable, 0x00, sizeof(InterfaceEntry) * ifCount);
+ if (superIfCount != 0) {
+ memcpy(clazz->iftable, clazz->super->iftable,
+ sizeof(InterfaceEntry) * superIfCount);
+ }
+
+ /*
+ * Create a flattened interface hierarchy of our immediate interfaces.
+ */
+ idx = superIfCount;
+
+ for (i = 0; i < clazz->interfaceCount; i++) {
+ ClassObject* interf;
+ int j;
+
+ interf = clazz->interfaces[i];
+ assert(interf != NULL);
+
+ /* make sure this is still an interface class */
+ if (!dvmIsInterfaceClass(interf)) {
+ LOGW("Class '%s' implements non-interface '%s'\n",
+ clazz->descriptor, interf->descriptor);
+ dvmThrowExceptionWithClassMessage(
+ "Ljava/lang/IncompatibleClassChangeError;",
+ clazz->descriptor);
+ goto bail;
+ }
+
+ /* add entry for this interface */
+ clazz->iftable[idx++].clazz = interf;
+
+ /* add entries for the interface's superinterfaces */
+ for (j = 0; j < interf->iftableCount; j++) {
+ clazz->iftable[idx++].clazz = interf->iftable[j].clazz;
+ }
+ }
+
+ assert(idx == ifCount);
+
+ if (false) {
+ /*
+ * Remove anything redundant from our recent additions. Note we have
+ * to traverse the recent adds when looking for duplicates, because
+ * it's possible the recent additions are self-redundant. This
+ * reduces the memory footprint of classes with lots of inherited
+ * interfaces.
+ *
+ * (I don't know if this will cause problems later on when we're trying
+ * to find a static field. It looks like the proper search order is
+ * (1) current class, (2) interfaces implemented by current class,
+ * (3) repeat with superclass. A field implemented by an interface
+ * and by a superclass might come out wrong if the superclass also
+ * implements the interface. The javac compiler will reject the
+ * situation as ambiguous, so the concern is somewhat artificial.)
+ *
+ * UPDATE: this makes ReferenceType.Interfaces difficult to implement,
+ * because it wants to return just the interfaces declared to be
+ * implemented directly by the class. I'm excluding this code for now.
+ */
+ for (i = superIfCount; i < ifCount; i++) {
+ int j;
+
+ for (j = 0; j < ifCount; j++) {
+ if (i == j)
+ continue;
+ if (clazz->iftable[i].clazz == clazz->iftable[j].clazz) {
+ LOGVV("INTF: redundant interface %s in %s\n",
+ clazz->iftable[i].clazz->descriptor,
+ clazz->descriptor);
+
+ if (i != ifCount-1)
+ memmove(&clazz->iftable[i], &clazz->iftable[i+1],
+ (ifCount - i -1) * sizeof(InterfaceEntry));
+ ifCount--;
+ i--; // adjust for i++ above
+ break;
+ }
+ }
+ }
+ LOGVV("INTF: class '%s' nodupes=%d\n", clazz->descriptor, ifCount);
+ } // if (false)
+
+ clazz->iftableCount = ifCount;
+
+ /*
+ * If we're an interface, we don't need the vtable pointers, so
+ * we're done. If this class doesn't implement an interface that our
+ * superclass doesn't have, then we again have nothing to do.
+ */
+ if (dvmIsInterfaceClass(clazz) || superIfCount == ifCount) {
+ //dvmDumpClass(clazz, kDumpClassFullDetail);
+ result = true;
+ goto bail;
+ }
+
+ /*
+ * When we're handling invokeinterface, we probably have an object
+ * whose type is an interface class rather than a concrete class. We
+ * need to convert the method reference into a vtable index. So, for
+ * every entry in "iftable", we create a list of vtable indices.
+ *
+ * Because our vtable encompasses the superclass vtable, we can use
+ * the vtable indices from our superclass for all of the interfaces
+ * that weren't directly implemented by us.
+ *
+ * Each entry in "iftable" has a pointer to the start of its set of
+ * vtable offsets. The iftable entries in the superclass point to
+ * storage allocated in the superclass, and the iftable entries added
+ * for this class point to storage allocated in this class. "iftable"
+ * is flat for fast access in a class and all of its subclasses, but
+ * "ifviPool" is only created for the topmost implementor.
+ */
+ int poolSize = 0;
+ for (i = superIfCount; i < ifCount; i++) {
+ /*
+ * Note it's valid for an interface to have no methods (e.g.
+ * java/io/Serializable).
+ */
+ LOGVV("INTF: pool: %d from %s\n",
+ clazz->iftable[i].clazz->virtualMethodCount,
+ clazz->iftable[i].clazz->descriptor);
+ poolSize += clazz->iftable[i].clazz->virtualMethodCount;
+ }
+
+ if (poolSize == 0) {
+ LOGVV("INTF: didn't find any new interfaces with methods\n");
+ result = true;
+ goto bail;
+ }
+
+ clazz->ifviPoolCount = poolSize;
+ clazz->ifviPool = (int*) dvmLinearAlloc(clazz->classLoader,
+ poolSize * sizeof(int*));
+ zapIfvipool = true;
+
+ /*
+ * Fill in the vtable offsets for the interfaces that weren't part of
+ * our superclass.
+ */
+ int poolOffset = 0;
+ Method** mirandaList = NULL;
+ int mirandaCount = 0, mirandaAlloc = 0;
+
+ for (i = superIfCount; i < ifCount; i++) {
+ ClassObject* interface;
+ int methIdx;
+
+ clazz->iftable[i].methodIndexArray = clazz->ifviPool + poolOffset;
+ interface = clazz->iftable[i].clazz;
+ poolOffset += interface->virtualMethodCount; // end here
+
+ /*
+ * For each method listed in the interface's method list, find the
+ * matching method in our class's method list. We want to favor the
+ * subclass over the superclass, which just requires walking
+ * back from the end of the vtable. (This only matters if the
+ * superclass defines a private method and this class redefines
+ * it -- otherwise it would use the same vtable slot. In Dalvik
+ * those don't end up in the virtual method table, so it shouldn't
+ * matter which direction we go. We walk it backward anyway.)
+ *
+ *
+ * Suppose we have the following arrangement:
+ * public interface MyInterface
+ * public boolean inInterface();
+ * public abstract class MirandaAbstract implements MirandaInterface
+ * //public abstract boolean inInterface(); // not declared!
+ * public boolean inAbstract() { stuff } // in vtable
+ * public class MirandClass extends MirandaAbstract
+ * public boolean inInterface() { stuff }
+ * public boolean inAbstract() { stuff } // in vtable
+ *
+ * The javac compiler happily compiles MirandaAbstract even though
+ * it doesn't declare all methods from its interface. When we try
+ * to set up a vtable for MirandaAbstract, we find that we don't
+ * have an slot for inInterface. To prevent this, we synthesize
+ * abstract method declarations in MirandaAbstract.
+ *
+ * We have to expand vtable and update some things that point at it,
+ * so we accumulate the method list and do it all at once below.
+ */
+ for (methIdx = 0; methIdx < interface->virtualMethodCount; methIdx++) {
+ Method* imeth = &interface->virtualMethods[methIdx];
+ int j;
+
+ IF_LOGVV() {
+ char* desc = dexProtoCopyMethodDescriptor(&imeth->prototype);
+ LOGVV("INTF: matching '%s' '%s'\n", imeth->name, desc);
+ free(desc);
+ }
+
+ for (j = clazz->vtableCount-1; j >= 0; j--) {
+ if (dvmCompareMethodNamesAndProtos(imeth, clazz->vtable[j])
+ == 0)
+ {
+ LOGVV("INTF: matched at %d\n", j);
+ if (!dvmIsPublicMethod(clazz->vtable[j])) {
+ LOGW("Implementation of %s.%s is not public\n",
+ clazz->descriptor, clazz->vtable[j]->name);
+ dvmThrowException("Ljava/lang/IllegalAccessError;",
+ "interface implementation not public");
+ goto bail;
+ }
+ clazz->iftable[i].methodIndexArray[methIdx] = j;
+ break;
+ }
+ }
+ if (j < 0) {
+ IF_LOGV() {
+ char* desc =
+ dexProtoCopyMethodDescriptor(&imeth->prototype);
+ LOGV("No match for '%s' '%s' in '%s' (creating miranda)\n",
+ imeth->name, desc, clazz->descriptor);
+ free(desc);
+ }
+ //dvmThrowException("Ljava/lang/RuntimeException;", "Miranda!");
+ //return false;
+
+ if (mirandaCount == mirandaAlloc) {
+ mirandaAlloc += 8;
+ if (mirandaList == NULL) {
+ mirandaList = dvmLinearAlloc(clazz->classLoader,
+ mirandaAlloc * sizeof(Method*));
+ } else {
+ dvmLinearReadOnly(clazz->classLoader, mirandaList);
+ mirandaList = dvmLinearRealloc(clazz->classLoader,
+ mirandaList, mirandaAlloc * sizeof(Method*));
+ }
+ assert(mirandaList != NULL); // mem failed + we leaked
+ }
+
+ /*
+ * These may be redundant (e.g. method with same name and
+ * signature declared in two interfaces implemented by the
+ * same abstract class). We can squeeze the duplicates
+ * out here.
+ */
+ int mir;
+ for (mir = 0; mir < mirandaCount; mir++) {
+ if (dvmCompareMethodNamesAndProtos(
+ mirandaList[mir], imeth) == 0)
+ {
+ IF_LOGVV() {
+ char* desc = dexProtoCopyMethodDescriptor(
+ &imeth->prototype);
+ LOGVV("MIRANDA dupe: %s and %s %s%s\n",
+ mirandaList[mir]->clazz->descriptor,
+ imeth->clazz->descriptor,
+ imeth->name, desc);
+ free(desc);
+ }
+ break;
+ }
+ }
+
+ /* point the iftable at a phantom slot index */
+ clazz->iftable[i].methodIndexArray[methIdx] =
+ clazz->vtableCount + mir;
+ LOGVV("MIRANDA: %s points at slot %d\n",
+ imeth->name, clazz->vtableCount + mir);
+
+ /* if non-duplicate among Mirandas, add to Miranda list */
+ if (mir == mirandaCount) {
+ //LOGV("MIRANDA: holding '%s' in slot %d\n",
+ // imeth->name, mir);
+ mirandaList[mirandaCount++] = imeth;
+ }
+ }
+ }
+ }
+
+ if (mirandaCount != 0) {
+ Method* newVirtualMethods;
+ Method* meth;
+ int oldMethodCount, oldVtableCount;
+
+ for (i = 0; i < mirandaCount; i++) {
+ LOGVV("MIRANDA %d: %s.%s\n", i,
+ mirandaList[i]->clazz->descriptor, mirandaList[i]->name);
+ }
+
+ /*
+ * We found methods in one or more interfaces for which we do not
+ * have vtable entries. We have to expand our virtualMethods
+ * table (which might be empty) to hold some new entries.
+ */
+ if (clazz->virtualMethods == NULL) {
+ newVirtualMethods = (Method*) dvmLinearAlloc(clazz->classLoader,
+ sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+ } else {
+ //dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ newVirtualMethods = (Method*) dvmLinearRealloc(clazz->classLoader,
+ clazz->virtualMethods,
+ sizeof(Method) * (clazz->virtualMethodCount + mirandaCount));
+ }
+ if (newVirtualMethods != clazz->virtualMethods) {
+ /*
+ * Table was moved in memory. We have to run through the
+ * vtable and fix the pointers. The vtable entries might be
+ * pointing at superclasses, so we flip it around: run through
+ * all locally-defined virtual methods, and fix their entries
+ * in the vtable. (This would get really messy if sub-classes
+ * had already been loaded.)
+ *
+ * Reminder: clazz->virtualMethods and clazz->virtualMethodCount
+ * hold the virtual methods declared by this class. The
+ * method's methodIndex is the vtable index, and is the same
+ * for all sub-classes (and all super classes in which it is
+ * defined). We're messing with these because the Miranda
+ * stuff makes it look like the class actually has an abstract
+ * method declaration in it.
+ */
+ LOGVV("MIRANDA fixing vtable pointers\n");
+ dvmLinearReadWrite(clazz->classLoader, clazz->vtable);
+ Method* meth = newVirtualMethods;
+ for (i = 0; i < clazz->virtualMethodCount; i++, meth++)
+ clazz->vtable[meth->methodIndex] = meth;
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ }
+
+ oldMethodCount = clazz->virtualMethodCount;
+ clazz->virtualMethods = newVirtualMethods;
+ clazz->virtualMethodCount += mirandaCount;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+ /*
+ * We also have to expand the vtable.
+ */
+ assert(clazz->vtable != NULL);
+ clazz->vtable = (Method**) dvmLinearRealloc(clazz->classLoader,
+ clazz->vtable,
+ sizeof(Method*) * (clazz->vtableCount + mirandaCount));
+ if (clazz->vtable == NULL) {
+ assert(false);
+ goto bail;
+ }
+ zapVtable = true;
+
+ oldVtableCount = clazz->vtableCount;
+ clazz->vtableCount += mirandaCount;
+
+ /*
+ * Now we need to create the fake methods. We clone the abstract
+ * method definition from the interface and then replace a few
+ * things.
+ */
+ meth = clazz->virtualMethods + oldMethodCount;
+ for (i = 0; i < mirandaCount; i++, meth++) {
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ cloneMethod(meth, mirandaList[i]);
+ meth->clazz = clazz;
+ meth->accessFlags |= ACC_MIRANDA;
+ meth->methodIndex = (u2) (oldVtableCount + i);
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+
+ /* point the new vtable entry at the new method */
+ clazz->vtable[oldVtableCount + i] = meth;
+ }
+
+ dvmLinearReadOnly(clazz->classLoader, mirandaList);
+ dvmLinearFree(clazz->classLoader, mirandaList);
+
+ }
+
+ /*
+ * TODO?
+ * Sort the interfaces by number of declared methods. All we really
+ * want is to get the interfaces with zero methods at the end of the
+ * list, so that when we walk through the list during invoke-interface
+ * we don't examine interfaces that can't possibly be useful.
+ *
+ * The set will usually be small, so a simple insertion sort works.
+ *
+ * We have to be careful not to change the order of two interfaces
+ * that define the same method. (Not a problem if we only move the
+ * zero-method interfaces to the end.)
+ *
+ * PROBLEM:
+ * If we do this, we will no longer be able to identify super vs.
+ * current class interfaces by comparing clazz->super->iftableCount. This
+ * breaks anything that only wants to find interfaces declared directly
+ * by the class (dvmFindStaticFieldHier, ReferenceType.Interfaces,
+ * dvmDbgOutputAllInterfaces, etc). Need to provide a workaround.
+ *
+ * We can sort just the interfaces implemented directly by this class,
+ * but that doesn't seem like it would provide much of an advantage. I'm
+ * not sure this is worthwhile.
+ *
+ * (This has been made largely obsolete by the interface cache mechanism.)
+ */
+
+ //dvmDumpClass(clazz);
+
+ result = true;
+
+bail:
+ if (zapIftable)
+ dvmLinearReadOnly(clazz->classLoader, clazz->iftable);
+ if (zapVtable)
+ dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
+ if (zapIfvipool)
+ dvmLinearReadOnly(clazz->classLoader, clazz->ifviPool);
+ return result;
+}
+
+
+/*
+ * Provide "stub" implementations for methods without them.
+ *
+ * Currently we provide an implementation for all abstract methods that
+ * throws an AbstractMethodError exception. This allows us to avoid an
+ * explicit check for abstract methods in every virtual call.
+ *
+ * NOTE: for Miranda methods, the method declaration is a clone of what
+ * was found in the interface class. That copy may already have had the
+ * function pointer filled in, so don't be surprised if it's not NULL.
+ *
+ * NOTE: this sets the "native" flag, giving us an "abstract native" method,
+ * which is nonsensical. Need to make sure that this doesn't escape the
+ * VM. We can either mask it out in reflection calls, or copy "native"
+ * into the high 16 bits of accessFlags and check that internally.
+ */
+static bool insertMethodStubs(ClassObject* clazz)
+{
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+
+ Method* meth;
+ int i;
+
+ meth = clazz->virtualMethods;
+ for (i = 0; i < clazz->virtualMethodCount; i++, meth++) {
+ if (dvmIsAbstractMethod(meth)) {
+ assert(meth->insns == NULL);
+ assert(meth->nativeFunc == NULL ||
+ meth->nativeFunc == (DalvikBridgeFunc)dvmAbstractMethodStub);
+
+ meth->accessFlags |= ACC_NATIVE;
+ meth->nativeFunc = (DalvikBridgeFunc) dvmAbstractMethodStub;
+ }
+ }
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ return true;
+}
+
+
+/*
+ * Swap two instance fields.
+ */
+static inline void swapField(InstField* pOne, InstField* pTwo)
+{
+ InstField swap;
+
+ LOGVV(" --- swap '%s' and '%s'\n", pOne->field.name, pTwo->field.name);
+ swap = *pOne;
+ *pOne = *pTwo;
+ *pTwo = swap;
+}
+
+/*
+ * Assign instance fields to u4 slots.
+ *
+ * The top portion of the instance field area is occupied by the superclass
+ * fields, the bottom by the fields for this class.
+ *
+ * "long" and "double" fields occupy two adjacent slots. On some
+ * architectures, 64-bit quantities must be 64-bit aligned, so we need to
+ * arrange fields (or introduce padding) to ensure this. We assume the
+ * fields of the topmost superclass (i.e. Object) are 64-bit aligned, so
+ * we can just ensure that the offset is "even". To avoid wasting space,
+ * we want to move non-reference 32-bit fields into gaps rather than
+ * creating pad words.
+ *
+ * In the worst case we will waste 4 bytes, but because objects are
+ * allocated on >= 64-bit boundaries, those bytes may well be wasted anyway
+ * (assuming this is the most-derived class).
+ *
+ * Pad words are not represented in the field table, so the field table
+ * itself does not change size.
+ *
+ * The number of field slots determines the size of the object, so we
+ * set that here too.
+ *
+ * This function feels a little more complicated than I'd like, but it
+ * has the property of moving the smallest possible set of fields, which
+ * should reduce the time required to load a class.
+ *
+ * NOTE: reference fields *must* come first, or precacheReferenceOffsets()
+ * will break.
+ */
+static bool computeFieldOffsets(ClassObject* clazz)
+{
+ int fieldOffset;
+ int i, j;
+
+ dvmLinearReadWrite(clazz->classLoader, clazz->ifields);
+
+ if (clazz->super != NULL)
+ fieldOffset = clazz->super->objectSize;
+ else
+ fieldOffset = offsetof(DataObject, instanceData);
+
+ LOGVV("--- computeFieldOffsets '%s'\n", clazz->descriptor);
+
+ //LOGI("OFFSETS fieldCount=%d\n", clazz->ifieldCount);
+ //LOGI("dataobj, instance: %d\n", offsetof(DataObject, instanceData));
+ //LOGI("classobj, access: %d\n", offsetof(ClassObject, accessFlags));
+ //LOGI("super=%p, fieldOffset=%d\n", clazz->super, fieldOffset);
+
+ /*
+ * Start by moving all reference fields to the front.
+ */
+ clazz->ifieldRefCount = 0;
+ j = clazz->ifieldCount - 1;
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ InstField* pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c != '[' && c != 'L') {
+ /* This isn't a reference field; see if any reference fields
+ * follow this one. If so, we'll move it to this position.
+ * (quicksort-style partitioning)
+ */
+ while (j > i) {
+ InstField* refField = &clazz->ifields[j--];
+ char rc = refField->field.signature[0];
+
+ if (rc == '[' || rc == 'L') {
+ /* Here's a reference field that follows at least one
+ * non-reference field. Swap it with the current field.
+ * (When this returns, "pField" points to the reference
+ * field, and "refField" points to the non-ref field.)
+ */
+ swapField(pField, refField);
+
+ /* Fix the signature.
+ */
+ c = rc;
+
+ clazz->ifieldRefCount++;
+ break;
+ }
+ }
+ /* We may or may not have swapped a field.
+ */
+ } else {
+ /* This is a reference field.
+ */
+ clazz->ifieldRefCount++;
+ }
+
+ /*
+ * If we've hit the end of the reference fields, break.
+ */
+ if (c != '[' && c != 'L')
+ break;
+
+ pField->byteOffset = fieldOffset;
+ fieldOffset += sizeof(u4);
+ LOGVV(" --- offset1 '%s'=%d\n", pField->field.name,pField->byteOffset);
+ }
+
+ /*
+ * Now we want to pack all of the double-wide fields together. If we're
+ * not aligned, though, we want to shuffle one 32-bit field into place.
+ * If we can't find one, we'll have to pad it.
+ */
+ if (i != clazz->ifieldCount && (fieldOffset & 0x04) != 0) {
+ LOGVV(" +++ not aligned\n");
+
+ InstField* pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c != 'J' && c != 'D') {
+ /*
+ * The field that comes next is 32-bit, so just advance past it.
+ */
+ assert(c != '[' && c != 'L');
+ pField->byteOffset = fieldOffset;
+ fieldOffset += sizeof(u4);
+ i++;
+ LOGVV(" --- offset2 '%s'=%d\n",
+ pField->field.name, pField->byteOffset);
+ } else {
+ /*
+ * Next field is 64-bit, so search for a 32-bit field we can
+ * swap into it.
+ */
+ bool found = false;
+ j = clazz->ifieldCount - 1;
+ while (j > i) {
+ InstField* singleField = &clazz->ifields[j--];
+ char rc = singleField->field.signature[0];
+
+ if (rc != 'J' && rc != 'D') {
+ swapField(pField, singleField);
+ //c = rc;
+ LOGVV(" +++ swapped '%s' for alignment\n",
+ pField->field.name);
+ pField->byteOffset = fieldOffset;
+ fieldOffset += sizeof(u4);
+ LOGVV(" --- offset3 '%s'=%d\n",
+ pField->field.name, pField->byteOffset);
+ found = true;
+ i++;
+ break;
+ }
+ }
+ if (!found) {
+ LOGV(" +++ inserting pad field in '%s'\n", clazz->descriptor);
+ fieldOffset += sizeof(u4);
+ }
+ }
+ }
+
+ /*
+ * Alignment is good, shuffle any double-wide fields forward, and
+ * finish assigning field offsets to all fields.
+ */
+ assert(i == clazz->ifieldCount || (fieldOffset & 0x04) == 0);
+ j = clazz->ifieldCount - 1;
+ for ( ; i < clazz->ifieldCount; i++) {
+ InstField* pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c != 'D' && c != 'J') {
+ /* This isn't a double-wide field; see if any double fields
+ * follow this one. If so, we'll move it to this position.
+ * (quicksort-style partitioning)
+ */
+ while (j > i) {
+ InstField* doubleField = &clazz->ifields[j--];
+ char rc = doubleField->field.signature[0];
+
+ if (rc == 'D' || rc == 'J') {
+ /* Here's a double-wide field that follows at least one
+ * non-double field. Swap it with the current field.
+ * (When this returns, "pField" points to the reference
+ * field, and "doubleField" points to the non-double field.)
+ */
+ swapField(pField, doubleField);
+ c = rc;
+
+ break;
+ }
+ }
+ /* We may or may not have swapped a field.
+ */
+ } else {
+ /* This is a double-wide field, leave it be.
+ */
+ }
+
+ pField->byteOffset = fieldOffset;
+ LOGVV(" --- offset4 '%s'=%d\n", pField->field.name,pField->byteOffset);
+ fieldOffset += sizeof(u4);
+ if (c == 'J' || c == 'D')
+ fieldOffset += sizeof(u4);
+ }
+
+#ifndef NDEBUG
+ /* Make sure that all reference fields appear before
+ * non-reference fields, and all double-wide fields are aligned.
+ */
+ j = 0; // seen non-ref
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ InstField *pField = &clazz->ifields[i];
+ char c = pField->field.signature[0];
+
+ if (c == 'D' || c == 'J') {
+ assert((pField->byteOffset & 0x07) == 0);
+ }
+
+ if (c != '[' && c != 'L') {
+ if (!j) {
+ assert(i == clazz->ifieldRefCount);
+ j = 1;
+ }
+ } else if (j) {
+ assert(false);
+ }
+ }
+ if (!j) {
+ assert(clazz->ifieldRefCount == clazz->ifieldCount);
+ }
+#endif
+
+ /*
+ * We map a C struct directly on top of java/lang/Class objects. Make
+ * sure we left enough room for the instance fields.
+ */
+ assert(clazz != gDvm.classJavaLangClass || (size_t)fieldOffset <
+ offsetof(ClassObject, instanceData) + sizeof(clazz->instanceData));
+
+ clazz->objectSize = fieldOffset;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->ifields);
+ return true;
+}
+
+/*
+ * Throw the VM-spec-mandated error when an exception is thrown during
+ * class initialization.
+ *
+ * The safest way to do this is to call the ExceptionInInitializerError
+ * constructor that takes a Throwable.
+ *
+ * [Do we want to wrap it if the original is an Error rather than
+ * an Exception?]
+ */
+static void throwClinitError(void)
+{
+ Thread* self = dvmThreadSelf();
+ Object* exception;
+ Object* eiie;
+
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
+ /*
+ * Always resolves to same thing -- no race condition.
+ */
+ gDvm.classJavaLangExceptionInInitializerError =
+ dvmFindSystemClass(
+ "Ljava/lang/ExceptionInInitializerError;");
+ if (gDvm.classJavaLangExceptionInInitializerError == NULL) {
+ LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
+ goto fail;
+ }
+
+ gDvm.methJavaLangExceptionInInitializerError_init =
+ dvmFindDirectMethodByDescriptor(gDvm.classJavaLangExceptionInInitializerError,
+ "<init>", "(Ljava/lang/Throwable;)V");
+ if (gDvm.methJavaLangExceptionInInitializerError_init == NULL) {
+ LOGE("Unable to prep java/lang/ExceptionInInitializerError\n");
+ goto fail;
+ }
+ }
+
+ eiie = dvmAllocObject(gDvm.classJavaLangExceptionInInitializerError,
+ ALLOC_DEFAULT);
+ if (eiie == NULL)
+ goto fail;
+
+ /*
+ * Construct the new object, and replace the exception with it.
+ */
+ JValue unused;
+ dvmCallMethod(self, gDvm.methJavaLangExceptionInInitializerError_init,
+ eiie, &unused, exception);
+ dvmSetException(self, eiie);
+ dvmReleaseTrackedAlloc(eiie, NULL);
+ dvmReleaseTrackedAlloc(exception, self);
+ return;
+
+fail: /* restore original exception */
+ dvmSetException(self, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+ return;
+}
+
+/*
+ * The class failed to initialize on a previous attempt, so we want to throw
+ * a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
+ * failed in verification, in which case v2 5.4.1 says we need to re-throw
+ * the previous error.
+ */
+static void throwEarlierClassFailure(ClassObject* clazz)
+{
+ LOGI("Rejecting re-init on previously-failed class %s v=%p\n",
+ clazz->descriptor, clazz->verifyErrorClass);
+
+ if (clazz->verifyErrorClass == NULL) {
+ dvmThrowExceptionWithClassMessage("Ljava/lang/NoClassDefFoundError;",
+ clazz->descriptor);
+ } else {
+ dvmThrowExceptionByClassWithClassMessage(clazz->verifyErrorClass,
+ clazz->descriptor);
+ }
+}
+
+/*
+ * Initialize any static fields whose values are stored in
+ * the DEX file. This must be done during class initialization.
+ */
+static void initSFields(ClassObject* clazz)
+{
+ Thread* self = dvmThreadSelf(); /* for dvmReleaseTrackedAlloc() */
+ DexFile* pDexFile;
+ const DexClassDef* pClassDef;
+ const DexEncodedArray* pValueList;
+ EncodedArrayIterator iterator;
+ int i;
+
+ if (clazz->sfieldCount == 0) {
+ return;
+ }
+ if (clazz->pDvmDex == NULL) {
+ /* generated class; any static fields should already be set up */
+ LOGV("Not initializing static fields in %s\n", clazz->descriptor);
+ return;
+ }
+ pDexFile = clazz->pDvmDex->pDexFile;
+
+ pClassDef = dexFindClass(pDexFile, clazz->descriptor);
+ assert(pClassDef != NULL);
+
+ pValueList = dexGetStaticValuesList(pDexFile, pClassDef);
+ if (pValueList == NULL) {
+ return;
+ }
+
+ dvmEncodedArrayIteratorInitialize(&iterator, pValueList, clazz);
+
+ /*
+ * Iterate over the initial values array, setting the corresponding
+ * static field for each array element.
+ */
+
+ for (i = 0; dvmEncodedArrayIteratorHasNext(&iterator); i++) {
+ AnnotationValue value;
+ bool parsed = dvmEncodedArrayIteratorGetNext(&iterator, &value);
+ StaticField* sfield = &clazz->sfields[i];
+ const char* descriptor = sfield->field.signature;
+ bool needRelease = false;
+
+ if (! parsed) {
+ /*
+ * TODO: Eventually verification should attempt to ensure
+ * that this can't happen at least due to a data integrity
+ * problem.
+ */
+ LOGE("Static initializer parse failed for %s at index %d",
+ clazz->descriptor, i);
+ dvmAbort();
+ }
+
+ /* Verify that the value we got was of a valid type. */
+
+ switch (descriptor[0]) {
+ case 'Z': parsed = (value.type == kDexAnnotationBoolean); break;
+ case 'B': parsed = (value.type == kDexAnnotationByte); break;
+ case 'C': parsed = (value.type == kDexAnnotationChar); break;
+ case 'S': parsed = (value.type == kDexAnnotationShort); break;
+ case 'I': parsed = (value.type == kDexAnnotationInt); break;
+ case 'J': parsed = (value.type == kDexAnnotationLong); break;
+ case 'F': parsed = (value.type == kDexAnnotationFloat); break;
+ case 'D': parsed = (value.type == kDexAnnotationDouble); break;
+ case '[': parsed = (value.type == kDexAnnotationNull); break;
+ case 'L': {
+ switch (value.type) {
+ case kDexAnnotationNull: {
+ /* No need for further tests. */
+ break;
+ }
+ case kDexAnnotationString: {
+ parsed =
+ (strcmp(descriptor, "Ljava/lang/String;") == 0);
+ needRelease = true;
+ break;
+ }
+ case kDexAnnotationType: {
+ parsed =
+ (strcmp(descriptor, "Ljava/lang/Class;") == 0);
+ needRelease = true;
+ break;
+ }
+ default: {
+ parsed = false;
+ break;
+ }
+ }
+ break;
+ }
+ default: {
+ parsed = false;
+ break;
+ }
+ }
+
+ if (parsed) {
+ /*
+ * All's well, so store the value. Note: This always
+ * stores the full width of a JValue, even though most of
+ * the time only the first word is needed.
+ */
+ sfield->value = value.value;
+ if (needRelease) {
+ dvmReleaseTrackedAlloc(value.value.l, self);
+ }
+ } else {
+ /*
+ * Something up above had a problem. TODO: See comment
+ * above the switch about verfication.
+ */
+ LOGE("Bogus static initialization: value type %d in field type "
+ "%s for %s at index %d",
+ value.type, descriptor, clazz->descriptor, i);
+ dvmAbort();
+ }
+ }
+}
+
+
+/*
+ * Determine whether "descriptor" yields the same class object in the
+ * context of clazz1 and clazz2.
+ *
+ * The caller must hold gDvm.loadedClasses.
+ *
+ * Returns "true" if they match.
+ */
+static bool compareDescriptorClasses(const char* descriptor,
+ const ClassObject* clazz1, const ClassObject* clazz2)
+{
+ ClassObject* result1;
+ ClassObject* result2;
+
+ /*
+ * Do the first lookup by name.
+ */
+ result1 = dvmFindClassNoInit(descriptor, clazz1->classLoader);
+
+ /*
+ * We can skip a second lookup by name if the second class loader is
+ * in the initiating loader list of the class object we retrieved.
+ * (This means that somebody already did a lookup of this class through
+ * the second loader, and it resolved to the same class.) If it's not
+ * there, we may simply not have had an opportunity to add it yet, so
+ * we do the full lookup.
+ *
+ * The initiating loader test should catch the majority of cases
+ * (in particular, the zillions of references to String/Object).
+ *
+ * Unfortunately we're still stuck grabbing a mutex to do the lookup.
+ *
+ * For this to work, the superclass/interface should be the first
+ * argument, so that way if it's from the bootstrap loader this test
+ * will work. (The bootstrap loader, by definition, never shows up
+ * as the initiating loader of a class defined by some other loader.)
+ */
+ dvmHashTableLock(gDvm.loadedClasses);
+ bool isInit = dvmLoaderInInitiatingList(result1, clazz2->classLoader);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ if (isInit) {
+ //printf("%s(obj=%p) / %s(cl=%p): initiating\n",
+ // result1->descriptor, result1,
+ // clazz2->descriptor, clazz2->classLoader);
+ return true;
+ } else {
+ //printf("%s(obj=%p) / %s(cl=%p): RAW\n",
+ // result1->descriptor, result1,
+ // clazz2->descriptor, clazz2->classLoader);
+ result2 = dvmFindClassNoInit(descriptor, clazz2->classLoader);
+ }
+
+ if (result1 == NULL || result2 == NULL) {
+ dvmClearException(dvmThreadSelf());
+ if (result1 == result2) {
+ /*
+ * Neither class loader could find this class. Apparently it
+ * doesn't exist.
+ *
+ * We can either throw some sort of exception now, or just
+ * assume that it'll fail later when something actually tries
+ * to use the class. For strict handling we should throw now,
+ * because a "tricky" class loader could start returning
+ * something later, and a pair of "tricky" loaders could set
+ * us up for confusion.
+ *
+ * I'm not sure if we're allowed to complain about nonexistent
+ * classes in method signatures during class init, so for now
+ * this will just return "true" and let nature take its course.
+ */
+ return true;
+ } else {
+ /* only one was found, so clearly they're not the same */
+ return false;
+ }
+ }
+
+ return result1 == result2;
+}
+
+/*
+ * For every component in the method descriptor, resolve the class in the
+ * context of the two classes and compare the results.
+ *
+ * For best results, the "superclass" class should be first.
+ *
+ * Returns "true" if the classes match, "false" otherwise.
+ */
+static bool checkMethodDescriptorClasses(const Method* meth,
+ const ClassObject* clazz1, const ClassObject* clazz2)
+{
+ DexParameterIterator iterator;
+ const char* descriptor;
+
+ /* walk through the list of parameters */
+ dexParameterIteratorInit(&iterator, &meth->prototype);
+ while (true) {
+ descriptor = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (descriptor == NULL)
+ break;
+
+ if (descriptor[0] == 'L' || descriptor[0] == '[') {
+ /* non-primitive type */
+ if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+ return false;
+ }
+ }
+
+ /* check the return type */
+ descriptor = dexProtoGetReturnType(&meth->prototype);
+ if (descriptor[0] == 'L' || descriptor[0] == '[') {
+ if (!compareDescriptorClasses(descriptor, clazz1, clazz2))
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Validate the descriptors in the superclass and interfaces.
+ *
+ * What we need to do is ensure that the classes named in the method
+ * descriptors in our ancestors and ourselves resolve to the same class
+ * objects. The only time this matters is when the classes come from
+ * different class loaders, and the resolver might come up with a
+ * different answer for the same class name depending on context.
+ *
+ * We don't need to check to see if an interface's methods match with
+ * its superinterface's methods, because you can't instantiate an
+ * interface and do something inappropriate with it. If interface I1
+ * extends I2 and is implemented by C, and I1 and I2 are in separate
+ * class loaders and have conflicting views of other classes, we will
+ * catch the conflict when we process C. Anything that implements I1 is
+ * doomed to failure, but we don't need to catch that while processing I1.
+ *
+ * On failure, throws an exception and returns "false".
+ */
+static bool validateSuperDescriptors(const ClassObject* clazz)
+{
+ int i;
+
+ if (dvmIsInterfaceClass(clazz))
+ return true;
+
+ /*
+ * Start with the superclass-declared methods.
+ */
+ if (clazz->super != NULL &&
+ clazz->classLoader != clazz->super->classLoader)
+ {
+ /*
+ * Walk through every method declared in the superclass, and
+ * compare resolved descriptor components. We pull the Method
+ * structs out of the vtable. It doesn't matter whether we get
+ * the struct from the parent or child, since we just need the
+ * UTF-8 descriptor, which must match.
+ *
+ * We need to do this even for the stuff inherited from Object,
+ * because it's possible that the new class loader has redefined
+ * a basic class like String.
+ */
+ const Method* meth;
+
+ //printf("Checking %s %p vs %s %p\n",
+ // clazz->descriptor, clazz->classLoader,
+ // clazz->super->descriptor, clazz->super->classLoader);
+ for (i = clazz->super->vtableCount - 1; i >= 0; i--) {
+ meth = clazz->vtable[i];
+ if (!checkMethodDescriptorClasses(meth, clazz->super, clazz)) {
+ LOGW("Method mismatch: %s in %s (cl=%p) and super %s (cl=%p)\n",
+ meth->name, clazz->descriptor, clazz->classLoader,
+ clazz->super->descriptor, clazz->super->classLoader);
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "Classes resolve differently in superclass");
+ return false;
+ }
+ }
+ }
+
+ /*
+ * Check all interfaces we implement.
+ */
+ for (i = 0; i < clazz->iftableCount; i++) {
+ const InterfaceEntry* iftable = &clazz->iftable[i];
+
+ if (clazz->classLoader != iftable->clazz->classLoader) {
+ const ClassObject* iface = iftable->clazz;
+ int j;
+
+ for (j = 0; j < iface->virtualMethodCount; j++) {
+ const Method* meth;
+ int vtableIndex;
+
+ vtableIndex = iftable->methodIndexArray[j];
+ meth = clazz->vtable[vtableIndex];
+
+ if (!checkMethodDescriptorClasses(meth, iface, clazz)) {
+ LOGW("Method mismatch: %s in %s (cl=%p) and "
+ "iface %s (cl=%p)\n",
+ meth->name, clazz->descriptor, clazz->classLoader,
+ iface->descriptor, iface->classLoader);
+ dvmThrowException("Ljava/lang/LinkageError;",
+ "Classes resolve differently in interface");
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Returns true if the class is being initialized by us (which means that
+ * calling dvmInitClass will return immediately after fiddling with locks).
+ *
+ * There isn't a race here, because either clazz->initThreadId won't match
+ * us, or it will and it was set in the same thread.
+ */
+bool dvmIsClassInitializing(const ClassObject* clazz)
+{
+ return (clazz->status == CLASS_INITIALIZING &&
+ clazz->initThreadId == dvmThreadSelf()->threadId);
+}
+
+/*
+ * If a class has not been initialized, do so by executing the code in
+ * <clinit>. The sequence is described in the VM spec v2 2.17.5.
+ *
+ * It is possible for multiple threads to arrive here simultaneously, so
+ * we need to lock the class while we check stuff. We know that no
+ * interpreted code has access to the class yet, so we can use the class's
+ * monitor lock.
+ *
+ * We will often be called recursively, e.g. when the <clinit> code resolves
+ * one of its fields, the field resolution will try to initialize the class.
+ *
+ * This can get very interesting if a class has a static field initialized
+ * to a new instance of itself. <clinit> will end up calling <init> on
+ * the members it is initializing, which is fine unless it uses the contents
+ * of static fields to initialize instance fields. This will leave the
+ * static-referenced objects in a partially initialized state. This is
+ * reasonably rare and can sometimes be cured with proper field ordering.
+ *
+ * On failure, returns "false" with an exception raised.
+ *
+ * -----
+ *
+ * It is possible to cause a deadlock by having a situation like this:
+ * class A { static { sleep(10000); new B(); } }
+ * class B { static { sleep(10000); new A(); } }
+ * new Thread() { public void run() { new A(); } }.start();
+ * new Thread() { public void run() { new B(); } }.start();
+ * This appears to be expected under the spec.
+ *
+ * The interesting question is what to do if somebody calls Thread.interrupt()
+ * on one of the deadlocked threads. According to the VM spec, they're both
+ * sitting in "wait". Should the interrupt code quietly raise the
+ * "interrupted" flag, or should the "wait" return immediately with an
+ * exception raised?
+ *
+ * This gets a little murky. The VM spec says we call "wait", and the
+ * spec for Thread.interrupt says Object.wait is interruptible. So it
+ * seems that, if we get unlucky and interrupt class initialization, we
+ * are expected to throw (which gets converted to ExceptionInInitializerError
+ * since InterruptedException is checked).
+ *
+ * There are a couple of problems here. First, all threads are expected to
+ * present a consistent view of class initialization, so we can't have it
+ * fail in one thread and succeed in another. Second, once a class fails
+ * to initialize, it must *always* fail. This means that a stray interrupt()
+ * call could render a class unusable for the lifetime of the VM.
+ *
+ * In most cases -- the deadlock example above being a counter-example --
+ * the interrupting thread can't tell whether the target thread handled
+ * the initialization itself or had to wait while another thread did the
+ * work. Refusing to interrupt class initialization is, in most cases,
+ * not something that a program can reliably detect.
+ *
+ * On the assumption that interrupting class initialization is highly
+ * undesirable in most circumstances, and that failing to do so does not
+ * deviate from the spec in a meaningful way, we don't allow class init
+ * to be interrupted by Thread.interrupt().
+ */
+bool dvmInitClass(ClassObject* clazz)
+{
+#if LOG_CLASS_LOADING
+ bool initializedByUs = false;
+#endif
+
+ Thread* self = dvmThreadSelf();
+ const Method* method;
+
+ dvmLockObject(self, (Object*) clazz);
+ assert(dvmIsClassLinked(clazz) || clazz->status == CLASS_ERROR);
+
+ /*
+ * If the class hasn't been verified yet, do so now.
+ */
+ if (clazz->status < CLASS_VERIFIED) {
+ /*
+ * If we're in an "erroneous" state, throw an exception and bail.
+ */
+ if (clazz->status == CLASS_ERROR) {
+ throwEarlierClassFailure(clazz);
+ goto bail_unlock;
+ }
+
+ assert(clazz->status == CLASS_RESOLVED);
+ assert(!IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+
+ if (gDvm.classVerifyMode == VERIFY_MODE_NONE ||
+ (gDvm.classVerifyMode == VERIFY_MODE_REMOTE &&
+ clazz->classLoader == NULL))
+ {
+ LOGV("+++ not verifying class %s (cl=%p)\n",
+ clazz->descriptor, clazz->classLoader);
+ goto noverify;
+ }
+
+ if (!gDvm.optimizing)
+ LOGV("+++ late verify on %s\n", clazz->descriptor);
+
+ /*
+ * We're not supposed to optimize an unverified class, but during
+ * development this mode was useful. We can't verify an optimized
+ * class because the optimization process discards information.
+ */
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) {
+ LOGW("Class '%s' was optimized without verification; "
+ "not verifying now\n",
+ clazz->descriptor);
+ LOGW(" ('rm /data/dalvik-cache/*' and restart to fix this)");
+ goto verify_failed;
+ }
+
+ clazz->status = CLASS_VERIFYING;
+ if (!dvmVerifyClass(clazz, VERIFY_DEFAULT)) {
+verify_failed:
+ dvmThrowExceptionWithClassMessage("Ljava/lang/VerifyError;",
+ clazz->descriptor);
+ clazz->verifyErrorClass = dvmGetException(self)->clazz;
+ clazz->status = CLASS_ERROR;
+ goto bail_unlock;
+ }
+
+ clazz->status = CLASS_VERIFIED;
+ }
+noverify:
+
+ if (clazz->status == CLASS_INITIALIZED)
+ goto bail_unlock;
+
+ while (clazz->status == CLASS_INITIALIZING) {
+ /* we caught somebody else in the act; was it us? */
+ if (clazz->initThreadId == self->threadId) {
+ //LOGV("HEY: found a recursive <clinit>\n");
+ goto bail_unlock;
+ }
+
+ if (dvmCheckException(self)) {
+ LOGW("GLITCH: exception pending at start of class init\n");
+ dvmAbort();
+ }
+
+ /*
+ * Wait for the other thread to finish initialization. We pass
+ * "false" for the "interruptShouldThrow" arg so it doesn't throw
+ * an exception on interrupt.
+ */
+ dvmObjectWait(self, (Object*) clazz, 0, 0, false);
+
+ /*
+ * When we wake up, repeat the test for init-in-progress. If there's
+ * an exception pending (only possible if "interruptShouldThrow"
+ * was set), bail out.
+ */
+ if (dvmCheckException(self)) {
+ LOGI("Class init of '%s' failing with wait() exception\n",
+ clazz->descriptor);
+ /*
+ * TODO: this is bogus, because it means the two threads have a
+ * different idea of the class status. We need to flag the
+ * class as bad and ensure that the initializer thread respects
+ * our notice. If we get lucky and wake up after the class has
+ * finished initialization but before being woken, we have to
+ * swallow the exception, perhaps raising thread->interrupted
+ * to preserve semantics.
+ *
+ * Since we're not currently allowing interrupts, this should
+ * never happen and we don't need to fix this.
+ */
+ assert(false);
+ throwClinitError();
+ clazz->status = CLASS_ERROR;
+ goto bail_unlock;
+ }
+ if (clazz->status == CLASS_INITIALIZING) {
+ LOGI("Waiting again for class init\n");
+ continue;
+ }
+ assert(clazz->status == CLASS_INITIALIZED ||
+ clazz->status == CLASS_ERROR);
+ if (clazz->status == CLASS_ERROR) {
+ /*
+ * The caller wants an exception, but it was thrown in a
+ * different thread. Synthesize one here.
+ */
+ dvmThrowException("Ljava/lang/UnsatisfiedLinkError;",
+ "(<clinit> failed, see exception in other thread)");
+ }
+ goto bail_unlock;
+ }
+
+ /* see if we failed previously */
+ if (clazz->status == CLASS_ERROR) {
+ // might be wise to unlock before throwing; depends on which class
+ // it is that we have locked
+ dvmUnlockObject(self, (Object*) clazz);
+ throwEarlierClassFailure(clazz);
+ return false;
+ }
+
+ /*
+ * We're ready to go, and have exclusive access to the class.
+ *
+ * Before we start initialization, we need to do one extra bit of
+ * validation: make sure that the methods declared here match up
+ * with our superclass and interfaces. We know that the UTF-8
+ * descriptors match, but classes from different class loaders can
+ * have the same name.
+ *
+ * We do this now, rather than at load/link time, for the same reason
+ * that we defer verification.
+ *
+ * It's unfortunate that we need to do this at all, but we risk
+ * mixing reference types with identical names (see Dalvik test 068).
+ */
+ if (!validateSuperDescriptors(clazz)) {
+ assert(dvmCheckException(self));
+ clazz->status = CLASS_ERROR;
+ goto bail_unlock;
+ }
+
+ /*
+ * Let's initialize this thing.
+ *
+ * We unlock the object so that other threads can politely sleep on
+ * our mutex with Object.wait(), instead of hanging or spinning trying
+ * to grab our mutex.
+ */
+ assert(clazz->status < CLASS_INITIALIZING);
+
+#if LOG_CLASS_LOADING
+ // We started initializing.
+ logClassLoad('+', clazz);
+ initializedByUs = true;
+#endif
+
+ clazz->status = CLASS_INITIALIZING;
+ clazz->initThreadId = self->threadId;
+ dvmUnlockObject(self, (Object*) clazz);
+
+ /* init our superclass */
+ if (clazz->super != NULL && clazz->super->status != CLASS_INITIALIZED) {
+ assert(!dvmIsInterfaceClass(clazz));
+ if (!dvmInitClass(clazz->super)) {
+ assert(dvmCheckException(self));
+ clazz->status = CLASS_ERROR;
+ /* wake up anybody who started waiting while we were unlocked */
+ dvmLockObject(self, (Object*) clazz);
+ goto bail_notify;
+ }
+ }
+
+ /* Initialize any static fields whose values are
+ * stored in the Dex file. This should include all of the
+ * simple "final static" fields, which are required to
+ * be initialized first. (vmspec 2 sec 2.17.5 item 8)
+ * More-complicated final static fields should be set
+ * at the beginning of <clinit>; all we can do is trust
+ * that the compiler did the right thing.
+ */
+ initSFields(clazz);
+
+ /* Execute any static initialization code.
+ */
+ method = dvmFindDirectMethodByDescriptor(clazz, "<clinit>", "()V");
+ if (method == NULL) {
+ LOGVV("No <clinit> found for %s\n", clazz->descriptor);
+ } else {
+ LOGVV("Invoking %s.<clinit>\n", clazz->descriptor);
+ JValue unused;
+ dvmCallMethod(self, method, NULL, &unused);
+ }
+
+ if (dvmCheckException(self)) {
+ /*
+ * We've had an exception thrown during static initialization. We
+ * need to throw an ExceptionInInitializerError, but we want to
+ * tuck the original exception into the "cause" field.
+ */
+ LOGW("Exception %s thrown during %s.<clinit>\n",
+ (dvmGetException(self)->clazz)->descriptor, clazz->descriptor);
+ throwClinitError();
+ //LOGW("+++ replaced\n");
+
+ dvmLockObject(self, (Object*) clazz);
+ clazz->status = CLASS_ERROR;
+ } else {
+ /* success! */
+ dvmLockObject(self, (Object*) clazz);
+ clazz->status = CLASS_INITIALIZED;
+ LOGVV("Initialized class: %s\n", clazz->descriptor);
+ }
+
+bail_notify:
+ /*
+ * Notify anybody waiting on the object.
+ */
+ dvmObjectNotifyAll(self, (Object*) clazz);
+
+bail_unlock:
+
+#if LOG_CLASS_LOADING
+ if (initializedByUs) {
+ // We finished initializing.
+ logClassLoad('-', clazz);
+ }
+#endif
+
+ dvmUnlockObject(self, (Object*) clazz);
+
+ return (clazz->status != CLASS_ERROR);
+}
+
+/*
+ * Replace method->nativeFunc and method->insns with new values. This is
+ * performed on resolution of a native method.
+ */
+void dvmSetNativeFunc(const Method* method, DalvikBridgeFunc func,
+ const u2* insns)
+{
+ ClassObject* clazz = method->clazz;
+
+ /* just open up both; easier that way */
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+ ((Method*)method)->nativeFunc = func;
+ ((Method*)method)->insns = insns;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * Add a RegisterMap to a Method. This is done when we verify the class
+ * and compute the register maps at class initialization time, which means
+ * that "pMap" is on the heap and should be freed when the Method is
+ * discarded.
+ */
+void dvmSetRegisterMap(Method* method, const RegisterMap* pMap)
+{
+ ClassObject* clazz = method->clazz;
+
+ if (method->registerMap != NULL) {
+ LOGW("WARNING: registerMap already set for %s.%s\n",
+ method->clazz->descriptor, method->name);
+ /* keep going */
+ }
+
+ /* might be virtual or direct */
+ dvmLinearReadWrite(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadWrite(clazz->classLoader, clazz->directMethods);
+
+ method->registerMap = pMap;
+
+ dvmLinearReadOnly(clazz->classLoader, clazz->virtualMethods);
+ dvmLinearReadOnly(clazz->classLoader, clazz->directMethods);
+}
+
+/*
+ * dvmHashForeach callback. A nonzero return value causes foreach to
+ * bail out.
+ */
+static int findClassCallback(void* vclazz, void* arg)
+{
+ ClassObject* clazz = vclazz;
+ const char* descriptor = (const char*) arg;
+
+ if (strcmp(clazz->descriptor, descriptor) == 0)
+ return (int) clazz;
+ return 0;
+}
+
+/*
+ * Find a loaded class by descriptor. Returns the first one found.
+ * Because there can be more than one if class loaders are involved,
+ * this is not an especially good API. (Currently only used by the
+ * debugger and "checking" JNI.)
+ *
+ * "descriptor" should have the form "Ljava/lang/Class;" or
+ * "[Ljava/lang/Class;", i.e. a descriptor and not an internal-form
+ * class name.
+ */
+ClassObject* dvmFindLoadedClass(const char* descriptor)
+{
+ int result;
+
+ dvmHashTableLock(gDvm.loadedClasses);
+ result = dvmHashForeach(gDvm.loadedClasses, findClassCallback,
+ (void*) descriptor);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+
+ return (ClassObject*) result;
+}
+
+/*
+ * Retrieve the system (a/k/a application) class loader.
+ */
+Object* dvmGetSystemClassLoader(void)
+{
+ ClassObject* clazz;
+ Method* getSysMeth;
+ Object* loader;
+
+ clazz = dvmFindSystemClass("Ljava/lang/ClassLoader;");
+ if (clazz == NULL)
+ return NULL;
+
+ getSysMeth = dvmFindDirectMethodByDescriptor(clazz, "getSystemClassLoader",
+ "()Ljava/lang/ClassLoader;");
+ if (getSysMeth == NULL)
+ return NULL;
+
+ JValue result;
+ dvmCallMethod(dvmThreadSelf(), getSysMeth, NULL, &result);
+ loader = (Object*)result.l;
+ return loader;
+}
+
+
+/*
+ * This is a dvmHashForeach callback.
+ */
+static int dumpClass(void* vclazz, void* varg)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ const ClassObject* super;
+ int flags = (int) varg;
+ char* desc;
+ int i;
+
+ if (clazz == NULL) {
+ LOGI("dumpClass: ignoring request to dump null class\n");
+ return 0;
+ }
+
+ if ((flags & kDumpClassFullDetail) == 0) {
+ bool showInit = (flags & kDumpClassInitialized) != 0;
+ bool showLoader = (flags & kDumpClassClassLoader) != 0;
+ const char* initStr;
+
+ initStr = dvmIsClassInitialized(clazz) ? "true" : "false";
+
+ if (showInit && showLoader)
+ LOGI("%s %p %s\n", clazz->descriptor, clazz->classLoader, initStr);
+ else if (showInit)
+ LOGI("%s %s\n", clazz->descriptor, initStr);
+ else if (showLoader)
+ LOGI("%s %p\n", clazz->descriptor, clazz->classLoader);
+ else
+ LOGI("%s\n", clazz->descriptor);
+
+ return 0;
+ }
+
+ /* clazz->super briefly holds the superclass index during class prep */
+ if ((u4)clazz->super > 0x10000 && (u4) clazz->super != (u4)-1)
+ super = clazz->super;
+ else
+ super = NULL;
+
+ LOGI("----- %s '%s' cl=%p ser=0x%08x -----\n",
+ dvmIsInterfaceClass(clazz) ? "interface" : "class",
+ clazz->descriptor, clazz->classLoader, clazz->serialNumber);
+ LOGI(" objectSize=%d (%d from super)\n", (int) clazz->objectSize,
+ super != NULL ? (int) super->objectSize : -1);
+ LOGI(" access=0x%04x.%04x\n", clazz->accessFlags >> 16,
+ clazz->accessFlags & JAVA_FLAGS_MASK);
+ if (super != NULL)
+ LOGI(" super='%s' (cl=%p)\n", super->descriptor, super->classLoader);
+ if (dvmIsArrayClass(clazz)) {
+ LOGI(" dimensions=%d elementClass=%s\n",
+ clazz->arrayDim, clazz->elementClass->descriptor);
+ }
+ if (clazz->iftableCount > 0) {
+ LOGI(" interfaces (%d):\n", clazz->iftableCount);
+ for (i = 0; i < clazz->iftableCount; i++) {
+ InterfaceEntry* ent = &clazz->iftable[i];
+ int j;
+
+ LOGI(" %2d: %s (cl=%p)\n",
+ i, ent->clazz->descriptor, ent->clazz->classLoader);
+
+ /* enable when needed */
+ if (false && ent->methodIndexArray != NULL) {
+ for (j = 0; j < ent->clazz->virtualMethodCount; j++)
+ LOGI(" %2d: %d %s %s\n",
+ j, ent->methodIndexArray[j],
+ ent->clazz->virtualMethods[j].name,
+ clazz->vtable[ent->methodIndexArray[j]]->name);
+ }
+ }
+ }
+ if (!dvmIsInterfaceClass(clazz)) {
+ LOGI(" vtable (%d entries, %d in super):\n", clazz->vtableCount,
+ super != NULL ? super->vtableCount : 0);
+ for (i = 0; i < clazz->vtableCount; i++) {
+ desc = dexProtoCopyMethodDescriptor(&clazz->vtable[i]->prototype);
+ LOGI(" %s%2d: %p %20s %s\n",
+ (i != clazz->vtable[i]->methodIndex) ? "*** " : "",
+ (u4) clazz->vtable[i]->methodIndex, clazz->vtable[i],
+ clazz->vtable[i]->name, desc);
+ free(desc);
+ }
+ LOGI(" direct methods (%d entries):\n", clazz->directMethodCount);
+ for (i = 0; i < clazz->directMethodCount; i++) {
+ desc = dexProtoCopyMethodDescriptor(
+ &clazz->directMethods[i].prototype);
+ LOGI(" %2d: %20s %s\n", i, clazz->directMethods[i].name,
+ desc);
+ free(desc);
+ }
+ } else {
+ LOGI(" interface methods (%d):\n", clazz->virtualMethodCount);
+ for (i = 0; i < clazz->virtualMethodCount; i++) {
+ desc = dexProtoCopyMethodDescriptor(
+ &clazz->virtualMethods[i].prototype);
+ LOGI(" %2d: %2d %20s %s\n", i,
+ (u4) clazz->virtualMethods[i].methodIndex,
+ clazz->virtualMethods[i].name,
+ desc);
+ free(desc);
+ }
+ }
+ if (clazz->sfieldCount > 0) {
+ LOGI(" static fields (%d entries):\n", clazz->sfieldCount);
+ for (i = 0; i < clazz->sfieldCount; i++) {
+ LOGI(" %2d: %20s %s\n", i, clazz->sfields[i].field.name,
+ clazz->sfields[i].field.signature);
+ }
+ }
+ if (clazz->ifieldCount > 0) {
+ LOGI(" instance fields (%d entries):\n", clazz->ifieldCount);
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ LOGI(" %2d: %20s %s\n", i, clazz->ifields[i].field.name,
+ clazz->ifields[i].field.signature);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Dump the contents of a single class.
+ *
+ * Pass kDumpClassFullDetail into "flags" to get lots of detail.
+ */
+void dvmDumpClass(const ClassObject* clazz, int flags)
+{
+ dumpClass((void*) clazz, (void*) flags);
+}
+
+/*
+ * Dump the contents of all classes.
+ */
+void dvmDumpAllClasses(int flags)
+{
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashForeach(gDvm.loadedClasses, dumpClass, (void*) flags);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+
+/*
+ * Get the number of loaded classes
+ */
+int dvmGetNumLoadedClasses()
+{
+ int count;
+ dvmHashTableLock(gDvm.loadedClasses);
+ count = dvmHashTableNumEntries(gDvm.loadedClasses);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+ return count;
+}
+
+/*
+ * Write some statistics to the log file.
+ */
+void dvmDumpLoaderStats(const char* msg)
+{
+ LOGV("VM stats (%s): cls=%d/%d meth=%d ifld=%d sfld=%d linear=%d\n",
+ msg, gDvm.numLoadedClasses, dvmHashTableNumEntries(gDvm.loadedClasses),
+ gDvm.numDeclaredMethods, gDvm.numDeclaredInstFields,
+ gDvm.numDeclaredStaticFields, gDvm.pBootLoaderAlloc->curOffset);
+#ifdef COUNT_PRECISE_METHODS
+ LOGI("GC precise methods: %d\n",
+ dvmPointerSetGetCount(gDvm.preciseMethods));
+#endif
+}
+
+#ifdef PROFILE_FIELD_ACCESS
+/*
+ * Dump the field access counts for all fields in this method.
+ */
+static int dumpAccessCounts(void* vclazz, void* varg)
+{
+ const ClassObject* clazz = (const ClassObject*) vclazz;
+ int i;
+
+ for (i = 0; i < clazz->ifieldCount; i++) {
+ Field* field = &clazz->ifields[i].field;
+
+ if (field->gets != 0)
+ printf("GI %d %s.%s\n", field->gets,
+ field->clazz->descriptor, field->name);
+ if (field->puts != 0)
+ printf("PI %d %s.%s\n", field->puts,
+ field->clazz->descriptor, field->name);
+ }
+ for (i = 0; i < clazz->sfieldCount; i++) {
+ Field* field = &clazz->sfields[i].field;
+
+ if (field->gets != 0)
+ printf("GS %d %s.%s\n", field->gets,
+ field->clazz->descriptor, field->name);
+ if (field->puts != 0)
+ printf("PS %d %s.%s\n", field->puts,
+ field->clazz->descriptor, field->name);
+ }
+
+ return 0;
+}
+
+/*
+ * Dump the field access counts for all loaded classes.
+ */
+void dvmDumpFieldAccessCounts(void)
+{
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashForeach(gDvm.loadedClasses, dumpAccessCounts, NULL);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+}
+#endif
+
+
+/*
+ * Mark all classes associated with the built-in loader.
+ */
+static int markClassObject(void *clazz, void *arg)
+{
+ UNUSED_PARAMETER(arg);
+
+ dvmMarkObjectNonNull((Object *)clazz);
+ return 0;
+}
+
+/*
+ * The garbage collector calls this to mark the class objects for all
+ * loaded classes.
+ */
+void dvmGcScanRootClassLoader()
+{
+ /* dvmClassStartup() may not have been called before the first GC.
+ */
+ if (gDvm.loadedClasses != NULL) {
+ dvmHashTableLock(gDvm.loadedClasses);
+ dvmHashForeach(gDvm.loadedClasses, markClassObject, NULL);
+ dvmHashTableUnlock(gDvm.loadedClasses);
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * Method Prototypes and Descriptors
+ * ===========================================================================
+ */
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(). The
+ * name is considered the "major" order and the prototype the "minor"
+ * order. The prototypes are compared as if by dvmCompareMethodProtos().
+ */
+int dvmCompareMethodNamesAndProtos(const Method* method1,
+ const Method* method2)
+{
+ int result = strcmp(method1->name, method2->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dvmCompareMethodProtos(method1, method2);
+}
+
+/*
+ * Compare the two method names and prototypes, a la strcmp(), ignoring
+ * the return value. The name is considered the "major" order and the
+ * prototype the "minor" order. The prototypes are compared as if by
+ * dvmCompareMethodArgProtos().
+ */
+int dvmCompareMethodNamesAndParameterProtos(const Method* method1,
+ const Method* method2)
+{
+ int result = strcmp(method1->name, method2->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dvmCompareMethodParameterProtos(method1, method2);
+}
+
+/*
+ * Compare a (name, prototype) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameProtoAndMethod(const char* name,
+ const DexProto* proto, const Method* method)
+{
+ int result = strcmp(name, method->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dexProtoCompare(proto, &method->prototype);
+}
+
+/*
+ * Compare a (name, method descriptor) pair with the (name, prototype) of
+ * a method, a la strcmp(). The name is considered the "major" order and
+ * the prototype the "minor" order. The descriptor and prototype are
+ * compared as if by dvmCompareDescriptorAndMethodProto().
+ */
+int dvmCompareNameDescriptorAndMethod(const char* name,
+ const char* descriptor, const Method* method)
+{
+ int result = strcmp(name, method->name);
+
+ if (result != 0) {
+ return result;
+ }
+
+ return dvmCompareDescriptorAndMethodProto(descriptor, method);
+}