| // Copyright (c) 2018, 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.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertThat; |
| 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.lang.reflect.Method; |
| import java.nio.file.Path; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| class WarnReflectiveAccessTestMain { |
| int counter = 0; |
| |
| WarnReflectiveAccessTestMain() { |
| } |
| |
| public void foo() { |
| System.out.println("TestMain::foo(" + counter++ + ")"); |
| } |
| |
| public int boo() { |
| System.out.println("TestMain::boo(" + counter + ")"); |
| return counter; |
| } |
| |
| public static void main(String[] args) throws Exception { |
| WarnReflectiveAccessTestMain instance = new WarnReflectiveAccessTestMain(); |
| |
| StringBuilder builder = new StringBuilder(); |
| builder.append("f"); |
| builder.append("o").append("o"); |
| Method foo = instance.getClass().getDeclaredMethod(builder.toString()); // Marked line. |
| foo.invoke(instance); |
| } |
| } |
| |
| @RunWith(Parameterized.class) |
| public class WarnReflectiveAccessTest extends TestBase { |
| |
| // See "Method foo" in WarnReflectiveAccessTestMain.main(). |
| private static int LINE_NUMBER_OF_MARKED_LINE = 57; |
| |
| private KeepingDiagnosticHandler handler; |
| private Reporter reporter; |
| |
| private Backend backend; |
| |
| @Parameterized.Parameters(name = "Backend: {0}") |
| public static Collection<Backend> data() { |
| return Arrays.asList(Backend.values()); |
| } |
| |
| public WarnReflectiveAccessTest(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; |
| o.testing.suppressExperimentalCfBackendWarning = true; |
| }); |
| } |
| |
| private void reflectionWithBuilder( |
| boolean explicitRule, |
| boolean enableMinification, |
| boolean forceProguardCompatibility) throws Exception { |
| byte[][] classes = { |
| ToolHelper.getClassAsBytes(WarnReflectiveAccessTestMain.class) |
| }; |
| Path out = temp.getRoot().toPath(); |
| AndroidApp processedApp = runR8(classes, WarnReflectiveAccessTestMain.class, out, |
| explicitRule, enableMinification, forceProguardCompatibility); |
| |
| String main = WarnReflectiveAccessTestMain.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()); |
| } |
| |
| } |