1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.io.IOException;
  24 import java.net.URI;
  25 import java.nio.file.FileSystem;
  26 import java.nio.file.FileSystems;
  27 import java.nio.file.Files;
  28 import java.nio.file.Path;
  29 import java.nio.file.Paths;
  30 import java.util.ArrayList;
  31 import java.util.Collection;
  32 import java.util.Collections;
  33 import java.util.HashMap;
  34 import java.util.Iterator;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import java.util.function.Predicate;
  39 import java.util.stream.Collectors;
  40 import java.util.stream.Stream;
  41 import jdk.tools.jlink.internal.ModulePoolImpl;
  42 import jdk.tools.jlink.internal.StringTable;
  43 
  44 import jdk.tools.jlink.internal.plugins.asm.AsmPlugin;
  45 import jdk.tools.jlink.internal.plugins.asm.AsmPools;
  46 import jdk.tools.jlink.plugin.ModuleEntry;
  47 import jdk.tools.jlink.plugin.ModulePool;
  48 
  49 public abstract class AsmPluginTestBase {
  50 
  51     protected static final String TEST_MODULE = "jlink.test";
  52     protected static final Map<String, List<String>> MODULES;
  53 
  54     private static final Predicate<ModuleEntry> isClass = r -> r.getPath().endsWith(".class");
  55     private final List<String> classes;
  56     private final List<String> resources;
  57     private final ModulePool pool;
  58 
  59     static {
  60         Map<String, List<String>> map = new HashMap<>();
  61         map.put("jdk.localedata", new ArrayList<>());
  62         map.put("java.base", new ArrayList<>());
  63         map.put(TEST_MODULE, new ArrayList<>());
  64         MODULES = Collections.unmodifiableMap(map);
  65     }
  66 
  67     public static boolean isImageBuild() {
  68         Path javaHome = Paths.get(System.getProperty("test.jdk"));
  69         Path jmods = javaHome.resolve("jmods");
  70         return Files.exists(jmods);
  71     }
  72 
  73     public AsmPluginTestBase() {
  74         try {
  75             List<String> classes = new ArrayList<>();
  76             List<String> resources = new ArrayList<>();
  77 
  78             pool = new ModulePoolImpl();
  79 
  80             FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
  81             Path root = fs.getPath("/modules");
  82 
  83             List<byte[]> moduleInfos = new ArrayList<>();
  84             try (Stream<Path> stream = Files.walk(root)) {
  85                 for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext(); ) {
  86                     Path p = iterator.next();
  87                     if (Files.isRegularFile(p)) {
  88                         String module = p.toString().substring("/modules/".length());
  89                         module = module.substring(0, module.indexOf("/"));
  90                         if (MODULES.keySet().contains(module)) {
  91                             try {
  92                                 boolean isModuleInfo = p.endsWith("module-info.class");
  93                                 if (isModuleInfo) {
  94                                     moduleInfos.add(Files.readAllBytes(p));
  95                                 }
  96                                 byte[] content = Files.readAllBytes(p);
  97                                 if (p.toString().endsWith(".class") && !isModuleInfo) {
  98                                     classes.add(toClassName(p));
  99                                 } else if (!isModuleInfo) {
 100                                     MODULES.get(module).add(toResourceFile(p));
 101                                 }
 102                                 resources.add(toPath(p.toString()));
 103                                 ModuleEntry res = ModuleEntry.create(toPath(p.toString()), content);
 104                                 pool.add(res);
 105                             } catch (Exception ex) {
 106                                 throw new RuntimeException(ex);
 107                             }
 108                         }
 109                     }
 110                 }
 111             }
 112             // There is more than 10 classes in java.base...
 113             if (classes.size() < 10 || pool.getEntryCount() < 10) {
 114                 throw new AssertionError("Not expected resource or class number");
 115             }
 116 
 117             //Add a fake resource file
 118             String content = "java.lang.Object";
 119             String path = "META-INF/services/com.foo.BarProvider";
 120             ModuleEntry resFile = ModuleEntry.create("/" + TEST_MODULE + "/" +
 121                     path, content.getBytes());
 122             pool.add(resFile);
 123             ModuleEntry fakeInfoFile = ModuleEntry.create("/" + TEST_MODULE
 124                     + "/module-info.class", moduleInfos.get(0));
 125             pool.add(fakeInfoFile);
 126             MODULES.get(TEST_MODULE).add(path);
 127             for(Map.Entry<String, List<String>> entry : MODULES.entrySet()) {
 128                 if (entry.getValue().isEmpty()) {
 129                     throw new AssertionError("No resource file for " + entry.getKey());
 130                 }
 131             }
 132             this.classes = Collections.unmodifiableList(classes);
 133             this.resources = Collections.unmodifiableList(resources);
 134         } catch (Exception e) {
 135             throw new ExceptionInInitializerError(e);
 136         }
 137     }
 138 
 139     public List<String> getClasses() {
 140         return classes;
 141     }
 142 
 143     public List<String> getResources() {
 144         return resources;
 145     }
 146 
 147     public ModulePool getPool() {
 148         return pool;
 149     }
 150 
 151     public abstract void test() throws Exception;
 152 
 153     public Collection<ModuleEntry> extractClasses(ModulePool pool) {
 154         return pool.entries()
 155                 .filter(isClass)
 156                 .collect(Collectors.toSet());
 157     }
 158 
 159     public Collection<ModuleEntry> extractResources(ModulePool pool) {
 160         return pool.entries()
 161                 .filter(isClass.negate())
 162                 .collect(Collectors.toSet());
 163     }
 164 
 165     public String getModule(String path) {
 166         int index = path.indexOf("/", 1);
 167         return path.substring(1, index);
 168     }
 169 
 170     public String removeModule(String path) {
 171         int index = path.indexOf("/", 1);
 172         return path.substring(index + 1);
 173     }
 174 
 175     private String toPath(String p) {
 176         return p.substring("/modules".length());
 177     }
 178 
 179     private String toClassName(Path p) {
 180         String path = p.toString();
 181         path = path.substring("/modules/".length());
 182         // remove module
 183         if (!path.endsWith("module-info.class")) {
 184             path = path.substring(path.indexOf("/") + 1);
 185         }
 186         path = path.substring(0, path.length() - ".class".length());
 187 
 188         return path;
 189     }
 190 
 191     private String toResourceFile(Path p) {
 192         String path = p.toString();
 193         path = path.substring("/modules/".length());
 194         // remove module
 195         path = path.substring(path.indexOf("/") + 1);
 196 
 197         return path;
 198     }
 199 
 200     public abstract class TestPlugin extends AsmPlugin {
 201 
 202         private AsmPools pools;
 203 
 204         public AsmPools getPools() {
 205             return pools;
 206         }
 207 
 208         public boolean isVisitCalled() {
 209             return pools != null;
 210         }
 211 
 212         public ModulePool visit(ModulePool inResources) throws IOException {
 213             try {
 214                 ModulePool outResources = new ModulePoolImpl(inResources.getByteOrder(), new StringTable() {
 215                     @Override
 216                     public int addString(String str) {
 217                         return -1;
 218                     }
 219 
 220                     @Override
 221                     public String getString(int id) {
 222                         return null;
 223                     }
 224                 });
 225                 visit(inResources, outResources);
 226                 return outResources;
 227             } catch (Exception e) {
 228                 throw new IOException(e);
 229             }
 230         }
 231 
 232         @Override
 233         public void visit(AsmPools pools) {
 234             if (isVisitCalled()) {
 235                 throw new AssertionError("Visit was called twice");
 236             }
 237             this.pools = pools;
 238             visit();
 239         }
 240 
 241         public abstract void visit();
 242         public abstract void test(ModulePool inResources, ModulePool outResources) throws Exception;
 243 
 244         @Override
 245         public String getName() {
 246             return "test-plugin";
 247         }
 248     }
 249 }