--- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/build.xml 2018-05-03 08:02:29.951159934 -0700 @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/src/openjdk/jcov/filter/simplemethods/Delegators.java 2018-05-03 08:02:30.191159933 -0700 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.function.BiPredicate; + +import static java.util.Arrays.binarySearch; +import static openjdk.jcov.filter.simplemethods.Utils.isInvokeInstruction; +import static openjdk.jcov.filter.simplemethods.Utils.isReturnInstruction; +import static openjdk.jcov.filter.simplemethods.Utils.isSimpleInstruction; + +public class Delegators implements BiPredicate { + + private final boolean sameNameDelegationOnly; + + public Delegators(boolean sameNameDelegationOnly) { + this.sameNameDelegationOnly = sameNameDelegationOnly; + } + + public Delegators() { + this(false); + } + + /** + * Identifies simple delegation. A simple delegator is a method obtaining any number of values with a "simple" code + * and then calling a method with the obtained values. + * @see Utils#isSimpleInstruction(int) + */ + @Override + public boolean test(ClassNode clazz, MethodNode m) { + int index = 0; + int opCode = -1; + //skip all instructions allowed to get values + for(; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + if (!isSimpleInstruction(opCode)) { + break; + } + } + } + //that should be an invocation instruction + if(!isInvokeInstruction(opCode)) { + return false; + } + if(sameNameDelegationOnly) { + //check name + AbstractInsnNode node = m.instructions.get(index); + String name; + if (node instanceof MethodInsnNode) { + name = ((MethodInsnNode) node).name; + } else if (node instanceof InvokeDynamicInsnNode) { + name = ((InvokeDynamicInsnNode) node).name; + } else { + throw new IllegalStateException("Unknown node type: " + node.getClass().getName()); + } + if(!m.name.equals(name)) { + return false; + } + } + //scroll to next instruction + for(index++; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + break; + } + } + //that should be a return instruction + return isReturnInstruction(opCode); + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/src/openjdk/jcov/filter/simplemethods/EmptyMethods.java 2018-05-03 08:02:30.431159932 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.function.BiPredicate; + +import static org.objectweb.asm.Opcodes.RETURN; + +public class EmptyMethods implements BiPredicate { + @Override + public boolean test(ClassNode node, MethodNode m) { + int index = 0; + int opCode = -1; + //skip all instructions allowed to get values + for(; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + if (!Utils.isSimpleInstruction(opCode)) { + break; + } + } + } + //that should be a return + return opCode == RETURN; + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/src/openjdk/jcov/filter/simplemethods/Getters.java 2018-05-03 08:02:30.671159931 -0700 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.function.BiPredicate; + +import static org.objectweb.asm.Opcodes.RETURN; + +public class Getters implements BiPredicate { + /** + * Identifies simple getters. A simple getter is a method obtaining a value with a "simple" code and returning it. + * @see Utils#isSimpleInstruction(int) + */ + @Override + public boolean test(ClassNode clazz, MethodNode m) { + int index = 0; + int opCode = -1; + //skip all instructions allowed to get values + for(; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + if (!Utils.isSimpleInstruction(opCode)) { + break; + } + } + } + //that should be a return instruction, but returning a value + return Utils.isReturnInstruction(opCode) && opCode != RETURN; + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/src/openjdk/jcov/filter/simplemethods/Scanner.java 2018-05-03 08:02:30.915159930 -0700 @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import com.sun.tdk.jcov.util.Utils; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.function.BiPredicate; + +import static java.util.stream.Collectors.joining; + +public class Scanner { + private static String USAGE = + "java -classpath jcov.jar:SimpleMethods.jar --usage\n" + + "\n" + + "java -classpath jcov.jar:simple_methods_anc.jar " + Scanner.class.getName() + + " [--include|-i ] [--exclude|-e ]" + + " [--include-module|-im ] [--exclude-module|-em ] \\\n" + + "[--getters ] " + + "[--setters ] " + + "[--delegators ] " + + "[--throwers ] " + + "[--empty ] \\\n" + + "jrt:/ | jar:file:/ | file:/\n" + + "\n" + + " Options\n" + + " --include - what classes to scan for simple methods.\n" + + " --exclude - what classes to exclude from scanning.\n" + + " --include-module - what modules to scan for simple methods.\n" + + " --exclude-module - what modules to exclude from scanning.\n" + + " Next options specify file names where to collect this or that type of methods. " + + "Only those which specified are detected. At least one kind of methods should be requested. " + + "Please consult the source code for exact details.\n" + + " --getters - methods which are just returning a value.\n" + + " --setters - methods which are just setting a field.\n" + + " --delegators - methods which are just calling another method.\n" + + " --throwers - methods which are just throwing an exception.\n" + + " --empty - methods with an empty body.\n" + + "\n" + + " Parameters define where to look for classes which are to be scanned.\n" + + " jrt:/ - scan JDK classes\n" + + " jar:file:/ - scan a jar file\n" + + " file:/ - scan a directory containing compiled classes."; + + private Utils.Pattern[] includes; + private Utils.Pattern[] excludes; + private final List filters = new ArrayList<>(); + private final List filesystems = new ArrayList<>(); + + public static void main(String[] args) throws IOException, URISyntaxException { + if (args.length == 1 && args[0].equals("--usage")) { + usage(); + return; + } + Scanner scanner = new Scanner(); + final List includes = new ArrayList<>(); + final List excludes = new ArrayList<>(); + for (int i = 0; i < args.length; i++) { + switch (args[i]) { + case "--include": + case "-i": + i++; + includes.add(new Utils.Pattern(args[i], true, false)); + break; + case "--exclude": + case "-e": + i++; + excludes.add(new Utils.Pattern(args[i], false, false)); + break; + case "--include-module": + case "-im": + i++; + includes.add(new Utils.Pattern(args[i], true, true)); + break; + case "--exclude-module": + case "-em": + i++; + excludes.add(new Utils.Pattern(args[i], false, true)); + break; + default: + //the only other options allowed are - + //see usage + if (args[i].startsWith("--")) { + Filter filter = Filter.get(args[i].substring(2)); + scanner.filters.add(filter); + i++; + filter.setOutputFile(args[i]); + } else { + try { + scanner.filesystems.add(new URI(args[i])); + } catch (URISyntaxException e) { + usage(); + throw e; + } + } + } + } + if (scanner.filters.size() == 0) { + usage(); + String filtersList = + Arrays.stream(Filter.values()).map(f -> "--" + f.description).collect(joining(",")); + throw new IllegalArgumentException("One or more of " + filtersList + " options must be specified"); + } + scanner.includes = includes.toArray(new Utils.Pattern[0]); + scanner.excludes = excludes.toArray(new Utils.Pattern[0]); + scanner.run(); + } + + private static void usage() { + System.out.println(USAGE); + } + + public void run() throws IOException { + try { + for (Filter f : filters) { + f.openFile(); + } + for (URI uri : filesystems) { + FileSystem fs; + Iterator roots; + switch (uri.getScheme()) { + case "jrt": + fs = FileSystems.getFileSystem(uri); + roots = Files.newDirectoryStream(fs.getPath("./modules")).iterator(); + break; + case "jar": + fs = FileSystems.newFileSystem(uri, new HashMap<>()); + roots = fs.getRootDirectories().iterator(); + break; + case "file": + fs = FileSystems.getDefault(); + roots = List.of(fs.getPath(uri.getPath())).iterator(); + break; + default: + throw new RuntimeException("TRI not supported: " + uri.toString()); + } + while (roots.hasNext()) { + Path root = roots.next(); + Files.walkFileTree(root, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.toString().endsWith(".class")) { + visitClass(root, file); + } + return FileVisitResult.CONTINUE; + } + }); + } + } + } finally { + for (Filter f : filters) { + f.closeFile(); + } + } + } + + private void visitClass(Path root, Path file) throws IOException { + try (InputStream in = Files.newInputStream(file)) { + ClassReader reader; + reader = new ClassReader(in); + String className = reader.getClassName();//.replace('/', '.'); + int lastDot = className.lastIndexOf('.'); + boolean included = + Utils.accept(includes, null, "/" + className, null); + boolean not_excluded = + Utils.accept(excludes, null, "/" + className, null); + if (included && not_excluded) { + ClassNode clazz = new ClassNode(); + reader.accept(clazz, 0); + for (MethodNode method : clazz.methods) { + for (Filter f : filters) { + if (f.filter.test(clazz, method)) { + f.add(clazz.name + "#" + method.name + method.desc); + } + } + } + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Exception while parsing file " + file + " from " + root, e); + } + } + + private enum Filter { + getters("simple getter", new Getters()), + setters("simple setter", new Setters()), + delegators("simple delegator", new Delegators()), + throwers("simple thrower", new Throwers()), + empty("empty methods", new EmptyMethods()); + private String description; + private BiPredicate filter; + private String outputFile; + private BufferedWriter output; + + Filter(String description, BiPredicate filter) { + this.description = description; + this.filter = filter; + } + + public void setOutputFile(String outputFile) { + this.outputFile = outputFile; + } + + public void openFile() throws IOException { + output = Files.newBufferedWriter(Paths.get(outputFile)); + output.write("#" + description); + output.newLine(); + output.flush(); + } + + public void closeFile() throws IOException { + if (outputFile != null) { + output.flush(); + output.close(); + } + } + + public void add(String s) throws IOException { + output.write(s); + output.newLine(); + output.flush(); + } + + static Filter get(String name) { + for(Filter f : values()) { + if(f.name().equals(name)) { + return f; + } + } + throw new RuntimeException("Unknown filter: " + name); + } + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/src/openjdk/jcov/filter/simplemethods/Setters.java 2018-05-03 08:02:31.155159929 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.function.BiPredicate; + +import static org.objectweb.asm.Opcodes.PUTFIELD; +import static org.objectweb.asm.Opcodes.PUTSTATIC; +import static org.objectweb.asm.Opcodes.RETURN; + +/** + * Identifies simple setters. A simple setter is a method obtaining a value with a "simple" code and + * assigning a field with it. + * @see Utils#isSimpleInstruction(int) + */ +public class Setters implements BiPredicate { + @Override + public boolean test(ClassNode clazz, MethodNode m) { + int index = 0; + int opCode = -1; + //skip all instructions allowed to get values + for(; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + if (!Utils.isSimpleInstruction(opCode)) { + break; + } + } + } + //that should be an instruction setting a field + if(opCode != PUTFIELD && opCode != PUTSTATIC) { + return false; + } + //find next + for(index++; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + if (!Utils.isSimpleInstruction(opCode)) { + break; + } + } + } + //and that should be a return + return opCode == RETURN; + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/src/openjdk/jcov/filter/simplemethods/Throwers.java 2018-05-03 08:02:31.391159929 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.function.BiPredicate; + +public class Throwers implements BiPredicate { + @Override + public boolean test(ClassNode cnode, MethodNode m) { + int index = 0; + int opCode = -1; + //find first instruction + for(; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + break; + } + } + //should be NEW + if(opCode != Opcodes.NEW) { + return false; + } + //next is DUP + index++; + opCode = m.instructions.get(index).getOpcode(); + if(opCode != Opcodes.DUP) { + return false; + } + //some more simple code + for(index++; index < m.instructions.size(); index++) { + opCode = m.instructions.get(index).getOpcode(); + if(opCode >=0) { + if (!Utils.isSimpleInstruction(opCode)) { + break; + } + } + } + //should be a constructor + if(opCode != Opcodes.INVOKESPECIAL) { + return false; + } + AbstractInsnNode node = m.instructions.get(index); + if(!(node instanceof MethodInsnNode)) { + return false; + } + if(!((MethodInsnNode)node).name.equals("")) { + return false; + } + index++; + opCode = m.instructions.get(index).getOpcode(); + return opCode == Opcodes.ATHROW; + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/src/openjdk/jcov/filter/simplemethods/Utils.java 2018-05-03 08:02:31.631159928 -0700 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnList; + +import java.util.Arrays; + +import static org.objectweb.asm.Opcodes.*; + +public class Utils { + private final static int[] SIMPLE_INSTRUCTIONS = new int[]{DUP, LDC, + BALOAD, CALOAD, AALOAD, DALOAD, FALOAD, IALOAD, SALOAD, + ACONST_NULL, + ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, ICONST_M1, + LCONST_0, LCONST_1, + FCONST_0, FCONST_1, FCONST_2, + DCONST_0, DCONST_1, + ALOAD, ILOAD, FLOAD, LLOAD, DLOAD, + GETFIELD, GETSTATIC, + BIPUSH, SIPUSH}; + private final static int[] INVOKE_INSTRUCTIONS = new int[]{INVOKEVIRTUAL, INVOKEINTERFACE, INVOKESTATIC, + INVOKEDYNAMIC, INVOKESPECIAL}; + private final static int[] RETURN_INSTRUCTIONS = new int[]{RETURN, ARETURN, IRETURN, FRETURN, LRETURN, DRETURN}; + + static { + Arrays.sort(SIMPLE_INSTRUCTIONS); + Arrays.sort(INVOKE_INSTRUCTIONS); + Arrays.sort(RETURN_INSTRUCTIONS); + } + + public static int countInstructions(InsnList list) { + int count = 0; + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getOpcode() > 0) count++; + } + return count; + } + public static AbstractInsnNode getInstruction(InsnList list, int index) { + return list.get(getInstructionIndex(list, index)); + } + public static int getInstructionIndex(InsnList list, int index) { + int count = 0; + for (int i = 0; i < list.size(); i++) { + if (list.get(i).getOpcode() > 0) { + if(count == index) return i; + count++; + } + } + throw new IllegalStateException(); + } + + /** + * An instruction is called "simple" if its only effect is to bring values onto the stack from stack, variables, fields, constants, etc. + * @param opCode + * @return + */ + public static boolean isSimpleInstruction(int opCode) { + return Arrays.binarySearch(SIMPLE_INSTRUCTIONS, opCode) >= 0; + } + public static boolean isReturnInstruction(int opCode) { + return Arrays.binarySearch(RETURN_INSTRUCTIONS, opCode) >= 0; + } + public static boolean isInvokeInstruction(int opCode) { + return Arrays.binarySearch(INVOKE_INSTRUCTIONS, opCode) >= 0; + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/test/openjdk/jcov/filter/simplemethods/DelegatorsTest.java 2018-05-03 08:02:31.867159927 -0700 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.testng.Assert.assertEquals; + +public class DelegatorsTest { + + Delegators any_name_delegator; + Delegators same_name_delegator; + ClassNode cls; + + @BeforeTest + public void init() throws IOException { + any_name_delegator = new Delegators(); + same_name_delegator = new Delegators(true); + cls = TestUtils.findTestClass(this.getClass()); + } + + @DataProvider(name = "cases") + public Object[][] cases() { + return new Object[][] { + {same_name_delegator, "foo(Ljava/lang/String;I)I", false, "Simple getter"}, + {same_name_delegator, "foo(I)I", true, "Using constants or parameters"}, + {same_name_delegator, "foo(J)I", true, "Using fields"}, + {same_name_delegator, "foo(Z)I", false, "Having condition"}, + {same_name_delegator, "foo(F)I", false, "Calling other methods"}, + {same_name_delegator, "bar(I)I", false, "Different method"}, + {any_name_delegator, "bar(I)I", true, "Different method"}, + {any_name_delegator, "empty()V", false, "Empty"} + }; + } + @Test(dataProvider = "cases") + public void test(Delegators delegator, String method, boolean result, String description) throws IOException { + assertEquals(delegator.test(cls, TestUtils.findTestMethod(cls, method)), result, description); + } + + //test data + int aField = 0; + static String aStaticField = null; + + int foo(String i, int j) {return j;} + + int foo(int j) { + return foo("", j); + } + + int foo(long s) { + return foo(aStaticField, aField); + } + + int foo(boolean s) { + if(s) + return foo(1); + else + return foo(0); + } + + int foo(float s) { + foo(null, 0); + return foo(null, 1); + } + + int bar(int j) { + return foo(null, j); + } + + void empty() { + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/test/openjdk/jcov/filter/simplemethods/EmptyMethodsTest.java 2018-05-03 08:02:32.111159926 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.testng.Assert.assertEquals; + +public class EmptyMethodsTest { + + EmptyMethods tested; + ClassNode cls; + + @BeforeTest + public void init() throws IOException { + tested = new EmptyMethods(); + cls = TestUtils.findTestClass(this.getClass()); + } + + @DataProvider(name = "cases") + public Object[][] cases() { + return new Object[][] { + {"empty()V", true, "Empty"}, + {"get()I", false, "A getter"}, + {"init()V", false, "init()"} + }; + } + @Test(dataProvider = "cases") + public void test(String method, boolean result, String description) { + assertEquals(tested.test(cls, TestUtils.findTestMethod(cls, method)), result, description); + } + + //test data + void empty() { + //doing nothing + } + int get(){return 0;} +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/test/openjdk/jcov/filter/simplemethods/GettersTest.java 2018-05-03 08:02:32.351159925 -0700 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static openjdk.jcov.filter.simplemethods.TestUtils.findTestMethod; +import static org.testng.Assert.assertEquals; + +public class GettersTest { + + Getters tested; + ClassNode cls; + + @BeforeTest + public void init() throws IOException { + tested = new Getters(); + cls = TestUtils.findTestClass(this.getClass()); + } + + @DataProvider(name = "cases") + public Object[][] cases() { + return new Object[][] { + {"getField()I", true, "A getter"}, + {"getConstant()Ljava/lang/Object;", true, "A constant getter"}, + {"getObjectField()Ljava/lang/String;", true, "An object getter"}, + {"getStaticConstant()I", true, "A constant getter using LDC"}, + {"getStaticField()Ljava/lang/Object;", true, "A static getter"}, + {"getFieldToString()Ljava/lang/String;", false, "Returning toString() of a field"}, + {"getOtherField()J", true, "A getter for other field object"}, + {"empty()V", false, "Empty"} + }; + } + @Test(dataProvider = "cases") + public void test(String method, boolean result, String description) { + assertEquals(tested.test(cls, findTestMethod(cls, method)), result, description); + } + + //test data + int aField; + String anObjectField; + static Object aStaticField; + static final int CONSTANT=7532957; + + class Other { + long aField; + } + + Other other; + + int getField() { + return aField; + } + + Object getConstant() { + return null; + } + + int getStaticConstant() { + return CONSTANT; + } + + long getOtherField() { + return other.aField; + } + + String getObjectField() { + return anObjectField; + } + + String getFieldToString() { + return anObjectField.toString(); + } + + static Object getStaticField() { + return aStaticField; + } + + void empty() { + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/test/openjdk/jcov/filter/simplemethods/MainTest.java 2018-05-03 08:02:32.587159924 -0700 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; + +public class MainTest { + + @Test + public void testJRTFS() throws IOException, URISyntaxException { + Path getters = Files.createTempFile("getters", ".lst"); + System.out.println("testJRTFS output file: " + getters.toAbsolutePath().toString()); + Scanner.main(new String[]{ + "--getters", getters.toAbsolutePath().toString(), + "--include", "java.util", + "jrt:/" + }); + assertTrue(Files.lines(getters).anyMatch(l -> l.equals("java/util/ArrayList#size()I"))); + Files.delete(getters); + } + + @Test + public void testDir() throws IOException, URISyntaxException { + String dir = System.getProperty("test.classes"); + Path delegators = Files.createTempFile("delegators", ".lst"); + System.out.println("testDir output file: " + delegators.toAbsolutePath().toString()); + Scanner.main(new String[]{ + "--delegators", delegators.toAbsolutePath().toString(), + "file://" + dir + }); + assertTrue(Files.lines(delegators).anyMatch(l -> + l.equals(DelegatorsTest.class.getName().replace('.', '/') + "#foo(I)I"))); + Files.delete(delegators); + } + + @Test + public void testJAR() throws IOException, URISyntaxException { + String jar = System.getProperty("test.jar"); + Path setters = Files.createTempFile("setters", ".lst"); + System.out.println("testJAR output file: " + setters.toAbsolutePath().toString()); + Scanner.main(new String[]{ + "--setters", setters.toAbsolutePath().toString(), + "jar:file:" + jar}); + assertTrue(Files.lines(setters).anyMatch(l -> + l.equals(SettersTest.class.getName().replace('.', '/') + "#setField(I)V"))); + } + + @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Unknown filter.*") + public void testWrongFilter() throws IOException, URISyntaxException { + Scanner.main(new String[]{ + "--a-non-existing-filter", "a_file", + "file:///a/path"}); + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/test/openjdk/jcov/filter/simplemethods/SettersTest.java 2018-05-03 08:02:32.823159923 -0700 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.testng.Assert.assertEquals; + +public class SettersTest { + + Setters tested; + ClassNode cls; + + @BeforeTest + public void init() throws IOException { + tested = new Setters(); + cls = TestUtils.findTestClass(this.getClass()); + } + + @DataProvider(name = "cases") + public Object[][] cases() { + return new Object[][] { + {"setObjectField(Ljava/lang/String;)V", true, "An object setter"}, + {"setFieldToToString(Ljava/lang/Object;)V", false, "Assigning toString() to a field"}, + {"setField(I)V", true, "A setter"}, + {"setStaticField(Ljava/lang/Object;)V", true, "A static setter"}, + {"setOtherField(J)V", true, "A setter for other field object"}, + {"empty()V", false, "Empty"} + }; + } + @Test(dataProvider = "cases") + public void test(String method, boolean result, String description) { + assertEquals(tested.test(cls, TestUtils.findTestMethod(cls, method)), result, description); + } + + //test data + int aField; + String anObjectField; + static Object aStaticField; + + class Other { + long aField; + } + + Other other; + + void setField(int v) { aField = v; } + + void setOtherField(long v) { + other.aField = v; + } + + void setObjectField(String v) { + anObjectField = v; + } + + void setFieldToToString(Object v) { + anObjectField = v.toString(); + } + + static void setStaticField(Object v) { + aStaticField = v; + } + + void empty() { + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/test/openjdk/jcov/filter/simplemethods/TestUtils.java 2018-05-03 08:02:33.063159922 -0700 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import java.io.IOException; + +public class TestUtils { + static ClassNode findTestClass(Class cls) throws IOException { + ClassNode result = new ClassNode(); + ClassReader reader = new ClassReader(cls.getClassLoader(). + getResourceAsStream(cls.getName().replace('.','/') + ".class")); + reader.accept(result, 0); + return result; + } + + static MethodNode findTestMethod(ClassNode cls, String methodSig) { + for(MethodNode m: cls.methods) { + if((m.name + m.desc).equals(methodSig)) { + return m; + } + } + throw new IllegalStateException("Method does not exist: " + methodSig + " in class " + cls.name); + } +} --- /dev/null 2018-05-03 07:48:58.155163031 -0700 +++ new/plugins/simple_methods_anc/test/openjdk/jcov/filter/simplemethods/ThrowersTest.java 2018-05-03 08:02:33.299159921 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018, 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 openjdk.jcov.filter.simplemethods; + +import org.objectweb.asm.tree.ClassNode; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static openjdk.jcov.filter.simplemethods.TestUtils.findTestMethod; +import static org.testng.Assert.assertEquals; + +public class ThrowersTest { + + Throwers tested; + ClassNode cls; + + @BeforeTest + public void init() throws IOException { + tested = new Throwers(); + cls = TestUtils.findTestClass(this.getClass()); + } + + @DataProvider(name = "cases") + public Object[][] cases() { + return new Object[][] { + {"notImplemented()V", true, "A thrower"}, + {"compoundMessage()V", false, "A thrower with a computed message"}, + {"empty()V", false, "Empty"} + }; + } + @Test(dataProvider = "cases") + public void test(String method, boolean result, String description) { + assertEquals(tested.test(cls, findTestMethod(cls, method)), result, description); + } + + //test data + String message = "not "; + void empty() {} + void notImplemented(){throw new RuntimeException(message);} + void compoundMessage(){throw new RuntimeException(message + "implemented");} +}