Correct handling of certain incompatible class changes.

Tests 065 and 066 depened on the old behavior of the verifier.  Updating
them to the new behavior wasn't enough, though, because they weren't
reporting the right error.  The verifier's resolution code now examines
the exception to see if the load failure was caused by an incompatible
class change error.

I also updated the description of test 071 to note that it will fail on
the device if you don't have an sdcard.

Added a method to get the exception "cause" field.  It handles the
"uninitialized" state, which I keep forgetting about.

Spruced up dvmDumpObject, which hadn't been used in a while.  Fixed a
warning in Profile.c.

For internal bug 1866729.
diff --git a/tests/065-mismatched-implements/expected.txt b/tests/065-mismatched-implements/expected.txt
index 36ebe5e..09c0596 100644
--- a/tests/065-mismatched-implements/expected.txt
+++ b/tests/065-mismatched-implements/expected.txt
@@ -1,3 +1 @@
-Dalvik VM unable to find static main(String[]) in 'Main'
-java.lang.VerifyError: Main
-	at dalvik.system.NativeStart.main(Native Method)
+Got expected ICCE
diff --git a/tests/065-mismatched-implements/info.txt b/tests/065-mismatched-implements/info.txt
index 58d9b69..74c3ff3 100644
--- a/tests/065-mismatched-implements/info.txt
+++ b/tests/065-mismatched-implements/info.txt
@@ -1,19 +1,2 @@
 This tests what happens when class A implements interface B, but somebody
 turns B into an abstract class without rebuilding A.
-
-If you run this with --no-verify you get reasonable results:
-
-java.lang.NoClassDefFoundError: Base
-	at Main.main(Main.java:8)
-	at dalvik.system.NativeStart.main(Native Method)
-Caused by: java.lang.IncompatibleClassChangeError: Base
-	at dalvik.system.DexFile.defineClass(Native Method)
-	at dalvik.system.DexFile.loadClass(DexFile.java:91)
-	at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:175)
-	at java.lang.ClassLoader.loadClass(ClassLoader.java:453)
-	at java.lang.ClassLoader.loadClass(ClassLoader.java:421)
-	... 2 more
-
-With the verifier enabled, you get a relatively content-free VerifyError
-with the detail only appearing in the log file.
-
diff --git a/tests/065-mismatched-implements/src/Indirect.java b/tests/065-mismatched-implements/src/Indirect.java
new file mode 100644
index 0000000..dd87a65
--- /dev/null
+++ b/tests/065-mismatched-implements/src/Indirect.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Error indirection class.
+ *
+ * Some VMs will load this class and fail on the "new" call, others will
+ * refuse to load this class at all.
+ */
+public class Indirect {
+    public static void main() {
+        Base base = new Base();
+    }
+}
+
diff --git a/tests/065-mismatched-implements/src/Main.java b/tests/065-mismatched-implements/src/Main.java
index 4a25232..6993b17 100644
--- a/tests/065-mismatched-implements/src/Main.java
+++ b/tests/065-mismatched-implements/src/Main.java
@@ -1,11 +1,30 @@
-// Copyright 2008 The Android Open Source Project
+/*
+ * 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.
+ */
 
 /*
  * Test field access through reflection.
  */
 public class Main {
     public static void main(String[] args) {
-        Base base = new Base();
+        try {
+            Indirect.main();
+            System.err.println("Succeeded unexpectedly");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected ICCE");
+        }
     }
 }
 
diff --git a/tests/066-mismatched-super/expected.txt b/tests/066-mismatched-super/expected.txt
index 36ebe5e..09c0596 100644
--- a/tests/066-mismatched-super/expected.txt
+++ b/tests/066-mismatched-super/expected.txt
@@ -1,3 +1 @@
-Dalvik VM unable to find static main(String[]) in 'Main'
-java.lang.VerifyError: Main
-	at dalvik.system.NativeStart.main(Native Method)
+Got expected ICCE
diff --git a/tests/066-mismatched-super/info.txt b/tests/066-mismatched-super/info.txt
index 2158899..7865ffc 100644
--- a/tests/066-mismatched-super/info.txt
+++ b/tests/066-mismatched-super/info.txt
@@ -1,19 +1,2 @@
 This tests what happens when class A extends abstract class B, but somebody
 turns B into an interface without rebuilding A.
-
-If you run this with --no-verify you get reasonable results:
-
-java.lang.NoClassDefFoundError: Base
-	at Main.main(Main.java:8)
-	at dalvik.system.NativeStart.main(Native Method)
-Caused by: java.lang.IncompatibleClassChangeError: superclass is an interface
-	at dalvik.system.DexFile.defineClass(Native Method)
-	at dalvik.system.DexFile.loadClass(DexFile.java:91)
-	at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:175)
-	at java.lang.ClassLoader.loadClass(ClassLoader.java:453)
-	at java.lang.ClassLoader.loadClass(ClassLoader.java:421)
-	... 2 more
-
-With the verifier enabled, you get a relatively content-free VerifyError
-with the detail only appearing in the log file.
-
diff --git a/tests/066-mismatched-super/src/Indirect.java b/tests/066-mismatched-super/src/Indirect.java
new file mode 100644
index 0000000..dd87a65
--- /dev/null
+++ b/tests/066-mismatched-super/src/Indirect.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Error indirection class.
+ *
+ * Some VMs will load this class and fail on the "new" call, others will
+ * refuse to load this class at all.
+ */
+public class Indirect {
+    public static void main() {
+        Base base = new Base();
+    }
+}
+
diff --git a/tests/066-mismatched-super/src/Main.java b/tests/066-mismatched-super/src/Main.java
index 4a25232..6993b17 100644
--- a/tests/066-mismatched-super/src/Main.java
+++ b/tests/066-mismatched-super/src/Main.java
@@ -1,11 +1,30 @@
-// Copyright 2008 The Android Open Source Project
+/*
+ * 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.
+ */
 
 /*
  * Test field access through reflection.
  */
 public class Main {
     public static void main(String[] args) {
-        Base base = new Base();
+        try {
+            Indirect.main();
+            System.err.println("Succeeded unexpectedly");
+        } catch (IncompatibleClassChangeError icce) {
+            System.out.println("Got expected ICCE");
+        }
     }
 }
 
diff --git a/tests/071-dexfile/info.txt b/tests/071-dexfile/info.txt
index 5eb0489..54d9ed0 100644
--- a/tests/071-dexfile/info.txt
+++ b/tests/071-dexfile/info.txt
@@ -1,2 +1,4 @@
 Exercise some Dalvik-specific DEX file features.  This is not expected to
 work on other VMs.
+
+NOTE: the test requires that /sdcard exists and is writable.
diff --git a/vm/Exception.c b/vm/Exception.c
index 4881640..3a56cc3 100644
--- a/vm/Exception.c
+++ b/vm/Exception.c
@@ -102,6 +102,9 @@
 
 /*
  * Cache pointers to some of the exception classes we use locally.
+ *
+ * Note this is NOT called during dexopt optimization.  Some of the fields
+ * are initialized by the verifier (dvmVerifyCodeFlow).
  */
 bool dvmExceptionStartup(void)
 {
@@ -377,6 +380,14 @@
         }
     }
 
+    if (cause != NULL) {
+        if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) {
+            LOGE("Tried to init exception with cause '%s'\n",
+                cause->clazz->descriptor);
+            dvmAbort();
+        }
+    }
+
     /*
      * The Throwable class has four public constructors:
      *  (1) Throwable()
@@ -625,6 +636,28 @@
 }
 
 /*
+ * Get the "cause" field from an exception.
+ *
+ * The Throwable class initializes the "cause" field to "this" to
+ * differentiate between being initialized to null and never being
+ * initialized.  We check for that here and convert it to NULL.
+ */
+Object* dvmGetExceptionCause(const Object* exception)
+{
+    if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) {
+        LOGE("Tried to get cause from object of type '%s'\n",
+            exception->clazz->descriptor);
+        dvmAbort();
+    }
+    Object* cause =
+        dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
+    if (cause == exception)
+        return NULL;
+    else
+        return cause;
+}
+
+/*
  * Print the stack trace of the current exception on stderr.  This is called
  * from the JNI ExceptionDescribe call.
  *
@@ -1208,9 +1241,8 @@
 
     for (;;) {
         logStackTraceOf(exception);
-        cause = (Object*) dvmGetFieldObject(exception,
-                    gDvm.offJavaLangThrowable_cause);
-        if ((cause == NULL) || (cause == exception)) {
+        cause = dvmGetExceptionCause(exception);
+        if (cause == NULL) {
             break;
         }
         LOGI("Caused by:\n");
diff --git a/vm/Exception.h b/vm/Exception.h
index 9887929..4044345 100644
--- a/vm/Exception.h
+++ b/vm/Exception.h
@@ -123,6 +123,13 @@
 void dvmWrapException(const char* newExcepStr);
 
 /*
+ * Get the "cause" field from an exception.
+ *
+ * Returns NULL if the field is null or uninitialized.
+ */
+Object* dvmGetExceptionCause(const Object* exception);
+
+/*
  * Print the exception stack trace on stderr.  Calls the exception's
  * print function.
  */
diff --git a/vm/Profile.c b/vm/Profile.c
index 8c731f9..ca25eb5 100644
--- a/vm/Profile.c
+++ b/vm/Profile.c
@@ -668,7 +668,7 @@
          * Fortunately, the trace tools can get by without the address, but
          * it would be nice to fix this.
          */
-         addr = method->nativeFunc;
+         addr = (u4) method->nativeFunc;
     } else {
         /*
          * The dexlist output shows the &DexCode.insns offset value, which
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index b37b9a9..0bb899d 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -2944,9 +2944,13 @@
     if (gDvm.classJavaLangString == NULL)
         gDvm.classJavaLangString =
             dvmFindSystemClassNoInit("Ljava/lang/String;");
-    if (gDvm.classJavaLangThrowable == NULL)
+    if (gDvm.classJavaLangThrowable == NULL) {
         gDvm.classJavaLangThrowable =
             dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
+        gDvm.offJavaLangThrowable_cause =
+            dvmFindFieldOffset(gDvm.classJavaLangThrowable,
+                "cause", "Ljava/lang/Throwable;");
+    }
     if (gDvm.classJavaLangObject == NULL)
         gDvm.classJavaLangObject =
             dvmFindSystemClassNoInit("Ljava/lang/Object;");
diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c
index 162ba9a..67acb5d 100644
--- a/vm/analysis/DexOptimize.c
+++ b/vm/analysis/DexOptimize.c
@@ -1684,9 +1684,24 @@
             LOGV("DexOpt: class %d (%s) not found\n",
                 classIdx,
                 dexStringByTypeIdx(pDvmDex->pDexFile, classIdx));
+            if (pFailure != NULL) {
+                /* dig through the wrappers to find the original failure */
+                Object* excep = dvmGetException(dvmThreadSelf());
+                while (true) {
+                    Object* cause = dvmGetExceptionCause(excep);
+                    if (cause == NULL)
+                        break;
+                    excep = cause;
+                }
+                if (strcmp(excep->clazz->descriptor,
+                    "Ljava/lang/IncompatibleClassChangeError;") == 0)
+                {
+                    *pFailure = VERIFY_ERROR_CLASS_CHANGE;
+                } else {
+                    *pFailure = VERIFY_ERROR_NO_CLASS;
+                }
+            }
             dvmClearOptException(dvmThreadSelf());
-            if (pFailure != NULL)
-                *pFailure = VERIFY_ERROR_NO_CLASS;
             return NULL;
         }
 
diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c
index 25075f4..10251db 100644
--- a/vm/analysis/DexVerify.c
+++ b/vm/analysis/DexVerify.c
@@ -36,7 +36,14 @@
     gDvm.instrWidth = dexCreateInstrWidthTable();
     gDvm.instrFormat = dexCreateInstrFormatTable();
     gDvm.instrFlags = dexCreateInstrFlagsTable();
-    return (gDvm.instrWidth != NULL && gDvm.instrFormat!= NULL);
+    if (gDvm.instrWidth == NULL || gDvm.instrFormat == NULL ||
+        gDvm.instrFlags == NULL)
+    {
+        LOGE("Unable to create instruction tables\n");
+        return false;
+    }
+
+    return true;
 }
 
 /*
diff --git a/vm/oo/Object.c b/vm/oo/Object.c
index 9cd0afd..96493f7 100644
--- a/vm/oo/Object.c
+++ b/vm/oo/Object.c
@@ -615,37 +615,44 @@
     }
 
     clazz = obj->clazz;
-    LOGV("----- Object dump: %p (%s, %d bytes) -----\n",
+    LOGD("----- Object dump: %p (%s, %d bytes) -----\n",
         obj, clazz->descriptor, (int) clazz->objectSize);
     //printHexDump(obj, clazz->objectSize);
-    LOGV("  Fields:\n");
-    for (i = 0; i < clazz->ifieldCount; i++) {
-        const InstField* pField = &clazz->ifields[i];
-        char type = pField->field.signature[0];
+    LOGD("  Fields:\n");
+    while (clazz != NULL) {
+        LOGD("    -- %s\n", clazz->descriptor);
+        for (i = 0; i < clazz->ifieldCount; i++) {
+            const InstField* pField = &clazz->ifields[i];
+            char type = pField->field.signature[0];
 
-        if (type == 'F' || type == 'D') {
-            double dval;
+            if (type == 'F' || type == 'D') {
+                double dval;
 
-            if (type == 'F')
-                dval = dvmGetFieldFloat(obj, pField->byteOffset);
-            else
-                dval = dvmGetFieldDouble(obj, pField->byteOffset);
+                if (type == 'F')
+                    dval = dvmGetFieldFloat(obj, pField->byteOffset);
+                else
+                    dval = dvmGetFieldDouble(obj, pField->byteOffset);
 
-            LOGV("  %2d: '%s' '%s' flg=%04x %.3f\n", i, pField->field.name,
-                pField->field.signature, pField->field.accessFlags, dval);
-        } else {
-            long long lval;
+                LOGD("    %2d: '%s' '%s' af=%04x off=%d %.3f\n", i,
+                    pField->field.name, pField->field.signature,
+                    pField->field.accessFlags, pField->byteOffset, dval);
+            } else {
+                u8 lval;
 
-            if (pField->field.signature[0] == 'J')
-                lval = dvmGetFieldLong(obj, pField->byteOffset);
-            else if (pField->field.signature[0] == 'Z')
-                lval = dvmGetFieldBoolean(obj, pField->byteOffset);
-            else
-                lval = dvmGetFieldInt(obj, pField->byteOffset);
+                if (type == 'J')
+                    lval = dvmGetFieldLong(obj, pField->byteOffset);
+                else if (type == 'Z')
+                    lval = dvmGetFieldBoolean(obj, pField->byteOffset);
+                else
+                    lval = dvmGetFieldInt(obj, pField->byteOffset);
 
-            LOGV("  %2d: '%s' '%s' af=%04x 0x%llx\n", i, pField->field.name,
-                pField->field.signature, pField->field.accessFlags, lval);
+                LOGD("    %2d: '%s' '%s' af=%04x off=%d 0x%08llx\n", i,
+                    pField->field.name, pField->field.signature,
+                    pField->field.accessFlags, pField->byteOffset, lval);
+            }
         }
+
+        clazz = clazz->super;
     }
 }