Fix proxy return ClassCastException and add test.

Add test for Bug: 8250775 that doesn't effect ART. Expand test case further.
ClassCastException has no constructor expecting a cause and so using this
creates a JNI failure. Switch to not using a wrapped exception as the wrapping
itself wasn't adding information.

Change-Id: I32dd541e1a1022089ec993fa4f4646042c5bf1fa
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 84a19cf..d76999c 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -440,11 +440,15 @@
       mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
       bool unboxed_okay = UnboxPrimitiveForResult(result_ref, result_type, result_unboxed);
       if (!unboxed_okay) {
-        soa.Self()->ThrowNewWrappedException("Ljava/lang/ClassCastException;",
-                                             StringPrintf("Couldn't convert result of type %s to %s",
-                                                          PrettyTypeOf(result_ref).c_str(),
-                                                          PrettyDescriptor(result_type).c_str()
-                                             ).c_str());
+        // UnboxPrimitiveForResult creates an IllegalArgumentException. Discard and create a
+        // meaningful ClassCastException.
+        DCHECK(soa.Self()->IsExceptionPending());
+        soa.Self()->ClearException();
+        soa.Self()->ThrowNewException("Ljava/lang/ClassCastException;",
+                                      StringPrintf("Couldn't convert result of type %s to %s",
+                                                   PrettyTypeOf(result_ref).c_str(),
+                                                   PrettyDescriptor(result_type).c_str()
+                                      ).c_str());
       }
       return result_unboxed;
     }
diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt
index 578fd97..eafaf1d 100644
--- a/test/044-proxy/expected.txt
+++ b/test/044-proxy/expected.txt
@@ -82,3 +82,11 @@
 Invoke public abstract void InterfaceW1.bothThrowBase() throws BaseException,SubException,SubSubException
  (no args)
 Got expected exception
+Proxy methods: [public final boolean $Proxy7.equals(java.lang.Object), public final java.lang.Object $Proxy7.foo(), public final java.lang.String $Proxy7.foo(), public final int $Proxy7.hashCode(), public final java.lang.String $Proxy7.toString()]
+Invocation of public abstract java.lang.String NarrowingTest$I2.foo()
+Invoking foo using I2 type: hello
+Invocation of public abstract java.lang.Object NarrowingTest$I1.foo()
+Invoking foo using I1 type: 1
+Invocation of public abstract java.lang.String NarrowingTest$I2.foo()
+Got expected exception
+Proxy narrowed invocation return type passed
diff --git a/test/044-proxy/src/Main.java b/test/044-proxy/src/Main.java
index 5396ab8..078569f 100644
--- a/test/044-proxy/src/Main.java
+++ b/test/044-proxy/src/Main.java
@@ -26,5 +26,6 @@
         Clash3.main(null);
         Clash4.main(null);
         WrappedThrow.main(null);
+        NarrowingTest.main(null);
     }
 }
diff --git a/test/044-proxy/src/NarrowingTest.java b/test/044-proxy/src/NarrowingTest.java
new file mode 100644
index 0000000..3b94b76
--- /dev/null
+++ b/test/044-proxy/src/NarrowingTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+import java.lang.reflect.*;
+import java.util.Arrays;
+
+class NarrowingTest {
+
+   interface I1 {
+       public Object foo();
+   }
+
+   interface I2 extends I1 {
+       // Note that this method declaration narrows the return type.
+       @Override
+       public String foo();
+   }
+
+   public static void main(String[] args) {
+       I2 proxy = (I2) Proxy.newProxyInstance(NarrowingTest.class.getClassLoader(),
+                                              new Class<?>[] { I2.class },
+               new InvocationHandler() {
+                   int count = 0;
+                   @Override
+                   public Object invoke(Object proxy, Method method,
+                                        Object[] args) throws Throwable {
+                       System.out.println("Invocation of " + method);
+                       if (count == 0) {
+                           count++;
+                           return "hello";
+                       } else {
+                           return Integer.valueOf(1);
+                       }
+                   }
+               });
+
+       Method[] methods = proxy.getClass().getDeclaredMethods();
+       System.out.println("Proxy methods: " + Arrays.deepToString(methods));
+
+       System.out.println("Invoking foo using I2 type: " + proxy.foo());
+
+       I1 proxyAsParent = proxy;
+       System.out.println("Invoking foo using I1 type: " + proxyAsParent.foo());
+
+       try {
+           proxy.foo();
+           System.out.println("Didn't get expected exception");
+       } catch (ClassCastException e) {
+           // With an I2 invocation returning an integer is an exception.
+           System.out.println("Got expected exception");
+       }
+
+       System.out.println("Proxy narrowed invocation return type passed");
+   }
+}