| // Copyright (c) 2020, 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.classmerging.vertical; |
| |
| import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix; |
| import static org.hamcrest.CoreMatchers.containsString; |
| import static org.hamcrest.CoreMatchers.not; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| |
| import com.android.tools.r8.OutputMode; |
| import com.android.tools.r8.debug.DebugTestBase; |
| import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command; |
| import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState; |
| import com.android.tools.r8.debug.DexDebugTestConfig; |
| import com.android.tools.r8.utils.AndroidApp; |
| import java.io.File; |
| import java.nio.file.Path; |
| import org.junit.rules.TemporaryFolder; |
| |
| public class VerticalClassMergerDebugTestRunner extends DebugTestBase { |
| |
| private final String main; |
| private final TemporaryFolder temp; |
| |
| private DebugTestRunner runner = null; |
| |
| public VerticalClassMergerDebugTestRunner(String main, TemporaryFolder temp) { |
| this.main = main; |
| this.temp = temp; |
| } |
| |
| public void run(AndroidApp app, Path proguardMapPath) throws Throwable { |
| Path appPath = File.createTempFile("app", ".zip", temp.getRoot()).toPath(); |
| app.writeToZip(appPath, OutputMode.DexIndexed); |
| |
| DexDebugTestConfig config = new DexDebugTestConfig(appPath); |
| config.allowUnprocessedCommands(); |
| config.setProguardMap(proguardMapPath); |
| |
| this.runner = |
| getDebugTestRunner( |
| config, main, breakpoint(main, "main"), run(), stepIntoUntilNoLongerInApp()); |
| this.runner.runBare(); |
| } |
| |
| private void checkState(DebuggeeState state) { |
| // If a class pkg.A is merged into pkg.B, and a method pkg.A.m() needs to be renamed, then |
| // it will be renamed to pkg.B.m$pkg$A(). Since all tests are in the package "classmerging", |
| // we check that no methods in the debugging state (i.e., after the Proguard map has been |
| // applied) contain "$classmerging$. |
| String qualifiedMethodSignature = |
| state.getClassSignature() + "->" + state.getMethodName() + state.getMethodSignature(); |
| boolean holderIsCompanionClass = state.getClassName().endsWith(getCompanionClassNameSuffix()); |
| if (!holderIsCompanionClass) { |
| assertThat(qualifiedMethodSignature, not(containsString("$classmerging$"))); |
| } |
| } |
| |
| // Keeps stepping in until it is no longer in a class from the classmerging package. |
| // Then starts stepping out until it is again in the classmerging package. |
| private Command stepIntoUntilNoLongerInApp() { |
| return stepUntil( |
| StepKind.INTO, |
| StepLevel.INSTRUCTION, |
| state -> { |
| if (state.getClassSignature().contains("classmerging")) { |
| checkState(state); |
| |
| // Continue stepping into. |
| return false; |
| } |
| |
| // Stop stepping into. |
| runner.enqueueCommandFirst(stepOutUntilInApp()); |
| return true; |
| }); |
| } |
| |
| // Keeps stepping out until it is in a class from the classmerging package. |
| // Then starts stepping in until it is no longer in the classmerging package. |
| private Command stepOutUntilInApp() { |
| return stepUntil( |
| StepKind.OUT, |
| StepLevel.INSTRUCTION, |
| state -> { |
| if (state.getClassSignature().contains("classmerging")) { |
| checkState(state); |
| |
| // Stop stepping out. |
| runner.enqueueCommandFirst(stepIntoUntilNoLongerInApp()); |
| return true; |
| } |
| |
| // Continue stepping out. |
| return false; |
| }); |
| } |
| } |