| // 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.ir.conversion; |
| |
| import static com.android.tools.r8.shaking.ProguardConfigurationSourceStrings.createConfigurationForTesting; |
| import static org.hamcrest.CoreMatchers.hasItem; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.dex.ApplicationReader; |
| import com.android.tools.r8.graph.AppInfoWithSubtyping; |
| import com.android.tools.r8.graph.AppServices; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.DexApplication; |
| import com.android.tools.r8.graph.DexClass; |
| import com.android.tools.r8.graph.DexEncodedMethod; |
| import com.android.tools.r8.ir.conversion.CallGraph.Node; |
| import com.android.tools.r8.shaking.AppInfoWithLiveness; |
| import com.android.tools.r8.shaking.Enqueuer; |
| import com.android.tools.r8.shaking.EnqueuerFactory; |
| import com.android.tools.r8.shaking.ProguardConfigurationParser; |
| import com.android.tools.r8.shaking.RootSetBuilder; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import com.android.tools.r8.utils.Timing; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Sets; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| import org.junit.Test; |
| |
| public class PartialCallGraphTest extends CallGraphTestBase { |
| private final AppView<AppInfoWithLiveness> appView; |
| private final InternalOptions options = new InternalOptions(); |
| private final ExecutorService executorService = ThreadUtils.getExecutorService(options); |
| |
| public PartialCallGraphTest() throws Exception { |
| Timing timing = new Timing("PartialCallGraphTest.setup"); |
| AndroidApp app = testForD8().addProgramClasses(TestClass.class).compile().app; |
| DexApplication application = new ApplicationReader(app, options, timing).read().toDirect(); |
| AppView<AppInfoWithSubtyping> appView = |
| AppView.createForR8(new AppInfoWithSubtyping(application), options); |
| appView.setAppServices(AppServices.builder(appView).build()); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(appView.dexItemFactory(), options.reporter); |
| parser.parse( |
| createConfigurationForTesting( |
| ImmutableList.of("-keep class ** { void m1(); void m5(); }"))); |
| appView.setRootSet( |
| new RootSetBuilder( |
| appView, application, parser.getConfig().getRules()).run(executorService)); |
| Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView); |
| this.appView = |
| appView.setAppInfo( |
| enqueuer.traceApplication( |
| appView.rootSet(), |
| parser.getConfig().getDontWarnPatterns(), |
| executorService, |
| timing)); |
| } |
| |
| @Test |
| public void testFullGraph() throws Exception { |
| CallGraph cg = |
| new CallGraphBuilder(appView).build(executorService, new Timing("testFullGraph")); |
| Node m1 = findNode(cg.nodes, "m1"); |
| Node m2 = findNode(cg.nodes, "m2"); |
| Node m3 = findNode(cg.nodes, "m3"); |
| Node m4 = findNode(cg.nodes, "m4"); |
| Node m5 = findNode(cg.nodes, "m5"); |
| Node m6 = findNode(cg.nodes, "m6"); |
| assertNotNull(m1); |
| assertNotNull(m2); |
| assertNotNull(m3); |
| assertNotNull(m4); |
| assertNotNull(m5); |
| assertNotNull(m6); |
| |
| Set<Node> wave = Sets.newIdentityHashSet(); |
| |
| MethodProcessor.extractLeaves(cg.nodes, wave::add); |
| assertEquals(4, wave.size()); // including <init> |
| assertThat(wave, hasItem(m3)); |
| assertThat(wave, hasItem(m4)); |
| assertThat(wave, hasItem(m6)); |
| wave.clear(); |
| |
| MethodProcessor.extractLeaves(cg.nodes, wave::add); |
| assertEquals(2, wave.size()); |
| assertThat(wave, hasItem(m2)); |
| assertThat(wave, hasItem(m5)); |
| wave.clear(); |
| |
| MethodProcessor.extractLeaves(cg.nodes, wave::add); |
| assertEquals(1, wave.size()); |
| assertThat(wave, hasItem(m1)); |
| assertTrue(cg.nodes.isEmpty()); |
| } |
| |
| @Test |
| public void testPartialGraph() throws Exception { |
| DexEncodedMethod em1 = findMethod("m1"); |
| DexEncodedMethod em2 = findMethod("m2"); |
| DexEncodedMethod em4 = findMethod("m4"); |
| DexEncodedMethod em5 = findMethod("m5"); |
| assertNotNull(em1); |
| assertNotNull(em2); |
| assertNotNull(em4); |
| assertNotNull(em5); |
| |
| CallGraph pg = |
| new PartialCallGraphBuilder(appView, ImmutableSet.of(em1, em2, em4, em5)) |
| .build(executorService, new Timing("tetPartialGraph")); |
| |
| Node m1 = findNode(pg.nodes, "m1"); |
| Node m2 = findNode(pg.nodes, "m2"); |
| Node m4 = findNode(pg.nodes, "m4"); |
| Node m5 = findNode(pg.nodes, "m5"); |
| assertNotNull(m1); |
| assertNotNull(m2); |
| assertNotNull(m4); |
| assertNotNull(m5); |
| |
| Set<Node> wave = Sets.newIdentityHashSet(); |
| |
| PostMethodProcessor.extractRoots(pg.nodes, wave::add); |
| assertEquals(2, wave.size()); |
| assertThat(wave, hasItem(m1)); |
| assertThat(wave, hasItem(m5)); |
| wave.clear(); |
| |
| PostMethodProcessor.extractRoots(pg.nodes, wave::add); |
| assertEquals(1, wave.size()); |
| assertThat(wave, hasItem(m2)); |
| wave.clear(); |
| |
| PostMethodProcessor.extractRoots(pg.nodes, wave::add); |
| assertEquals(1, wave.size()); |
| assertThat(wave, hasItem(m4)); |
| assertTrue(pg.nodes.isEmpty()); |
| } |
| |
| private Node findNode(Iterable<Node> nodes, String name) { |
| for (Node n : nodes) { |
| if (n.method.method.name.toString().equals(name)) { |
| return n; |
| } |
| } |
| return null; |
| } |
| |
| private DexEncodedMethod findMethod(String name) { |
| for (DexClass clazz : appView.appInfo().classes()) { |
| for (DexEncodedMethod method : clazz.methods()) { |
| if (method.method.name.toString().equals(name)) { |
| return method; |
| } |
| } |
| } |
| return null; |
| } |
| |
| static class TestClass { |
| void m1() { |
| System.out.println("m1"); |
| m2(); |
| } |
| |
| void m2() { |
| System.out.println("m2"); |
| m3(); |
| m4(); |
| } |
| |
| void m3() { |
| System.out.println("m3"); |
| } |
| |
| void m4() { |
| System.out.println("m4"); |
| } |
| |
| void m5() { |
| System.out.println("m5"); |
| m6(); |
| m4(); |
| } |
| |
| void m6() { |
| System.out.println("m6"); |
| } |
| } |
| } |