| // Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package com.android.tools.r8.naming; | |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; | |
| import static org.hamcrest.CoreMatchers.containsString; | |
| import static org.hamcrest.MatcherAssert.assertThat; | |
| import static org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertFalse; | |
| import static org.junit.Assert.assertNotEquals; | |
| import static org.junit.Assert.assertTrue; | |
| import com.android.tools.r8.DiagnosticsChecker; | |
| import com.android.tools.r8.R8Command; | |
| import com.android.tools.r8.TestBase; | |
| import com.android.tools.r8.ToolHelper; | |
| import com.android.tools.r8.ToolHelper.ProcessResult; | |
| import com.android.tools.r8.origin.Origin; | |
| import com.android.tools.r8.utils.AndroidApp; | |
| import com.android.tools.r8.utils.KeepingDiagnosticHandler; | |
| import com.android.tools.r8.utils.Reporter; | |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; | |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; | |
| import com.google.common.collect.ImmutableList; | |
| import java.nio.file.Path; | |
| import java.util.Collections; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| import org.junit.runner.RunWith; | |
| import org.junit.runners.Parameterized; | |
| @RunWith(Parameterized.class) | |
| public class WarnReflectiveAccessTestRunner extends TestBase { | |
| // See "Method foo" in WarnReflectiveAccessTestMain.main(). | |
| private static int LINE_NUMBER_OF_MARKED_LINE = 28; | |
| private KeepingDiagnosticHandler handler; | |
| private Reporter reporter; | |
| private Backend backend; | |
| @Parameterized.Parameters(name = "Backend: {0}") | |
| public static Backend[] data() { | |
| return ToolHelper.getBackends(); | |
| } | |
| public WarnReflectiveAccessTestRunner(Backend backend) { | |
| this.backend = backend; | |
| } | |
| @Before | |
| public void reset() { | |
| handler = new KeepingDiagnosticHandler(); | |
| reporter = new Reporter(handler); | |
| } | |
| private AndroidApp runR8( | |
| byte[][] classes, | |
| Class main, | |
| Path out, | |
| boolean explicitRule, | |
| boolean enableMinification, | |
| boolean forceProguardCompatibility) | |
| throws Exception { | |
| String minificationModifier = enableMinification ? ",allowobfuscation " : " "; | |
| String reflectionRules = | |
| explicitRule | |
| ? "-identifiernamestring class java.lang.Class {\n" | |
| + "static java.lang.Class forName(java.lang.String);\n" | |
| + "java.lang.reflect.Method getDeclaredMethod(java.lang.String," | |
| + " java.lang.Class[]);\n" | |
| + "}\n" | |
| : ""; | |
| R8Command.Builder commandBuilder = | |
| R8Command.builder(reporter) | |
| .addProguardConfiguration( | |
| ImmutableList.of( | |
| "-keep" | |
| + minificationModifier | |
| + "class " | |
| + main.getCanonicalName() | |
| + " {" | |
| + " <methods>;" | |
| + "}", | |
| "-printmapping", | |
| "-keepattributes LineNumberTable", | |
| reflectionRules), | |
| Origin.unknown()) | |
| .setOutput(out, outputMode(backend)); | |
| for (byte[] clazz : classes) { | |
| commandBuilder.addClassProgramData(clazz, Origin.unknown()); | |
| } | |
| commandBuilder.addLibraryFiles(TestBase.runtimeJar(backend)); | |
| return ToolHelper.runR8( | |
| commandBuilder.build(), | |
| o -> { | |
| o.enableInlining = false; | |
| o.forceProguardCompatibility = forceProguardCompatibility; | |
| }); | |
| } | |
| private void reflectionWithBuilder( | |
| boolean explicitRule, boolean enableMinification, boolean forceProguardCompatibility) | |
| throws Exception { | |
| byte[][] classes = {ToolHelper.getClassAsBytes(WarnReflectiveAccessTest.class)}; | |
| Path out = temp.getRoot().toPath(); | |
| AndroidApp processedApp = | |
| runR8( | |
| classes, | |
| WarnReflectiveAccessTest.class, | |
| out, | |
| explicitRule, | |
| enableMinification, | |
| forceProguardCompatibility); | |
| String main = WarnReflectiveAccessTest.class.getCanonicalName(); | |
| CodeInspector codeInspector = new CodeInspector(processedApp); | |
| ClassSubject mainSubject = codeInspector.clazz(main); | |
| assertThat(mainSubject, isPresent()); | |
| ProcessResult javaOutput = runOnJavaRaw(main, classes); | |
| assertEquals(0, javaOutput.exitCode); | |
| assertThat(javaOutput.stdout, containsString("TestMain::foo")); | |
| String mainClassName = enableMinification ? mainSubject.getFinalName() : main; | |
| ProcessResult output; | |
| String errorString; | |
| if (backend == Backend.DEX) { | |
| output = runOnArtRaw(processedApp, mainClassName); | |
| errorString = "NoSuchMethodError"; | |
| } else { | |
| assert backend == Backend.CF; | |
| output = runOnJavaRaw(processedApp, mainClassName, Collections.emptyList()); | |
| errorString = "method not found"; | |
| } | |
| if (enableMinification) { | |
| assertNotEquals(0, output.exitCode); | |
| assertThat(output.stderr, containsString(errorString)); | |
| } else { | |
| assertEquals(0, output.exitCode); | |
| assertThat(output.stdout, containsString("TestMain::foo")); | |
| } | |
| } | |
| @Test | |
| public void test_explicit_minification_forceProguardCompatibility() throws Exception { | |
| reflectionWithBuilder(true, true, true); | |
| assertFalse(handler.warnings.isEmpty()); | |
| DiagnosticsChecker.checkDiagnostic( | |
| handler.warnings.get(0), | |
| (Path) null, | |
| LINE_NUMBER_OF_MARKED_LINE, | |
| 1, | |
| "Cannot determine", | |
| "getDeclaredMethod", | |
| "-identifiernamestring", | |
| "resolution failure"); | |
| } | |
| @Test | |
| public void test_explicit_noMinification_forceProguardCompatibility() throws Exception { | |
| reflectionWithBuilder(true, false, true); | |
| assertFalse(handler.warnings.isEmpty()); | |
| DiagnosticsChecker.checkDiagnostic( | |
| handler.warnings.get(0), | |
| (Path) null, | |
| LINE_NUMBER_OF_MARKED_LINE, | |
| 1, | |
| "Cannot determine", | |
| "getDeclaredMethod", | |
| "-identifiernamestring", | |
| "resolution failure"); | |
| } | |
| @Test | |
| public void test_explicit_minification_R8() throws Exception { | |
| reflectionWithBuilder(true, true, false); | |
| assertFalse(handler.warnings.isEmpty()); | |
| DiagnosticsChecker.checkDiagnostic( | |
| handler.warnings.get(0), | |
| (Path) null, | |
| LINE_NUMBER_OF_MARKED_LINE, | |
| 1, | |
| "Cannot determine", | |
| "getDeclaredMethod", | |
| "-identifiernamestring", | |
| "resolution failure"); | |
| } | |
| @Test | |
| public void test_explicit_noMinification_R8() throws Exception { | |
| reflectionWithBuilder(true, false, false); | |
| assertFalse(handler.warnings.isEmpty()); | |
| DiagnosticsChecker.checkDiagnostic( | |
| handler.warnings.get(0), | |
| (Path) null, | |
| LINE_NUMBER_OF_MARKED_LINE, | |
| 1, | |
| "Cannot determine", | |
| "getDeclaredMethod", | |
| "-identifiernamestring", | |
| "resolution failure"); | |
| } | |
| @Test | |
| public void test_implicit_minification_forceProguardCompatibility() throws Exception { | |
| reflectionWithBuilder(false, true, true); | |
| assertTrue(handler.warnings.isEmpty()); | |
| } | |
| @Test | |
| public void test_implicit_noMinification_forceProguardCompatibility() throws Exception { | |
| reflectionWithBuilder(false, false, true); | |
| assertTrue(handler.warnings.isEmpty()); | |
| } | |
| @Test | |
| public void test_implicit_minification_R8() throws Exception { | |
| reflectionWithBuilder(false, true, false); | |
| assertTrue(handler.warnings.isEmpty()); | |
| } | |
| @Test | |
| public void test_implicit_noMinification_R8() throws Exception { | |
| reflectionWithBuilder(false, false, false); | |
| assertTrue(handler.warnings.isEmpty()); | |
| } | |
| } |