jdk/test/tools/jlink/JLinkOptimTest.java
changeset 39491 504b835d71a8
parent 39490 1450367e1082
child 39492 697da62a538a
equal deleted inserted replaced
39490:1450367e1082 39491:504b835d71a8
     1 
       
     2 import java.lang.reflect.Method;
       
     3 import java.net.URI;
       
     4 import java.nio.file.FileSystem;
       
     5 import java.nio.file.FileSystems;
       
     6 import java.nio.file.Files;
       
     7 import java.nio.file.Path;
       
     8 import java.nio.file.Paths;
       
     9 import java.util.ArrayList;
       
    10 import java.util.Collections;
       
    11 import java.util.HashMap;
       
    12 import java.util.Iterator;
       
    13 import java.util.List;
       
    14 import java.util.Map;
       
    15 import java.util.stream.Stream;
       
    16 import jdk.internal.org.objectweb.asm.ClassReader;
       
    17 import jdk.internal.org.objectweb.asm.Opcodes;
       
    18 import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode;
       
    19 import jdk.internal.org.objectweb.asm.tree.ClassNode;
       
    20 import jdk.internal.org.objectweb.asm.tree.MethodInsnNode;
       
    21 import jdk.internal.org.objectweb.asm.tree.MethodNode;
       
    22 import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode;
       
    23 import jdk.tools.jlink.internal.PluginRepository;
       
    24 import jdk.tools.jlink.internal.ModulePoolImpl;
       
    25 import jdk.tools.jlink.internal.plugins.OptimizationPlugin;
       
    26 import jdk.tools.jlink.internal.plugins.asm.AsmModulePool;
       
    27 import jdk.tools.jlink.internal.plugins.asm.AsmPlugin;
       
    28 import jdk.tools.jlink.internal.plugins.asm.AsmPools;
       
    29 import jdk.tools.jlink.internal.plugins.optim.ControlFlow;
       
    30 import jdk.tools.jlink.internal.plugins.optim.ControlFlow.Block;
       
    31 import jdk.tools.jlink.plugin.ModuleEntry;
       
    32 import jdk.tools.jlink.plugin.ModulePool;
       
    33 
       
    34 import tests.Helper;
       
    35 import tests.JImageGenerator;
       
    36 
       
    37 /*
       
    38  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
    39  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
    40  *
       
    41  * This code is free software; you can redistribute it and/or modify it
       
    42  * under the terms of the GNU General Public License version 2 only, as
       
    43  * published by the Free Software Foundation.
       
    44  *
       
    45  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    46  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    47  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    48  * version 2 for more details (a copy is included in the LICENSE file that
       
    49  * accompanied this code).
       
    50  *
       
    51  * You should have received a copy of the GNU General Public License version
       
    52  * 2 along with this work; if not, write to the Free Software Foundation,
       
    53  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    54  *
       
    55  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    56  * or visit www.oracle.com if you need additional information or have any
       
    57  * questions.
       
    58  */
       
    59 
       
    60  /*
       
    61  * @test
       
    62  * @summary Test image creation with class optimization
       
    63  * @author Jean-Francois Denise
       
    64  * @library ../lib
       
    65  * @modules java.base/jdk.internal.jimage
       
    66  *          jdk.jdeps/com.sun.tools.classfile
       
    67  *          jdk.jlink/jdk.tools.jlink.internal
       
    68  *          jdk.jlink/jdk.tools.jmod
       
    69  *          jdk.jlink/jdk.tools.jimage
       
    70  *          jdk.jlink/jdk.tools.jlink.internal.plugins
       
    71  *          jdk.jlink/jdk.tools.jlink.internal.plugins.asm
       
    72  *          jdk.jlink/jdk.tools.jlink.internal.plugins.optim
       
    73  *          java.base/jdk.internal.org.objectweb.asm
       
    74  *          java.base/jdk.internal.org.objectweb.asm.tree
       
    75  *          java.base/jdk.internal.org.objectweb.asm.util
       
    76  *          jdk.compiler
       
    77  * @build tests.*
       
    78  * @run main JLinkOptimTest
       
    79  */
       
    80 public class JLinkOptimTest {
       
    81 
       
    82     private static final String EXPECTED = "expected";
       
    83     private static Helper helper;
       
    84 
       
    85     public static class ControlFlowPlugin extends AsmPlugin {
       
    86 
       
    87         private boolean called;
       
    88         private int numMethods;
       
    89         private int numBlocks;
       
    90 
       
    91         private static final String NAME = "test-optim";
       
    92 
       
    93         private ControlFlowPlugin() {
       
    94         }
       
    95 
       
    96         @Override
       
    97         public void visit(AsmPools pools) {
       
    98             called = true;
       
    99             for (AsmModulePool p : pools.getModulePools()) {
       
   100 
       
   101                 p.visitClassReaders((reader) -> {
       
   102                     ClassNode cn = new ClassNode();
       
   103                     if ((reader.getAccess() & Opcodes.ACC_INTERFACE) == 0) {
       
   104                         reader.accept(cn, ClassReader.EXPAND_FRAMES);
       
   105                         for (MethodNode m : cn.methods) {
       
   106                             if ((m.access & Opcodes.ACC_ABSTRACT) == 0
       
   107                                     && (m.access & Opcodes.ACC_NATIVE) == 0) {
       
   108                                 numMethods += 1;
       
   109                                 try {
       
   110                                     ControlFlow f
       
   111                                             = ControlFlow.createControlFlow(cn.name, m);
       
   112                                     for (Block b : f.getBlocks()) {
       
   113                                         numBlocks += 1;
       
   114                                         f.getClosure(b);
       
   115                                     }
       
   116                                 } catch (Throwable ex) {
       
   117                                     //ex.printStackTrace();
       
   118                                     throw new RuntimeException("Exception in "
       
   119                                             + cn.name + "." + m.name, ex);
       
   120                                 }
       
   121                             }
       
   122                         }
       
   123                     }
       
   124                     return null;
       
   125                 });
       
   126             }
       
   127         }
       
   128 
       
   129         @Override
       
   130         public String getName() {
       
   131             return NAME;
       
   132         }
       
   133     }
       
   134 
       
   135     private static void testForName() throws Exception {
       
   136         String moduleName = "optimplugin";
       
   137         Path src = Paths.get(System.getProperty("test.src")).resolve(moduleName);
       
   138         Path classes = helper.getJmodClassesDir().resolve(moduleName);
       
   139         JImageGenerator.compile(src, classes);
       
   140 
       
   141         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
       
   142         Path root = fs.getPath("/modules/java.base");
       
   143         // Access module-info.class to be reused as fake module-info.class
       
   144         List<ModuleEntry> javabaseResources = new ArrayList<>();
       
   145         try (Stream<Path> stream = Files.walk(root)) {
       
   146             for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext();) {
       
   147                 Path p = iterator.next();
       
   148                 if (Files.isRegularFile(p)) {
       
   149                     try {
       
   150                         javabaseResources.add(ModuleEntry.create(p.toString().
       
   151                                 substring("/modules".length()), Files.readAllBytes(p)));
       
   152                     } catch (Exception ex) {
       
   153                         throw new RuntimeException(ex);
       
   154                     }
       
   155                 }
       
   156             }
       
   157         }
       
   158 
       
   159         //forName folding
       
   160         ModulePoolImpl pool = new ModulePoolImpl();
       
   161         byte[] content = Files.readAllBytes(classes.
       
   162                 resolve("optim").resolve("ForNameTestCase.class"));
       
   163         byte[] content2 = Files.readAllBytes(classes.
       
   164                 resolve("optim").resolve("AType.class"));
       
   165         byte[] mcontent = Files.readAllBytes(classes.resolve("module-info.class"));
       
   166 
       
   167         pool.add(ModuleEntry.create("/optimplugin/optim/ForNameTestCase.class", content));
       
   168         pool.add(ModuleEntry.create("/optimplugin/optim/AType.class", content2));
       
   169         pool.add(ModuleEntry.create("/optimplugin/module-info.class", mcontent));
       
   170 
       
   171         for (ModuleEntry r : javabaseResources) {
       
   172             pool.add(r);
       
   173         }
       
   174 
       
   175         OptimizationPlugin plugin = new OptimizationPlugin();
       
   176         Map<String, String> optional = new HashMap<>();
       
   177         optional.put(OptimizationPlugin.NAME, OptimizationPlugin.FORNAME_REMOVAL);
       
   178         optional.put(OptimizationPlugin.LOG, "forName.log");
       
   179         plugin.configure(optional);
       
   180         ModulePool out = new ModulePoolImpl();
       
   181         plugin.visit(pool, out);
       
   182 
       
   183         ModuleEntry result = out.entries().iterator().next();
       
   184 
       
   185         ClassReader optimReader = new ClassReader(result.getBytes());
       
   186         ClassNode optimClass = new ClassNode();
       
   187         optimReader.accept(optimClass, ClassReader.EXPAND_FRAMES);
       
   188 
       
   189         if (!optimClass.name.equals("optim/ForNameTestCase")) {
       
   190             throw new Exception("Invalid class " + optimClass.name);
       
   191         }
       
   192         if (optimClass.methods.size() < 2) {
       
   193             throw new Exception("Not enough methods in new class");
       
   194         }
       
   195         for (MethodNode mn : optimClass.methods) {
       
   196             if (!mn.name.contains("forName") && !mn.name.contains("<clinit>")) {
       
   197                 continue;
       
   198             }
       
   199             if (mn.name.startsWith("negative")) {
       
   200                 checkForName(mn);
       
   201             } else {
       
   202                 checkNoForName(mn);
       
   203             }
       
   204         }
       
   205         Map<String, byte[]> newClasses = new HashMap<>();
       
   206         newClasses.put("optim.ForNameTestCase", result.getBytes());
       
   207         newClasses.put("optim.AType", content2);
       
   208         MemClassLoader loader = new MemClassLoader(newClasses);
       
   209         Class<?> loaded = loader.loadClass("optim.ForNameTestCase");
       
   210         if (loaded.getDeclaredMethods().length < 2) {
       
   211             throw new Exception("Not enough methods in new class");
       
   212         }
       
   213         for (Method m : loaded.getDeclaredMethods()) {
       
   214             if (m.getName().contains("Exception")) {
       
   215                 try {
       
   216                     m.invoke(null);
       
   217                 } catch (Exception ex) {
       
   218                     //ex.getCause().printStackTrace();
       
   219                     if (!ex.getCause().getMessage().equals(EXPECTED)) {
       
   220                         throw new Exception("Unexpected exception " + ex);
       
   221                     }
       
   222                 }
       
   223             } else if (!m.getName().startsWith("negative")) {
       
   224                 Class<?> clazz = (Class<?>) m.invoke(null);
       
   225                 if (clazz != String.class && clazz != loader.findClass("optim.AType")) {
       
   226                     throw new Exception("Invalid class " + clazz);
       
   227                 }
       
   228             }
       
   229         }
       
   230     }
       
   231 
       
   232     private static void checkNoForName(MethodNode m) throws Exception {
       
   233         Iterator<AbstractInsnNode> it = m.instructions.iterator();
       
   234         while (it.hasNext()) {
       
   235             AbstractInsnNode n = it.next();
       
   236             if (n instanceof MethodInsnNode) {
       
   237                 MethodInsnNode met = (MethodInsnNode) n;
       
   238                 if (met.name.equals("forName")
       
   239                         && met.owner.equals("java/lang/Class")
       
   240                         && met.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) {
       
   241                     throw new Exception("forName not removed in " + m.name);
       
   242                 }
       
   243             }
       
   244         }
       
   245         for (TryCatchBlockNode tcb : m.tryCatchBlocks) {
       
   246             if (tcb.type.equals(ClassNotFoundException.class.getName().replaceAll("\\.", "/"))) {
       
   247                 throw new Exception("ClassNotFoundException Block not removed for " + m.name);
       
   248             }
       
   249         }
       
   250     }
       
   251 
       
   252     private static void checkForName(MethodNode m) throws Exception {
       
   253         Iterator<AbstractInsnNode> it = m.instructions.iterator();
       
   254         boolean found = false;
       
   255         while (it.hasNext()) {
       
   256             AbstractInsnNode n = it.next();
       
   257             if (n instanceof MethodInsnNode) {
       
   258                 MethodInsnNode met = (MethodInsnNode) n;
       
   259                 if (met.name.equals("forName")
       
   260                         && met.owner.equals("java/lang/Class")
       
   261                         && met.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) {
       
   262                     found = true;
       
   263                     break;
       
   264                 }
       
   265             }
       
   266         }
       
   267         if (!found) {
       
   268             throw new Exception("forName removed but shouldn't have");
       
   269         }
       
   270         found = false;
       
   271         for (TryCatchBlockNode tcb : m.tryCatchBlocks) {
       
   272             if (tcb.type.equals(ClassNotFoundException.class.getName().replaceAll("\\.", "/"))) {
       
   273                 found = true;
       
   274                 break;
       
   275             }
       
   276         }
       
   277         if (!found) {
       
   278             throw new Exception("tryCatchBlocks removed but shouldn't have");
       
   279         }
       
   280     }
       
   281 
       
   282     static class MemClassLoader extends ClassLoader {
       
   283 
       
   284         private final Map<String, byte[]> classes;
       
   285         private final Map<String, Class<?>> cache = new HashMap<>();
       
   286 
       
   287         MemClassLoader(Map<String, byte[]> classes) {
       
   288             super(null);
       
   289             this.classes = classes;
       
   290         }
       
   291 
       
   292         @Override
       
   293         public Class findClass(String name) throws ClassNotFoundException {
       
   294             Class<?> clazz = cache.get(name);
       
   295             if (clazz == null) {
       
   296                 byte[] b = classes.get(name);
       
   297                 if (b == null) {
       
   298                     return super.findClass(name);
       
   299                 } else {
       
   300                     clazz = defineClass(name, b, 0, b.length);
       
   301                     cache.put(name, clazz);
       
   302                 }
       
   303             }
       
   304             return clazz;
       
   305         }
       
   306     }
       
   307 
       
   308     public static void main(String[] args) throws Exception {
       
   309         helper = Helper.newHelper();
       
   310         if (helper == null) {
       
   311             System.err.println("Test not run");
       
   312             return;
       
   313         }
       
   314 
       
   315         testForName();
       
   316 
       
   317         helper.generateDefaultModules();
       
   318         helper.generateDefaultJModule("optim1", "java.se");
       
   319         {
       
   320             String[] userOptions = {"--class-optim=all:log=./class-optim-log.txt"};
       
   321 
       
   322             Path imageDir = helper.generateDefaultImage(userOptions, "optim1").assertSuccess();
       
   323             helper.checkImage(imageDir, "optim1", null, null);
       
   324         }
       
   325 
       
   326         {
       
   327             String[] userOptions = {"--class-optim=forName-folding:log=./class-optim-log.txt"};
       
   328             Path imageDir = helper.generateDefaultImage(userOptions, "optim1").assertSuccess();
       
   329             helper.checkImage(imageDir, "optim1", null, null);
       
   330         }
       
   331 
       
   332         {
       
   333             ControlFlowPlugin plugin = new ControlFlowPlugin();
       
   334             PluginRepository.registerPlugin(plugin);
       
   335             String[] userOptions = {"--test-optim"};
       
   336             Path imageDir = helper.generateDefaultImage(userOptions, "optim1").assertSuccess();
       
   337             helper.checkImage(imageDir, "optim1", null, null);
       
   338             //System.out.println("Num methods analyzed " + provider.numMethods
       
   339             //        + "num blocks " + provider.numBlocks);
       
   340             if (!plugin.called) {
       
   341                 throw new Exception("Plugin not called");
       
   342             }
       
   343             if (plugin.numMethods < 1000) {
       
   344                 throw new Exception("Not enough method called,  should be "
       
   345                         + "around 10000 but is " + plugin.numMethods);
       
   346             }
       
   347             if (plugin.numBlocks < 100000) {
       
   348                 throw new Exception("Not enough blocks,  should be "
       
   349                         + "around 640000 but is " + plugin.numMethods);
       
   350             }
       
   351         }
       
   352     }
       
   353 
       
   354 }