# HG changeset patch # User jlaskey # Date 1467732074 10800 # Tue Jul 05 12:21:14 2016 -0300 # Node ID 57d149c09eec41f278722719f53d087a4bb0f7f7 # Parent 265f07335d163d103509ec17c857e8de97aeeb96 8160829: Remove ASMPool support from jlink Reviewed-by: sundar, psandoz, forax diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ClassForNamePlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ClassForNamePlugin.java new file mode 100644 --- /dev/null +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ClassForNamePlugin.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.tools.jlink.internal.plugins; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import jdk.tools.jlink.plugin.ModulePool; +import jdk.tools.jlink.plugin.Plugin.Category; +import jdk.internal.org.objectweb.asm.ClassReader; +import static jdk.internal.org.objectweb.asm.ClassReader.*; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.Type; +import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; +import jdk.internal.org.objectweb.asm.tree.ClassNode; +import jdk.internal.org.objectweb.asm.tree.InsnList; +import jdk.internal.org.objectweb.asm.tree.LabelNode; +import jdk.internal.org.objectweb.asm.tree.LdcInsnNode; +import jdk.internal.org.objectweb.asm.tree.LineNumberNode; +import jdk.internal.org.objectweb.asm.tree.MethodInsnNode; +import jdk.internal.org.objectweb.asm.tree.MethodNode; +import jdk.tools.jlink.plugin.ModuleEntry; +import jdk.tools.jlink.plugin.Plugin; + +public final class ClassForNamePlugin implements Plugin { + public static final String NAME = "class-for-name"; + + private static String binaryClassName(String path) { + return path.substring(path.indexOf('/', 1) + 1, + path.length() - ".class".length()); + } + + private static int getAccess(ModuleEntry resource) { + ClassReader cr = new ClassReader(resource.getBytes()); + + return cr.getAccess(); + } + + private static String getPackage(String binaryName) { + int index = binaryName.lastIndexOf("/"); + + return index == -1 ? "" : binaryName.substring(0, index); + } + + private ModuleEntry transform(ModuleEntry resource, Map classes) { + byte[] inBytes = resource.getBytes(); + ClassReader cr = new ClassReader(inBytes); + ClassNode cn = new ClassNode(); + cr.accept(cn, EXPAND_FRAMES); + List ms = cn.methods; + boolean modified = false; + LdcInsnNode ldc = null; + + String thisPackage = getPackage(binaryClassName(resource.getPath())); + + for (MethodNode mn : ms) { + InsnList il = mn.instructions; + Iterator it = il.iterator(); + + while (it.hasNext()) { + AbstractInsnNode insn = it.next(); + + if (insn instanceof LdcInsnNode) { + ldc = (LdcInsnNode)insn; + } else if (insn instanceof MethodInsnNode && ldc != null) { + MethodInsnNode min = (MethodInsnNode)insn; + + if (min.getOpcode() == Opcodes.INVOKESTATIC && + min.name.equals("forName") && + min.owner.equals("java/lang/Class") && + min.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { + String ldcClassName = ldc.cst.toString(); + String thatClassName = ldcClassName.replaceAll("\\.", "/"); + ModuleEntry thatClass = classes.get(thatClassName); + + if (thatClass != null) { + int thatAccess = getAccess(thatClass); + String thatPackage = getPackage(thatClassName); + + if ((thatAccess & Opcodes.ACC_PRIVATE) != Opcodes.ACC_PRIVATE && + ((thatAccess & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC || + thisPackage.equals(thatPackage))) { + Type type = Type.getObjectType(thatClassName); + il.remove(ldc); + il.set(min, new LdcInsnNode(type)); + modified = true; + } + } + } + + ldc = null; + } else if (!(insn instanceof LabelNode) && + !(insn instanceof LineNumberNode)) { + ldc = null; + } + + } + } + + if (modified) { + ClassWriter cw = new ClassWriter(cr, 0); + cn.accept(cw); + byte[] outBytes = cw.toByteArray(); + + return resource.create(outBytes); + } + + return resource; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public void visit(ModulePool in, ModulePool out) { + Objects.requireNonNull(in); + Objects.requireNonNull(out); + Map classes = in.entries() + .filter(resource -> resource != null && + resource.getPath().endsWith(".class") && + !resource.getPath().endsWith("/module-info.class")) + .collect(Collectors.toMap(resource -> binaryClassName(resource.getPath()), + resource -> resource)); + in.entries() + .filter(resource -> resource != null) + .forEach(resource -> { + String path = resource.getPath(); + + if (path.endsWith(".class") && !path.endsWith("/module-info.class")) { + out.add(transform(resource, classes)); + } else { + out.add(resource); + } + }); + } + + @Override + public Category getType() { + return Category.TRANSFORMER; + } + + @Override + public boolean hasArguments() { + return false; + } + + @Override + public String getDescription() { + return PluginsResourceBundle.getDescription(NAME); + } + + @Override + public String getArgumentsDescription() { + return PluginsResourceBundle.getArgument(NAME); + } + + @Override + public void configure(Map config) { + + } +} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Consumer; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.tools.jlink.internal.plugins.asm.AsmPools; -import jdk.tools.jlink.internal.plugins.asm.AsmPlugin; -import jdk.internal.org.objectweb.asm.tree.ClassNode; -import jdk.internal.org.objectweb.asm.tree.MethodNode; -import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.internal.plugins.optim.ForNameFolding; -import jdk.tools.jlink.internal.plugins.optim.ReflectionOptimizer.TypeResolver; -import jdk.tools.jlink.plugin.PluginException; - -/** - * - * Optimize Classes following various strategies. Strategies are implementation - * of ClassOptimizer and MethodOptimizer. - */ -public final class OptimizationPlugin extends AsmPlugin { - - public static final String NAME = "class-optim"; - public static final String LOG = "log"; - public static final String ALL = "all"; - public static final String FORNAME_REMOVAL = "forName-folding"; - - /** - * Default resolver. A resolver that retrieve types that are in an - * accessible package, are public or are located in the same package as the - * caller. - */ - private static final class DefaultTypeResolver implements TypeResolver { - - private final Set packages; - private final AsmPools pools; - - DefaultTypeResolver(AsmPools pools, AsmModulePool modulePool) { - Objects.requireNonNull(pools); - Objects.requireNonNull(modulePool); - this.pools = pools; - packages = pools.getGlobalPool().getAccessiblePackages(modulePool.getModuleName()); - } - - @Override - public ClassReader resolve(ClassNode cn, MethodNode mn, String type) { - int classIndex = cn.name.lastIndexOf("/"); - String callerPkg = classIndex == -1 ? "" - : cn.name.substring(0, classIndex); - int typeClassIndex = type.lastIndexOf("/"); - String pkg = typeClassIndex == - 1 ? "" - : type.substring(0, typeClassIndex); - ClassReader reader = null; - if (packages.contains(pkg) || pkg.equals(callerPkg)) { - ClassReader r = pools.getGlobalPool().getClassReader(type); - if (r != null) { - // if not private - if ((r.getAccess() & Opcodes.ACC_PRIVATE) - != Opcodes.ACC_PRIVATE) { - // public - if (((r.getAccess() & Opcodes.ACC_PUBLIC) - == Opcodes.ACC_PUBLIC)) { - reader = r; - } else if (pkg.equals(callerPkg)) { - reader = r; - } - } - } - } - return reader; - } - } - - public interface Optimizer { - - void close() throws IOException; - } - - public interface ClassOptimizer extends Optimizer { - - boolean optimize(Consumer logger, AsmPools pools, - AsmModulePool modulePool, - ClassNode cn) throws Exception; - } - - public interface MethodOptimizer extends Optimizer { - - boolean optimize(Consumer logger, AsmPools pools, - AsmModulePool modulePool, - ClassNode cn, MethodNode m, TypeResolver resolver) throws Exception; - } - - private List optimizers = new ArrayList<>(); - - private OutputStream stream; - private int numMethods; - - private void log(String content) { - if (stream != null) { - try { - content = content + "\n"; - stream.write(content.getBytes(StandardCharsets.UTF_8)); - } catch (IOException ex) { - System.err.println(ex); - } - } - } - - private void close() throws IOException { - log("Num analyzed methods " + numMethods); - - for (Optimizer optimizer : optimizers) { - try { - optimizer.close(); - } catch (IOException ex) { - System.err.println("Error closing optimizer " + ex); - } - } - if (stream != null) { - stream.close(); - } - } - - @Override - public String getName() { - return NAME; - } - - @Override - public void visit(AsmPools pools) { - try { - for (AsmModulePool p : pools.getModulePools()) { - DefaultTypeResolver resolver = new DefaultTypeResolver(pools, p); - p.visitClassReaders((reader) -> { - ClassWriter w = null; - try { - w = optimize(pools, p, reader, resolver); - } catch (IOException ex) { - throw new PluginException("Problem optimizing " - + reader.getClassName(), ex); - } - return w; - }); - } - } finally { - try { - close(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - } - - private ClassWriter optimize(AsmPools pools, AsmModulePool modulePool, - ClassReader reader, TypeResolver resolver) - throws IOException { - ClassNode cn = new ClassNode(); - ClassWriter writer = null; - if ((reader.getAccess() & Opcodes.ACC_INTERFACE) == 0) { - reader.accept(cn, ClassReader.EXPAND_FRAMES); - boolean optimized = false; - for (Optimizer optimizer : optimizers) { - if (optimizer instanceof ClassOptimizer) { - try { - boolean optim = ((ClassOptimizer) optimizer). - optimize(this::log, pools, modulePool, cn); - if (optim) { - optimized = true; - } - } catch (Throwable ex) { - throw new PluginException("Exception optimizing " - + reader.getClassName(), ex); - } - } else { - MethodOptimizer moptimizer = (MethodOptimizer) optimizer; - for (MethodNode m : cn.methods) { - if ((m.access & Opcodes.ACC_ABSTRACT) == 0 - && (m.access & Opcodes.ACC_NATIVE) == 0) { - numMethods += 1; - try { - boolean optim = moptimizer. - optimize(this::log, pools, modulePool, cn, - m, resolver); - if (optim) { - optimized = true; - } - } catch (Throwable ex) { - throw new PluginException("Exception optimizing " - + reader.getClassName() + "." + m.name, ex); - } - - } - } - } - } - - if (optimized) { - writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); - try { - // add a validation layer in between to check for class vallidity - CheckClassAdapter ca = new CheckClassAdapter(writer); - cn.accept(ca); - } catch (Exception ex) { - throw new PluginException("Exception optimizing class " + cn.name, ex); - } - } - } - return writer; - } - - @Override - public String getDescription() { - return PluginsResourceBundle.getDescription(NAME); - } - - @Override - public boolean hasArguments() { - return true; - } - - @Override - public String getArgumentsDescription() { - return PluginsResourceBundle.getArgument(NAME); - } - - @Override - public void configure(Map config) { - String strategies = config.get(NAME); - String[] arr = strategies.split(","); - for (String s : arr) { - if (s.equals(ALL)) { - optimizers.clear(); - optimizers.add(new ForNameFolding()); - break; - } else if (s.equals(FORNAME_REMOVAL)) { - optimizers.add(new ForNameFolding()); - } else { - throw new IllegalArgumentException("Unknown optimization: " + s); - } - } - String f = config.get(LOG); - if (f != null) { - try { - stream = new FileOutputStream(f); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmGlobalPool.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmGlobalPool.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmGlobalPool.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.asm; - -import java.util.Set; - -/** - * A pool containing all class and resource files. - */ -public interface AsmGlobalPool extends AsmPool { - - /** - * Associate a package to a module, useful when adding new classes in new - * packages. WARNING: In order to properly handle new package and/or new - * module, module-info class must be added and/or updated. - * - * @param pkg The new package, following java binary syntax (/-separated - * path name). - * @param module An existing or new module. - * @throws jdk.tools.jlink.plugins.PluginException If a mapping already - * exist for this package. - */ - public void addPackageModuleMapping(String pkg, String module); - - /** - * Return the set of accessible packages for a given module. - * - * @param module The module from which packages are accessible. - * @return Set of packages or null if the module is not found. - */ - public Set getAccessiblePackages(String module); -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmModulePool.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmModulePool.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmModulePool.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.asm; - -import java.lang.module.ModuleDescriptor; -import java.util.Set; -import jdk.internal.org.objectweb.asm.ClassReader; - -/** - * A pool for a given module - */ -public interface AsmModulePool extends AsmPool { - - /** - * Associate a package to this module, useful when adding new classes in new - * packages. WARNING: In order to properly handle new package and/or new - * module, module-info class must be added and/or updated. - * - * @param pkg The new package, following java binary syntax (/-separated - * path name). - * @throws jdk.tools.jlink.plugins.PluginException If a mapping already - * exist for this package. - */ - public void addPackage(String pkg); - - /** - * The module name of this pool. - * @return The module name; - */ - public String getModuleName(); - - /** - * Lookup the class in this pool and the required pools. NB: static module - * readability can be different at execution time. - * - * @param binaryName The class to lookup. - * @return The reader or null if not found - * @throws jdk.tools.jlink.plugins.PluginException - */ - public ClassReader getClassReaderInDependencies(String binaryName); - - /** - * Lookup the class in the exported packages of this module. "public - * requires" modules are looked up. NB: static module readability can be - * different at execution time. - * - * @param callerModule Name of calling module. - * @param binaryName The class to lookup. - * @return The reader or null if not found - * @throws jdk.tools.jlink.plugins.PluginException - */ - public ClassReader getExportedClassReader(String callerModule, - String binaryName); - - /** - * The module descriptor. - * - * @return The module descriptor; - */ - public ModuleDescriptor getDescriptor(); - - /** - * Retrieve the internal and exported packages. - * - * @return - */ - public Set getAllPackages(); -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPlugin.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.asm; - -import java.util.Objects; -import jdk.tools.jlink.plugin.Plugin; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; -import jdk.tools.jlink.internal.ModulePoolImpl; - -/** - * Extend this class to develop your own plugin in order to transform jimage - * resources. - * - */ -public abstract class AsmPlugin implements Plugin { - - public AsmPlugin() { - } - - @Override - public void visit(ModulePool allContent, ModulePool outResources) { - Objects.requireNonNull(allContent); - Objects.requireNonNull(outResources); - ModulePoolImpl resources = new ModulePoolImpl(allContent.getByteOrder()); - allContent.entries().forEach(md -> { - if(md.getType().equals(ModuleEntry.Type.CLASS_OR_RESOURCE)) { - resources.add(md); - } else { - outResources.add(md); - } - }); - AsmPools pools = new AsmPools(resources); - visit(pools); - pools.fillOutputResources(outResources); - } - - /** - * This is the method to implement in order to - * apply Asm transformation to jimage contained classes. - * @param pools The pool of Asm classes and other resource files. - * @param strings To add a string to the jimage strings table. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public abstract void visit(AsmPools pools); -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPool.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPool.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPool.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.asm; - -import java.util.Collection; -import java.util.List; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -/** - * A pool of ClassReader and other resource files. - * This class allows to transform and sort classes and resource files. - *

- * Classes in the class pool are named following java binary name specification. - * For example, java.lang.Object class is named java/lang/Object - *

- * Module information has been stripped out from class and other resource files - * (.properties, binary files, ...).

- */ -public interface AsmPool { - - /** - * A resource that is not a class file. - *

- * The path of a resource is a /-separated path name that identifies the - * resource. For example com.foo.bar.Bundle.properties resource name is - * com/foo/bar/Bundle.properties

- *

- */ - public class ResourceFile { - - private final String path; - private final byte[] content; - - public ResourceFile(String path, byte[] content) { - this.path = path; - this.content = content; - } - - public String getPath() { - return path; - } - - public byte[] getContent() { - return content; - } - } - - /** - * To visit each Class contained in the pool - */ - public interface ClassReaderVisitor { - - /** - * Called for each ClassReader located in the pool. - * - * @param reader A class reader. - * @return A writer or null if the class has not been transformed. - */ - public ClassWriter visit(ClassReader reader); - } - - /** - * To visit each Resource contained in the pool - */ - public interface ResourceFileVisitor { - - /** - * Called for each Resource file located in the pool. - * - * @param reader A resource file. - * @return A resource file or null if the resource has not been - * transformed. - */ - public ResourceFile visit(ResourceFile reader); - } - - /** - * Contains the transformed classes. When the jimage file is generated, - * transformed classes take precedence on unmodified ones. - */ - public interface WritableClassPool { - - /** - * Add a class to the pool, if a class already exists, it is replaced. - * - * @param writer The class writer. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public void addClass(ClassWriter writer); - - /** - * The class will be not added to the jimage file. - * - * @param className The class name to forget. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public void forgetClass(String className); - - /** - * Get a transformed class. - * - * @param binaryName The java class binary name - * @return The ClassReader or null if the class is not found. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public ClassReader getClassReader(String binaryName); - - /** - * Get a transformed class. - * - * @param res A class resource. - * @return The ClassReader or null if the class is not found. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public ClassReader getClassReader(ModuleEntry res); - - /** - * Returns all the classes contained in the writable pool. - * - * @return The collection of classes. - */ - public Collection getClasses(); - } - - /** - * Contains the transformed resources. When the jimage file is generated, - * transformed resources take precedence on unmodified ones. - */ - public interface WritableResourcePool { - - /** - * Add a resource, if the resource exists, it is replaced. - * - * @param resFile The resource file to add. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public void addResourceFile(ResourceFile resFile); - - /** - * The resource will be not added to the jimage file. - * - * @param resourceName - * @throws jdk.tools.jlink.plugin.PluginException If the resource to - * forget doesn't exist or is null. - */ - public void forgetResourceFile(String resourceName); - - /** - * Get a transformed resource. - * - * @param name The java resource name - * @return The Resource or null if the resource is not found. - */ - public ResourceFile getResourceFile(String name); - - /** - * Get a transformed resource. - * - * @param res The java resource - * @return The Resource or null if the resource is not found. - */ - public ResourceFile getResourceFile(ModuleEntry res); - - /** - * Returns all the resources contained in the writable pool. - * - * @return The array of resources. - */ - public Collection getResourceFiles(); - } - - /** - * To order the classes and resources within a jimage file. - */ - public interface Sorter { - - /** - * @param resources The resources will be added to the jimage following - * the order of this ResourcePool. - * @return The resource paths ordered in the way to use for storage in the jimage. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public List sort(ModulePool resources); - } - - /** - * The writable pool used to store transformed resources. - * - * @return The writable pool. - */ - public WritableClassPool getTransformedClasses(); - - /** - * The writable pool used to store transformed resource files. - * - * @return The writable pool. - */ - public WritableResourcePool getTransformedResourceFiles(); - - /** - * Set a sorter instance to sort all files. If no sorter is set, then input - * Resources will be added in the order they have been received followed by - * newly added resources. - * - * @param sorter - */ - public void setSorter(Sorter sorter); - - /** - * Returns the classes contained in the pool. - * - * @return The classes. - */ - public Collection getClasses(); - - /** - * Returns the resources contained in the pool. Resources are all the file - * that are not classes (eg: properties file, binary files, ...) - * - * @return The array of resource files. - */ - public Collection getResourceFiles(); - - /** - * Retrieves a resource based on the binary name. This name doesn't contain - * the module name. - * NB: When dealing with resources that have the same name in various - * modules (eg: META-INFO/*), you should use the ResourcePool - * referenced from this AsmClassPool. - * - * @param binaryName Name of a Java resource or null if the resource doesn't - * exist. - * @return - */ - public ResourceFile getResourceFile(String binaryName); - - /** - * Retrieves a resource for the passed resource. - * - * @param res The resource - * @return The resource file or null if it doesn't exist. - */ - public ResourceFile getResourceFile(ModuleEntry res); - - /** - * Retrieve a ClassReader from the pool. - * - * @param binaryName Class binary name - * @return A reader or null if the class is unknown - * @throws jdk.tools.jlink.plugin.PluginException - */ - public ClassReader getClassReader(String binaryName); - - /** - * Retrieve a ClassReader from the pool. - * - * @param res A resource. - * @return A reader or null if the class is unknown - * @throws jdk.tools.jlink.plugin.PluginException - */ - public ClassReader getClassReader(ModuleEntry res); - - /** - * To visit the set of ClassReaders. - * - * @param visitor The visitor. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public void visitClassReaders(ClassReaderVisitor visitor); - - /** - * To visit the set of ClassReaders. - * - * @param visitor The visitor. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public void visitResourceFiles(ResourceFileVisitor visitor); - - /** - * Returns the pool of all the resources (transformed and unmodified). - * The input resources are replaced by the transformed ones. - * If a sorter has been set, it is used to sort the returned resources. - * - * @param output The pool used to fill the jimage. - * @throws jdk.tools.jlink.plugin.PluginException - */ - public void fillOutputResources(ModulePool output); - -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPoolImpl.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPoolImpl.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPoolImpl.java +++ /dev/null @@ -1,698 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.asm; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleDescriptor.Requires; -import java.lang.module.ModuleDescriptor.Requires.Modifier; -import java.lang.module.ModuleDescriptor.Exports; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.tools.jlink.internal.ImageFileCreator; -import jdk.tools.jlink.internal.ModulePoolImpl; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.PluginException; -import jdk.tools.jlink.plugin.ModulePool; - -/** - * A pool of ClassReader and other resource files. This class allows to - * transform and sort classes and resource files. - *

- * Classes in the class pool are named following java binary name specification. - * For example, java.lang.Object class is named java/lang/Object - *

- * Module information has been stripped out from class and other resource files - * (.properties, binary files, ...).

- */ -final class AsmPoolImpl implements AsmModulePool { - - /** - * Contains the transformed classes. When the jimage file is generated, - * transformed classes take precedence on unmodified ones. - */ - public final class WritableClassPoolImpl implements WritableClassPool { - - private WritableClassPoolImpl() { - } - - /** - * Add a class to the pool, if a class already exists, it is replaced. - * - * @param writer The class writer. - * @throws java.io.IOException - */ - @Override - public void addClass(ClassWriter writer) { - Objects.requireNonNull(writer); - // Retrieve the className - ClassReader reader = newClassReader(writer.toByteArray()); - String className = reader.getClassName(); - String path; - if (className.endsWith("module-info")) { - // remove the module name contained in the class name - className = className.substring(className.indexOf("/") + 1); - path = "/" + moduleName + "/" + className; - } else { - path = toClassNamePath(className); - } - - byte[] content = writer.toByteArray(); - ModuleEntry res = ModuleEntry.create(path, content); - transformedClasses.put(className, res); - } - - /** - * The class will be not added to the jimage file. - * - * @param className The class name to forget. - */ - @Override - public void forgetClass(String className) { - Objects.requireNonNull(className); - // do we have a resource? - ModuleEntry res = transformedClasses.get(className); - if (res == null) { - res = inputClasses.get(className); - if (res == null) { - throw new PluginException("Unknown class " + className); - } - } - String path = toClassNamePath(className); - forgetResources.add(path); - // Just in case it has been added. - transformedClasses.remove(className); - } - - /** - * Get a transformed class. - * - * @param binaryName The java class binary name - * @return The ClassReader or null if the class is not found. - */ - @Override - public ClassReader getClassReader(String binaryName) { - Objects.requireNonNull(binaryName); - ModuleEntry res = transformedClasses.get(binaryName); - ClassReader reader = null; - if (res != null) { - reader = getClassReader(res); - } - return reader; - } - - /** - * Returns all the classes contained in the writable pool. - * - * @return The array of transformed classes. - */ - @Override - public Collection getClasses() { - List classes = new ArrayList<>(); - for (Entry entry : transformedClasses.entrySet()) { - classes.add(entry.getValue()); - } - return classes; - } - - @Override - public ClassReader getClassReader(ModuleEntry res) { - return newClassReader(res.getBytes()); - } - } - - /** - * Contains the transformed resources. When the jimage file is generated, - * transformed resources take precedence on unmodified ones. - */ - public final class WritableResourcePoolImpl implements WritableResourcePool { - - private WritableResourcePoolImpl() { - } - - /** - * Add a resource, if the resource exists, it is replaced. - * - * @param resFile The resource file to add. - */ - @Override - public void addResourceFile(ResourceFile resFile) { - Objects.requireNonNull(resFile); - String path = toResourceNamePath(resFile.getPath()); - ModuleEntry res = ModuleEntry.create(path, resFile.getContent()); - transformedResources.put(resFile.getPath(), res); - } - - /** - * The resource will be not added to the jimage file. - * - * @param resourceName - * @throws java.io.IOException - */ - @Override - public void forgetResourceFile(String resourceName) { - Objects.requireNonNull(resourceName); - String path = toResourceNamePath(resourceName); - // do we have a resource? - ModuleEntry res = transformedResources.get(resourceName); - if (res == null) { - res = inputResources.get(resourceName); - if (res == null) { - throw new PluginException("Unknown resource " + resourceName); - } - } - forgetResources.add(path); - // Just in case it has been added. - transformedResources.remove(resourceName); - } - - /** - * Get a transformed resource. - * - * @param name The java resource name - * @return The Resource or null if the resource is not found. - */ - @Override - public ResourceFile getResourceFile(String name) { - Objects.requireNonNull(name); - ModuleEntry res = transformedResources.get(name); - ResourceFile resFile = null; - if (res != null) { - resFile = getResourceFile(res); - } - return resFile; - } - - /** - * Returns all the resources contained in the writable pool. - * - * @return The array of transformed classes. - */ - @Override - public Collection getResourceFiles() { - List resources = new ArrayList<>(); - for (Entry entry : transformedResources.entrySet()) { - resources.add(entry.getValue()); - } - return resources; - } - - @Override - public ResourceFile getResourceFile(ModuleEntry res) { - return new ResourceFile(toJavaBinaryResourceName(res.getPath()), - res.getBytes()); - } - } - - private final ModulePool jimageResources; - private final Map inputClasses; - private final Map inputResources; - private final Map inputClassPackageMapping; - private final Map inputOtherPackageMapping; - - private final WritableClassPool transClassesPool - = new WritableClassPoolImpl(); - private final WritableResourcePool transResourcesPool - = new WritableResourcePoolImpl(); - - private Sorter sorter; - - private final Map transformedClasses - = new LinkedHashMap<>(); - private final Map transformedResources - = new LinkedHashMap<>(); - private final List forgetResources = new ArrayList<>(); - private final Map newPackageMapping = new HashMap<>(); - - private final String moduleName; - - private final ModuleDescriptor descriptor; - private final AsmPools pools; - - /** - * A new Asm pool. - * - * @param inputResources The raw resources to build the pool from. - * @param moduleName The name of a module. - * @param pools The resource pools. - * @param descriptor The module descriptor. - */ - AsmPoolImpl(ModulePool inputResources, String moduleName, - AsmPools pools, - ModuleDescriptor descriptor) { - Objects.requireNonNull(inputResources); - Objects.requireNonNull(moduleName); - Objects.requireNonNull(pools); - Objects.requireNonNull(descriptor); - this.jimageResources = inputResources; - this.moduleName = moduleName; - this.pools = pools; - this.descriptor = descriptor; - Map classes = new LinkedHashMap<>(); - Map resources = new LinkedHashMap<>(); - Map packageClassToModule = new HashMap<>(); - Map packageOtherToModule = new HashMap<>(); - inputResources.entries().forEach(res -> { - if (res.getPath().endsWith(".class")) { - classes.put(toJavaBinaryClassName(res.getPath()), res); - } else { - resources.put(toJavaBinaryResourceName(res.getPath()), res); - } - String[] split = ImageFileCreator.splitPath(res.getPath()); - if (ImageFileCreator.isClassPackage(res.getPath())) { - packageClassToModule.put(split[1], res.getModule()); - } else { - // Keep a map of other resources - // Same resource names such as META-INF/* should be handled with full path name. - if (!split[1].isEmpty()) { - packageOtherToModule.put(split[1], res.getModule()); - } - } - }); - this.inputClasses = Collections.unmodifiableMap(classes); - this.inputResources = Collections.unmodifiableMap(resources); - - this.inputClassPackageMapping = Collections.unmodifiableMap(packageClassToModule); - this.inputOtherPackageMapping = Collections.unmodifiableMap(packageOtherToModule); - } - - @Override - public String getModuleName() { - return moduleName; - } - - /** - * The writable pool used to store transformed resources. - * - * @return The writable pool. - */ - @Override - public WritableClassPool getTransformedClasses() { - return transClassesPool; - } - - /** - * The writable pool used to store transformed resource files. - * - * @return The writable pool. - */ - @Override - public WritableResourcePool getTransformedResourceFiles() { - return transResourcesPool; - } - - /** - * Set a sorter instance to sort all files. If no sorter is set, then input - * Resources will be added in the order they have been received followed by - * newly added resources. - * - * @param sorter - */ - @Override - public void setSorter(Sorter sorter) { - this.sorter = sorter; - } - - /** - * Returns the classes contained in the pool. - * - * @return The array of classes. - */ - @Override - public Collection getClasses() { - return inputClasses.values(); - } - - /** - * Returns the resources contained in the pool. Resources are all the file - * that are not classes (eg: properties file, binary files, ...) - * - * @return The array of classes. - */ - @Override - public Collection getResourceFiles() { - return inputResources.values(); - } - - /** - * Retrieves a resource based on the binary name. This name doesn't contain - * the module name. - * NB: When dealing with resources that have the same name in various - * modules (eg: META-INFO/*), you should use the ResourcePool - * referenced from this AsmClassPool. - * - * @param binaryName Name of a Java resource or null if the resource doesn't - * exist. - * @return - */ - @Override - public ResourceFile getResourceFile(String binaryName) { - Objects.requireNonNull(binaryName); - ModuleEntry res = inputResources.get(binaryName); - ResourceFile resFile = null; - if (res != null) { - resFile = getResourceFile(res); - } - return resFile; - } - - /** - * Retrieve a ClassReader from the pool. - * - * @param binaryName Class binary name - * @return A reader or null if the class is unknown - */ - @Override - public ClassReader getClassReader(String binaryName) { - Objects.requireNonNull(binaryName); - ModuleEntry res = inputClasses.get(binaryName); - ClassReader reader = null; - if (res != null) { - reader = getClassReader(res); - } - return reader; - } - - @Override - public ResourceFile getResourceFile(ModuleEntry res) { - return new ResourceFile(toJavaBinaryResourceName(res.getPath()), - res.getBytes()); - } - - @Override - public ClassReader getClassReader(ModuleEntry res) { - return newClassReader(res.getBytes()); - } - - /** - * Lookup the class in this pool and the required pools. NB: static module - * readability can be different at execution time. - * - * @param binaryName The class to lookup. - * @return The reader or null if not found - */ - @Override - public ClassReader getClassReaderInDependencies(String binaryName) { - Objects.requireNonNull(binaryName); - ClassReader reader = getClassReader(binaryName); - if (reader == null) { - for (Requires requires : descriptor.requires()) { - AsmModulePool pool = pools.getModulePool(requires.name()); - reader = pool.getExportedClassReader(moduleName, binaryName); - if (reader != null) { - break; - } - } - } - return reader; - } - - /** - * Lookup the class in the exported packages of this module. "public - * requires" modules are looked up. NB: static module readability can be - * different at execution time. - * - * @param callerModule Name of calling module. - * @param binaryName The class to lookup. - * @return The reader or null if not found - */ - @Override - public ClassReader getExportedClassReader(String callerModule, String binaryName) { - Objects.requireNonNull(callerModule); - Objects.requireNonNull(binaryName); - boolean exported = false; - ClassReader clazz = null; - for (Exports e : descriptor.exports()) { - String pkg = e.source(); - Set targets = e.targets(); - System.out.println("PKG " + pkg); - if (targets.isEmpty() || targets.contains(callerModule)) { - if (binaryName.startsWith(pkg)) { - String className = binaryName.substring(pkg.length()); - System.out.println("CLASS " + className); - exported = !className.contains("."); - } - if (exported) { - break; - } - } - } - // public requires (re-export) - if (!exported) { - for (Requires requires : descriptor.requires()) { - if (requires.modifiers().contains(Modifier.PUBLIC)) { - AsmModulePool pool = pools.getModulePool(requires.name()); - clazz = pool.getExportedClassReader(moduleName, binaryName); - if (clazz != null) { - break; - } - } - } - } else { - clazz = getClassReader(binaryName); - } - return clazz; - - } - - @Override - public ModuleDescriptor getDescriptor() { - return descriptor; - } - - /** - * To visit the set of ClassReaders. - * - * @param visitor The visitor. - */ - @Override - public void visitClassReaders(ClassReaderVisitor visitor) { - Objects.requireNonNull(visitor); - for (ModuleEntry res : getClasses()) { - ClassReader reader = newClassReader(res.getBytes()); - ClassWriter writer = visitor.visit(reader); - if (writer != null) { - - getTransformedClasses().addClass(writer); - } - } - } - - /** - * To visit the set of ClassReaders. - * - * @param visitor The visitor. - */ - @Override - public void visitResourceFiles(ResourceFileVisitor visitor) { - Objects.requireNonNull(visitor); - for (ModuleEntry resource : getResourceFiles()) { - ResourceFile resFile - = new ResourceFile(toJavaBinaryResourceName(resource.getPath()), - resource.getBytes()); - ResourceFile res = visitor.visit(resFile); - if (res != null) { - getTransformedResourceFiles().addResourceFile(res); - } - } - } - - /** - * Returns the pool of all the resources (transformed and unmodified). The - * input resources are replaced by the transformed ones. If a sorter has - * been set, it is used to sort the returned resources. * - */ - @Override - public void fillOutputResources(ModulePool outputResources) { - List added = new ArrayList<>(); - // If the sorter is null, use the input order. - // New resources are added at the end - // First input classes that have not been removed - ModulePool output = new ModulePoolImpl(outputResources.getByteOrder(), - ((ModulePoolImpl)outputResources).getStringTable()); - jimageResources.entries().forEach(inResource -> { - if (!forgetResources.contains(inResource.getPath())) { - ModuleEntry resource = inResource; - // Do we have a transformed class with the same name? - ModuleEntry res = transformedResources. - get(toJavaBinaryResourceName(inResource.getPath())); - if (res != null) { - resource = res; - } else { - res = transformedClasses. - get(toJavaBinaryClassName(inResource.getPath())); - if (res != null) { - resource = res; - } - } - output.add(resource); - added.add(resource.getPath()); - } - }); - // Then new resources - for (Map.Entry entry : transformedResources.entrySet()) { - ModuleEntry resource = entry.getValue(); - if (!forgetResources.contains(resource.getPath())) { - if (!added.contains(resource.getPath())) { - output.add(resource); - } - } - } - // And new classes - for (Map.Entry entry : transformedClasses.entrySet()) { - ModuleEntry resource = entry.getValue(); - if (!forgetResources.contains(resource.getPath())) { - if (!added.contains(resource.getPath())) { - output.add(resource); - } - } - } - - AsmPools.sort(outputResources, output, sorter); - } - - /** - * Associate a package to this module, useful when adding new classes in new - * packages. WARNING: In order to properly handle new package and/or new - * module, module-info class must be added and/or updated. - * - * @param pkg The new package, following java binary syntax (/-separated - * path name). - * @throws PluginException If a mapping already exist for this package. - */ - @Override - public void addPackage(String pkg) { - Objects.requireNonNull(pkg); - Objects.requireNonNull(moduleName); - pkg = pkg.replaceAll("/", "."); - String mod = newPackageMapping.get(pkg); - if (mod != null) { - throw new PluginException(mod + " module already contains package " + pkg); - } - newPackageMapping.put(pkg, moduleName); - } - - @Override - public Set getAllPackages() { - ModuleDescriptor desc = getDescriptor(); - Set packages = new HashSet<>(); - for (String p : desc.conceals()) { - packages.add(p.replaceAll("\\.", "/")); - } - for (String p : newPackageMapping.keySet()) { - packages.add(p.replaceAll("\\.", "/")); - } - for (Exports ex : desc.exports()) { - packages.add(ex.source().replaceAll("\\.", "/")); - } - return packages; - } - - private static ClassReader newClassReader(byte[] bytes) { - try { - ByteArrayInputStream stream = new ByteArrayInputStream(bytes); - ClassReader reader = new ClassReader(stream); - return reader; - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - private static String toJavaBinaryClassName(String path) { - if (path.endsWith("module-info.class")) { - path = removeClassExtension(path); - } else { - path = removeModuleName(path); - path = removeClassExtension(path); - } - return path; - } - - private static String toJavaBinaryResourceName(String path) { - if (!path.endsWith("module-info.class")) { - path = removeModuleName(path); - } - return path; - } - - private static String removeClassExtension(String path) { - return path.substring(0, path.length() - ".class".length()); - } - - private static String removeModuleName(String path) { - path = path.substring(1); - return path.substring(path.indexOf("/") + 1, path.length()); - } - - private String toClassNamePath(String className) { - return toResourceNamePath(className) + ".class"; - } - - /** - * Entry point to manage resource<->module association. - */ - private String toResourceNamePath(String resourceName) { - if (!resourceName.startsWith("/")) { - resourceName = "/" + resourceName; - } - String pkg = toPackage(resourceName); - String module = inputClassPackageMapping.get(pkg); - if (module == null) { - module = newPackageMapping.get(pkg); - if (module == null) { - module = inputOtherPackageMapping.get(pkg); - if (module == null) { - throw new PluginException("No module for package" + pkg); - } - } - } - return "/" + module + resourceName; - } - - private static String toPackage(String path) { - if (path.startsWith("/")) { - path = path.substring(1); - } - int i = path.lastIndexOf("/"); - if (i == -1) { - // Default package... - return ""; - } - return path.substring(0, i).replaceAll("/", "."); - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPools.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPools.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/asm/AsmPools.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.asm; - -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleDescriptor.Exports; -import java.lang.module.ModuleDescriptor.Requires; -import static java.lang.module.ModuleDescriptor.Requires.Modifier.PUBLIC; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.tools.jlink.internal.ModulePoolImpl; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.Sorter; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.PluginException; -import jdk.tools.jlink.plugin.ModulePool; - -/** - * A container for pools of ClassReader and other resource files. A pool of all - * the resources or a pool for a given module can be retrieved - */ -public final class AsmPools { - - /** - * Sort the order in which the modules will be stored in the jimage file. - */ - public interface ModuleSorter { - - /** - * Sort the list of modules. - * - * @param modules The list of module names. The module will be stored in - * the jimage following this order. - * @return A list of module names that expresses the order in which the - * modules are stored in the jimage. - */ - public List sort(List modules); - } - - private class AsmGlobalPoolImpl implements AsmGlobalPool { - - private Sorter sorter = null; - - private class GlobalWritableClassPool implements WritableClassPool { - - @Override - public void addClass(ClassWriter writer) { - visitFirstNonFailingPool((AsmModulePool pool) -> { - pool.getTransformedClasses().addClass(writer); - }); - } - - @Override - public void forgetClass(String className) { - visitFirstNonFailingPool((AsmModulePool pool) -> { - pool.getTransformedClasses().forgetClass(className); - }); - } - - @Override - public ClassReader getClassReader(String binaryName) { - return visitPools((AsmModulePool pool) -> { - return pool.getTransformedClasses().getClassReader(binaryName); - }); - } - - @Override - public Collection getClasses() { - List all = new ArrayList<>(); - visitAllPools((AsmModulePool pool) -> { - for (ModuleEntry rf : pool.getTransformedClasses().getClasses()) { - all.add(rf); - } - }); - return all; - } - - @Override - public ClassReader getClassReader(ModuleEntry res) { - return visitPools((AsmModulePool pool) -> { - return pool.getTransformedClasses().getClassReader(res); - }); - } - - } - - private class GlobalWritableResourcePool implements WritableResourcePool { - - @Override - public void addResourceFile(ResourceFile resFile) { - visitFirstNonFailingPool((AsmModulePool pool) -> { - pool.getTransformedResourceFiles().addResourceFile(resFile); - }); - } - - @Override - public void forgetResourceFile(String resourceName) { - visitFirstNonFailingPool((AsmModulePool pool) -> { - pool.getTransformedResourceFiles().forgetResourceFile(resourceName); - }); - } - - @Override - public ResourceFile getResourceFile(String name) { - return visitPools((AsmModulePool pool) -> { - return pool.getTransformedResourceFiles().getResourceFile(name); - }); - } - - @Override - public Collection getResourceFiles() { - List all = new ArrayList<>(); - visitAllPools((AsmModulePool pool) -> { - for (ModuleEntry rf : pool.getTransformedResourceFiles().getResourceFiles()) { - all.add(rf); - } - }); - return all; - } - - @Override - public ResourceFile getResourceFile(ModuleEntry res) { - return visitPools((AsmModulePool pool) -> { - return pool.getTransformedResourceFiles().getResourceFile(res); - }); - } - - } - - @Override - public AsmPool.WritableClassPool getTransformedClasses() { - return new GlobalWritableClassPool(); - } - - @Override - public AsmPool.WritableResourcePool getTransformedResourceFiles() { - return new GlobalWritableResourcePool(); - } - - @Override - public void setSorter(AsmPool.Sorter sorter) { - this.sorter = sorter; - } - - @Override - public Collection getClasses() { - List all = new ArrayList<>(); - visitAllPools((AsmModulePool pool) -> { - for (ModuleEntry rf : pool.getClasses()) { - all.add(rf); - } - }); - return all; - } - - @Override - public Collection getResourceFiles() { - List all = new ArrayList<>(); - visitAllPools((AsmModulePool pool) -> { - for (ModuleEntry rf : pool.getResourceFiles()) { - all.add(rf); - } - }); - return all; - } - - @Override - public AsmPool.ResourceFile getResourceFile(String binaryName) { - return visitPools((AsmModulePool pool) -> { - return pool.getResourceFile(binaryName); - }); - } - - @Override - public ClassReader getClassReader(String binaryName) { - return visitPoolsEx((AsmModulePool pool) -> { - return pool.getClassReader(binaryName); - }); - } - - @Override - public ResourceFile getResourceFile(ModuleEntry res) { - return visitPools((AsmModulePool pool) -> { - return pool.getResourceFile(res); - }); - } - - @Override - public ClassReader getClassReader(ModuleEntry res) { - return visitPoolsEx((AsmModulePool pool) -> { - return pool.getClassReader(res); - }); - } - - @Override - public void visitClassReaders(AsmPool.ClassReaderVisitor visitor) { - visitAllPoolsEx((AsmModulePool pool) -> { - pool.visitClassReaders(visitor); - }); - } - - @Override - public void visitResourceFiles(AsmPool.ResourceFileVisitor visitor) { - visitAllPoolsEx((AsmModulePool pool) -> { - pool.visitResourceFiles(visitor); - }); - } - - @Override - public void fillOutputResources(ModulePool outputResources) { - AsmPools.this.fillOutputResources(outputResources); - } - - @Override - public void addPackageModuleMapping(String pkg, String module) { - AsmModulePool p = pools.get(module); - if (p == null) { - throw new PluginException("Unknown module " + module); - } - p.addPackage(pkg); - } - - @Override - public Set getAccessiblePackages(String module) { - AsmModulePool p = pools.get(module); - if (p == null) { - return null; - } - ModuleDescriptor desc = p.getDescriptor(); - Set packages = new HashSet<>(); - packages.addAll(p.getAllPackages()); - - // Retrieve direct dependencies and indirect ones (public) - Set modules = new HashSet<>(); - for (Requires req : desc.requires()) { - modules.add(req.name()); - addAllRequirePublicModules(req.name(), modules); - } - // Add exported packages of readable modules - for (String readable : modules) { - AsmModulePool mp = pools.get(readable); - if (mp != null) { - for (Exports e : mp.getDescriptor().exports()) { - // exported to all or to the targeted module - if (e.targets().isEmpty() || e.targets().contains(module)) { - packages.add(e.source().replaceAll("\\.", "/")); - } - } - - } - } - return packages; - } - - private void addAllRequirePublicModules(String module, Set modules) { - AsmModulePool p = pools.get(module); - if (p != null) { - for (Requires req : p.getDescriptor().requires()) { - if (req.modifiers().contains(PUBLIC)) { - modules.add(req.name()); - addAllRequirePublicModules(req.name(), modules); - } - } - } - } - - } - - private interface VoidPoolVisitor { - - void visit(AsmModulePool pool); - } - - private interface VoidPoolVisitorEx { - - void visit(AsmModulePool pool); - } - - private interface RetPoolVisitor

{ - - P visit(AsmModulePool pool); - } - - private final Map pools = new LinkedHashMap<>(); - private final AsmModulePool[] poolsArray; - private final AsmGlobalPoolImpl global; - - private ModuleSorter moduleSorter; - - /** - * A new Asm pools. - * - * @param inputResources The raw resources to build the pool from. - */ - public AsmPools(ModulePool inputResources) { - Objects.requireNonNull(inputResources); - Map resPools = new LinkedHashMap<>(); - Map descriptors = new HashMap<>(); - inputResources.entries().forEach(res -> { - ModulePool p = resPools.get(res.getModule()); - if (p == null) { - p = new ModulePoolImpl(inputResources.getByteOrder(), - ((ModulePoolImpl)inputResources).getStringTable()); - resPools.put(res.getModule(), p); - } - if (res.getPath().endsWith("module-info.class")) { - ByteBuffer bb = ByteBuffer.wrap(res.getBytes()); - ModuleDescriptor descriptor = ModuleDescriptor.read(bb); - descriptors.put(res.getModule(), descriptor); - } - p.add(res); - }); - poolsArray = new AsmModulePool[resPools.size()]; - int i = 0; - - for (Entry entry : resPools.entrySet()) { - ModuleDescriptor descriptor = descriptors.get(entry.getKey()); - if (descriptor == null) { - throw new PluginException("module-info.class not found for " + entry.getKey() + " module"); - } - AsmModulePool p = new AsmPoolImpl(entry.getValue(), - entry.getKey(), this, descriptor); - pools.put(entry.getKey(), p); - poolsArray[i] = p; - i += 1; - } - global = new AsmGlobalPoolImpl(); - } - - /** - * The pool containing all classes and other resources. - * - * @return The global pool - */ - public AsmGlobalPool getGlobalPool() { - return global; - } - - /** - * A pool for a given module - * - * @param name The module name - * @return The pool that contains content of the passed module or null if - * the module doesn't exist. - */ - public AsmModulePool getModulePool(String name) { - Objects.requireNonNull(name); - return pools.get(name); - } - - /** - * The array of module pools. - * @return The module pool array. - */ - public AsmModulePool[] getModulePools() { - return poolsArray.clone(); - } - - /** - * Set a module sorter. Sorter is used when computing the output resources. - * - * @param moduleSorter The module sorter - */ - public void setModuleSorter(ModuleSorter moduleSorter) { - Objects.requireNonNull(moduleSorter); - this.moduleSorter = moduleSorter; - } - - /** - * Returns the pool of all the resources (transformed and unmodified). The - * input resources are replaced by the transformed ones. If a sorter has - * been set, it is used to sort in modules. - * - * @param outputResources The pool used to fill the jimage. - */ - public void fillOutputResources(ModulePool outputResources) { - // First sort modules - List modules = new ArrayList<>(); - for (String k : pools.keySet()) { - modules.add(k); - } - if (moduleSorter != null) { - modules = moduleSorter.sort(modules); - } - ModulePool output = new ModulePoolImpl(outputResources.getByteOrder(), - ((ModulePoolImpl)outputResources).getStringTable()); - for (String mn : modules) { - AsmPool pool = pools.get(mn); - pool.fillOutputResources(output); - } - sort(outputResources, output, global.sorter); - } - - static void sort(ModulePool outputResources, - ModulePool transientOutput, Sorter sorter) { - if (sorter != null) { - List order = sorter.sort(transientOutput); - for (String s : order) { - outputResources.add(transientOutput.findEntry(s).get()); - } - } else { - transientOutput.entries().forEach(res-> { - outputResources.add(res); - }); - } - } - - private void visitFirstNonFailingPool(VoidPoolVisitorEx pv) { - boolean found = false; - for (Entry entry : pools.entrySet()) { - try { - pv.visit(entry.getValue()); - found = true; - break; - } catch (Exception ex) { - // XXX OK, try another one. - } - } - if (!found) { - throw new PluginException("No module found"); - } - } - - private void visitAllPools(VoidPoolVisitor pv) { - for (Entry entry : pools.entrySet()) { - pv.visit(entry.getValue()); - } - } - - private void visitAllPoolsEx(VoidPoolVisitorEx pv) { - for (Entry entry : pools.entrySet()) { - pv.visit(entry.getValue()); - } - } - - private

P visitPoolsEx(RetPoolVisitor

pv) { - P p = null; - for (Entry entry : pools.entrySet()) { - try { - p = pv.visit(entry.getValue()); - if (p != null) { - break; - } - } catch (Exception ex) { - // XXX OK, try another one. - } - } - return p; - } - - private

P visitPools(RetPoolVisitor

pv) { - P p = null; - for (Entry entry : pools.entrySet()) { - try { - p = pv.visit(entry.getValue()); - if (p != null) { - break; - } - } catch (Exception ex) { - // XXX OK, try another one. - } - } - return p; - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ControlFlow.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ControlFlow.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ControlFlow.java +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.optim; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.Stack; -import java.util.TreeSet; -import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; -import jdk.internal.org.objectweb.asm.tree.MethodNode; -import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer; -import jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException; -import jdk.internal.org.objectweb.asm.tree.analysis.BasicInterpreter; -import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue; - -/** - * Split Java method onto a control flow. - * - */ -public final class ControlFlow { - - /** - * A block of control - */ - public static final class Block implements Comparable { - - private final InstructionNode firstInstruction; - private final List instr = new ArrayList<>(); - private final List reachable = new ArrayList<>(); - private final List exceptionHandlers = new ArrayList<>(); - private boolean isExceptionHandler; - - private Block(InstructionNode firstInstruction) { - this.firstInstruction = firstInstruction; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Block)) { - return false; - } - Block b = (Block) other; - return firstInstruction.equals(b.firstInstruction); - } - - @Override - public int hashCode() { - return Objects.hashCode(this.firstInstruction); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - for (InstructionNode in : instr) { - builder.append(in).append(" "); - } - builder.append(" reachables: "); - for (Block r : reachable) { - builder.append(r.getFirstInstruction()).append(" "); - } - builder.append(" exception handlers: "); - for (Block r : exceptionHandlers) { - builder.append(r.getFirstInstruction()).append(" "); - } - - return "block[" + getFirstInstruction() + "],ex:" - + isExceptionHandler + ", " + builder.toString(); - } - - /** - * @return the firstInstruction - */ - public InstructionNode getFirstInstruction() { - return firstInstruction; - } - - /** - * @return the instr - */ - public List getInstructions() { - return Collections.unmodifiableList(instr); - } - - /** - * @return the reachable - */ - public List getReachableBlocks() { - return Collections.unmodifiableList(reachable); - } - - /** - * @return the exceptionHandlers - */ - public List getExceptionHandlerBlocks() { - return Collections.unmodifiableList(exceptionHandlers); - } - - @Override - public int compareTo(Block t) { - return this.firstInstruction.index - t.firstInstruction.index; - } - - public boolean isExceptionHandler() { - return isExceptionHandler; - } - - } - - private class ClosureBuilder { - - private final Block root; - - private ClosureBuilder(Block root) { - Objects.requireNonNull(root); - this.root = root; - } - - private Set build() { - Set allReachable = new TreeSet<>(); - addAll(root, allReachable); - // filter out the reachable from outside this graph - Iterator it = allReachable.iterator(); - Set toExclude = new HashSet<>(); - while (it.hasNext()) { - Block b = it.next(); - for (Block ref : blocks) { - if (!allReachable.contains(ref) && ref.reachable.contains(b)) { - addAll(b, toExclude); - break; - } - } - } - //System.err.println("TO EXCLUDE:\n " + toExclude); - allReachable.removeAll(toExclude); - //System.err.println("CLOSURE:\n " + allReachable); - return Collections.unmodifiableSet(allReachable); - } - - // Compute the set of blocks reachable from the current block - private void addAll(Block current, Set closure) { - Objects.requireNonNull(current); - closure.add(current); - for (Block ex : current.exceptionHandlers) { - Objects.requireNonNull(ex); - if (!closure.contains(ex)) { - addAll(ex, closure); - } - } - for (Block r : current.reachable) { - Objects.requireNonNull(r); - if (!closure.contains(r)) { - addAll(r, closure); - } - } - - } - } - - /** - * An instruction - */ - public static final class InstructionNode { - - private final int index; - private final List next = new ArrayList<>(); - private final AbstractInsnNode instr; - - private InstructionNode(int index, AbstractInsnNode instr) { - this.index = index; - this.instr = instr; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof InstructionNode)) { - return false; - } - final InstructionNode other = (InstructionNode) obj; - return this.getIndex() == other.getIndex(); - } - - @Override - public int hashCode() { - return this.getIndex(); - } - - @Override - public String toString() { - return getIndex() + "(" + (getInstr().getOpcode() == - 1 ? -1 - : Integer.toHexString(getInstr().getOpcode())) + ")"; - } - - /** - * @return the index - */ - public int getIndex() { - return index; - } - - /** - * @return the instr - */ - public AbstractInsnNode getInstr() { - return instr; - } - - } - - private final Map allBlocks; - private final List blocks = new ArrayList<>(); - - private ControlFlow(Map allBlocks) { - this.allBlocks = allBlocks; - for (Block b : allBlocks.values()) { - blocks.add(b); - } - Collections.sort(blocks); - } - - public List getBlocks() { - - return Collections.unmodifiableList(blocks); - } - - public Block getBlock(int firstInstr) { - return allBlocks.get(firstInstr); - } - - public static ControlFlow createControlFlow(String owner, - MethodNode method) throws Exception { - - BlockBuilder bb = new BlockBuilder(owner, method); - return bb.build(); - } - - /** - * Return the set of blocks that are only reachable from this block For - * example, if b is an Exception handler, returns all the blocks reachable - * only from this handler - * - * @param b - * @return - */ - public Set getClosure(Block b) { - return new ClosureBuilder(b).build(); - } - - private static final class BlockBuilder { - - private InstructionNode root; - private final Map instructions = new HashMap<>(); - private final Map> handlers = new HashMap<>(); - private final Map allBlocks = new HashMap<>(); - - private final String owner; - private final MethodNode method; - - private BlockBuilder(String owner, MethodNode method) { - this.owner = owner; - this.method = method; - } - - private void analyze() throws AnalyzerException { - Analyzer analyzer = new Analyzer(new BasicInterpreter()) { - - @Override - protected boolean newControlFlowExceptionEdge(int insn, - int successor) { - List lst = handlers.get(successor); - if (lst == null) { - lst = new ArrayList<>(); - handlers.put(successor, lst); - } - lst.add(insn); - return true; - } - - @Override - protected void newControlFlowEdge(int from, - int to) { - if (root == null) { - root = new InstructionNode(from, method.instructions.get(from)); - instructions.put(from, root); - } - InstructionNode fromNode = instructions.get(from); - if (fromNode == null) { - fromNode = new InstructionNode(from, method.instructions.get(from)); - instructions.put(from, fromNode); - } - InstructionNode toNode = instructions.get(to); - if (toNode == null) { - toNode = new InstructionNode(to, method.instructions.get(to)); - instructions.put(to, toNode); - } - if (!fromNode.next.contains(toNode)) { - fromNode.next.add(toNode); - } - - } - }; - analyzer.analyze(owner, method); - } - - private Block newBlock(InstructionNode firstInstruction) { - Objects.requireNonNull(firstInstruction); - Block b = new Block(firstInstruction); - allBlocks.put(firstInstruction.getIndex(), b); - return b; - } - - private ControlFlow build() throws AnalyzerException { - analyze(); - buildBlocks(); - return new ControlFlow(allBlocks); - } - - private void buildBlocks() { - List reachableBlocks = new ArrayList<>(); - createBlocks(root, reachableBlocks); - List handlersBlocks = new ArrayList<>(); - for (Entry> entry : handlers.entrySet()) { - InstructionNode node = instructions.get(entry.getKey()); - createBlocks(node, handlersBlocks); - } - - // attach handler to try blocks - for (Entry> entry : handlers.entrySet()) { - Block handlerBlock = allBlocks.get(entry.getKey()); - handlerBlock.isExceptionHandler = true; - int startTry = entry.getValue().get(0); - Block tryBlock = allBlocks.get(startTry); - if (tryBlock == null) { - // Need to find the block that contains the instruction and - // make a new block - Block split = null; - for (Block b : allBlocks.values()) { - Iterator it = b.instr.iterator(); - while (it.hasNext()) { - InstructionNode in = it.next(); - if (split == null) { - if (in.index == startTry) { - split = newBlock(in); - split.instr.add(in); - it.remove(); - } - } else { - split.instr.add(in); - it.remove(); - } - } - if (split != null) { - Iterator reachables = b.reachable.iterator(); - while (reachables.hasNext()) { - Block r = reachables.next(); - split.reachable.add(r); - reachables.remove(); - } - b.reachable.add(split); - break; - } - } - if (split == null) { - throw new RuntimeException("No try block for handler " + handlerBlock); - } - split.exceptionHandlers.add(handlerBlock); - } else { - tryBlock.exceptionHandlers.add(handlerBlock); - } - } - -// System.err.println("ALL BLOCKS FOUND"); -// Iterator> blockIt0 = allBlocks.entrySet().iterator(); -// while (blockIt0.hasNext()) { -// Block b = blockIt0.next().getValue(); -// System.err.println(b); -// } - //compute real exception blocks, if an instruction is in another block, stop. - Iterator> blockIt = allBlocks.entrySet().iterator(); - while (blockIt.hasNext()) { - Block b = blockIt.next().getValue(); - Iterator in = b.instr.iterator(); - boolean found = false; - while (in.hasNext()) { - int i = in.next().getIndex(); - if (found) { - in.remove(); - } else { - if (startsWith(b, i, allBlocks.values())) { - // Move it to reachable - Block r = allBlocks.get(i); - b.reachable.add(r); - found = true; - in.remove(); - } else { - } - } - } - } - -// System.err.println("Reduced blocks"); -// Iterator> blockIt1 = allBlocks.entrySet().iterator(); -// while (blockIt1.hasNext()) { -// Block b = blockIt1.next().getValue(); -// System.err.println(b); -// } - } - - private boolean startsWith(Block block, int index, Collection reachableBlocks) { - for (Block b : reachableBlocks) { - if (b != block && !b.instr.isEmpty() && b.instr.get(0).getIndex() == index) { - return true; - } - } - return false; - } - - private static final class StackItem { - - private final InstructionNode instr; - private final Block currentBlock; - - private StackItem(InstructionNode instr, Block currentBlock) { - Objects.requireNonNull(instr); - Objects.requireNonNull(currentBlock); - this.instr = instr; - this.currentBlock = currentBlock; - } - } - - /** - * This algorithm can't be recursive, possibly too much instructions in - * methods. - */ - private void createBlocks(InstructionNode root, List blocks) { - final Stack stack = new Stack<>(); - stack.push(new StackItem(root, newBlock(root))); - while (!stack.isEmpty()) { - final StackItem item = stack.pop(); - final Block currentBlock = item.currentBlock; - final InstructionNode current = item.instr; - // loop - if (currentBlock.instr.contains(current)) { - currentBlock.reachable.add(currentBlock); - continue; - } - Block existing = allBlocks.get(current.index); - if (existing != null && existing != currentBlock) { - currentBlock.reachable.add(existing); - continue; - } - int previous = currentBlock.instr.size() > 0 - ? currentBlock.instr.get(currentBlock.instr.size() - 1).getIndex() : -1; - if (previous == -1 || current.getIndex() == previous + 1) { - currentBlock.instr.add(current); - if (current.next.isEmpty()) { - blocks.add(currentBlock); - } else { - if (current.next.size() > 1) { - blocks.add(currentBlock); - for (InstructionNode n : current.next) { - Block loop = allBlocks.get(n.index); - if (loop == null) { - Block newBlock = newBlock(n); - currentBlock.reachable.add(newBlock); - stack.push(new StackItem(n, newBlock)); - } else { // loop - currentBlock.reachable.add(loop); - } - } - } else { - stack.push(new StackItem(current.next.get(0), - currentBlock)); - } - } - } else { // to a new block... - // Do nothing... - blocks.add(currentBlock); - Block newBlock = newBlock(current); - currentBlock.reachable.add(newBlock); - stack.push(new StackItem(current, newBlock)); - } - } - } - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ForNameFolding.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ForNameFolding.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ForNameFolding.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.optim; - -import java.io.IOException; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Consumer; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.tools.jlink.internal.plugins.asm.AsmPools; -import jdk.internal.org.objectweb.asm.tree.ClassNode; -import jdk.internal.org.objectweb.asm.tree.MethodNode; -import jdk.tools.jlink.internal.plugins.OptimizationPlugin.MethodOptimizer; -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.internal.plugins.optim.ControlFlow.Block; -import jdk.tools.jlink.internal.plugins.optim.ReflectionOptimizer.Data; -import jdk.tools.jlink.internal.plugins.optim.ReflectionOptimizer.TypeResolver; - - -/** - * MethodOptimizer that removes Class.forName when possible. - * WARNING: This code is experimental. - * TODO: Need to check that the type is accessible prior to replace with a constant. - */ -public class ForNameFolding implements MethodOptimizer { - - private int numNotReplaced; - private int numReplacement; - private int numRemovedHandlers; - private int instructionsRemoved; - - private Consumer logger; - - @Override - public boolean optimize(Consumer logger, AsmPools pools, - AsmModulePool modulePool, - ClassNode cn, MethodNode m, TypeResolver resolver) throws Exception { - this.logger = logger; - Data data = ReflectionOptimizer.replaceWithClassConstant(cn, m, createResolver(resolver)); - instructionsRemoved += data.removedInstructions(); - numRemovedHandlers += data.removedHandlers().size(); - for (Entry> entry : data.removedHandlers().entrySet()) { - logRemoval(cn.name + "." + m.name + "removed block for " + entry.getKey() - + " : " + entry.getValue()); - } - return data.removedInstructions() > 0; - } - - public TypeResolver createResolver(TypeResolver resolver) { - return (ClassNode cn, MethodNode mn, String type) -> { - ClassReader reader = resolver.resolve(cn, mn, type); - if (reader == null) { - logNotReplaced(type); - } else { - logReplaced(type); - } - return reader; - }; - } - - private void logReplaced(String type) { - numReplacement += 1; - } - - private void logNotReplaced(String type) { - numNotReplaced += 1; - if (logger != null) { - logger.accept(type + " not resolved"); - } - } - - private void logRemoval(String content) { - numRemovedHandlers += 1; - if (logger != null) { - logger.accept(content); - } - } - - @Override - public void close() throws IOException { - if (logger != null) { - logger.accept("Class.forName Folding results:\n " + numReplacement - + " removed reflection. " + numRemovedHandlers - + " removed exception handlers." - + numNotReplaced + " types unknown. " - + instructionsRemoved + " instructions removed\n"); - } - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ReflectionOptimizer.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ReflectionOptimizer.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/ReflectionOptimizer.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.optim; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.Type; -import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; -import jdk.internal.org.objectweb.asm.tree.ClassNode; -import jdk.internal.org.objectweb.asm.tree.LabelNode; -import jdk.internal.org.objectweb.asm.tree.LdcInsnNode; -import jdk.internal.org.objectweb.asm.tree.LineNumberNode; -import jdk.internal.org.objectweb.asm.tree.MethodInsnNode; -import jdk.internal.org.objectweb.asm.tree.MethodNode; -import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; -import jdk.tools.jlink.internal.plugins.optim.ControlFlow.Block; - -/** - * Implement the reflection optimization. - */ -public class ReflectionOptimizer { - - public static class Data { - - private int removedInstructions; - private final Map> removedHandlers = new HashMap<>(); - - private Data() { - } - - public int removedInstructions() { - return removedInstructions; - } - - public Map> removedHandlers() { - return Collections.unmodifiableMap(removedHandlers); - } - } - - public interface TypeResolver { - - public ClassReader resolve(ClassNode cn, MethodNode m, String type); - } - - public static Data replaceWithClassConstant(ClassNode cn, MethodNode m, - TypeResolver cch) - throws Exception { - Iterator it = m.instructions.iterator(); - LdcInsnNode insNode = null; - Map replacement = new IdentityHashMap<>(); - Data data = new Data(); - while (it.hasNext()) { - AbstractInsnNode n = it.next(); - if (n instanceof LdcInsnNode) { - LdcInsnNode ldc = (LdcInsnNode) n; - if (ldc.cst instanceof String) { - insNode = ldc; - } - } else { - if (n instanceof MethodInsnNode && insNode != null) { - MethodInsnNode met = (MethodInsnNode) n; - if (met.name.equals("forName") - && met.owner.equals("java/lang/Class") - && met.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { - // Can we load the type? - Type type = null; - String binaryName = insNode.cst.toString().replaceAll("\\.", "/"); - String unaryClassName = binaryName; - int arrayIndex = binaryName.lastIndexOf("["); - if (arrayIndex >= 0) { - int objIndex = unaryClassName.indexOf("L"); - if (objIndex >= 0) { - unaryClassName = unaryClassName.substring(objIndex + 1); - unaryClassName = unaryClassName.substring(0, - unaryClassName.length() - 1); - } else { - //primitive, this is just fine. - type = Type.getObjectType(binaryName); - } - } - if (type == null) { - if (cch.resolve(cn, m, unaryClassName) != null) { - type = Type.getObjectType(binaryName); - } - } - if (type != null) { - replacement.put(insNode, new LdcInsnNode(type)); - it.remove(); - data.removedInstructions += 1; - } - } else { - insNode = null; - } - // Virtual node, not taken into account - } else if (!(n instanceof LabelNode) && !(n instanceof LineNumberNode)) { - insNode = null; - } - } - } - for (Map.Entry entry : replacement.entrySet()) { - m.instructions.set(entry.getKey(), entry.getValue()); - } - if (!replacement.isEmpty()) { - String[] types = {"java/lang/ClassNotFoundException"}; - data.removedInstructions += deleteExceptionHandlers(cch, data, cn, m, types); - - } - return data; - } - - private static int deleteExceptionHandlers(TypeResolver cch, Data data, - ClassNode cn, MethodNode m, String[] exTypes) - throws Exception { - int instructionsRemoved = 0; - for (String ex : exTypes) { - ControlFlow f = ControlFlow.createControlFlow(cn.name, m); - List removed = new ArrayList<>(); - Set blocksToRemove = new TreeSet<>(); - Iterator it = m.tryCatchBlocks.iterator(); - List tcbToRemove = new ArrayList<>(); - while (it.hasNext()) { - TryCatchBlockNode bn = it.next(); - if (bn.type == null - || !bn.type.equals(ex) // An empty block - || tcbToRemove.contains(bn)) { - continue; - } - // Check that the handler is still required - if (!Utils.canThrowCheckedException(cch, cn, m, bn)) { - // try to suppress it. - int block = m.instructions.indexOf(bn.handler); - ControlFlow.Block blockHandler = f.getBlock(block); - if (blockHandler == null) { - if (removed.contains(block)) { - continue; - } else { - throw new Exception(cn.name - + ", no block for handler " + block); - } - } - tcbToRemove.add(bn); - // Don't delete block if shared (eg: ClassNotFoundException | NoSuchMethodException | - Iterator it2 = m.tryCatchBlocks.iterator(); - boolean cont = false; - while (it2.hasNext()) { - TryCatchBlockNode bn2 = it2.next(); - if (bn2 != bn) { - if (bn2.start.equals(bn.start)) { - cont = true; - } - } - } - if (cont) { - continue; - } - // An handler is a root, blocks that are only reachable by it - // can be removed. - Set blocks = f.getClosure(blockHandler); - StringBuilder sb = new StringBuilder(); - for (ControlFlow.Block b : blocks) { - sb.append(b).append("\n"); - removed.add(b.getFirstInstruction().getIndex()); - // Remove Exception handler if the associated block has been removed - for (TryCatchBlockNode tcb : m.tryCatchBlocks) { - if (tcb != bn) { - // An exception handler removed as a side effect. - if (b.isExceptionHandler() - && b.getFirstInstruction().getInstr() == tcb.handler) { - tcbToRemove.add(tcb); - } - } - } - } - blocksToRemove.addAll(blocks); - - data.removedHandlers.put(ex, blocks); - - } - } - - m.tryCatchBlocks.removeAll(tcbToRemove); - - if (!blocksToRemove.isEmpty()) { - for (ControlFlow.Block b : blocksToRemove) { - for (ControlFlow.InstructionNode ins : b.getInstructions()) { - if (ins.getInstr().getOpcode() > 0) { - instructionsRemoved += 1; - } - } - } - Utils.suppressBlocks(m, blocksToRemove); - } - } - return instructionsRemoved; - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/Utils.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/Utils.java deleted file mode 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/optim/Utils.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.tools.jlink.internal.plugins.optim; - -import java.util.Iterator; -import java.util.Set; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; -import jdk.internal.org.objectweb.asm.tree.ClassNode; -import jdk.internal.org.objectweb.asm.tree.MethodInsnNode; -import jdk.internal.org.objectweb.asm.tree.MethodNode; -import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; - -/** - * Optimization utility methods - */ -public class Utils { - - public static boolean canThrowCheckedException(ReflectionOptimizer.TypeResolver cch, - ClassNode classNode, MethodNode m, TryCatchBlockNode bn) throws Exception { - int istart = m.instructions.indexOf(bn.start); - int iend = m.instructions.indexOf(bn.end); - for (int i = istart; i < iend - 1; i++) { - AbstractInsnNode instr = m.instructions.get(i); - if (instr instanceof MethodInsnNode) { - MethodInsnNode meth = (MethodInsnNode) instr; - ClassReader reader = cch.resolve(classNode, m, meth.owner); - if (reader != null) { - ClassNode cn = new ClassNode(); - reader.accept(cn, ClassReader.EXPAND_FRAMES); - for (MethodNode method : cn.methods) { - if (method.name.equals(meth.name)) { - for (String e : method.exceptions) { - if (e.equals(bn.type)) { - return true; - } - } - } - } - } else { - return true; - } - } - } - return false; - } - - public static void suppressBlocks(MethodNode m, Set toRemove) throws Exception { - m.instructions.resetLabels(); - Iterator it = m.instructions.iterator(); - while (it.hasNext()) { - AbstractInsnNode n = it.next(); - Iterator handlers = m.tryCatchBlocks.iterator(); - boolean cont = false; - // Do not delete instructions that are end of other try block. - while (handlers.hasNext()) { - TryCatchBlockNode handler = handlers.next(); - if (handler.end == n) { - cont = true; - } - } - if (cont) { - continue; - } - - for (ControlFlow.Block b : toRemove) { - for (ControlFlow.InstructionNode ins : b.getInstructions()) { - if (ins.getInstr() == n) { - it.remove(); - } - } - } - } - } -} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -31,11 +31,10 @@ Any number of = pairs can be passed.\n\ del: is to delete the list of keys in release file. -class-optim.argument=[:log=] +class-for-name.argument= -class-optim.description=\ -Class optimization. Warning: This plugin is experimental.\n\ -An optional can be specified to log applied optimizations. +class-for-name.description=\ +Class optimization: convert Class.forName calls to constant loads. compress.argument=<0|1|2>[:filter=] @@ -47,7 +46,6 @@ An optional filter can be specified to list the pattern of\n\ files to be included. - compact-cp.argument= compact-cp.description=Constant Pool strings sharing.\n\ diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java --- a/src/jdk.jlink/share/classes/module-info.java +++ b/src/jdk.jlink/share/classes/module-info.java @@ -39,9 +39,9 @@ provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.StripNativeCommandsPlugin; provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.OrderResourcesPlugin; provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.DefaultCompressPlugin; - provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.OptimizationPlugin; provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.ExcludeVMPlugin; provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.IncludeLocalesPlugin; provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.GenerateJLIClassesPlugin; provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.ReleaseInfoPlugin; -} + provides jdk.tools.jlink.plugin.Plugin with jdk.tools.jlink.internal.plugins.ClassForNamePlugin; + } diff --git a/test/tools/jlink/JLinkOptimTest.java b/test/tools/jlink/JLinkOptimTest.java deleted file mode 100644 --- a/test/tools/jlink/JLinkOptimTest.java +++ /dev/null @@ -1,354 +0,0 @@ - -import java.lang.reflect.Method; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; -import jdk.internal.org.objectweb.asm.tree.ClassNode; -import jdk.internal.org.objectweb.asm.tree.MethodInsnNode; -import jdk.internal.org.objectweb.asm.tree.MethodNode; -import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; -import jdk.tools.jlink.internal.PluginRepository; -import jdk.tools.jlink.internal.ModulePoolImpl; -import jdk.tools.jlink.internal.plugins.OptimizationPlugin; -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.internal.plugins.asm.AsmPlugin; -import jdk.tools.jlink.internal.plugins.asm.AsmPools; -import jdk.tools.jlink.internal.plugins.optim.ControlFlow; -import jdk.tools.jlink.internal.plugins.optim.ControlFlow.Block; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -import tests.Helper; -import tests.JImageGenerator; - -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - /* - * @test - * @summary Test image creation with class optimization - * @author Jean-Francois Denise - * @library ../lib - * @modules java.base/jdk.internal.jimage - * jdk.jdeps/com.sun.tools.classfile - * jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jmod - * jdk.jlink/jdk.tools.jimage - * jdk.jlink/jdk.tools.jlink.internal.plugins - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * jdk.jlink/jdk.tools.jlink.internal.plugins.optim - * java.base/jdk.internal.org.objectweb.asm - * java.base/jdk.internal.org.objectweb.asm.tree - * java.base/jdk.internal.org.objectweb.asm.util - * jdk.compiler - * @build tests.* - * @run main JLinkOptimTest - */ -public class JLinkOptimTest { - - private static final String EXPECTED = "expected"; - private static Helper helper; - - public static class ControlFlowPlugin extends AsmPlugin { - - private boolean called; - private int numMethods; - private int numBlocks; - - private static final String NAME = "test-optim"; - - private ControlFlowPlugin() { - } - - @Override - public void visit(AsmPools pools) { - called = true; - for (AsmModulePool p : pools.getModulePools()) { - - p.visitClassReaders((reader) -> { - ClassNode cn = new ClassNode(); - if ((reader.getAccess() & Opcodes.ACC_INTERFACE) == 0) { - reader.accept(cn, ClassReader.EXPAND_FRAMES); - for (MethodNode m : cn.methods) { - if ((m.access & Opcodes.ACC_ABSTRACT) == 0 - && (m.access & Opcodes.ACC_NATIVE) == 0) { - numMethods += 1; - try { - ControlFlow f - = ControlFlow.createControlFlow(cn.name, m); - for (Block b : f.getBlocks()) { - numBlocks += 1; - f.getClosure(b); - } - } catch (Throwable ex) { - //ex.printStackTrace(); - throw new RuntimeException("Exception in " - + cn.name + "." + m.name, ex); - } - } - } - } - return null; - }); - } - } - - @Override - public String getName() { - return NAME; - } - } - - private static void testForName() throws Exception { - String moduleName = "optimplugin"; - Path src = Paths.get(System.getProperty("test.src")).resolve(moduleName); - Path classes = helper.getJmodClassesDir().resolve(moduleName); - JImageGenerator.compile(src, classes); - - FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); - Path root = fs.getPath("/modules/java.base"); - // Access module-info.class to be reused as fake module-info.class - List javabaseResources = new ArrayList<>(); - try (Stream stream = Files.walk(root)) { - for (Iterator iterator = stream.iterator(); iterator.hasNext();) { - Path p = iterator.next(); - if (Files.isRegularFile(p)) { - try { - javabaseResources.add(ModuleEntry.create(p.toString(). - substring("/modules".length()), Files.readAllBytes(p))); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - } - } - - //forName folding - ModulePoolImpl pool = new ModulePoolImpl(); - byte[] content = Files.readAllBytes(classes. - resolve("optim").resolve("ForNameTestCase.class")); - byte[] content2 = Files.readAllBytes(classes. - resolve("optim").resolve("AType.class")); - byte[] mcontent = Files.readAllBytes(classes.resolve("module-info.class")); - - pool.add(ModuleEntry.create("/optimplugin/optim/ForNameTestCase.class", content)); - pool.add(ModuleEntry.create("/optimplugin/optim/AType.class", content2)); - pool.add(ModuleEntry.create("/optimplugin/module-info.class", mcontent)); - - for (ModuleEntry r : javabaseResources) { - pool.add(r); - } - - OptimizationPlugin plugin = new OptimizationPlugin(); - Map optional = new HashMap<>(); - optional.put(OptimizationPlugin.NAME, OptimizationPlugin.FORNAME_REMOVAL); - optional.put(OptimizationPlugin.LOG, "forName.log"); - plugin.configure(optional); - ModulePool out = new ModulePoolImpl(); - plugin.visit(pool, out); - - ModuleEntry result = out.entries().iterator().next(); - - ClassReader optimReader = new ClassReader(result.getBytes()); - ClassNode optimClass = new ClassNode(); - optimReader.accept(optimClass, ClassReader.EXPAND_FRAMES); - - if (!optimClass.name.equals("optim/ForNameTestCase")) { - throw new Exception("Invalid class " + optimClass.name); - } - if (optimClass.methods.size() < 2) { - throw new Exception("Not enough methods in new class"); - } - for (MethodNode mn : optimClass.methods) { - if (!mn.name.contains("forName") && !mn.name.contains("")) { - continue; - } - if (mn.name.startsWith("negative")) { - checkForName(mn); - } else { - checkNoForName(mn); - } - } - Map newClasses = new HashMap<>(); - newClasses.put("optim.ForNameTestCase", result.getBytes()); - newClasses.put("optim.AType", content2); - MemClassLoader loader = new MemClassLoader(newClasses); - Class loaded = loader.loadClass("optim.ForNameTestCase"); - if (loaded.getDeclaredMethods().length < 2) { - throw new Exception("Not enough methods in new class"); - } - for (Method m : loaded.getDeclaredMethods()) { - if (m.getName().contains("Exception")) { - try { - m.invoke(null); - } catch (Exception ex) { - //ex.getCause().printStackTrace(); - if (!ex.getCause().getMessage().equals(EXPECTED)) { - throw new Exception("Unexpected exception " + ex); - } - } - } else if (!m.getName().startsWith("negative")) { - Class clazz = (Class) m.invoke(null); - if (clazz != String.class && clazz != loader.findClass("optim.AType")) { - throw new Exception("Invalid class " + clazz); - } - } - } - } - - private static void checkNoForName(MethodNode m) throws Exception { - Iterator it = m.instructions.iterator(); - while (it.hasNext()) { - AbstractInsnNode n = it.next(); - if (n instanceof MethodInsnNode) { - MethodInsnNode met = (MethodInsnNode) n; - if (met.name.equals("forName") - && met.owner.equals("java/lang/Class") - && met.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { - throw new Exception("forName not removed in " + m.name); - } - } - } - for (TryCatchBlockNode tcb : m.tryCatchBlocks) { - if (tcb.type.equals(ClassNotFoundException.class.getName().replaceAll("\\.", "/"))) { - throw new Exception("ClassNotFoundException Block not removed for " + m.name); - } - } - } - - private static void checkForName(MethodNode m) throws Exception { - Iterator it = m.instructions.iterator(); - boolean found = false; - while (it.hasNext()) { - AbstractInsnNode n = it.next(); - if (n instanceof MethodInsnNode) { - MethodInsnNode met = (MethodInsnNode) n; - if (met.name.equals("forName") - && met.owner.equals("java/lang/Class") - && met.desc.equals("(Ljava/lang/String;)Ljava/lang/Class;")) { - found = true; - break; - } - } - } - if (!found) { - throw new Exception("forName removed but shouldn't have"); - } - found = false; - for (TryCatchBlockNode tcb : m.tryCatchBlocks) { - if (tcb.type.equals(ClassNotFoundException.class.getName().replaceAll("\\.", "/"))) { - found = true; - break; - } - } - if (!found) { - throw new Exception("tryCatchBlocks removed but shouldn't have"); - } - } - - static class MemClassLoader extends ClassLoader { - - private final Map classes; - private final Map> cache = new HashMap<>(); - - MemClassLoader(Map classes) { - super(null); - this.classes = classes; - } - - @Override - public Class findClass(String name) throws ClassNotFoundException { - Class clazz = cache.get(name); - if (clazz == null) { - byte[] b = classes.get(name); - if (b == null) { - return super.findClass(name); - } else { - clazz = defineClass(name, b, 0, b.length); - cache.put(name, clazz); - } - } - return clazz; - } - } - - public static void main(String[] args) throws Exception { - helper = Helper.newHelper(); - if (helper == null) { - System.err.println("Test not run"); - return; - } - - testForName(); - - helper.generateDefaultModules(); - helper.generateDefaultJModule("optim1", "java.se"); - { - String[] userOptions = {"--class-optim=all:log=./class-optim-log.txt"}; - - Path imageDir = helper.generateDefaultImage(userOptions, "optim1").assertSuccess(); - helper.checkImage(imageDir, "optim1", null, null); - } - - { - String[] userOptions = {"--class-optim=forName-folding:log=./class-optim-log.txt"}; - Path imageDir = helper.generateDefaultImage(userOptions, "optim1").assertSuccess(); - helper.checkImage(imageDir, "optim1", null, null); - } - - { - ControlFlowPlugin plugin = new ControlFlowPlugin(); - PluginRepository.registerPlugin(plugin); - String[] userOptions = {"--test-optim"}; - Path imageDir = helper.generateDefaultImage(userOptions, "optim1").assertSuccess(); - helper.checkImage(imageDir, "optim1", null, null); - //System.out.println("Num methods analyzed " + provider.numMethods - // + "num blocks " + provider.numBlocks); - if (!plugin.called) { - throw new Exception("Plugin not called"); - } - if (plugin.numMethods < 1000) { - throw new Exception("Not enough method called, should be " - + "around 10000 but is " + plugin.numMethods); - } - if (plugin.numBlocks < 100000) { - throw new Exception("Not enough blocks, should be " - + "around 640000 but is " + plugin.numMethods); - } - } - } - -} diff --git a/test/tools/jlink/asmplugin/AddForgetResourcesTest.java b/test/tools/jlink/asmplugin/AddForgetResourcesTest.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/AddForgetResourcesTest.java +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Asm plugin testing. - * @test - * @summary Test resource transformation. - * @author Andrei Eremeev - * @modules java.base/jdk.internal.org.objectweb.asm - * jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * jdk.jdeps/com.sun.tools.classfile - * @build AsmPluginTestBase - * @run main AddForgetResourcesTest -*/ - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import com.sun.tools.classfile.ClassFile; -import com.sun.tools.classfile.Method; -import java.io.UncheckedIOException; -import java.util.Set; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.tools.jlink.internal.plugins.asm.AsmGlobalPool; -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.ResourceFile; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.WritableClassPool; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.WritableResourcePool; -import jdk.tools.jlink.internal.plugins.asm.AsmPools; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -public class AddForgetResourcesTest extends AsmPluginTestBase { - - public static void main(String[] args) throws Exception { - if (!isImageBuild()) { - System.err.println("Test not run. Not image build."); - return; - } - new AddForgetResourcesTest().test(); - } - - @Override - public void test() throws Exception { - TestPlugin[] plugins = new TestPlugin[] { - new AddClassesPlugin(), - new AddResourcesPlugin(), - new ReplaceClassesPlugin(), - new ReplaceResourcesPlugin(), - new ForgetClassesPlugin(), - new ForgetResourcesPlugin(), - new AddForgetClassesPlugin(), - new AddForgetResourcesPlugin(), - new ComboPlugin() - }; - for (TestPlugin p : plugins) { - ModulePool out = p.visit(getPool()); - p.test(getPool(), out); - } - } - - private static final String SUFFIX = "HELLOWORLD"; - - private static class RenameClassVisitor extends ClassVisitor { - - public RenameClassVisitor(ClassWriter cv) { - super(Opcodes.ASM5, cv); - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - super.visit(version, access, name + SUFFIX, signature, superName, interfaces); - } - } - - private static class AddMethodClassVisitor extends ClassVisitor { - - public AddMethodClassVisitor(ClassWriter cv) { - super(Opcodes.ASM5, cv); - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - this.visitMethod(0, SUFFIX, "()V", null, null); - super.visit(version, access, name, signature, superName, interfaces); - } - } - - private class AddClassesPlugin extends TestPlugin { - - private int expected = 0; - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - WritableClassPool transformedClasses = globalPool.getTransformedClasses(); - expected = globalPool.getClasses().size(); - for (ModuleEntry res : globalPool.getClasses()) { - ClassReader reader = globalPool.getClassReader(res); - String className = reader.getClassName(); - if (!className.endsWith("module-info")) { - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); - reader.accept(new RenameClassVisitor(writer), ClassReader.EXPAND_FRAMES); - transformedClasses.addClass(writer); - ++expected; - } - } - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) { - Collection inClasses = extractClasses(inResources); - Collection outClasses = extractClasses(outResources); - if (expected != outClasses.size()) { - throw new AssertionError("Classes were not added. Expected: " + expected - + ", got: " + outClasses.size()); - } - for (ModuleEntry in : inClasses) { - String path = in.getPath(); - if (!outClasses.contains(in)) { - throw new AssertionError("Class not found: " + path); - } - if (path.endsWith("module-info.class")) { - continue; - } - String modifiedPath = path.replace(".class", SUFFIX + ".class"); - if (!outClasses.contains(ModuleEntry.create(modifiedPath, new byte[0]))) { - throw new AssertionError("Class not found: " + modifiedPath); - } - } - } - } - - private class AddResourcesPlugin extends TestPlugin { - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - for (ModuleEntry res : globalPool.getResourceFiles()) { - String path = res.getPath(); - String moduleName = getModule(path); - AsmModulePool modulePool = pools.getModulePool(moduleName); - WritableResourcePool resourcePool = modulePool.getTransformedResourceFiles(); - resourcePool.addResourceFile(new ResourceFile(removeModule(res.getPath()) + SUFFIX, - res.getBytes())); - } - } - - @Override - public void test(ModulePool in, ModulePool out) throws Exception { - Collection inResources = extractResources(in); - Collection outResources = extractResources(out); - if (2 * inResources.size() != outResources.size()) { - throw new AssertionError("Classes were not added. Expected: " + (2 * inResources.size()) - + ", got: " + outResources.size()); - } - for (ModuleEntry r : inResources) { - String path = r.getPath(); - if (!outResources.contains(r)) { - throw new AssertionError("Class not found: " + path); - } - String modifiedPath = path + SUFFIX; - if (!outResources.contains(ModuleEntry.create(modifiedPath, new byte[0]))) { - throw new AssertionError("Class not found: " + modifiedPath); - } - } - } - } - - private class ReplaceClassesPlugin extends TestPlugin { - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - WritableClassPool transformedClasses = globalPool.getTransformedClasses(); - for (ModuleEntry res : globalPool.getClasses()) { - ClassReader reader = globalPool.getClassReader(res); - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); - reader.accept(new AddMethodClassVisitor(writer), ClassReader.EXPAND_FRAMES); - transformedClasses.addClass(writer); - } - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws Exception { - Collection inClasses = extractClasses(inResources); - Collection outClasses = extractClasses(outResources); - if (inClasses.size() != outClasses.size()) { - throw new AssertionError("Number of classes. Expected: " + (inClasses.size()) - + ", got: " + outClasses.size()); - } - for (ModuleEntry out : outClasses) { - String path = out.getPath(); - if (!inClasses.contains(out)) { - throw new AssertionError("Class not found: " + path); - } - ClassFile cf = ClassFile.read(new ByteArrayInputStream(out.getBytes())); - if (path.endsWith("module-info.class")) { - continue; - } - boolean failed = true; - for (Method m : cf.methods) { - if (m.getName(cf.constant_pool).equals(SUFFIX)) { - failed = false; - } - } - if (failed) { - throw new AssertionError("Not found method with name " + SUFFIX + " in class " + path); - } - } - } - } - - private class ReplaceResourcesPlugin extends TestPlugin { - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - for (ModuleEntry res : globalPool.getResourceFiles()) { - String path = res.getPath(); - AsmModulePool modulePool = pools.getModulePool(getModule(path)); - modulePool.getTransformedResourceFiles().addResourceFile(new ResourceFile(removeModule(path), - "HUI".getBytes())); - } - } - - @Override - public void test(ModulePool in, ModulePool out) throws Exception { - Collection inResources = extractResources(in); - Collection outResources = extractResources(out); - if (inResources.size() != outResources.size()) { - throw new AssertionError("Number of resources. Expected: " + inResources.size() - + ", got: " + outResources.size()); - } - for (ModuleEntry r : outResources) { - String path = r.getPath(); - if (!inResources.contains(r)) { - throw new AssertionError("Resource not found: " + path); - } - String content = new String(r.getBytes()); - if (!"HUI".equals(content)) { - throw new AssertionError("Content expected: 'HUI', got: " + content); - } - } - } - } - - private class ForgetClassesPlugin extends TestPlugin { - - private int expected = 0; - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - WritableClassPool transformedClasses = globalPool.getTransformedClasses(); - int i = 0; - for (ModuleEntry res : globalPool.getClasses()) { - String path = removeModule(res.getPath()); - String className = path.replace(".class", ""); - if ((i & 1) == 0 && !className.endsWith("module-info")) { - transformedClasses.forgetClass(className); - } else { - ++expected; - } - i ^= 1; - } - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws Exception { - Collection outClasses = extractClasses(outResources); - if (expected != outClasses.size()) { - throw new AssertionError("Number of classes. Expected: " + expected + - ", got: " + outClasses.size()); - } - } - } - - private class ForgetResourcesPlugin extends TestPlugin { - - private int expectedAmount = 0; - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - int i = 0; - for (ModuleEntry res : globalPool.getResourceFiles()) { - String path = res.getPath(); - if (!path.contains("META-INF/services")) { - if ((i & 1) == 0) { - AsmModulePool modulePool = pools.getModulePool(getModule(path)); - modulePool.getTransformedResourceFiles().forgetResourceFile(removeModule(res.getPath())); - } else { - ++expectedAmount; - } - i ^= 1; - } else { - ++expectedAmount; - } - } - } - - @Override - public void test(ModulePool in, ModulePool out) throws Exception { - Collection outResources = extractResources(out); - if (expectedAmount != outResources.size()) { - throw new AssertionError("Number of classes. Expected: " + expectedAmount - + ", got: " + outResources.size()); - } - } - } - - private class AddForgetClassesPlugin extends TestPlugin { - - private int expected = 0; - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - WritableClassPool transformedClasses = globalPool.getTransformedClasses(); - int i = 0; - for (ModuleEntry res : globalPool.getClasses()) { - ClassReader reader = globalPool.getClassReader(res); - String className = reader.getClassName(); - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); - if (!className.endsWith("module-info")) { - reader.accept(new RenameClassVisitor(writer), ClassReader.EXPAND_FRAMES); - transformedClasses.addClass(writer); - ++expected; - } - - if ((i & 1) == 0 && !className.endsWith("module-info")) { - transformedClasses.forgetClass(className); - } else { - ++expected; - } - i ^= 1; - } - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws Exception { - Collection outClasses = extractClasses(outResources); - if (expected != outClasses.size()) { - throw new AssertionError("Number of classes. Expected: " + expected - + ", got: " + outClasses.size()); - } - } - } - - private class AddForgetResourcesPlugin extends TestPlugin { - - private int expectedAmount = 0; - - @Override - public void visit() { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - int i = 0; - for (ModuleEntry res : globalPool.getResourceFiles()) { - String path = res.getPath(); - String moduleName = getModule(path); - if (!path.contains("META-INF")) { - AsmModulePool modulePool = pools.getModulePool(moduleName); - WritableResourcePool transformedResourceFiles = modulePool.getTransformedResourceFiles(); - String newPath = removeModule(path) + SUFFIX; - transformedResourceFiles.addResourceFile(new ResourceFile(newPath, res.getBytes())); - if ((i & 1) == 0) { - transformedResourceFiles.forgetResourceFile(newPath); - } else { - ++expectedAmount; - } - i ^= 1; - } - ++expectedAmount; - } - } - - @Override - public void test(ModulePool inResources, ModulePool out) throws Exception { - Collection outResources = extractResources(out); - if (expectedAmount != outResources.size()) { - throw new AssertionError("Number of classes. Expected: " + expectedAmount - + ", got: " + outResources.size()); - } - } - } - - private class ComboPlugin extends TestPlugin { - - private class RenameClassVisitor extends ClassVisitor { - - public RenameClassVisitor(ClassWriter cv) { - super(Opcodes.ASM5, cv); - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - super.visit(version, access, name + SUFFIX, signature, superName, interfaces); - } - } - - @Override - public void visit() { - try { - renameClasses(); - renameResources(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws Exception { - if (!isVisitCalled()) { - throw new AssertionError("Resources not visited"); - } - AsmGlobalPool globalPool = getPools().getGlobalPool(); - if (globalPool.getTransformedClasses().getClasses().size() != getClasses().size()) { - throw new AssertionError("Number of transformed classes not equal to expected"); - } - // Check that only renamed classes and resource files are in the result. - outResources.entries().forEach(r -> { - String resourceName = r.getPath(); - if (resourceName.endsWith(".class") && !resourceName.endsWith("module-info.class")) { - if (!resourceName.endsWith(SUFFIX + ".class")) { - throw new AssertionError("Class not renamed " + resourceName); - } - } else if (resourceName.contains("META-INF/services/") && MODULES.containsKey(r.getModule())) { - String newClassName = new String(r.getBytes()); - if(!newClassName.endsWith(SUFFIX)) { - throw new AssertionError("Resource file not renamed " + resourceName); - } - } - }); - } - - private void renameResources() throws IOException { - AsmPools pools = getPools(); - // Rename the resource Files - for (Map.Entry> mod : MODULES.entrySet()) { - String moduleName = mod.getKey(); - AsmModulePool modulePool = pools.getModulePool(moduleName); - for (ModuleEntry res : modulePool.getResourceFiles()) { - ResourceFile resFile = modulePool.getResourceFile(res); - if (resFile.getPath().startsWith("META-INF/services/")) { - String newContent = new String(resFile.getContent()) + SUFFIX; - ResourceFile newResourceFile = new ResourceFile(resFile.getPath(), - newContent.getBytes()); - modulePool.getTransformedResourceFiles().addResourceFile(newResourceFile); - } - } - } - } - - private void renameClasses() throws IOException { - AsmPools pools = getPools(); - AsmGlobalPool globalPool = pools.getGlobalPool(); - WritableClassPool transformedClasses = globalPool.getTransformedClasses(); - for (ModuleEntry res : globalPool.getClasses()) { - if (res.getPath().endsWith("module-info.class")) { - continue; - } - ClassReader reader = globalPool.getClassReader(res); - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); - RenameClassVisitor visitor = new RenameClassVisitor(writer); - reader.accept(visitor, ClassReader.EXPAND_FRAMES); - - transformedClasses.forgetClass(reader.getClassName()); - transformedClasses.addClass(writer); - } - } - } -} diff --git a/test/tools/jlink/asmplugin/AsmPluginTestBase.java b/test/tools/jlink/asmplugin/AsmPluginTestBase.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/AsmPluginTestBase.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import jdk.tools.jlink.internal.ModulePoolImpl; -import jdk.tools.jlink.internal.StringTable; - -import jdk.tools.jlink.internal.plugins.asm.AsmPlugin; -import jdk.tools.jlink.internal.plugins.asm.AsmPools; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -public abstract class AsmPluginTestBase { - - protected static final String TEST_MODULE = "jlink.test"; - protected static final Map> MODULES; - - private static final Predicate isClass = r -> r.getPath().endsWith(".class"); - private final List classes; - private final List resources; - private final ModulePool pool; - - static { - Map> map = new HashMap<>(); - map.put("jdk.localedata", new ArrayList<>()); - map.put("java.base", new ArrayList<>()); - map.put(TEST_MODULE, new ArrayList<>()); - MODULES = Collections.unmodifiableMap(map); - } - - public static boolean isImageBuild() { - Path javaHome = Paths.get(System.getProperty("test.jdk")); - Path jmods = javaHome.resolve("jmods"); - return Files.exists(jmods); - } - - public AsmPluginTestBase() { - try { - List classes = new ArrayList<>(); - List resources = new ArrayList<>(); - - pool = new ModulePoolImpl(); - - FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); - Path root = fs.getPath("/modules"); - - List moduleInfos = new ArrayList<>(); - try (Stream stream = Files.walk(root)) { - for (Iterator iterator = stream.iterator(); iterator.hasNext(); ) { - Path p = iterator.next(); - if (Files.isRegularFile(p)) { - String module = p.toString().substring("/modules/".length()); - module = module.substring(0, module.indexOf("/")); - if (MODULES.keySet().contains(module)) { - try { - boolean isModuleInfo = p.endsWith("module-info.class"); - if (isModuleInfo) { - moduleInfos.add(Files.readAllBytes(p)); - } - byte[] content = Files.readAllBytes(p); - if (p.toString().endsWith(".class") && !isModuleInfo) { - classes.add(toClassName(p)); - } else if (!isModuleInfo) { - MODULES.get(module).add(toResourceFile(p)); - } - resources.add(toPath(p.toString())); - ModuleEntry res = ModuleEntry.create(toPath(p.toString()), content); - pool.add(res); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - } - } - } - // There is more than 10 classes in java.base... - if (classes.size() < 10 || pool.getEntryCount() < 10) { - throw new AssertionError("Not expected resource or class number"); - } - - //Add a fake resource file - String content = "java.lang.Object"; - String path = "META-INF/services/com.foo.BarProvider"; - ModuleEntry resFile = ModuleEntry.create("/" + TEST_MODULE + "/" + - path, content.getBytes()); - pool.add(resFile); - ModuleEntry fakeInfoFile = ModuleEntry.create("/" + TEST_MODULE - + "/module-info.class", moduleInfos.get(0)); - pool.add(fakeInfoFile); - MODULES.get(TEST_MODULE).add(path); - for(Map.Entry> entry : MODULES.entrySet()) { - if (entry.getValue().isEmpty()) { - throw new AssertionError("No resource file for " + entry.getKey()); - } - } - this.classes = Collections.unmodifiableList(classes); - this.resources = Collections.unmodifiableList(resources); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } - } - - public List getClasses() { - return classes; - } - - public List getResources() { - return resources; - } - - public ModulePool getPool() { - return pool; - } - - public abstract void test() throws Exception; - - public Collection extractClasses(ModulePool pool) { - return pool.entries() - .filter(isClass) - .collect(Collectors.toSet()); - } - - public Collection extractResources(ModulePool pool) { - return pool.entries() - .filter(isClass.negate()) - .collect(Collectors.toSet()); - } - - public String getModule(String path) { - int index = path.indexOf("/", 1); - return path.substring(1, index); - } - - public String removeModule(String path) { - int index = path.indexOf("/", 1); - return path.substring(index + 1); - } - - private String toPath(String p) { - return p.substring("/modules".length()); - } - - private String toClassName(Path p) { - String path = p.toString(); - path = path.substring("/modules/".length()); - // remove module - if (!path.endsWith("module-info.class")) { - path = path.substring(path.indexOf("/") + 1); - } - path = path.substring(0, path.length() - ".class".length()); - - return path; - } - - private String toResourceFile(Path p) { - String path = p.toString(); - path = path.substring("/modules/".length()); - // remove module - path = path.substring(path.indexOf("/") + 1); - - return path; - } - - public abstract class TestPlugin extends AsmPlugin { - - private AsmPools pools; - - public AsmPools getPools() { - return pools; - } - - public boolean isVisitCalled() { - return pools != null; - } - - public ModulePool visit(ModulePool inResources) throws IOException { - try { - ModulePool outResources = new ModulePoolImpl(inResources.getByteOrder(), new StringTable() { - @Override - public int addString(String str) { - return -1; - } - - @Override - public String getString(int id) { - return null; - } - }); - visit(inResources, outResources); - return outResources; - } catch (Exception e) { - throw new IOException(e); - } - } - - @Override - public void visit(AsmPools pools) { - if (isVisitCalled()) { - throw new AssertionError("Visit was called twice"); - } - this.pools = pools; - visit(); - } - - public abstract void visit(); - public abstract void test(ModulePool inResources, ModulePool outResources) throws Exception; - - @Override - public String getName() { - return "test-plugin"; - } - } -} diff --git a/test/tools/jlink/asmplugin/BasicTest.java b/test/tools/jlink/asmplugin/BasicTest.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/BasicTest.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Asm plugin testing. - * @test - * @summary Test basic functionality. - * @author Jean-Francois Denise - * @modules java.base/jdk.internal.org.objectweb.asm - * jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * @build AsmPluginTestBase - * @run main BasicTest - */ - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.internal.plugins.asm.AsmPool; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -public class BasicTest extends AsmPluginTestBase { - - public static void main(String[] args) throws Exception { - if (!isImageBuild()) { - System.err.println("Test not run. Not image build."); - return; - } - new BasicTest().test(); - } - - @Override - public void test() throws Exception { - BasicPlugin basicPlugin = new BasicPlugin(getClasses()); - ModulePool res = basicPlugin.visit(getPool()); - basicPlugin.test(getPool(), res); - } - - private class BasicPlugin extends TestPlugin { - - private final List classes; - - public BasicPlugin(List classes) { - this.classes = classes; - } - - @Override - public void visit() { - for (String m : MODULES.keySet()) { - AsmModulePool pool = getPools().getModulePool(m); - if (pool == null) { - throw new AssertionError(m + " pool not found"); - } - if(!pool.getModuleName().equals(m)) { - throw new AssertionError("Invalid module name " + - pool.getModuleName() + " should be "+ m); - } - if (pool.getClasses().size() == 0 && !m.equals(TEST_MODULE)) { - throw new AssertionError("Empty pool " + m); - } - pool.addPackage("toto"); - if (!pool.getTransformedClasses().getClasses().isEmpty()) { - throw new AssertionError("Should be empty"); - } - for(String res : MODULES.get(m)) { - AsmPool.ResourceFile resFile = pool.getResourceFile(res); - if(resFile == null) { - throw new AssertionError("No resource file for " + res); - } - } - } - try { - testPools(); - testVisitor(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws Exception { - if (!isVisitCalled()) { - throw new AssertionError("Resources not visited"); - } - if (inResources.getEntryCount() != outResources.getEntryCount()) { - throw new AssertionError("Input size " + inResources.getEntryCount() + - " != to " + outResources.getEntryCount()); - } - } - - private void testVisitor() throws IOException { - List seen = new ArrayList<>(); - getPools().getGlobalPool().visitClassReaders((reader) -> { - String className = reader.getClassName(); - // Wrong naming of module-info.class in ASM - if (className.endsWith("module-info")) { - return null; - } - if (!classes.contains(className)) { - throw new AssertionError("Class is not expected " + className); - } - if (getPools().getGlobalPool().getClassReader(className) == null) { - throw new AssertionError("Class not found in pool " + className); - } - seen.add(className); - return null; - }); - - if (!seen.equals(classes)) { - throw new AssertionError("Expected and seen are not equal"); - } - } - - private void testPools() throws IOException { - Set remain = new HashSet<>(classes); - for (ModuleEntry res : getPools().getGlobalPool().getClasses()) { - ClassReader reader = getPools().getGlobalPool().getClassReader(res); - String className = reader.getClassName(); - // Wrong naming of module-info.class in ASM - if (className.endsWith("module-info")) { - continue; - } - if (!classes.contains(className)) { - throw new AssertionError("Class is not expected " + className); - } - if (getPools().getGlobalPool().getClassReader(className) == null) { - throw new AssertionError("Class " + className + " not found in pool "); - } - // Check the module pool - boolean found = false; - for(AsmModulePool mp : getPools().getModulePools()) { - if(mp.getClassReader(className) != null) { - found = true; - break; - } - } - if(!found) { - throw new AssertionError("No modular pool for " + - className); - } - remain.remove(className); - } - if (!remain.isEmpty()) { - throw new AssertionError("Remaining classes " + remain); - } - } - } -} diff --git a/test/tools/jlink/asmplugin/IdentityPluginTest.java b/test/tools/jlink/asmplugin/IdentityPluginTest.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/IdentityPluginTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Asm plugin testing. - * @test - * @summary Test basic functionality. - * @author Jean-Francois Denise - * @modules java.base/jdk.internal.org.objectweb.asm - * jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * @build AsmPluginTestBase - * @run main IdentityPluginTest - */ - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.UncheckedIOException; - -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.WritableClassPool; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -public class IdentityPluginTest extends AsmPluginTestBase { - - public static void main(String[] args) throws Exception { - if (!isImageBuild()) { - System.err.println("Test not run. Not image build."); - return; - } - new IdentityPluginTest().test(); - } - - public void test() throws Exception { - IdentityPlugin asm = new IdentityPlugin(); - ModulePool resourcePool = asm.visit(getPool()); - asm.test(getPool(), resourcePool); - } - - private class IdentityPlugin extends TestPlugin { - - @Override - public void visit() { - for (ModuleEntry res : getPools().getGlobalPool().getClasses()) { - if (res.getPath().endsWith("module-info.class")) { - continue; - } - ClassReader reader = getPools().getGlobalPool().getClassReader(res); - ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); - IdentityClassVisitor visitor = new IdentityClassVisitor(writer); - reader.accept(visitor, ClassReader.EXPAND_FRAMES); - getPools().getGlobalPool().getTransformedClasses().addClass(writer); - } - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws IOException { - if (outResources.isEmpty()) { - throw new AssertionError("Empty result"); - } - if (!isVisitCalled()) { - throw new AssertionError("Resources not visited"); - } - WritableClassPool transformedClasses = getPools().getGlobalPool().getTransformedClasses(); - if (transformedClasses.getClasses().size() != getClasses().size()) { - throw new AssertionError("Number of transformed classes not equal to expected"); - } - for (String className : getClasses()) { - if (transformedClasses.getClassReader(className) == null) { - throw new AssertionError("Class not transformed " + className); - } - } - outResources.entries().forEach(r -> { - if (r.getPath().endsWith(".class") && !r.getPath().endsWith("module-info.class")) { - try { - ClassReader reader = new ClassReader(new ByteArrayInputStream(r.getBytes())); - ClassWriter w = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); - reader.accept(w, ClassReader.EXPAND_FRAMES); - } catch (IOException exp) { - throw new UncheckedIOException(exp); - } - } - }); - } - - @Override - public String getName() { - return "identity-plugin"; - } - } - - private static class IdentityClassVisitor extends ClassVisitor { - public IdentityClassVisitor(ClassWriter cv) { - super(Opcodes.ASM5, cv); - } - } -} diff --git a/test/tools/jlink/asmplugin/NegativeTest.java b/test/tools/jlink/asmplugin/NegativeTest.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/NegativeTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Asm plugin testing. - * @test - * @summary Test basic functionality. - * @author Andrei Eremeev - * @modules java.base/jdk.internal.org.objectweb.asm - * jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * @build AsmPluginTestBase - * @run main NegativeTest - */ - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.ByteOrder; -import java.util.Map; -import java.util.Set; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.tools.jlink.plugin.Plugin; -import jdk.tools.jlink.internal.ModulePoolImpl; -import jdk.tools.jlink.internal.StringTable; -import jdk.tools.jlink.internal.plugins.asm.AsmGlobalPool; -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.internal.plugins.asm.AsmPlugin; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.ResourceFile; -import jdk.tools.jlink.internal.plugins.asm.AsmPools; -import jdk.tools.jlink.plugin.PluginException; -import jdk.tools.jlink.plugin.ModulePool; - -public class NegativeTest extends AsmPluginTestBase { - public static void main(String[] args) throws Exception { - if (!isImageBuild()) { - System.err.println("Test not run. Not image build."); - return; - } - new NegativeTest().test(); - } - - @Override - public void test() throws Exception { - testNull(); - testUnknownPackage(); - } - - private void testUnknownPackage() throws Exception { - AsmPlugin t = new AsmPlugin() { - @Override - public void visit(AsmPools pools) { - try { - AsmGlobalPool globalPool = pools.getGlobalPool(); - AsmModulePool javabase = pools.getModulePool("java.base"); - ClassReader cr = new ClassReader(NegativeTest.class.getResourceAsStream("NegativeTest.class")); - ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); - cr.accept(new RenameClassVisitor(cw), ClassReader.EXPAND_FRAMES); - action(() -> globalPool.getTransformedClasses().addClass(cw), - "Unknown package", PluginException.class); - action(() -> javabase.getTransformedClasses().addClass(cw), - "Unknown package", PluginException.class); - - ResourceFile newResFile = new ResourceFile("java/aaa/file", new byte[0]); - action(() -> globalPool.getTransformedResourceFiles().addResourceFile(newResFile), - "Unknown package", PluginException.class); - action(() -> javabase.getTransformedResourceFiles().addResourceFile(newResFile), - "Unknown package", PluginException.class); - - action(() -> globalPool.getTransformedClasses().forgetClass("java/aaa/file"), - "Unknown package", PluginException.class); - action(() -> javabase.getTransformedClasses().forgetClass("java/aaa/file"), - "Unknown package", PluginException.class); - action(() -> globalPool.getTransformedResourceFiles().forgetResourceFile("java/aaa/file"), - "Unknown package", PluginException.class); - action(() -> javabase.getTransformedResourceFiles().forgetResourceFile("java/aaa/file"), - "Unknown package", PluginException.class); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - }; - ModulePool resources = new ModulePoolImpl(ByteOrder.BIG_ENDIAN, new StringTable() { - @Override - public int addString(String str) { - return -1; - } - - @Override - public String getString(int id) { - throw new UnsupportedOperationException("Not supported yet."); - } - }); - t.visit(getPool(), resources); - } - - private static class RenameClassVisitor extends ClassVisitor { - - public RenameClassVisitor(ClassWriter cv) { - super(Opcodes.ASM5, cv); - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - super.visit(version, access, "RENAMED", signature, superName, interfaces); - } - } - - private void testNull() throws Exception { - AsmPlugin t = new AsmPlugin() { - @Override - public void visit(AsmPools pools) { - action(() -> pools.getModulePool(null), "Module name is null", NullPointerException.class); - action(() -> pools.fillOutputResources(null), "Output resource is null", NullPointerException.class); - } - }; - ModulePool resources = new ModulePoolImpl(ByteOrder.BIG_ENDIAN, new StringTable() { - @Override - public int addString(String str) { - return -1; - } - - @Override - public String getString(int id) { - throw new UnsupportedOperationException("Not supported yet."); - } - }); - action(() -> t.visit(null, resources), "Input resource is null", NullPointerException.class); - action(() -> t.visit(resources, null), "Output resource is null", NullPointerException.class); - t.visit(resources, resources); - } - - private void action(Action action, String message, Class expected) { - try { - System.err.println("Testing: " + message); - action.call(); - throw new AssertionError(message + ": should have failed"); - } catch (Exception e) { - if (!expected.isInstance(e)) { - throw new RuntimeException(e); - } else { - System.err.println("Got exception as expected: " + e); - } - } - } - - private interface Action { - void call() throws Exception; - } -} diff --git a/test/tools/jlink/asmplugin/PackageMappingTest.java b/test/tools/jlink/asmplugin/PackageMappingTest.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/PackageMappingTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Asm plugin testing. - * @test - * @summary Test plugins - * @author Andrei Eremeev - * @modules jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * @run main PackageMappingTest - */ -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -import jdk.tools.jlink.internal.plugins.asm.AsmGlobalPool; -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.ResourceFile; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.WritableResourcePool; -import jdk.tools.jlink.plugin.PluginException; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -public class PackageMappingTest extends AsmPluginTestBase { - - private final List newFiles = Arrays.asList( - "/java.base/a1/bbb/c", - "/" + TEST_MODULE + "/a2/bbb/d" - ); - - public static void main(String[] args) throws Exception { - if (!isImageBuild()) { - System.err.println("Test not run. Not image build."); - return; - } - new PackageMappingTest().test(); - } - - public void test() throws Exception { - TestPlugin[] plugins = new TestPlugin[]{ - new PackageMappingPlugin(newFiles, false), - new PackageMappingPlugin(newFiles, true) - }; - for (TestPlugin p : plugins) { - ModulePool pool = p.visit(getPool()); - p.test(getPool(), pool); - } - } - - public class PackageMappingPlugin extends TestPlugin { - - private final Map> newFiles; - private final boolean testGlobal; - - private String getModuleName(String res) { - return res.substring(1, res.indexOf("/", 1)); - } - - private PackageMappingPlugin(List files, boolean testGlobal) { - this.newFiles = new HashMap<>(); - this.testGlobal = testGlobal; - for (String file : files) { - String moduleName = getModuleName(file); - String path = file.substring(1 + moduleName.length() + 1); - newFiles.computeIfAbsent(moduleName, $ -> new ArrayList<>()).add( - new ResourceFile(path, new byte[0])); - } - } - - @Override - public void visit() { - testMapToUnknownModule(); - testMapPackageTwice(); - testPackageMapping(); - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) { - Set in = getPools().getGlobalPool().getResourceFiles().stream() - .map(ModuleEntry::getPath) - .collect(Collectors.toSet()); - Set out = extractResources(outResources).stream() - .map(ModuleEntry::getPath) - .collect(Collectors.toSet()); - in.addAll(PackageMappingTest.this.newFiles); - if (!Objects.equals(in, out)) { - throw new AssertionError("Expected: " + in + ", got: " + outResources); - } - } - - private void testPackageMapping() { - AsmGlobalPool globalPool = getPools().getGlobalPool(); - try { - Map> mappedPackages = new HashMap<>(); - Function> produceSet = $ -> new HashSet<>(); - for (Map.Entry> entry : newFiles.entrySet()) { - String moduleName = entry.getKey(); - Set module = mappedPackages.computeIfAbsent(moduleName, produceSet); - AsmModulePool modulePool = getPools().getModulePool(moduleName); - for (ResourceFile r : entry.getValue()) { - String name = r.getPath(); - String packageName = name.substring(0, name.lastIndexOf('/')); - if (module.add(packageName)) { - globalPool.addPackageModuleMapping(packageName, moduleName); - } - WritableResourcePool transformedResourceFiles = testGlobal - ? globalPool.getTransformedResourceFiles() - : modulePool.getTransformedResourceFiles(); - transformedResourceFiles.addResourceFile(r); - } - try { - modulePool.getTransformedResourceFiles().addResourceFile( - new ResourceFile("a3/bbb", new byte[0])); - throw new AssertionError("Exception expected"); - } catch (Exception ex) { - // expected - } - } - try { - globalPool.getTransformedResourceFiles().addResourceFile( - new ResourceFile("a3/bbb", new byte[0])); - throw new AssertionError("Exception expected"); - } catch (Exception ex) { - // expected - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void testMapPackageTwice() { - try { - AsmGlobalPool globalPool = getPools().getGlobalPool(); - globalPool.addPackageModuleMapping("a/p1", TEST_MODULE); - globalPool.addPackageModuleMapping("a/p1", TEST_MODULE); - throw new AssertionError("Exception expected after mapping a package twice to the same module"); - } catch (Exception e) { - if (e instanceof PluginException) { - // expected - String message = e.getMessage(); - if (!(TEST_MODULE + " module already contains package a.p1").equals(message)) { - throw new AssertionError(e); - } - } else { - throw new AssertionError(e); - } - } - } - - private void testMapToUnknownModule() { - AsmModulePool unknownModule = getPools().getModulePool("UNKNOWN"); - if (unknownModule != null) { - throw new AssertionError("getModulePool returned not null value: " + unknownModule.getModuleName()); - } - try { - AsmGlobalPool globalPool = getPools().getGlobalPool(); - globalPool.addPackageModuleMapping("a/b", "UNKNOWN"); - throw new AssertionError("Exception expected after mapping a package to unknown module"); - } catch (Exception e) { - String message = e.getMessage(); - if (message == null || !message.startsWith("Unknown module UNKNOWN")) { - throw new AssertionError(e); - } - } - } - } -} diff --git a/test/tools/jlink/asmplugin/SortingTest.java b/test/tools/jlink/asmplugin/SortingTest.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/SortingTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Asm plugin testing. - * @test - * @summary Test resource sorting. - * @author Jean-Francois Denise - * @modules jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * @build AsmPluginTestBase - * @run main SortingTest - */ - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import jdk.tools.jlink.internal.plugins.asm.AsmModulePool; -import jdk.tools.jlink.plugin.PluginException; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -public class SortingTest extends AsmPluginTestBase { - - public static void main(String[] args) throws Exception { - if (!isImageBuild()) { - System.err.println("Test not run. Not image build."); - return; - } - new SortingTest().test(); - } - - @Override - public void test() { - try { - classSorting(); - moduleSorting(); - } catch (Exception ex) { - throw new PluginException(ex); - } - } - - private void classSorting() throws Exception { - List sorted = new ArrayList<>(getResources()); - sorted.sort(null); - ClassSorterPlugin sorterPlugin = new ClassSorterPlugin(sorted); - ModulePool resourcePool = sorterPlugin.visit(getPool()); - sorterPlugin.test(getPool(), resourcePool); - } - - private String getModuleName(String p) { - return p.substring(1, p.indexOf('/', 1)); - } - - private void moduleSorting() throws Exception { - List sorted = new ArrayList<>(getResources()); - sorted.sort((s1, s2) -> -getModuleName(s1).compareTo(getModuleName(s2))); - ModuleSorterPlugin sorterPlugin = new ModuleSorterPlugin(); - ModulePool resourcePool = sorterPlugin.visit(getPool()); - sorterPlugin.test(getPool(), resourcePool); - } - - private class ModuleSorterPlugin extends TestPlugin { - - @Override - public void visit() { - for (AsmModulePool modulePool : getPools().getModulePools()) { - modulePool.setSorter(resources -> { - List sort = resources.entries() - .map(ModuleEntry::getPath) - .collect(Collectors.toList()); - sort.sort(null); - return sort; - }); - } - getPools().setModuleSorter(modules -> { - modules.sort((s1, s2) -> -s1.compareTo(s2)); - return modules; - }); - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws Exception { - if (!isVisitCalled()) { - throw new AssertionError("Resources not visited"); - } - List sortedResourcePaths = outResources.entries() - .map(ModuleEntry::getPath) - .collect(Collectors.toList()); - - List defaultResourceOrder = new ArrayList<>(); - inResources.entries().forEach(r -> { - if (!inResources.contains(r)) { - throw new AssertionError("Resource " + r.getPath() + " not in result pool"); - } - defaultResourceOrder.add(r.getPath()); - }); - // Check that default sorting is not equal to sorted one - if (defaultResourceOrder.equals(sortedResourcePaths)) { - throw new AssertionError("Sorting not applied, default ordering"); - } - // Check module order. - for (int i = 0; i < sortedResourcePaths.size() - 1; ++i) { - String first = sortedResourcePaths.get(i); - String p1 = getModuleName(first); - String second = sortedResourcePaths.get(i + 1); - String p2 = getModuleName(second); - if (p1.compareTo(p2) < 0 || p1.compareTo(p2) == 0 && - removeModule(first).compareTo(removeModule(second)) >= 0) { - throw new AssertionError("Modules are not sorted properly: resources: " + first + " " + second); - } - } - } - } - - private class ClassSorterPlugin extends TestPlugin { - - private final List expectedClassesOrder; - - private ClassSorterPlugin(List expectedClassesOrder) { - this.expectedClassesOrder = expectedClassesOrder; - } - - @Override - public void visit() { - getPools().getGlobalPool().setSorter( - (resources) -> expectedClassesOrder.stream() - .map(resources::findEntry) - .map(Optional::get) - .map(ModuleEntry::getPath) - .collect(Collectors.toList())); - } - - @Override - public void test(ModulePool inResources, ModulePool outResources) throws Exception { - if (!isVisitCalled()) { - throw new AssertionError("Resources not visited"); - } - List sortedResourcePaths = outResources.entries() - .map(ModuleEntry::getPath) - .collect(Collectors.toList()); - - List defaultResourceOrder = new ArrayList<>(); - getPool().entries().forEach(r -> { - if (!getPool().contains(r)) { - throw new AssertionError("Resource " + r.getPath() + " not in result pool"); - } - defaultResourceOrder.add(r.getPath()); - }); - // Check that default sorting is not equal to sorted one - if (defaultResourceOrder.equals(sortedResourcePaths)) { - throw new AssertionError("Sorting not applied, default ordering"); - } - // Check that sorted is equal to result. - if (!expectedClassesOrder.equals(sortedResourcePaths)) { - throw new AssertionError("Sorting not properly applied"); - } - } - } -} diff --git a/test/tools/jlink/asmplugin/VisitorTest.java b/test/tools/jlink/asmplugin/VisitorTest.java deleted file mode 100644 --- a/test/tools/jlink/asmplugin/VisitorTest.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * Asm plugin testing. - * @test - * @summary Test visitors. - * @author Andrei Eremeev - * @modules java.base/jdk.internal.org.objectweb.asm - * jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink.internal.plugins.asm - * @build AsmPluginTestBase - * @run main VisitorTest - */ - -import java.io.IOException; -import java.util.Collection; -import java.util.function.Function; - -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassVisitor; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.tools.jlink.internal.plugins.asm.AsmPool; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.ClassReaderVisitor; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.ResourceFile; -import jdk.tools.jlink.internal.plugins.asm.AsmPool.ResourceFileVisitor; -import jdk.tools.jlink.internal.plugins.asm.AsmPools; -import jdk.tools.jlink.plugin.ModuleEntry; -import jdk.tools.jlink.plugin.ModulePool; - -public class VisitorTest extends AsmPluginTestBase { - - public static void main(String[] args) throws Exception { - if (!isImageBuild()) { - System.err.println("Test not run. Not image build."); - return; - } - new VisitorTest().test(); - } - - @Override - public void test() throws Exception { - TestPlugin[] plugins = new TestPlugin[] { - new ClassVisitorPlugin("Class-global-pool", AsmPools::getGlobalPool), - new ClassVisitorPlugin("Class-module-pool", pools -> pools.getModulePool("java.base")), - new ResourceVisitorPlugin("Resource-global-pool", AsmPools::getGlobalPool), - new ResourceVisitorPlugin("Resource-module-pool", pools -> pools.getModulePool("java.base")) - }; - for (TestPlugin p : plugins) { - System.err.println("Testing: " + p.getName()); - ModulePool out = p.visit(getPool()); - p.test(getPool(), out); - } - } - - private static class CustomClassReaderVisitor implements ClassReaderVisitor { - private int amount = 0; - private int changed = 0; - - @Override - public ClassWriter visit(ClassReader reader) { - if ((amount++ % 2) == 0) { - String className = reader.getClassName(); - if (className.endsWith("module-info")) { - return null; - } - ClassWriter cw = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES); - reader.accept(new ClassVisitor(Opcodes.ASM5, cw) { - @Override - public void visit(int i, int i1, String s, String s1, String s2, String[] strings) { - super.visit(i, i1, s + "Changed", s1, s2, strings); - } - }, ClassReader.EXPAND_FRAMES); - ++changed; - return cw; - } else { - return null; - } - } - - public int getAmount() { - return amount; - } - - public int getNumberOfChanged() { - return changed; - } - } - - private static class CustomResourceFileVisitor implements ResourceFileVisitor { - private int amount = 0; - private int changed = 0; - - @Override - public ResourceFile visit(ResourceFile resourceFile) { - if ((amount++ % 2) == 0) { - ++changed; - return new ResourceFile(resourceFile.getPath() + "Changed", resourceFile.getContent()); - } else { - return null; - } - } - - public int getAmount() { - return amount; - } - - public int getNumberOfChanged() { - return changed; - } - } - - public class ClassVisitorPlugin extends TestPlugin { - - private final String name; - private final Function getPool; - private final CustomClassReaderVisitor classReaderVisitor = new CustomClassReaderVisitor(); - - public ClassVisitorPlugin(String name, Function getPool) { - this.name = name; - this.getPool = getPool; - } - - @Override - public void visit() { - AsmPool pool = getPool.apply(getPools()); - pool.visitClassReaders(classReaderVisitor); - } - - @Override - public void test(ModulePool in, ModulePool out) throws Exception { - Collection inClasses = getPool.apply(getPools()).getClasses(); - if (inClasses.size() != classReaderVisitor.getAmount()) { - throw new AssertionError("Testing " + name + ". Number of visited classes. Expected: " + - inClasses.size() + ", got: " + classReaderVisitor.getAmount()); - } - Collection outClasses = extractClasses(out); - int changedClasses = 0; - for (ModuleEntry r : outClasses) { - if (r.getPath().endsWith("Changed.class")) { - ++changedClasses; - } - } - if (changedClasses != classReaderVisitor.getNumberOfChanged()) { - throw new AssertionError("Testing " + name + ". Changed classes. Expected: " + changedClasses + - ", got: " + classReaderVisitor.getNumberOfChanged()); - } - } - - @Override - public String getName() { - return name; - } - } - - public class ResourceVisitorPlugin extends TestPlugin { - - private final String name; - private final Function getPool; - private final CustomResourceFileVisitor resourceFileVisitor = new CustomResourceFileVisitor(); - - public ResourceVisitorPlugin(String name, Function getPool) { - this.name = name; - this.getPool = getPool; - } - - @Override - public void visit() { - AsmPool pool = getPool.apply(getPools()); - pool.visitResourceFiles(resourceFileVisitor); - } - - @Override - public void test(ModulePool in, ModulePool out) throws Exception { - Collection inResources = getPool.apply(getPools()).getResourceFiles(); - if (inResources.size() != resourceFileVisitor.getAmount()) { - throw new AssertionError("Testing " + name + ". Number of visited resources. Expected: " + - inResources.size() + ", got: " + resourceFileVisitor.getAmount()); - } - Collection outResources = extractResources(out); - int changedClasses = 0; - for (ModuleEntry r : outResources) { - if (r.getPath().endsWith("Changed")) { - ++changedClasses; - } - } - if (changedClasses != resourceFileVisitor.getNumberOfChanged()) { - throw new AssertionError("Testing " + name + ". Changed classes. Expected: " + changedClasses + - ", got: " + resourceFileVisitor.getNumberOfChanged()); - } - } - - @Override - public String getName() { - return name; - } - } -}