| // 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.ir.optimize.checkcast; |
| |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertThat; |
| |
| import com.android.tools.r8.ClassFileConsumer; |
| import com.android.tools.r8.CompilationMode; |
| import com.android.tools.r8.DexIndexedConsumer; |
| import com.android.tools.r8.NeverInline; |
| import com.android.tools.r8.R8Command; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.debug.CfDebugTestConfig; |
| import com.android.tools.r8.debug.DebugTestBase; |
| import com.android.tools.r8.debug.DebugTestConfig; |
| import com.android.tools.r8.debug.DexDebugTestConfig; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.codeinspector.ClassSubject; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.android.tools.r8.utils.codeinspector.InstructionSubject; |
| import com.android.tools.r8.utils.codeinspector.MethodSubject; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Streams; |
| import java.nio.file.Path; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public class CheckCastDebugTestRunner extends DebugTestBase { |
| private static final Class<?> MAIN = CheckCastDebugTest.class; |
| private final Backend backend; |
| |
| private Path r8Out; |
| private CodeInspector inspector; |
| |
| @Parameterized.Parameters(name = "Backend: {0}") |
| public static Backend[] data() { |
| return Backend.values(); |
| } |
| |
| public CheckCastDebugTestRunner(Backend backend) { |
| this.backend = backend; |
| } |
| |
| private static int testCounter = 0; |
| |
| @Before |
| public void setUp() throws Exception { |
| AndroidApp app = readClasses(NeverInline.class, A.class, B.class, C.class, MAIN); |
| r8Out = temp.newFile(String.format("r8Out-%s-%d.zip", backend, testCounter++)).toPath(); |
| R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app); |
| if (backend == Backend.DEX) { |
| builder.setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(r8Out)); |
| } else { |
| assert backend == Backend.CF; |
| builder.setProgramConsumer(new ClassFileConsumer.ArchiveConsumer(r8Out)); |
| } |
| builder.addProguardConfiguration( |
| ImmutableList.of( |
| "-dontobfuscate", |
| keepMainProguardConfigurationWithInliningAnnotation(MAIN)), |
| Origin.unknown()); |
| builder.setMode(CompilationMode.DEBUG); |
| ToolHelper.allowTestProguardOptions(builder); |
| ToolHelper.runR8(builder.build(), o -> o.enableVerticalClassMerging = false); |
| inspector = new CodeInspector(r8Out); |
| ClassSubject classSubject = inspector.clazz(MAIN); |
| assertThat(classSubject, isPresent()); |
| } |
| |
| @Test |
| public void test_differentLocals() throws Throwable { |
| ClassSubject classSubject = inspector.clazz(MAIN); |
| MethodSubject method = classSubject.method("void", "differentLocals", ImmutableList.of()); |
| assertThat(method, isPresent()); |
| long count = |
| Streams.stream(method.iterateInstructions(InstructionSubject::isCheckCast)).count(); |
| assertEquals(0, count); |
| |
| DebugTestConfig config = backend == Backend.CF |
| ? new CfDebugTestConfig() |
| : new DexDebugTestConfig(); |
| config.addPaths(r8Out); |
| runDebugTest(config, MAIN.getCanonicalName(), |
| // Object obj = new C(); |
| breakpoint(MAIN.getCanonicalName(), "differentLocals", "()V", 35), |
| run(), |
| checkNoLocal("obj"), |
| checkNoLocal("a"), |
| checkNoLocal("b"), |
| checkNoLocal("c"), |
| // A a = (A) obj; |
| breakpoint(MAIN.getCanonicalName(), "differentLocals", "()V", 36), |
| run(), |
| checkLocal("obj"), |
| checkNoLocal("a"), |
| checkNoLocal("b"), |
| checkNoLocal("c"), |
| // B b = (B) a; |
| breakpoint(MAIN.getCanonicalName(), "differentLocals", "()V", 37), |
| run(), |
| checkLocal("obj"), |
| checkLocal("a"), |
| checkNoLocal("b"), |
| checkNoLocal("c"), |
| // C c = (C) b; |
| breakpoint(MAIN.getCanonicalName(), "differentLocals", "()V", 38), |
| run(), |
| checkLocal("obj"), |
| checkLocal("a"), |
| checkLocal("b"), |
| checkNoLocal("c"), |
| // System.out.println(c.toString()); |
| breakpoint(MAIN.getCanonicalName(), "differentLocals", "()V", 39), |
| run(), |
| checkLocal("obj"), |
| checkLocal("a"), |
| checkLocal("b"), |
| checkLocal("c"), |
| run() |
| ); |
| } |
| |
| @Test |
| public void test_sameLocal() throws Throwable { |
| ClassSubject classSubject = inspector.clazz(MAIN); |
| MethodSubject method = classSubject.method("void", "sameLocal", ImmutableList.of()); |
| assertThat(method, isPresent()); |
| long count = |
| Streams.stream(method.iterateInstructions(InstructionSubject::isCheckCast)).count(); |
| assertEquals(0, count); |
| |
| DebugTestConfig config = backend == Backend.CF |
| ? new CfDebugTestConfig() |
| : new DexDebugTestConfig(); |
| config.addPaths(r8Out); |
| runDebugTest(config, MAIN.getCanonicalName(), |
| // Object obj = new C(); |
| breakpoint(MAIN.getCanonicalName(), "sameLocal", "()V", 44), |
| run(), |
| checkNoLocal("obj"), |
| // obj = (A) obj; |
| breakpoint(MAIN.getCanonicalName(), "sameLocal", "()V", 45), |
| run(), |
| checkLocal("obj"), |
| // obj = (B) obj; |
| breakpoint(MAIN.getCanonicalName(), "sameLocal", "()V", 46), |
| run(), |
| checkLocal("obj"), |
| // obj = (C) obj; |
| breakpoint(MAIN.getCanonicalName(), "sameLocal", "()V", 47), |
| run(), |
| checkLocal("obj"), |
| // System.out.println(obj.toString()); |
| breakpoint(MAIN.getCanonicalName(), "sameLocal", "()V", 48), |
| run(), |
| checkLocal("obj"), |
| run() |
| ); |
| } |
| |
| } |