Merge changes from topic "mockito-dexmaker-update-revert" am: ae3172e5fc
am: 3a0e7c2c89

Change-Id: I2450d9ee7788af230d92b402408875fc74fbd422
diff --git a/README.version b/README.version
index 35f650c..33436a1 100644
--- a/README.version
+++ b/README.version
@@ -9,4 +9,4 @@
 The source can be updated using the update_source.sh script.
 
 Local Modifications:
-        None
+         New API to clean up all inline mocks after test (8bdfbf053ab6e4fc14a3eaecb613f5838fdf0f09)
diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java
index 2d06e58..4058340 100644
--- a/src/main/java/org/mockito/Mockito.java
+++ b/src/main/java/org/mockito/Mockito.java
@@ -103,7 +103,8 @@
  *      <a href="#43">43. New API for integrations: <code>MockitoSession</code> is usable by testing frameworks (Since 2.15.+)</a><br/>
  *      <a href="#44">44. Deprecated <code>org.mockito.plugins.InstantiatorProvider</code> as it was leaking internal API. it was replaced by <code>org.mockito.plugins.InstantiatorProvider2 (Since 2.15.4)</code></a><br/>
  *      <a href="#45">45. New JUnit Jupiter (JUnit5+) extension</a><br/>
- *      <a href="#46">46. New <code>Mockito.lenient()</code> and <code>MockSettings.lenient()</code> methods (Since 2.20.0</a><br/>
+ *      <a href="#46">46. New <code>Mockito.lenient()</code> and <code>MockSettings.lenient()</code> methods (Since 2.20.0)</a><br/>
+ *      <a href="#47">47. New API for clearing mock state in inline mocking (Since 2.25.0)</a><br/>
  * </b>
  *
  * <h3 id="0">0. <a class="meaningful_link" href="#mockito2" name="mockito2">Migrating to Mockito 2</a></h3>
@@ -1532,6 +1533,15 @@
  *
  * For more information refer to {@link Mockito#lenient()}.
  * Let us know how do you find the new feature by opening a GitHub issue to discuss!
+ *
+ * <h3 id="47">47. <a class="meaningful_link" href="#clear_inline_mocks" name="clear_inline_mocks">New API for clearing mock state in inline mocking (Since 2.25.0)</a></h3>
+ *
+ * In certain specific, rare scenarios (issue <a href="https://github.com/mockito/mockito/pull/1619">#1619</a>)
+ * inline mocking causes memory leaks.
+ * There is no clean way to mitigate this problem completely.
+ * Hence, we introduced a new API to explicitly clear mock state (only make sense in inline mocking!).
+ * See example usage in {@link MockitoFramework#clearInlineMocks()}.
+ * If you have feedback or a better idea how to solve the problem please reach out.
  */
 @SuppressWarnings("unchecked")
 public class Mockito extends ArgumentMatchers {
diff --git a/src/main/java/org/mockito/MockitoFramework.java b/src/main/java/org/mockito/MockitoFramework.java
index 5ffe272..58cd4b6 100644
--- a/src/main/java/org/mockito/MockitoFramework.java
+++ b/src/main/java/org/mockito/MockitoFramework.java
@@ -92,4 +92,55 @@
      */
     @Incubating
     InvocationFactory getInvocationFactory();
+
+    /**
+     * Clears up internal state of all inline mocks.
+     * This method is only meaningful if inline mock maker is in use.
+     * Otherwise this method is a no-op and need not be used.
+     * <p>
+     * This method is useful to tackle subtle memory leaks that are possible due to the nature of inline mocking
+     * (issue <a href="https://github.com/mockito/mockito/pull/1619">#1619</a>).
+     * If you are facing those problems, call this method at the end of the test (or in "@After" method).
+     * See examples of using "clearInlineMocks" in Mockito test code.
+     * To find out why inline mock maker keeps track of the mock objects see {@link org.mockito.plugins.InlineMockMaker}.
+     * <p>
+     * Mockito's "inline mocking" enables mocking final types, enums and final methods
+     * (read more in section 39 of {@link Mockito} javadoc).
+     * This method is only meaningful when {@link org.mockito.plugins.InlineMockMaker} is in use.
+     * If you're using a different {@link org.mockito.plugins.MockMaker} then this method is a no-op.
+     *
+     * <pre class="code"><code class="java">
+     * public class ExampleTest {
+     *
+     *     &#064;After
+     *     public void clearMocks() {
+     *         Mockito.framework().clearInlineMocks();
+     *     }
+     *
+     *     &#064;Test
+     *     public void someTest() {
+     *         ...
+     *     }
+     * }
+     * </pre>
+     *
+     * If you have feedback or a better idea how to solve the problem please reach out.
+     *
+     * @since 2.25.0
+     * @see #clearInlineMock(Object)
+     */
+    @Incubating
+    void clearInlineMocks();
+
+    /**
+     * Clears up internal state of specific inline mock.
+     * This method is a single-mock variant of {@link #clearInlineMocks()}.
+     * Please read javadoc for {@link #clearInlineMocks()}.
+     *
+     * @param mock to clear up
+     * @since 2.25.0
+     * @see #clearInlineMocks()
+     */
+    @Incubating
+    void clearInlineMock(Object mock);
 }
diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java
index 42f10ce..dfe2061 100644
--- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java
+++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMaker.java
@@ -14,6 +14,7 @@
 import org.mockito.internal.util.concurrent.WeakConcurrentMap;
 import org.mockito.invocation.MockHandler;
 import org.mockito.mock.MockCreationSettings;
+import org.mockito.plugins.InlineMockMaker;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -87,7 +88,7 @@
  * support this feature.
  */
 @Incubating
-public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker {
+public class InlineByteBuddyMockMaker implements ClassCreatingMockMaker, InlineMockMaker {
 
     private static final Instrumentation INSTRUMENTATION;
 
@@ -276,6 +277,16 @@
     }
 
     @Override
+    public void clearMock(Object mock) {
+        mocks.remove(mock);
+    }
+
+    @Override
+    public void clearAllMocks() {
+        mocks.clear();
+    }
+
+    @Override
     public TypeMockability isTypeMockable(final Class<?> type) {
         return new TypeMockability() {
             @Override
diff --git a/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java b/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java
index 69a733c..d92fc28 100644
--- a/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java
+++ b/src/main/java/org/mockito/internal/framework/DefaultMockitoFramework.java
@@ -10,6 +10,8 @@
 import org.mockito.internal.util.Checks;
 import org.mockito.invocation.InvocationFactory;
 import org.mockito.listeners.MockitoListener;
+import org.mockito.plugins.InlineMockMaker;
+import org.mockito.plugins.MockMaker;
 import org.mockito.plugins.MockitoPlugins;
 
 import static org.mockito.internal.progress.ThreadSafeMockingProgress.mockingProgress;
@@ -37,4 +39,25 @@
     public InvocationFactory getInvocationFactory() {
         return new DefaultInvocationFactory();
     }
+
+    private InlineMockMaker getInlineMockMaker() {
+        MockMaker mockMaker = Plugins.getMockMaker();
+        return (mockMaker instanceof InlineMockMaker) ? (InlineMockMaker) mockMaker : null;
+    }
+
+    @Override
+    public void clearInlineMocks() {
+        InlineMockMaker mockMaker = getInlineMockMaker();
+        if (mockMaker != null) {
+            mockMaker.clearAllMocks();
+        }
+    }
+
+    @Override
+    public void clearInlineMock(Object mock) {
+        InlineMockMaker mockMaker = getInlineMockMaker();
+        if (mockMaker != null) {
+            mockMaker.clearMock(mock);
+        }
+    }
 }
diff --git a/src/main/java/org/mockito/plugins/InlineMockMaker.java b/src/main/java/org/mockito/plugins/InlineMockMaker.java
new file mode 100644
index 0000000..8771aa7
--- /dev/null
+++ b/src/main/java/org/mockito/plugins/InlineMockMaker.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.plugins;
+
+import org.mockito.Incubating;
+import org.mockito.MockitoFramework;
+
+/**
+ * Extension to {@link MockMaker} for mock makers that changes inline method implementations
+ * and need keep track of created mock objects.
+ * <p>
+ * Mockito's default inline mock maker keeps track of created mock objects via weak reference map.
+ * This poses a risk of memory leaks in certain scenarios
+ * (issue <a href="https://github.com/mockito/mockito/pull/1619">#1619</a>).
+ * There is no clean way to tackle those problems at the moment.
+ * Hence, {@code InlineMockMaker} interface exposes methods to explicitly clear mock references.
+ * Those methods are called by {@link MockitoFramework#clearInlineMocks()}.
+ * When the user encounters a leak, he can mitigate the problem with {@link MockitoFramework#clearInlineMocks()}.
+ * <p>
+ * {@code InlineMockMaker} is for expert users and framework integrators, when custom inline mock maker is in use.
+ * If you have a custom {@link MockMaker} that keeps track of mock objects,
+ * please have your mock maker implement {@code InlineMockMaker} interface.
+ * This way, it can participate in {@link MockitoFramework#clearInlineMocks()} API.
+ *
+ * @since 2.25.0
+ */
+@Incubating
+public interface InlineMockMaker extends MockMaker {
+
+    /**
+     * Clean up internal state for specified {@code mock}. You may assume there won't be any interaction to the specific
+     * mock after this is called.
+     *
+     * @param mock the mock instance whose internal state is to be cleaned.
+     * @since 2.25.0
+     */
+    @Incubating
+    void clearMock(Object mock);
+
+    /**
+     * Cleans up internal state for all existing mocks. You may assume there won't be any interaction to mocks created
+     * previously after this is called.
+     *
+     * @since 2.25.0
+     */
+    @Incubating
+    void clearAllMocks();
+
+}
diff --git a/src/test/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMakerTest.java b/src/test/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMakerTest.java
index 51c321d..3613b5f 100644
--- a/src/test/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMakerTest.java
+++ b/src/test/java/org/mockito/internal/creation/bytebuddy/InlineByteBuddyMockMakerTest.java
@@ -20,7 +20,11 @@
 import org.mockito.mock.SerializableMode;
 import org.mockito.plugins.MockMaker;
 
-import java.util.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 import static net.bytebuddy.ClassFileVersion.JAVA_V8;
@@ -287,6 +291,37 @@
                 .getOnly().getParameters().getOnly().getName()).isEqualTo("bar");
     }
 
+    @Test
+    public void test_clear_mock_clears_handler() {
+        MockCreationSettings<GenericSubClass> settings = settingsFor(GenericSubClass.class);
+        GenericSubClass proxy = mockMaker.createMock(settings, new MockHandlerImpl<GenericSubClass>(settings));
+        assertThat(mockMaker.getHandler(proxy)).isNotNull();
+
+        //when
+        mockMaker.clearMock(proxy);
+
+        //then
+        assertThat(mockMaker.getHandler(proxy)).isNull();
+    }
+
+    @Test
+    public void test_clear_all_mock_clears_handler() {
+        MockCreationSettings<GenericSubClass> settings = settingsFor(GenericSubClass.class);
+        GenericSubClass proxy1 = mockMaker.createMock(settings, new MockHandlerImpl<GenericSubClass>(settings));
+        assertThat(mockMaker.getHandler(proxy1)).isNotNull();
+
+        settings = settingsFor(GenericSubClass.class);
+        GenericSubClass proxy2 = mockMaker.createMock(settings, new MockHandlerImpl<GenericSubClass>(settings));
+        assertThat(mockMaker.getHandler(proxy1)).isNotNull();
+
+        //when
+        mockMaker.clearAllMocks();
+
+        //then
+        assertThat(mockMaker.getHandler(proxy1)).isNull();
+        assertThat(mockMaker.getHandler(proxy2)).isNull();
+    }
+
     private static <T> MockCreationSettings<T> settingsFor(Class<T> type, Class<?>... extraInterfaces) {
         MockSettingsImpl<T> mockSettings = new MockSettingsImpl<T>();
         mockSettings.setTypeToMock(type);
diff --git a/src/test/java/org/mockito/internal/framework/DefaultMockitoFrameworkTest.java b/src/test/java/org/mockito/internal/framework/DefaultMockitoFrameworkTest.java
index 0937b69..ae21488 100644
--- a/src/test/java/org/mockito/internal/framework/DefaultMockitoFrameworkTest.java
+++ b/src/test/java/org/mockito/internal/framework/DefaultMockitoFrameworkTest.java
@@ -10,15 +10,26 @@
 import org.mockito.MockSettings;
 import org.mockito.StateMaster;
 import org.mockito.exceptions.misusing.RedundantListenerException;
+import org.mockito.internal.configuration.plugins.Plugins;
 import org.mockito.listeners.MockCreationListener;
 import org.mockito.listeners.MockitoListener;
 import org.mockito.mock.MockCreationSettings;
+import org.mockito.plugins.InlineMockMaker;
 import org.mockitoutil.TestBase;
 
 import java.util.List;
 import java.util.Set;
 
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockingDetails;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.withSettings;
 import static org.mockitoutil.ThrowableAssert.assertThat;
 
 public class DefaultMockitoFrameworkTest extends TestBase {
@@ -112,5 +123,52 @@
                     "For more information, see the javadoc for RedundantListenerException class.");
     }
 
+    @Test
+    public void clearing_all_mocks_is_safe_regardless_of_mock_maker_type() {
+        List mock = mock(List.class);
+
+        //expect
+        assertTrue(mockingDetails(mock).isMock());
+        framework.clearInlineMocks();
+    }
+
+    @Test
+    public void clears_all_mocks() {
+        //clearing mocks only works with inline mocking
+        assumeTrue(Plugins.getMockMaker() instanceof InlineMockMaker);
+
+        //given
+        List list1 = mock(List.class);
+        assertTrue(mockingDetails(list1).isMock());
+        List list2 = mock(List.class);
+        assertTrue(mockingDetails(list2).isMock());
+
+        //when
+        framework.clearInlineMocks();
+
+        //then
+        assertFalse(mockingDetails(list1).isMock());
+        assertFalse(mockingDetails(list2).isMock());
+    }
+
+    @Test
+    public void clears_mock() {
+        //clearing mocks only works with inline mocking
+        assumeTrue(Plugins.getMockMaker() instanceof InlineMockMaker);
+
+        //given
+        List list1 = mock(List.class);
+        assertTrue(mockingDetails(list1).isMock());
+        List list2 = mock(List.class);
+        assertTrue(mockingDetails(list2).isMock());
+
+        //when
+        framework.clearInlineMock(list1);
+
+        //then
+        assertFalse(mockingDetails(list1).isMock());
+        assertTrue(mockingDetails(list2).isMock());
+    }
+
     private static class MyListener implements MockitoListener {}
 }
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java b/subprojects/inline/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java
new file mode 100644
index 0000000..439c888
--- /dev/null
+++ b/subprojects/inline/src/test/java/org/mockitoinline/bugs/CyclicMockMethodArgumentMemoryLeakTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockitoinline.bugs;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.framework;
+import static org.mockito.Mockito.mock;
+
+public class CyclicMockMethodArgumentMemoryLeakTest {
+    private static final int ARRAY_LENGTH = 1 << 20;  // 4 MB
+
+    @Test
+    public void no_memory_leak_when_cyclically_calling_method_with_mocks() {
+        for (int i = 0; i < 100; ++i) {
+            final A a = mock(A.class);
+            a.largeArray = new int[ARRAY_LENGTH];
+            final B b = mock(B.class);
+
+            a.accept(b);
+            b.accept(a);
+
+            framework().clearInlineMocks();
+        }
+    }
+
+    private static class A {
+        private int[] largeArray;
+
+        void accept(B b) {}
+    }
+
+    private static class B {
+        void accept(A a) {}
+    }
+}
diff --git a/subprojects/inline/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java b/subprojects/inline/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java
new file mode 100644
index 0000000..f4bb55a
--- /dev/null
+++ b/subprojects/inline/src/test/java/org/mockitoinline/bugs/SelfSpyReferenceMemoryLeakTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockitoinline.bugs;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.framework;
+import static org.mockito.Mockito.spy;
+
+public class SelfSpyReferenceMemoryLeakTest {
+    private static final int ARRAY_LENGTH = 1 << 20;  // 4 MB
+
+    @Test
+    public void no_memory_leak_when_spy_holds_reference_to_self() {
+        for (int i = 0; i < 100; ++i) {
+            final DeepRefSelfClass instance = spy(new DeepRefSelfClass());
+            instance.refInstance(instance);
+
+            framework().clearInlineMocks();
+        }
+    }
+
+    private static class DeepRefSelfClass {
+        private final DeepRefSelfClass[] array = new DeepRefSelfClass[1];
+
+        private final int[] largeArray = new int[ARRAY_LENGTH];
+
+        private void refInstance(DeepRefSelfClass instance) {
+            array[0] = instance;
+        }
+    }
+}