Update to latest dexmaker.

This fixes the bug below which prevented mocking of any ViewGroup
extension.
https://code.google.com/p/dexmaker/issues/detail?id=12

Change-Id: I459fb259244476b89b9a4b50c6a6cf88f2e1f2f6
diff --git a/README b/README
index a32a9a1..82f57a4 100644
--- a/README
+++ b/README
@@ -3,7 +3,7 @@
 The latest version of dexmaker can be found at this url:
     http://code.google.com/p/dexmaker/
 
-Version: b4fdb175545f
+Version: f54f6eac0c81120f53265b30adf9ce602e8dfc41
 License: Apache 2.0
 
 Description:
diff --git a/src/main/java/com/google/dexmaker/AppDataDirGuesser.java b/src/main/java/com/google/dexmaker/AppDataDirGuesser.java
index b59670e..86f34a1 100644
--- a/src/main/java/com/google/dexmaker/AppDataDirGuesser.java
+++ b/src/main/java/com/google/dexmaker/AppDataDirGuesser.java
@@ -61,7 +61,7 @@
         }
 
         // Parsing toString() method: yuck.  But no other way to get the path.
-        // Strip out the bit between angle brackets, that's our path.
+        // Strip out the bit between square brackets, that's our path.
         String result = classLoader.toString();
         int index = result.lastIndexOf('[');
         result = (index == -1) ? result : result.substring(index + 1);
@@ -71,13 +71,7 @@
 
     File[] guessPath(String input) {
         List<File> results = new ArrayList<File>();
-        // Post JB, the system property is set to the applications private data cache
-        // directory.
-        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
-        if (isWriteableDirectory(tmpDir)) {
-            results.add(tmpDir);
-        }
-        for (String potential : input.split(":")) {
+        for (String potential : splitPathList(input)) {
             if (!potential.startsWith("/data/app/")) {
                 continue;
             }
@@ -105,6 +99,18 @@
         return results.toArray(new File[results.size()]);
     }
 
+    static String[] splitPathList(String input) {
+       String trimmed = input;
+       if (input.startsWith("dexPath=")) {
+            int start = "dexPath=".length();
+            int end = input.indexOf(',');
+
+           trimmed = (end == -1) ? input.substring(start) : input.substring(start, end);
+       }
+
+       return trimmed.split(":");
+    }
+
     boolean fileOrDirExists(File file) {
         return file.exists();
     }
diff --git a/src/main/java/com/google/dexmaker/Code.java b/src/main/java/com/google/dexmaker/Code.java
index 4ea5e67..54409a5 100644
--- a/src/main/java/com/google/dexmaker/Code.java
+++ b/src/main/java/com/google/dexmaker/Code.java
@@ -100,7 +100,7 @@
  * unconditionally with {@link #jump jump(Label)} or conditionally based on a
  * comparison using {@link #compare compare()}.
  *
- * <p>Most methods should contain either a return instruction. Void methods
+ * <p>Most methods should contain a return instruction. Void methods
  * should use {@link #returnVoid()}; non-void methods should use {@link
  * #returnValue returnValue()} with a local whose return type matches the
  * method's return type. Constructors are considered void methods and should
@@ -506,9 +506,12 @@
     }
 
     /**
-     * Executes {@code op} and sets {@code target} to the result.
+     * Executes {@code op} and sets {@code target} to the result. For most
+     * binary operations, the types of {@code a} and {@code b} must be the same.
+     * Shift operations (like {@link BinaryOp#SHIFT_LEFT}) require {@code b} to
+     * be an {@code int}, even when {@code a} is a {@code long}.
      */
-    public <T> void op(BinaryOp op, Local<T> target, Local<T> a, Local<T> b) {
+    public <T1, T2> void op(BinaryOp op, Local<T1> target, Local<T1> a, Local<T2> b) {
         Rop rop = op.rop(StdTypeList.make(a.type.ropType, b.type.ropType));
         RegisterSpecList sources = RegisterSpecList.make(a.spec(), b.spec());
 
diff --git a/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java b/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java
index 639b3dc..774c5da 100644
--- a/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java
+++ b/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java
@@ -595,11 +595,12 @@
      */
     private Method[] getMethodsToProxyRecursive() {
         Set<MethodSetEntry> methodsToProxy = new HashSet<MethodSetEntry>();
+        Set<MethodSetEntry> seenFinalMethods = new HashSet<MethodSetEntry>();
         for (Class<?> c = baseClass; c != null; c = c.getSuperclass()) {
-            getMethodsToProxy(methodsToProxy, c);
+            getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
         }
         for (Class<?> c : interfaces) {
-            getMethodsToProxy(methodsToProxy, c);
+            getMethodsToProxy(methodsToProxy, seenFinalMethods, c);
         }
 
         Method[] results = new Method[methodsToProxy.size()];
@@ -610,10 +611,14 @@
         return results;
     }
 
-    private void getMethodsToProxy(Set<MethodSetEntry> sink, Class<?> c) {
+    private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods,
+            Class<?> c) {
         for (Method method : c.getDeclaredMethods()) {
             if ((method.getModifiers() & Modifier.FINAL) != 0) {
-                // Skip final methods, we can't override them.
+                // Skip final methods, we can't override them. We
+                // also need to remember them, in case the same
+                // method exists in a parent class.
+                seenFinalMethods.add(new MethodSetEntry(method));
                 continue;
             }
             if ((method.getModifiers() & STATIC) != 0) {
@@ -624,11 +629,17 @@
                 // Skip finalize method, it's likely important that it execute as normal.
                 continue;
             }
-            sink.add(new MethodSetEntry(method));
+            MethodSetEntry entry = new MethodSetEntry(method);
+            if (seenFinalMethods.contains(entry)) {
+                // This method is final in a child class.
+                // We can't override it.
+                continue;
+            }
+            sink.add(entry);
         }
-        
+
         for (Class<?> i : c.getInterfaces()) {
-            getMethodsToProxy(sink, i);
+            getMethodsToProxy(sink, seenFinalMethods, i);
         }
     }
 
diff --git a/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java b/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java
index bf33342..b29c267 100644
--- a/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java
+++ b/src/mockito/java/com/google/dexmaker/mockito/DexmakerMockMaker.java
@@ -22,14 +22,16 @@
 import java.lang.reflect.Proxy;
 import java.util.Set;
 import org.mockito.exceptions.base.MockitoException;
+import org.mockito.exceptions.stacktrace.StackTraceCleaner;
 import org.mockito.invocation.MockHandler;
 import org.mockito.mock.MockCreationSettings;
 import org.mockito.plugins.MockMaker;
+import org.mockito.plugins.StackTraceCleanerProvider;
 
 /**
  * Generates mock instances on Android's runtime.
  */
-public final class DexmakerMockMaker implements MockMaker {
+public final class DexmakerMockMaker implements MockMaker, StackTraceCleanerProvider {
     private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
 
     public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
@@ -94,4 +96,15 @@
 
         return null;
     }
+
+    public StackTraceCleaner getStackTraceCleaner(final StackTraceCleaner defaultCleaner) {
+        return new StackTraceCleaner() {
+            public boolean isOut(StackTraceElement candidate) {
+                return defaultCleaner.isOut(candidate)
+                        || candidate.getClassName().endsWith("_Proxy") // dexmaker class proxies
+                        || candidate.getClassName().startsWith("$Proxy") // dalvik interface proxies
+                        || candidate.getClassName().startsWith("com.google.dexmaker.mockito.");
+            }
+        };
+    }
 }
diff --git a/src/mockito/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider b/src/mockito/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider
new file mode 100644
index 0000000..9f6df44
--- /dev/null
+++ b/src/mockito/resources/mockito-extensions/org.mockito.plugins.StackTraceCleanerProvider
@@ -0,0 +1 @@
+com.google.dexmaker.mockito.DexmakerMockMaker
diff --git a/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java b/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java
index 36ac383..bbceaa3 100644
--- a/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java
+++ b/src/test/java/com/google/dexmaker/AppDataDirGuesserTest.java
@@ -66,6 +66,15 @@
                 .shouldGive("/data/data/com.google.android.voicesearch/cache");
     }
 
+    public void testSplitPathList() {
+        final String[] expected = { "foo", "bar" };
+        assertTrue(Arrays.equals(expected, AppDataDirGuesser.splitPathList("foo:bar")));
+        assertTrue(Arrays.equals(expected,
+                AppDataDirGuesser.splitPathList("dexPath=foo:bar")));
+        assertTrue(Arrays.equals(expected,
+                AppDataDirGuesser.splitPathList("dexPath=foo:bar,bazPath=bar:bar2")));
+    }
+
     private interface TestCondition {
         TestCondition withNonWriteable(String... files);
         void shouldGive(String... files);
diff --git a/src/test/java/com/google/dexmaker/DexMakerTest.java b/src/test/java/com/google/dexmaker/DexMakerTest.java
index 2355916..47fb340 100644
--- a/src/test/java/com/google/dexmaker/DexMakerTest.java
+++ b/src/test/java/com/google/dexmaker/DexMakerTest.java
@@ -730,16 +730,16 @@
     }
 
     public void testIntBinaryOps() throws Exception {
-        Method add = binaryOpMethod(int.class, BinaryOp.ADD);
+        Method add = binaryOpMethod(int.class, int.class, BinaryOp.ADD);
         assertEquals(79, add.invoke(null, 75, 4));
 
-        Method subtract = binaryOpMethod(int.class, BinaryOp.SUBTRACT);
+        Method subtract = binaryOpMethod(int.class, int.class, BinaryOp.SUBTRACT);
         assertEquals(71, subtract.invoke(null, 75, 4));
 
-        Method multiply = binaryOpMethod(int.class, BinaryOp.MULTIPLY);
+        Method multiply = binaryOpMethod(int.class, int.class, BinaryOp.MULTIPLY);
         assertEquals(300, multiply.invoke(null, 75, 4));
 
-        Method divide = binaryOpMethod(int.class, BinaryOp.DIVIDE);
+        Method divide = binaryOpMethod(int.class, int.class, BinaryOp.DIVIDE);
         assertEquals(18, divide.invoke(null, 75, 4));
         try {
             divide.invoke(null, 75, 0);
@@ -748,7 +748,7 @@
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        Method remainder = binaryOpMethod(int.class, BinaryOp.REMAINDER);
+        Method remainder = binaryOpMethod(int.class, int.class, BinaryOp.REMAINDER);
         assertEquals(3, remainder.invoke(null, 75, 4));
         try {
             remainder.invoke(null, 75, 0);
@@ -757,117 +757,117 @@
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        Method and = binaryOpMethod(int.class, BinaryOp.AND);
+        Method and = binaryOpMethod(int.class, int.class, BinaryOp.AND);
         assertEquals(0xff000000, and.invoke(null, 0xff00ff00, 0xffff0000));
 
-        Method or = binaryOpMethod(int.class, BinaryOp.OR);
+        Method or = binaryOpMethod(int.class, int.class, BinaryOp.OR);
         assertEquals(0xffffff00, or.invoke(null, 0xff00ff00, 0xffff0000));
 
-        Method xor = binaryOpMethod(int.class, BinaryOp.XOR);
+        Method xor = binaryOpMethod(int.class, int.class, BinaryOp.XOR);
         assertEquals(0x00ffff00, xor.invoke(null, 0xff00ff00, 0xffff0000));
 
-        Method shiftLeft = binaryOpMethod(int.class, BinaryOp.SHIFT_LEFT);
+        Method shiftLeft = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_LEFT);
         assertEquals(0xcd123400, shiftLeft.invoke(null, 0xabcd1234, 8));
 
-        Method shiftRight = binaryOpMethod(int.class, BinaryOp.SHIFT_RIGHT);
+        Method shiftRight = binaryOpMethod(int.class, int.class, BinaryOp.SHIFT_RIGHT);
         assertEquals(0xffabcd12, shiftRight.invoke(null, 0xabcd1234, 8));
 
         Method unsignedShiftRight = binaryOpMethod(int.class,
-                BinaryOp.UNSIGNED_SHIFT_RIGHT);
+                int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT);
         assertEquals(0x00abcd12, unsignedShiftRight.invoke(null, 0xabcd1234, 8));
     }
 
     public void testLongBinaryOps() throws Exception {
-        Method add = binaryOpMethod(long.class, BinaryOp.ADD);
-        assertEquals(79L, add.invoke(null, 75L, 4L));
+        Method add = binaryOpMethod(long.class, long.class, BinaryOp.ADD);
+        assertEquals(30000000079L, add.invoke(null, 10000000075L, 20000000004L));
 
-        Method subtract = binaryOpMethod(long.class, BinaryOp.SUBTRACT);
-        assertEquals(71L, subtract.invoke(null, 75L, 4L));
+        Method subtract = binaryOpMethod(long.class, long.class, BinaryOp.SUBTRACT);
+        assertEquals(20000000071L, subtract.invoke(null, 30000000075L, 10000000004L));
 
-        Method multiply = binaryOpMethod(long.class, BinaryOp.MULTIPLY);
-        assertEquals(300L, multiply.invoke(null, 75L, 4L));
+        Method multiply = binaryOpMethod(long.class, long.class, BinaryOp.MULTIPLY);
+        assertEquals(-8742552812415203028L, multiply.invoke(null, 30000000075L, 20000000004L));
 
-        Method divide = binaryOpMethod(long.class, BinaryOp.DIVIDE);
-        assertEquals(18L, divide.invoke(null, 75L, 4L));
+        Method divide = binaryOpMethod(long.class, long.class, BinaryOp.DIVIDE);
+        assertEquals(-2L, divide.invoke(null, -8742552812415203028L, 4142552812415203028L));
         try {
-            divide.invoke(null, 75L, 0L);
+            divide.invoke(null, -8742552812415203028L, 0L);
             fail();
         } catch (InvocationTargetException expected) {
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        Method remainder = binaryOpMethod(long.class, BinaryOp.REMAINDER);
-        assertEquals(3L, remainder.invoke(null, 75L, 4L));
+        Method remainder = binaryOpMethod(long.class, long.class, BinaryOp.REMAINDER);
+        assertEquals(10000000004L, remainder.invoke(null, 30000000079L, 20000000075L));
         try {
-            remainder.invoke(null, 75L, 0L);
+            remainder.invoke(null, 30000000079L, 0L);
             fail();
         } catch (InvocationTargetException expected) {
             assertEquals(ArithmeticException.class, expected.getCause().getClass());
         }
 
-        Method and = binaryOpMethod(long.class, BinaryOp.AND);
+        Method and = binaryOpMethod(long.class, long.class, BinaryOp.AND);
         assertEquals(0xff00ff0000000000L,
                 and.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
 
-        Method or = binaryOpMethod(long.class, BinaryOp.OR);
+        Method or = binaryOpMethod(long.class, long.class, BinaryOp.OR);
         assertEquals(0xffffffffff00ff00L,
                 or.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
 
-        Method xor = binaryOpMethod(long.class, BinaryOp.XOR);
+        Method xor = binaryOpMethod(long.class, long.class, BinaryOp.XOR);
         assertEquals(0x00ff00ffff00ff00L,
                 xor.invoke(null, 0xff00ff00ff00ff00L, 0xffffffff00000000L));
 
-        Method shiftLeft = binaryOpMethod(long.class, BinaryOp.SHIFT_LEFT);
-        assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8L));
+        Method shiftLeft = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_LEFT);
+        assertEquals(0xcdef012345678900L, shiftLeft.invoke(null, 0xabcdef0123456789L, 8));
 
-        Method shiftRight = binaryOpMethod(long.class, BinaryOp.SHIFT_RIGHT);
-        assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8L));
+        Method shiftRight = binaryOpMethod(long.class, int.class, BinaryOp.SHIFT_RIGHT);
+        assertEquals(0xffabcdef01234567L, shiftRight.invoke(null, 0xabcdef0123456789L, 8));
 
-        Method unsignedShiftRight = binaryOpMethod(long.class,
-                BinaryOp.UNSIGNED_SHIFT_RIGHT);
-        assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8L));
+        Method unsignedShiftRight = binaryOpMethod(
+                long.class, int.class, BinaryOp.UNSIGNED_SHIFT_RIGHT);
+        assertEquals(0x00abcdef01234567L, unsignedShiftRight.invoke(null, 0xabcdef0123456789L, 8));
     }
 
     public void testFloatBinaryOps() throws Exception {
-        Method add = binaryOpMethod(float.class, BinaryOp.ADD);
+        Method add = binaryOpMethod(float.class, float.class, BinaryOp.ADD);
         assertEquals(6.75f, add.invoke(null, 5.5f, 1.25f));
 
-        Method subtract = binaryOpMethod(float.class, BinaryOp.SUBTRACT);
+        Method subtract = binaryOpMethod(float.class, float.class, BinaryOp.SUBTRACT);
         assertEquals(4.25f, subtract.invoke(null, 5.5f, 1.25f));
 
-        Method multiply = binaryOpMethod(float.class, BinaryOp.MULTIPLY);
+        Method multiply = binaryOpMethod(float.class, float.class, BinaryOp.MULTIPLY);
         assertEquals(6.875f, multiply.invoke(null, 5.5f, 1.25f));
 
-        Method divide = binaryOpMethod(float.class, BinaryOp.DIVIDE);
+        Method divide = binaryOpMethod(float.class, float.class, BinaryOp.DIVIDE);
         assertEquals(4.4f, divide.invoke(null, 5.5f, 1.25f));
         assertEquals(Float.POSITIVE_INFINITY, divide.invoke(null, 5.5f, 0.0f));
 
-        Method remainder = binaryOpMethod(float.class, BinaryOp.REMAINDER);
+        Method remainder = binaryOpMethod(float.class, float.class, BinaryOp.REMAINDER);
         assertEquals(0.5f, remainder.invoke(null, 5.5f, 1.25f));
         assertEquals(Float.NaN, remainder.invoke(null, 5.5f, 0.0f));
     }
 
     public void testDoubleBinaryOps() throws Exception {
-        Method add = binaryOpMethod(double.class, BinaryOp.ADD);
+        Method add = binaryOpMethod(double.class, double.class, BinaryOp.ADD);
         assertEquals(6.75, add.invoke(null, 5.5, 1.25));
 
-        Method subtract = binaryOpMethod(double.class, BinaryOp.SUBTRACT);
+        Method subtract = binaryOpMethod(double.class, double.class, BinaryOp.SUBTRACT);
         assertEquals(4.25, subtract.invoke(null, 5.5, 1.25));
 
-        Method multiply = binaryOpMethod(double.class, BinaryOp.MULTIPLY);
+        Method multiply = binaryOpMethod(double.class, double.class, BinaryOp.MULTIPLY);
         assertEquals(6.875, multiply.invoke(null, 5.5, 1.25));
 
-        Method divide = binaryOpMethod(double.class, BinaryOp.DIVIDE);
+        Method divide = binaryOpMethod(double.class, double.class, BinaryOp.DIVIDE);
         assertEquals(4.4, divide.invoke(null, 5.5, 1.25));
         assertEquals(Double.POSITIVE_INFINITY, divide.invoke(null, 5.5, 0.0));
 
-        Method remainder = binaryOpMethod(double.class, BinaryOp.REMAINDER);
+        Method remainder = binaryOpMethod(double.class, double.class, BinaryOp.REMAINDER);
         assertEquals(0.5, remainder.invoke(null, 5.5, 1.25));
         assertEquals(Double.NaN, remainder.invoke(null, 5.5, 0.0));
     }
 
-    private <T> Method binaryOpMethod(Class<T> valueClass, BinaryOp op)
-            throws Exception {
+    private <T1, T2> Method binaryOpMethod(
+            Class<T1> valueAClass, Class<T2> valueBClass, BinaryOp op) throws Exception {
         /*
          * public static int binaryOp(int a, int b) {
          *   int result = a + b;
@@ -875,12 +875,13 @@
          * }
          */
         reset();
-        TypeId<T> valueType = TypeId.get(valueClass);
-        MethodId<?, T> methodId = GENERATED.getMethod(valueType, "call", valueType, valueType);
+        TypeId<T1> valueAType = TypeId.get(valueAClass);
+        TypeId<T2> valueBType = TypeId.get(valueBClass);
+        MethodId<?, T1> methodId = GENERATED.getMethod(valueAType, "call", valueAType, valueBType);
         Code code = dexMaker.declare(methodId, PUBLIC | STATIC);
-        Local<T> localA = code.getParameter(0, valueType);
-        Local<T> localB = code.getParameter(1, valueType);
-        Local<T> localResult = code.newLocal(valueType);
+        Local<T1> localA = code.getParameter(0, valueAType);
+        Local<T2> localB = code.getParameter(1, valueBType);
+        Local<T1> localResult = code.newLocal(valueAType);
         code.op(op, localResult, localA, localB);
         code.returnValue(localResult);
         return getMethod();
diff --git a/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java b/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java
index 1b65ea8..62e0e6c 100644
--- a/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java
+++ b/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java
@@ -771,6 +771,17 @@
                 .build();
     }
 
+    public static class FinalToString {
+        @Override public final String toString() {
+            return "no proxy";
+        }
+    }
+
+    // https://code.google.com/p/dexmaker/issues/detail?id=12
+    public void testFinalToString() throws Throwable {
+        assertEquals("no proxy", proxyFor(FinalToString.class).build().toString());
+    }
+
     /** Simple helper to add the most common args for this test to the proxy builder. */
     private <T> ProxyBuilder<T> proxyFor(Class<T> clazz) throws Exception {
         return ProxyBuilder.forClass(clazz)