diff options
author | John Wu <topjohnwu@google.com> | 2022-09-26 18:45:21 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-09-26 18:45:21 +0000 |
commit | d454288adf6756afe4e84d06edd508c1ff8e5ec0 (patch) | |
tree | 377b97acdc9589837a3b1c6ff73ace50a169cba9 | |
parent | 79aba3b995a71a04ebfd1e5e41012746a8f2fb90 (diff) | |
parent | 44976556324abeb1212b6a3252e3f7828b846f61 (diff) | |
download | dexmaker-d454288adf6756afe4e84d06edd508c1ff8e5ec0.tar.gz |
Merge "Revert^2 "Update to upstream dexmaker"" am: 60da4e1d7b am: 4497655632
Original change: https://android-review.googlesource.com/c/platform/external/dexmaker/+/2218809
Change-Id: I3d981ffde7ec50ed3816f569fb56e1f517e5f1b0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
20 files changed, 357 insertions, 367 deletions
diff --git a/README.version b/README.version index 3e8229a..e3a494a 100644 --- a/README.version +++ b/README.version @@ -1,5 +1,5 @@ URL: https://github.com/linkedin/dexmaker/ -Version: master (8ff85edb2793cc1e0f6a93c67b127cb9c43d924e) +Version: main (65749b3da4b058c0b322b2adbf3e8f70488f8d8b) License: Apache 2.0 Description: Dexmaker is a Java-language API for doing compile time or runtime code generation targeting the Dalvik VM. Unlike cglib or ASM, this library creates Dalvik .dex files instead of Java .class files. @@ -13,8 +13,5 @@ Local Modifications: Allow to share classloader via dexmaker.share_classloader system property (I324cddd644610eef811c620a1fccf6a24b2b9406) Do not read Build.VERSION to allow non-standard Android distributions (Ia8c4ba4c82cd6f193c565f1bfe48faffc4aac08f) Temporarily ignore failing test (Ibf7b6c2eb05c5ff83f0817f9224369e20c0b775d) - Restrict InspectClass to current thread. (Ic62951ff81bed60ac7512455fad65210e4b728a9, need upstreaming) Exclude Stress#mockALot from presubmit (Ic9a2927ffa07924bd759429e31c56dc1b71a826c) Extend timeout of Stress#mockALot() for CTS. (Iad30a8cb07b38054b490b7006d11908fc752a024) - Update to Mockito 2.25.0 and impl InlineMockMaker (29a8674036d345e4ec8635b1d38d8b2a4fe91980a, need upstreaming) - guessPath not to depend on the first level of app directory (I66f1d7036949c2f05e6d37bc270d47f8e77e51c1, need upstreaming) diff --git a/dexmaker-mockito-inline-dispatcher/build.gradle b/dexmaker-mockito-inline-dispatcher/build.gradle index 73a93f3..b811904 100644 --- a/dexmaker-mockito-inline-dispatcher/build.gradle +++ b/dexmaker-mockito-inline-dispatcher/build.gradle @@ -1,18 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 32 defaultConfig { applicationId 'com.android.dexmaker.mockito.inline.dispatcher' minSdkVersion 28 - targetSdkVersion 28 + targetSdkVersion 32 versionName VERSION_NAME } } - -repositories { - jcenter() - google() -} diff --git a/dexmaker-mockito-inline-extended-tests/build.gradle b/dexmaker-mockito-inline-extended-tests/build.gradle index b5664f2..e5dc213 100644 --- a/dexmaker-mockito-inline-extended-tests/build.gradle +++ b/dexmaker-mockito-inline-extended-tests/build.gradle @@ -1,20 +1,7 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } -} - -apply plugin: "net.ltgt.errorprone" apply plugin: 'com.android.library' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 32 android { lintOptions { @@ -24,8 +11,7 @@ android { defaultConfig { minSdkVersion 28 - targetSdkVersion 28 - versionName VERSION_NAME + targetSdkVersion 32 testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } @@ -36,19 +22,14 @@ android { } } -repositories { - jcenter() - google() -} - dependencies { implementation project(':dexmaker-mockito-inline-tests') compileOnly project(':dexmaker-mockito-inline-extended') androidTestImplementation project(':dexmaker-mockito-inline-extended') - implementation 'junit:junit:4.12' - implementation 'com.android.support.test:runner:1.0.2' - implementation 'com.android.support.test:rules:1.0.2' + implementation 'junit:junit:4.13.2' + implementation 'androidx.test:runner:1.4.0' + implementation 'androidx.test:rules:1.4.0' - api 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' } + api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' } } diff --git a/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java b/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java index 7c7941b..eff42ab 100644 --- a/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java +++ b/dexmaker-mockito-inline-extended-tests/src/main/java/com/android/dx/mockito/inline/extended/tests/StaticMockitoSession.java @@ -16,6 +16,7 @@ package com.android.dx.mockito.inline.extended.tests; +import android.app.PendingIntent; import android.content.ContentResolver; import android.provider.Settings; @@ -33,8 +34,9 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; public class StaticMockitoSession { + @Test - public void strictUnnecessaryStubbing() throws Exception { + public void strictUnnecessaryStubbing() { MockitoSession session = mockitoSession().spyStatic(Settings.Global.class).startMocking(); // Set up unnecessary stubbing @@ -51,7 +53,7 @@ public class StaticMockitoSession { } @Test - public void lenientUnnecessaryStubbing() throws Exception { + public void lenientUnnecessaryStubbing() { MockitoSession session = mockitoSession().strictness(Strictness.LENIENT) .spyStatic(Settings.Global.class).startMocking(); @@ -61,4 +63,22 @@ public class StaticMockitoSession { session.finishMocking(); } + + @Test + public void spyStatic() { + mockitoSession() + .initMocks(this) + .spyStatic(PendingIntent.class) + .startMocking() + .finishMocking(); + } + + @Test + public void mockStatic() { + mockitoSession() + .initMocks(this) + .mockStatic(PendingIntent.class) + .startMocking() + .finishMocking(); + } } diff --git a/dexmaker-mockito-inline-extended/build.gradle b/dexmaker-mockito-inline-extended/build.gradle index fee152e..b02cd37 100644 --- a/dexmaker-mockito-inline-extended/build.gradle +++ b/dexmaker-mockito-inline-extended/build.gradle @@ -1,25 +1,13 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } +plugins { + id("net.ltgt.errorprone") version "1.3.0" } - -apply plugin: "net.ltgt.errorprone" apply plugin: 'com.android.library' -apply plugin: 'maven-publish' -apply plugin: 'ivy-publish' -apply plugin: 'com.jfrog.artifactory' +apply from: "$rootDir/gradle/publishing_aar.gradle" -version = VERSION_NAME +description = 'Extension of the Mockito Inline API to allow mocking static methods on the Android Dalvik VM' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 32 android { lintOptions { @@ -29,9 +17,8 @@ android { } defaultConfig { - minSdkVersion 1 - targetSdkVersion 28 - versionName VERSION_NAME + minSdkVersion 9 + targetSdkVersion 32 } externalNativeBuild { @@ -47,77 +34,16 @@ android { } tasks.withType(JavaCompile) { - options.compilerArgs += ["-Xep:StringSplitter:OFF"] -} - -task sourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.srcDirs -} - -task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - failOnError false -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -publishing { - publications { - ivyLib(IvyPublication) { - from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies()) - artifact sourcesJar - artifact javadocJar - } - - lib(MavenPublication) { - from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies()) - - artifact sourcesJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - description = 'Extension of the Mockito Inline API to allow mocking static methods on the Android Dalvik VM' - url 'https://github.com/linkedin/dexmaker' - scm { - url 'https://github.com/linkedin/dexmaker' - connection 'scm:git:git://github.com/linkedin/dexmaker.git' - developerConnection 'https://github.com/linkedin/dexmaker.git' - } - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/license/LICENSE-2.0.txt' - distribution 'repo' - } - } - - developers { - developer { - id 'com.linkedin' - name 'LinkedIn Corp' - email '' - } - } - } - } - } + options.errorprone { + disable("StringSplitter") } } -repositories { - jcenter() - google() -} - dependencies { + errorprone "com.google.errorprone:error_prone_core:2.5.1" + errorproneJavac "com.google.errorprone:javac:9+181-r4173-1" + implementation project(':dexmaker-mockito-inline') - implementation 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' } + api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' } } diff --git a/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java index 6ba11cd..05b067d 100644 --- a/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java +++ b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java @@ -112,7 +112,7 @@ public final class InlineStaticMockMaker implements MockMaker { * are modified, some are not. This list helps the {@link MockMethodAdvice} help figure out if a * object's method calls should be intercepted. */ - private final HashMap<Object, InvocationHandlerAdapter> markerToHandler = new HashMap<>(); + private final Map<Object, InvocationHandlerAdapter> markerToHandler = new MarkerToHandlerMap(); private final Map<Class, Object> classToMarker = new HashMap<>(); /** @@ -126,8 +126,7 @@ public final class InlineStaticMockMaker implements MockMaker { public InlineStaticMockMaker() { if (INITIALIZATION_ERROR != null) { throw new RuntimeException("Could not initialize inline mock maker.\n" + "\n" + - "Release: Android " + Build.VERSION.RELEASE_OR_CODENAME + " " - + Build.VERSION.INCREMENTAL + "Release: Android " + Build.VERSION.RELEASE + " " + Build.VERSION.INCREMENTAL + "Device: " + Build.BRAND + " " + Build.MODEL, INITIALIZATION_ERROR); } diff --git a/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/MarkerToHandlerMap.java b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/MarkerToHandlerMap.java new file mode 100644 index 0000000..74a38b8 --- /dev/null +++ b/dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/MarkerToHandlerMap.java @@ -0,0 +1,118 @@ +package com.android.dx.mockito.inline; + +import org.mockito.invocation.MockHandler; +import org.mockito.mock.MockCreationSettings; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A map for mock marker object -> {@link InvocationHandlerAdapter} but + * does not use the mock marker object as the key directly. + * The problem of not doing so is that the object's real hashCode() and equals() = + * methods will be invoked during + * {@link InlineStaticMockMaker#createMock(MockCreationSettings, MockHandler)}. This poses a + * potential test runtime error depending on the object's hashCode() implementation + */ +class MarkerToHandlerMap implements Map<Object, InvocationHandlerAdapter> { + + private final Map<MockMarkerKey, InvocationHandlerAdapter> markerToHandler = new HashMap<>(); + + @Override + public int size() { + return markerToHandler.size(); + } + + @Override + public boolean isEmpty() { + return markerToHandler.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return markerToHandler.containsKey(new MockMarkerKey(key)); + } + + @Override + public boolean containsValue(Object value) { + return markerToHandler.containsValue(value); + } + + @Override + public InvocationHandlerAdapter get(Object key) { + return markerToHandler.get(new MockMarkerKey(key)); + } + + @Override + public InvocationHandlerAdapter put(Object key, InvocationHandlerAdapter value) { + return markerToHandler.put(new MockMarkerKey(key), value); + } + + @Override + public InvocationHandlerAdapter remove(Object key) { + return markerToHandler.remove(new MockMarkerKey(key)); + } + + @Override + public void putAll(Map<?, ? extends InvocationHandlerAdapter> m) { + for (Entry<?, ? extends InvocationHandlerAdapter> entry : m.entrySet()) { + put(new MockMarkerKey(entry.getKey()), entry.getValue()); + } + } + + @Override + public void clear() { + markerToHandler.clear(); + } + + @Override + public Set<Object> keySet() { + Set<Object> set = new HashSet<>(entrySet().size()); + for (MockMarkerKey key : markerToHandler.keySet()) { + set.add(key.mockMarker); + } + return set; + } + + @Override + public Collection<InvocationHandlerAdapter> values() { + return markerToHandler.values(); + } + + @Override + public Set<Entry<Object, InvocationHandlerAdapter>> entrySet() { + Set<Entry<Object, InvocationHandlerAdapter>> set = new HashSet<>(entrySet().size()); + for (Entry<MockMarkerKey, InvocationHandlerAdapter> entry : markerToHandler.entrySet()) { + set.add(new AbstractMap.SimpleImmutableEntry<>(entry.getKey().mockMarker, entry.getValue())); + } + return set; + } + + private static class MockMarkerKey { + + private final Object mockMarker; + + public MockMarkerKey(Object mockMarker) { + this.mockMarker = mockMarker; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MockMarkerKey mockMarkerKey = (MockMarkerKey) o; + + return mockMarker == mockMarkerKey.mockMarker; + } + + @Override + public int hashCode() { + return System.identityHashCode(mockMarker); + } + } +} diff --git a/dexmaker-mockito-inline-tests/build.gradle b/dexmaker-mockito-inline-tests/build.gradle index 02d0751..4961c5e 100644 --- a/dexmaker-mockito-inline-tests/build.gradle +++ b/dexmaker-mockito-inline-tests/build.gradle @@ -1,20 +1,7 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } -} - -apply plugin: "net.ltgt.errorprone" apply plugin: 'com.android.library' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 32 android { lintOptions { @@ -25,8 +12,7 @@ android { defaultConfig { minSdkVersion 28 - targetSdkVersion 28 - versionName VERSION_NAME + targetSdkVersion 32 testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } @@ -38,17 +24,12 @@ android { } } -repositories { - jcenter() - google() -} - dependencies { implementation project(':dexmaker-mockito-tests') compileOnly project(':dexmaker-mockito-inline') androidTestImplementation project(':dexmaker-mockito-inline') - implementation 'junit:junit:4.12' - implementation 'com.android.support.test:runner:1.0.2' - api 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' } + implementation 'junit:junit:4.13.2' + implementation 'androidx.test:runner:1.4.0' + api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' } } diff --git a/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java b/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java index d78bb10..55f5893 100644 --- a/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java +++ b/dexmaker-mockito-inline-tests/src/main/java/com/android/dx/mockito/inline/tests/MemoryLeaks.java @@ -29,7 +29,7 @@ public class MemoryLeaks { private static final int ARRAY_LENGTH = 1 << 20; // 4 MB @Test - public void callMethodWithMocksCycalically() { + public void callMethodWithMocksCyclically() { for (int i = 0; i < 100; ++i) { final A a = mock(A.class); a.largeArray = new int[ARRAY_LENGTH]; diff --git a/dexmaker-mockito-inline/build.gradle b/dexmaker-mockito-inline/build.gradle index 00913a4..dcffd65 100644 --- a/dexmaker-mockito-inline/build.gradle +++ b/dexmaker-mockito-inline/build.gradle @@ -1,25 +1,13 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } +plugins { + id("net.ltgt.errorprone") version "1.3.0" } - -apply plugin: "net.ltgt.errorprone" apply plugin: 'com.android.library' -apply plugin: 'maven-publish' -apply plugin: 'ivy-publish' -apply plugin: 'com.jfrog.artifactory' +apply from: "$rootDir/gradle/publishing_aar.gradle" -version = VERSION_NAME +description = 'Implementation of the Mockito Inline API for use on the Android Dalvik VM' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 32 android { lintOptions { @@ -30,8 +18,7 @@ android { defaultConfig { minSdkVersion 1 - targetSdkVersion 28 - versionName VERSION_NAME + targetSdkVersion 32 } externalNativeBuild { @@ -42,77 +29,16 @@ android { } tasks.withType(JavaCompile) { - options.compilerArgs += ["-Xep:StringSplitter:OFF"] -} - -task sourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.srcDirs -} - -task javadoc(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -publishing { - publications { - ivyLib(IvyPublication) { - from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies()) - artifact sourcesJar - artifact javadocJar - } - - lib(MavenPublication) { - from new org.gradle.api.internal.java.JavaLibrary(new org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact(project.getName(), 'aar', 'aar', null, new Date(), new File("$buildDir/outputs/aar/${project.getName()}-release.aar"), assemble), project.configurations.implementation.getAllDependencies()) - - artifact sourcesJar - artifact javadocJar - - pom.withXml { - asNode().children().last() + { - resolveStrategy = Closure.DELEGATE_FIRST - description = 'Implementation of the Mockito Inline API for use on the Android Dalvik VM' - url 'https://github.com/linkedin/dexmaker' - scm { - url 'https://github.com/linkedin/dexmaker' - connection 'scm:git:git://github.com/linkedin/dexmaker.git' - developerConnection 'https://github.com/linkedin/dexmaker.git' - } - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/license/LICENSE-2.0.txt' - distribution 'repo' - } - } - - developers { - developer { - id 'com.linkedin' - name 'LinkedIn Corp' - email '' - } - } - } - } - } + options.errorprone { + disable("StringSplitter") } } -repositories { - jcenter() - google() -} - dependencies { + errorprone "com.google.errorprone:error_prone_core:2.5.1" + errorproneJavac "com.google.errorprone:javac:9+181-r4173-1" + implementation project(':dexmaker') - implementation 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' } + api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' } } - diff --git a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java index 9741118..0a594e0 100644 --- a/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java +++ b/dexmaker-mockito-inline/src/main/java/com/android/dx/mockito/inline/InlineDexmakerMockMaker.java @@ -180,8 +180,7 @@ public final class InlineDexmakerMockMaker implements InlineMockMaker { throw new RuntimeException( "Could not initialize inline mock maker.\n" + "\n" - + "Release: Android " + Build.VERSION.RELEASE_OR_CODENAME + " " - + Build.VERSION.INCREMENTAL + + "Release: Android " + Build.VERSION.RELEASE + " " + Build.VERSION.INCREMENTAL + "Device: " + Build.BRAND + " " + Build.MODEL, INITIALIZATION_ERROR); } diff --git a/dexmaker-mockito-tests/build.gradle b/dexmaker-mockito-tests/build.gradle index 9379d17..918f277 100644 --- a/dexmaker-mockito-tests/build.gradle +++ b/dexmaker-mockito-tests/build.gradle @@ -1,19 +1,7 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } -} - -apply plugin: "net.ltgt.errorprone" apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 32 android { lintOptions { @@ -23,24 +11,18 @@ android { } defaultConfig { - minSdkVersion 8 - targetSdkVersion 28 - versionName VERSION_NAME + minSdkVersion 14 + targetSdkVersion 32 testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } } -repositories { - jcenter() - google() -} - dependencies { compileOnly project(':dexmaker-mockito') androidTestImplementation project(':dexmaker-mockito') - implementation 'com.android.support.test:runner:0.5' - implementation 'junit:junit:4.12' - api 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' } + implementation 'androidx.test:runner:1.4.0' + implementation 'junit:junit:4.13.2' + api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' } } diff --git a/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java b/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java index 4a345b3..126647d 100644 --- a/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java +++ b/dexmaker-mockito-tests/src/main/java/com/android/dx/mockito/tests/BlacklistedApis.java @@ -117,7 +117,7 @@ public class BlacklistedApis { parent.measure(100, 100); } - @SuppressLint({"PrivateApi", "CheckReturnValue"}) + @SuppressLint({"PrivateApi", "CheckReturnValue", "SoonBlockedPrivateApi"}) @Test public void cannotCallBlackListedAfterSpying() { // Spying and mocking might change the View class's byte code @@ -134,7 +134,7 @@ public class BlacklistedApis { } public static class CallBlackListedMethod { - @SuppressLint("PrivateApi") + @SuppressLint({"PrivateApi", "SoonBlockedPrivateApi"}) boolean callingBlacklistedMethodCausesException() { // Settings.Global#isValidZenMode is a blacklisted method. Resolving it should fail try { @@ -153,7 +153,7 @@ public class BlacklistedApis { } public static abstract class CallBlacklistedMethodAbstract { - @SuppressLint("PrivateApi") + @SuppressLint({"PrivateApi", "SoonBlockedPrivateApi"}) public boolean callingBlacklistedMethodCausesException() { // Settings.Global#isValidZenMode is a blacklisted method. Resolving it should fail try { diff --git a/dexmaker-mockito/build.gradle b/dexmaker-mockito/build.gradle index b15b30c..c3095ad 100644 --- a/dexmaker-mockito/build.gradle +++ b/dexmaker-mockito/build.gradle @@ -1,30 +1,26 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } +plugins { + id("net.ltgt.errorprone") version "1.3.0" } -apply plugin: "net.ltgt.errorprone" -apply plugin: 'java' -apply from: "$rootDir/gradle/publishing.gradle" - -version = VERSION_NAME description = "Implementation of the Mockito API for use on the Android Dalvik VM" +apply plugin: 'java-library' +apply from: "$rootDir/gradle/publishing.gradle" + targetCompatibility = '1.7' sourceCompatibility = '1.7' -repositories { - jcenter() +tasks.withType(JavaCompile) { + options.errorprone { + disable("StringSplitter") + } } dependencies { + errorprone "com.google.errorprone:error_prone_core:2.5.1" + errorproneJavac "com.google.errorprone:javac:9+181-r4173-1" + implementation project(':dexmaker') - implementation 'org.mockito:mockito-core:2.25.0', { exclude group: 'net.bytebuddy' } + api 'org.mockito:mockito-core:2.28.2', { exclude group: 'net.bytebuddy' } } diff --git a/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java b/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java index 4cdaf44..df14e66 100644 --- a/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java +++ b/dexmaker-mockito/src/main/java/com/android/dx/mockito/DexmakerMockMaker.java @@ -39,11 +39,12 @@ public final class DexmakerMockMaker implements MockMaker, StackTraceCleanerProv private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); private boolean isApi28; - public DexmakerMockMaker() throws Exception { + public DexmakerMockMaker() { try { Class buildVersion = Class.forName("android.os.Build$VERSION"); + isApi28 = buildVersion.getDeclaredField("SDK_INT").getInt(null) >= 28; - } catch (ClassNotFoundException e) { + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { System.err.println("Could not determine platform API level, assuming >= 28: " + e); isApi28 = true; } @@ -76,6 +77,17 @@ public final class DexmakerMockMaker implements MockMaker, StackTraceCleanerProv System.err.println("Cannot allow LenientCopyTool to copy spies of blacklisted " + "fields. This might break spying on system classes."); } + + // The ProxyBuilder class needs to be able to see hidden methods in order to proxy + // them correctly. If it cannot see blacklisted methods, then other system classes + // which call hidden methods on the mock will call through to the real method rather + // than the proxy, which may cause crashes or other unexpected behavior. + try { + allowHiddenApiReflectionFromMethod.invoke(null, ProxyBuilder.class); + } catch (InvocationTargetException | IllegalAccessException e) { + System.err.println("Cannot allow ProxyBuilder to proxy blacklisted " + + "methods. This might break mocking on system classes."); + } } } diff --git a/dexmaker-tests/build.gradle b/dexmaker-tests/build.gradle index 5ae5f2d..996008d 100644 --- a/dexmaker-tests/build.gradle +++ b/dexmaker-tests/build.gradle @@ -1,25 +1,12 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } -} - -apply plugin: "net.ltgt.errorprone" apply plugin: 'com.android.application' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 32 defaultConfig { applicationId 'com.linkedin.dexmaker' - minSdkVersion 8 - targetSdkVersion 28 + minSdkVersion 14 + targetSdkVersion 32 versionCode 1 versionName VERSION_NAME @@ -27,15 +14,9 @@ android { } } -repositories { - jcenter() - google() -} - dependencies { implementation project(":dexmaker") - //noinspection GradleDependency - androidTestImplementation 'com.android.support.test:runner:0.5' - androidTestImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'junit:junit:4.13.2' } diff --git a/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java b/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java index af520e1..d718aaa 100644 --- a/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java +++ b/dexmaker-tests/src/androidTest/java/com/android/dx/DexMakerTest.java @@ -172,6 +172,26 @@ public final class DexMakerTest { } @Test + public void testLoadDeferredClassConstant() throws Exception { + /* + * public static String call() { + * Class clazz = Generated.class; + * return clazz.getSimpleName(); + * } + */ + MethodId<?, String> methodId = GENERATED.getMethod(TypeId.STRING, "call"); + Code code = dexMaker.declare(methodId, PUBLIC | STATIC); + Local<Class> clazz = code.newLocal(TypeId.get(Class.class)); + Local<String> retValue = code.newLocal(TypeId.STRING); + code.loadDeferredClassConstant(clazz, GENERATED); + MethodId<Class, String> getSimpleName = TypeId.get(Class.class).getMethod(TypeId.STRING, "getSimpleName"); + code.invokeVirtual(getSimpleName, retValue, clazz); + code.returnValue(retValue); + + assertEquals("Generated", getMethod().invoke(null)); + } + + @Test public void testCreateLocalMethodAsNull() throws Exception { /* * public void call(int value) { @@ -481,6 +501,58 @@ public final class DexMakerTest { } @Test + public void testDeclareNativeMethod() throws Exception { + /* + * class Generated { + * public Generated() { + * } + * public native void nativeMethod(); + * } + */ + + addDefaultConstructor(); + String nativeMethodName = "nativeMethod"; + MethodId<?, Void> nativeMethodToGenerate = GENERATED.getMethod(TypeId.VOID, nativeMethodName); + dexMaker.declare(nativeMethodToGenerate, java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.NATIVE); + + Class<?> generatedClass = generateAndLoad(); + Object instance = generatedClass.getConstructor().newInstance(); + Method nativeMethod = instance.getClass().getMethod(nativeMethodName); + + assertTrue((nativeMethod.getModifiers() & NATIVE) != 0); + assertTrue((nativeMethod.getModifiers() & PUBLIC) != 0); + assertEquals(void.class, nativeMethod.getReturnType()); + assertEquals(nativeMethodName, nativeMethod.getName()); + assertEquals(nativeMethod.getParameterTypes().length, 0); + } + + @Test + public void testDeclareAbstractClassWithAbstractMethod() throws Exception { + /* + * public abstract class AbstractClass { + * public abstract void abstractMethod(); + * } + */ + + dexMaker = new DexMaker(); + dexMaker.declare(GENERATED, "AbstractClass.java", PUBLIC, TypeId.OBJECT); + + String abstractMethodName = "abstractMethod"; + MethodId<?, Void> nativeMethodToGenerate = GENERATED.getMethod(TypeId.VOID, abstractMethodName); + dexMaker.declare(nativeMethodToGenerate, java.lang.reflect.Modifier.PUBLIC | ABSTRACT); + + Class<?> generatedClass = generateAndLoad(); + Method nativeMethod = generatedClass.getMethod(abstractMethodName); + + assertTrue((nativeMethod.getModifiers() & ABSTRACT) != 0); + assertTrue((nativeMethod.getModifiers() & PUBLIC) != 0); + assertEquals(void.class, nativeMethod.getReturnType()); + assertEquals(abstractMethodName, nativeMethod.getName()); + assertEquals(nativeMethod.getParameterTypes().length, 0); + + } + + @Test public void testReturnType() throws Exception { testReturnType(boolean.class, true); testReturnType(byte.class, (byte) 5); @@ -1956,26 +2028,6 @@ public final class DexMakerTest { } @Test - public void testAbstractMethodsAreUnsupported() { - MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call"); - try { - dexMaker.declare(methodId, ABSTRACT); - fail(); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testNativeMethodsAreUnsupported() { - MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call"); - try { - dexMaker.declare(methodId, NATIVE); - fail(); - } catch (IllegalArgumentException expected) { - } - } - - @Test public void testSynchronizedFieldsAreUnsupported() { try { FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "synchronizedField"); @@ -2005,7 +2057,6 @@ public final class DexMakerTest { // TODO: don't generate multiple times (?) // TODO: test array types // TODO: test generating an interface - // TODO: declare native method or abstract method // TODO: get a thrown exception 'e' into a local // TODO: move a primitive or reference diff --git a/dexmaker/build.gradle b/dexmaker/build.gradle index 7e8762f..01b0bdb 100644 --- a/dexmaker/build.gradle +++ b/dexmaker/build.gradle @@ -1,32 +1,28 @@ -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13" - } +plugins { + id("net.ltgt.errorprone") version "1.3.0" } -apply plugin: "net.ltgt.errorprone" +description = "A utility for doing compile or runtime code generation targeting Android's Dalvik VM" + apply plugin: 'java' apply from: "$rootDir/gradle/publishing.gradle" -version = VERSION_NAME -description = "A utility for doing compile or runtime code generation targeting Android's Dalvik VM" - targetCompatibility = '1.7' sourceCompatibility = '1.7' -repositories { - jcenter() +tasks.withType(JavaCompile) { + options.errorprone { + disable("StringSplitter") + } } -tasks.withType(JavaCompile) { - options.compilerArgs += ["-Xep:StringSplitter:OFF"] +javadoc { + options.addBooleanOption("Xdoclint:-html", true) } dependencies { + errorprone "com.google.errorprone:error_prone_core:2.5.1" + errorproneJavac "com.google.errorprone:javac:9+181-r4173-1" + implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3' } diff --git a/dexmaker/src/main/java/com/android/dx/Code.java b/dexmaker/src/main/java/com/android/dx/Code.java index 715d2b4..c0f2759 100644 --- a/dexmaker/src/main/java/com/android/dx/Code.java +++ b/dexmaker/src/main/java/com/android/dx/Code.java @@ -476,15 +476,27 @@ public final class Code { * must be a primitive, String, Class, TypeId, or null. */ public <T> void loadConstant(Local<T> target, T value) { + loadConstantInternal(target, value); + } + + /** + * Copies a class type in {@code target}. The benefit to using this method vs {@link Code#loadConstant(Local, Object)} + * is that the {@code value} can itself be a generated type - {@link TypeId} allows for deferred referencing of class types. + */ + public void loadDeferredClassConstant(Local<Class> target, TypeId value) { + loadConstantInternal(target, value); + } + + private void loadConstantInternal(Local target, Object value) { Rop rop = value == null - ? Rops.CONST_OBJECT_NOTHROW - : Rops.opConst(target.type.ropType); + ? Rops.CONST_OBJECT_NOTHROW + : Rops.opConst(target.type.ropType); if (rop.getBranchingness() == BRANCH_NONE) { addInstruction(new PlainCstInsn(rop, sourcePosition, target.spec(), - RegisterSpecList.EMPTY, Constants.getConstant(value))); + RegisterSpecList.EMPTY, Constants.getConstant(value))); } else { addInstruction(new ThrowingCstInsn(rop, sourcePosition, - RegisterSpecList.EMPTY, catches, Constants.getConstant(value))); + RegisterSpecList.EMPTY, catches, Constants.getConstant(value))); moveResult(target, true); } } diff --git a/dexmaker/src/main/java/com/android/dx/DexMaker.java b/dexmaker/src/main/java/com/android/dx/DexMaker.java index e45ead0..b672c53 100644 --- a/dexmaker/src/main/java/com/android/dx/DexMaker.java +++ b/dexmaker/src/main/java/com/android/dx/DexMaker.java @@ -32,6 +32,7 @@ import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.StdTypeList; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -46,8 +47,8 @@ import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR; -import static java.lang.reflect.Modifier.PRIVATE; -import static java.lang.reflect.Modifier.STATIC; +import static java.lang.reflect.Modifier.*; +; /** * Generates a <strong>D</strong>alvik <strong>EX</strong>ecutable (dex) @@ -230,7 +231,7 @@ public final class DexMaker { * Modifier#FINAL} and {@link Modifier#ABSTRACT}. */ public void declare(TypeId<?> type, String sourceFile, int flags, - TypeId<?> supertype, TypeId<?>... interfaces) { + TypeId<?> supertype, TypeId<?>... interfaces) { TypeDeclaration declaration = getTypeDeclaration(type); int supportedFlags = Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT | AccessFlags.ACC_SYNTHETIC; @@ -265,7 +266,7 @@ public final class DexMaker { throw new IllegalStateException("already declared: " + method); } - int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED + int supportedFlags = Modifier.ABSTRACT | Modifier.NATIVE | Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED | AccessFlags.ACC_SYNTHETIC | AccessFlags.ACC_BRIDGE; if ((flags & ~supportedFlags) != 0) { @@ -515,7 +516,12 @@ public final class DexMaker { // Check that the file exists. If it does, return a DexClassLoader and skip all // the dex bytecode generation. if (result.exists()) { - return generateClassLoader(result, dexCache, parent); + if (!result.canWrite()) { + return generateClassLoader(result, dexCache, parent); + } else { + // Old writable files should be ignored and re-generated + result.delete(); + } } byte[] dex = generate(); @@ -527,14 +533,23 @@ public final class DexMaker { * * TODO: load the dex from memory where supported. */ - result.createNewFile(); - JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result)); - JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME); - entry.setSize(dex.length); - jarOut.putNextEntry(entry); - jarOut.write(dex); - jarOut.closeEntry(); - jarOut.close(); + + JarOutputStream jarOut = + new JarOutputStream(new BufferedOutputStream(new FileOutputStream(result))); + result.setReadOnly(); + try { + JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME); + entry.setSize(dex.length); + jarOut.putNextEntry(entry); + try { + jarOut.write(dex); + } finally { + jarOut.closeEntry(); + } + } finally { + jarOut.close(); + } + return generateClassLoader(result, dexCache, parent); } @@ -645,6 +660,10 @@ public final class DexMaker { } EncodedMethod toEncodedMethod(DexOptions dexOptions) { + if((flags & ABSTRACT) != 0 || (flags & NATIVE) != 0){ + return new EncodedMethod(method.constant, flags, null, StdTypeList.EMPTY); + } + RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0); LocalVariableInfo locals = null; DalvCode dalvCode = RopTranslator.translate( |