/* * Copyright (C) 2015 RoboVM AB * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. */ package org.robovm.compiler; import static org.junit.Assert.*; import java.io.File; import java.io.IOException; import org.apache.commons.lang3.tuple.ImmutableTriple; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.robovm.compiler.clazz.Clazz; import org.robovm.compiler.config.Config; import org.robovm.compiler.config.FakeHome; import org.robovm.compiler.config.Config.TreeShakerMode; import org.robovm.rt.annotation.StronglyLinked; import org.robovm.rt.annotation.WeaklyLinked; import soot.Scene; import soot.options.Options; /** * Tests {@link DependencyGraph}. */ public class DependencyGraphTest { Config config; Clazz Root; Clazz A; Clazz B; Clazz C; @BeforeClass public static void initializeSoot() throws IOException { soot.G.reset(); Options.v().set_output_format(Options.output_format_jimple); Options.v().set_include_all(true); Options.v().set_print_tags_in_output(true); Options.v().set_allow_phantom_refs(true); Options.v().set_soot_classpath(System.getProperty("sun.boot.class.path") + ":" + System.getProperty("java.class.path")); Scene.v().loadNecessaryClasses(); } @Before public void setup() throws Exception { Config.Builder builder = new Config.Builder() .home(new FakeHome()) .skipRuntimeLib(true) .skipLinking(true); for (String path : System.getProperty("sun.boot.class.path").split(File.pathSeparator)) { builder.addBootClasspathEntry(new File(path)); } for (String path : System.getProperty("java.class.path").split(File.pathSeparator)) { builder.addClasspathEntry(new File(path)); } config = builder.build(); Root = loadClazz(Root.class); A = loadClazz(A.class); B = loadClazz(B.class); C = loadClazz(C.class); Root.getClazzInfo().addClassDependency(A.getInternalName(), false); Root.getClazzInfo().addClassDependency(B.getInternalName(), false); Root.getClazzInfo().addClassDependency(C.getInternalName(), false); } private Clazz loadClazz(Class<?> cls) { Clazz clazz = config.getClazzes().load(cls.getName().replace('.', '/')); if (clazz.getClazzInfo() == null) { clazz.resetClazzInfo().initClassInfo(); } return clazz; } public static class Root {} public static class A { public A(byte a) {} @StronglyLinked public A(short a) {} @WeaklyLinked public A(int a) {} public void a() {} @StronglyLinked public void b() {} @WeaklyLinked public void c() {} } @WeaklyLinked public static class B { public B(byte a) {} @StronglyLinked public B(short a) {} @WeaklyLinked public B(int a) {} public void a() {} @StronglyLinked public void b() {} @WeaklyLinked public void c() {} } @StronglyLinked public static class C { public C(byte a) {} @StronglyLinked public C(short a) {} @WeaklyLinked public C(int a) {} public void a() {} @StronglyLinked public void b() {} @WeaklyLinked public void c() {} } @Test public void testWeaklyStronglyLinkedMethodInfos() throws Exception { assertFalse(A.getClazzInfo().getMethod("<init>", "(B)V").isStronglyLinked()); assertFalse(A.getClazzInfo().getMethod("<init>", "(B)V").isWeaklyLinked()); assertTrue(A.getClazzInfo().getMethod("<init>", "(S)V").isStronglyLinked()); assertFalse(A.getClazzInfo().getMethod("<init>", "(S)V").isWeaklyLinked()); assertFalse(A.getClazzInfo().getMethod("<init>", "(I)V").isStronglyLinked()); assertTrue(A.getClazzInfo().getMethod("<init>", "(I)V").isWeaklyLinked()); assertFalse(A.getClazzInfo().getMethod("a", "()V").isStronglyLinked()); assertFalse(A.getClazzInfo().getMethod("a", "()V").isWeaklyLinked()); assertTrue(A.getClazzInfo().getMethod("b", "()V").isStronglyLinked()); assertFalse(A.getClazzInfo().getMethod("b", "()V").isWeaklyLinked()); assertFalse(A.getClazzInfo().getMethod("c", "()V").isStronglyLinked()); assertTrue(A.getClazzInfo().getMethod("c", "()V").isWeaklyLinked()); assertFalse(B.getClazzInfo().getMethod("<init>", "(B)V").isStronglyLinked()); assertTrue(B.getClazzInfo().getMethod("<init>", "(B)V").isWeaklyLinked()); assertTrue(B.getClazzInfo().getMethod("<init>", "(S)V").isStronglyLinked()); assertFalse(B.getClazzInfo().getMethod("<init>", "(S)V").isWeaklyLinked()); assertFalse(B.getClazzInfo().getMethod("<init>", "(I)V").isStronglyLinked()); assertTrue(B.getClazzInfo().getMethod("<init>", "(I)V").isWeaklyLinked()); assertFalse(B.getClazzInfo().getMethod("a", "()V").isStronglyLinked()); assertTrue(B.getClazzInfo().getMethod("a", "()V").isWeaklyLinked()); assertTrue(B.getClazzInfo().getMethod("b", "()V").isStronglyLinked()); assertFalse(B.getClazzInfo().getMethod("b", "()V").isWeaklyLinked()); assertFalse(B.getClazzInfo().getMethod("c", "()V").isStronglyLinked()); assertTrue(B.getClazzInfo().getMethod("c", "()V").isWeaklyLinked()); assertTrue(C.getClazzInfo().getMethod("<init>", "(B)V").isStronglyLinked()); assertFalse(C.getClazzInfo().getMethod("<init>", "(B)V").isWeaklyLinked()); assertTrue(C.getClazzInfo().getMethod("<init>", "(S)V").isStronglyLinked()); assertFalse(C.getClazzInfo().getMethod("<init>", "(S)V").isWeaklyLinked()); assertFalse(C.getClazzInfo().getMethod("<init>", "(I)V").isStronglyLinked()); assertTrue(C.getClazzInfo().getMethod("<init>", "(I)V").isWeaklyLinked()); assertTrue(C.getClazzInfo().getMethod("a", "()V").isStronglyLinked()); assertFalse(C.getClazzInfo().getMethod("a", "()V").isWeaklyLinked()); assertTrue(C.getClazzInfo().getMethod("b", "()V").isStronglyLinked()); assertFalse(C.getClazzInfo().getMethod("b", "()V").isWeaklyLinked()); assertFalse(C.getClazzInfo().getMethod("c", "()V").isStronglyLinked()); assertTrue(C.getClazzInfo().getMethod("c", "()V").isWeaklyLinked()); } @Test public void testFindReachableClassesNoTreeShaking() throws Exception { DependencyGraph graph = new DependencyGraph(TreeShakerMode.none); graph.add(Root, true); graph.add(A, false); graph.add(B, false); graph.add(C, false); assertTrue(graph.findReachableClasses().contains(Root.getInternalName())); assertTrue(graph.findReachableClasses().contains(A.getInternalName())); assertTrue(graph.findReachableClasses().contains(B.getInternalName())); assertTrue(graph.findReachableClasses().contains(C.getInternalName())); } @Test public void testFindReachableMethodsNoTreeShaking() throws Exception { DependencyGraph graph = new DependencyGraph(TreeShakerMode.none); graph.add(Root, true); graph.add(A, false); graph.add(B, false); graph.add(C, false); assertEquals(19, graph.findReachableMethods().size()); assertTrue(graph.findReachableMethods() .contains(new ImmutableTriple<>(Root.getInternalName(), "<init>", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "<init>", "(B)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "<init>", "(I)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "a", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "b", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "c", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "<init>", "(B)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "<init>", "(I)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "a", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "b", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "c", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "<init>", "(B)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "<init>", "(I)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "a", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "b", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "c", "()V"))); } @Test public void testFindReachableMethodsConservativeTreeShaking() throws Exception { DependencyGraph graph = new DependencyGraph(TreeShakerMode.conservative); graph.add(Root, true); graph.add(A, false); graph.add(B, false); graph.add(C, false); assertEquals(11, graph.findReachableMethods().size()); assertTrue(graph.findReachableMethods() .contains(new ImmutableTriple<>(Root.getInternalName(), "<init>", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "<init>", "(B)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "a", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "b", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "b", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "<init>", "(B)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "a", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "b", "()V"))); } @Test public void testFindReachableMethodsAggressiveTreeShaking() throws Exception { DependencyGraph graph = new DependencyGraph(TreeShakerMode.aggressive); graph.add(Root, true); graph.add(A, false); graph.add(B, false); graph.add(C, false); assertEquals(10, graph.findReachableMethods().size()); assertTrue(graph.findReachableMethods() .contains(new ImmutableTriple<>(Root.getInternalName(), "<init>", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "<init>", "(B)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(A.getInternalName(), "b", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(B.getInternalName(), "b", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "<init>", "(B)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "<init>", "(S)V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "a", "()V"))); assertTrue(graph.findReachableMethods().contains(new ImmutableTriple<>(C.getInternalName(), "b", "()V"))); } }