You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/11/20 19:31:14 UTC

[commons-bcel] branch master updated: Code coverage and bug fixes for bcelifier (#171)

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git


The following commit(s) were added to refs/heads/master by this push:
     new c96eb974 Code coverage and bug fixes for bcelifier (#171)
c96eb974 is described below

commit c96eb9743a970d3686a858b3e3a56a0ebbd18ea2
Author: nbauma109 <nb...@users.noreply.github.com>
AuthorDate: Sun Nov 20 20:31:09 2022 +0100

    Code coverage and bug fixes for bcelifier (#171)
    
    * Code coverage bcelifier
    
    * Clean up Javadoc
    
    * Clean up Javadoc
    
    * Use constants instead of magic numbers
    
    * Keep constructors in AB order
    
    * Fix indentation, no tabs, spaces
    
    * Javadoc
    
    * A little less verbose
    
    Place new method in AB order.
    
    * Remove unused import
    
    Co-authored-by: Gary Gregory <ga...@users.noreply.github.com>
---
 .github/workflows/coverage.yml                     |   2 +-
 pom.xml                                            |  23 +++++
 .../java/org/apache/bcel/classfile/JavaClass.java  |  18 +++-
 .../apache/bcel/generic/InstructionFactory.java    |   7 +-
 src/main/java/org/apache/bcel/generic/LDC.java     |   2 +-
 src/main/java/org/apache/bcel/generic/PUSH.java    |  15 +++
 .../java/org/apache/bcel/util/BCELFactory.java     |  32 ++++++-
 src/main/java/org/apache/bcel/util/BCELifier.java  |  34 ++++++-
 .../java/org/apache/bcel/AbstractTestCase.java     |  12 +++
 .../java/org/apache/bcel/HelloWorldCreator.java    |  88 ++++++++++++++++++
 .../apache/bcel/generic/EmptyVisitorTestCase.java  |   1 +
 .../org/apache/bcel/util/BCELifierTestCase.java    | 102 ++++++++++++++++-----
 src/test/resources/Java4Example.class              | Bin 0 -> 1738 bytes
 src/test/resources/Java4Example.java               |  70 ++++++++++++++
 src/test/resources/Java8Example2.class             | Bin 0 -> 4061 bytes
 src/test/resources/Java8Example2.java              |  89 ++++++++++++++++++
 16 files changed, 460 insertions(+), 35 deletions(-)

diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index d8e23279..71db7bfd 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -44,7 +44,7 @@ jobs:
         distribution: 'temurin'
         java-version: ${{ matrix.java }}
     - name: Build with Maven
-      run: mvn -V test jacoco:report --file pom.xml --no-transfer-progress
+      run: mvn -V install jacoco:report --file pom.xml --no-transfer-progress
 
     - name: Upload coverage to Codecov
       uses: codecov/codecov-action@v3
diff --git a/pom.xml b/pom.xml
index e2110495..85acf4e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -362,6 +362,29 @@
           </rulesets>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>merge-all-jacoco</id>
+            <goals>
+              <goal>merge</goal>
+            </goals>
+            <phase>install</phase>
+            <configuration> 
+              <fileSets>
+                <fileSet>
+                  <directory>${project.build.directory}</directory>
+                  <includes>
+                    <include>*.exec</include>
+                  </includes>
+                </fileSet>
+              </fileSets>
+            </configuration> 
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
 
diff --git a/src/main/java/org/apache/bcel/classfile/JavaClass.java b/src/main/java/org/apache/bcel/classfile/JavaClass.java
index 5aa443ab..4094a60a 100644
--- a/src/main/java/org/apache/bcel/classfile/JavaClass.java
+++ b/src/main/java/org/apache/bcel/classfile/JavaClass.java
@@ -571,12 +571,28 @@ public class JavaClass extends AccessFlags implements Cloneable, Node, Comparabl
     }
 
     /**
-     * @return absolute path to file where this class was read from
+     * @return file name where this class was read from
      */
     public String getSourceFileName() {
         return sourceFileName;
     }
 
+    /**
+     * Gets the source file path including the package path.
+     *
+     * @return path to original source file of parsed class, relative to original source directory.
+     * @since 6.7.0
+     */
+    public String getSourceFilePath() {
+        final StringBuilder outFileName = new StringBuilder();
+        if (!packageName.isEmpty()) {
+            outFileName.append(packageName.replace('.', '/'));
+            outFileName.append('/');
+        }
+        outFileName.append(sourceFileName);
+        return outFileName.toString();
+    }
+
     /**
      * @return the superclass for this JavaClass object, or null if this is java.lang.Object
      * @throws ClassNotFoundException if the superclass can't be found
diff --git a/src/main/java/org/apache/bcel/generic/InstructionFactory.java b/src/main/java/org/apache/bcel/generic/InstructionFactory.java
index 9a30335d..69e25d5d 100644
--- a/src/main/java/org/apache/bcel/generic/InstructionFactory.java
+++ b/src/main/java/org/apache/bcel/generic/InstructionFactory.java
@@ -702,11 +702,10 @@ public class InstructionFactory implements InstructionConstants {
      */
     public InstructionList createPrintln(final String s) {
         final InstructionList il = new InstructionList();
-        final int out = cp.addFieldref("java.lang.System", "out", "Ljava/io/PrintStream;");
-        final int println = cp.addMethodref("java.io.PrintStream", "println", "(Ljava/lang/String;)V");
-        il.append(new GETSTATIC(out));
+        il.append(createGetStatic("java.lang.System", "out", Type.getType("Ljava/io/PrintStream;")));
         il.append(new PUSH(cp, s));
-        il.append(new INVOKEVIRTUAL(println));
+        MethodObject methodObject = new MethodObject("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.getType("Ljava/lang/String;") });
+        il.append(createInvoke(methodObject, Const.INVOKEVIRTUAL));
         return il;
     }
 
diff --git a/src/main/java/org/apache/bcel/generic/LDC.java b/src/main/java/org/apache/bcel/generic/LDC.java
index e069346e..85f1c7c2 100644
--- a/src/main/java/org/apache/bcel/generic/LDC.java
+++ b/src/main/java/org/apache/bcel/generic/LDC.java
@@ -108,7 +108,7 @@ public class LDC extends CPInstruction implements PushInstruction, ExceptionThro
         case org.apache.bcel.Const.CONSTANT_Class:
             final int nameIndex = ((org.apache.bcel.classfile.ConstantClass) c).getNameIndex();
             c = cpg.getConstantPool().getConstant(nameIndex);
-            return new ObjectType(((org.apache.bcel.classfile.ConstantUtf8) c).getBytes());
+            return Type.getType(((org.apache.bcel.classfile.ConstantUtf8) c).getBytes());
         default: // Never reached
             throw new IllegalArgumentException("Unknown or invalid constant type at " + super.getIndex());
         }
diff --git a/src/main/java/org/apache/bcel/generic/PUSH.java b/src/main/java/org/apache/bcel/generic/PUSH.java
index b29947dd..395002b0 100644
--- a/src/main/java/org/apache/bcel/generic/PUSH.java
+++ b/src/main/java/org/apache/bcel/generic/PUSH.java
@@ -28,6 +28,21 @@ public final class PUSH implements CompoundInstruction, VariableLengthInstructio
     private final Instruction instruction;
 
     /**
+     * Pushes an array type constant, for example {@code int[].class}, {@code String[].class}, and so on.
+     *
+     * @param cp generated constant pool.
+     * @param value to be pushed.
+     * @since 6.7.0
+     */
+    public PUSH(final ConstantPoolGen cp, final ArrayType value) {
+        if (value == null) {
+            instruction = InstructionConst.ACONST_NULL;
+        } else {
+            instruction = new LDC(cp.addArrayClass(value));
+        }
+    }
+
+   /**
      * @param cp Constant pool
      * @param value to be pushed
      */
diff --git a/src/main/java/org/apache/bcel/util/BCELFactory.java b/src/main/java/org/apache/bcel/util/BCELFactory.java
index 84f25d5c..554e3b13 100644
--- a/src/main/java/org/apache/bcel/util/BCELFactory.java
+++ b/src/main/java/org/apache/bcel/util/BCELFactory.java
@@ -85,12 +85,35 @@ class BCELFactory extends EmptyVisitor {
         } else if (value instanceof Character) {
             embed = "(char)0x" + Integer.toHexString(((Character) value).charValue());
         } else if (value instanceof Float) {
-            embed += "f";
+            final Float f = (Float) value;
+            if (Float.isNaN(f)) {
+                embed = "Float.NaN";
+            } else if (f == Float.POSITIVE_INFINITY) {
+                embed = "Float.POSITIVE_INFINITY";
+            } else if (f == Float.NEGATIVE_INFINITY) {
+                embed = "Float.NEGATIVE_INFINITY";
+            } else {
+                embed += "f";
+            }
+        }  else if (value instanceof Double) {
+            final Double d = (Double) value;
+            if (Double.isNaN(d)) {
+                embed = "Double.NaN";
+            } else if (d == Double.POSITIVE_INFINITY) {
+                embed = "Double.POSITIVE_INFINITY";
+            } else if (d == Double.NEGATIVE_INFINITY) {
+                embed = "Double.NEGATIVE_INFINITY";
+            } else {
+                embed += "d";
+            }
         } else if (value instanceof Long) {
             embed += "L";
         } else if (value instanceof ObjectType) {
             final ObjectType ot = (ObjectType) value;
             embed = "new ObjectType(\"" + ot.getClassName() + "\")";
+        } else if (value instanceof ArrayType) {
+            final ArrayType at = (ArrayType) value;
+            embed = "new ArrayType(" + BCELifier.printType(at.getBasicType()) + ", " + at.getDimensions() + ")";
         }
 
         printWriter.println("il.append(new PUSH(_cp, " + embed + "));");
@@ -164,11 +187,12 @@ class BCELFactory extends EmptyVisitor {
         case Const.MULTIANEWARRAY:
             dim = ((MULTIANEWARRAY) i).getDimensions();
             //$FALL-THROUGH$
-        case Const.ANEWARRAY:
         case Const.NEWARRAY:
             if (type instanceof ArrayType) {
                 type = ((ArrayType) type).getBasicType();
             }
+            //$FALL-THROUGH$
+        case Const.ANEWARRAY:
             printWriter.println("il.append(_factory.createNewArray(" + BCELifier.printType(type) + ", (short) " + dim + "));");
             break;
         default:
@@ -252,7 +276,7 @@ class BCELFactory extends EmptyVisitor {
     @Override
     public void visitINSTANCEOF(final INSTANCEOF i) {
         final Type type = i.getType(constantPoolGen);
-        printWriter.println("il.append(new INSTANCEOF(_cp.addClass(" + BCELifier.printType(type) + ")));");
+        printWriter.println("il.append(_factory.createInstanceOf(" + BCELifier.printType(type) + "));");
     }
 
     private boolean visitInstruction(final Instruction i) {
@@ -299,7 +323,7 @@ class BCELFactory extends EmptyVisitor {
 
     @Override
     public void visitRET(final RET i) {
-        printWriter.println("il.append(new RET(" + i.getIndex() + ")));");
+        printWriter.println("il.append(new RET(" + i.getIndex() + "));");
     }
 
     @Override
diff --git a/src/main/java/org/apache/bcel/util/BCELifier.java b/src/main/java/org/apache/bcel/util/BCELifier.java
index 54072967..f719146e 100644
--- a/src/main/java/org/apache/bcel/util/BCELifier.java
+++ b/src/main/java/org/apache/bcel/util/BCELifier.java
@@ -27,6 +27,7 @@ import org.apache.bcel.Const;
 import org.apache.bcel.Repository;
 import org.apache.bcel.classfile.ClassParser;
 import org.apache.bcel.classfile.ConstantValue;
+import org.apache.bcel.classfile.ExceptionTable;
 import org.apache.bcel.classfile.Field;
 import org.apache.bcel.classfile.JavaClass;
 import org.apache.bcel.classfile.Method;
@@ -211,7 +212,27 @@ public class BCELifier extends org.apache.bcel.classfile.EmptyVisitor {
             "    field = new FieldGen(" + printFlags(field.getAccessFlags()) + ", " + printType(field.getSignature()) + ", \"" + field.getName() + "\", _cp);");
         final ConstantValue cv = field.getConstantValue();
         if (cv != null) {
-            printWriter.println("    field.setInitValue(" + cv + ")");
+            printWriter.print("    field.setInitValue(");
+            if (field.getType() == Type.CHAR) {
+                printWriter.print("(char)");
+            }
+            if (field.getType() == Type.SHORT) {
+                printWriter.print("(short)");
+            }
+            if (field.getType() == Type.BYTE) {
+                printWriter.print("(byte)");
+            }
+            printWriter.print(cv);
+            if (field.getType() == Type.LONG) {
+                printWriter.print("L");
+            }
+            if (field.getType() == Type.FLOAT) {
+                printWriter.print("F");
+            }
+            if (field.getType() == Type.DOUBLE) {
+                printWriter.print("D");
+            }
+            printWriter.println(");");
         }
         printWriter.println("    _cg.addField(field.getField());");
     }
@@ -222,7 +243,7 @@ public class BCELifier extends org.apache.bcel.classfile.EmptyVisitor {
         final String superName = clazz.getSuperclassName();
         final String packageName = clazz.getPackageName();
         final String inter = Utility.printArray(clazz.getInterfaceNames(), false, true);
-        if (StringUtils.isNotEmpty(inter)) {
+        if (StringUtils.isNotEmpty(packageName)) {
             className = className.substring(packageName.length() + 1);
             printWriter.println("package " + packageName + ";");
             printWriter.println();
@@ -276,6 +297,15 @@ public class BCELifier extends org.apache.bcel.classfile.EmptyVisitor {
         printWriter.println("    MethodGen method = new MethodGen(" + printFlags(method.getAccessFlags(), FLAGS.METHOD) + ", " + printType(mg.getReturnType())
             + ", " + printArgumentTypes(mg.getArgumentTypes()) + ", " + "new String[] { " + Utility.printArray(mg.getArgumentNames(), false, true) + " }, \""
             + method.getName() + "\", \"" + clazz.getClassName() + "\", il, _cp);");
+        final ExceptionTable exceptionTable = method.getExceptionTable();
+        if (exceptionTable != null) {
+            final String[] exceptionNames = exceptionTable.getExceptionNames();
+            for (final String exceptionName : exceptionNames) {
+                printWriter.print("    method.addException(\"");
+                printWriter.print(exceptionName);
+                printWriter.println("\");");
+            }
+        }
         printWriter.println();
         final BCELFactory factory = new BCELFactory(mg, printWriter);
         factory.start();
diff --git a/src/test/java/org/apache/bcel/AbstractTestCase.java b/src/test/java/org/apache/bcel/AbstractTestCase.java
index 5e3021bc..b8b150a6 100644
--- a/src/test/java/org/apache/bcel/AbstractTestCase.java
+++ b/src/test/java/org/apache/bcel/AbstractTestCase.java
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.lang.management.ManagementFactory;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -138,6 +139,16 @@ public abstract class AbstractTestCase {
         return chosenAttrsList.toArray(Attribute.EMPTY_ARRAY);
     }
 
+    /**
+     * Gets the javaagent input argument of the current running JVM.
+     *
+     * @return javaagent input argument of the current running JVM, null if not set.
+     */
+    protected String getJavaAgent() {
+        final List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
+        return jvmArgs.stream().filter(arg -> arg.startsWith("-javaagent")).findFirst().orElse(null);
+    }
+
     protected Method getMethod(final JavaClass cl, final String methodname) {
         for (final Method m : cl.getMethods()) {
             if (m.getName().equals(methodname)) {
@@ -182,4 +193,5 @@ public abstract class AbstractTestCase {
         }
         return b;
     }
+
 }
diff --git a/src/test/java/org/apache/bcel/HelloWorldCreator.java b/src/test/java/org/apache/bcel/HelloWorldCreator.java
new file mode 100644
index 00000000..678369a6
--- /dev/null
+++ b/src/test/java/org/apache/bcel/HelloWorldCreator.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.bcel;
+
+import org.apache.bcel.generic.ArrayType;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.InstructionFactory;
+import org.apache.bcel.generic.InstructionList;
+import org.apache.bcel.generic.MethodGen;
+import org.apache.bcel.generic.Type;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class HelloWorldCreator {
+    private static final String ORG_APACHE_BCEL_HELLO_WORLD = "org.apache.bcel.HelloWorld";
+    private InstructionFactory factory;
+    private ConstantPoolGen cp;
+    private ClassGen cg;
+
+    public HelloWorldCreator() {
+        cg = new ClassGen(ORG_APACHE_BCEL_HELLO_WORLD, "java.lang.Object", "HelloWorld.java", Const.ACC_PUBLIC | Const.ACC_SUPER, new String[] {});
+        cg.setMajor(Const.MAJOR_1_8);
+        cg.setMinor(Const.MINOR_1_8);
+
+        cp = cg.getConstantPool();
+        factory = new InstructionFactory(cg, cp);
+    }
+
+    public void create(OutputStream out) throws IOException {
+        createConstructor();
+        createMainMethod();
+        cg.getJavaClass().dump(out);
+    }
+
+    private void createConstructor() {
+        InstructionList il = new InstructionList();
+        MethodGen method = new MethodGen(Const.ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[] {}, "<init>", ORG_APACHE_BCEL_HELLO_WORLD, il, cp);
+
+        il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
+        il.append(factory.createInvoke("java.lang.Object", "<init>", Type.VOID, Type.NO_ARGS, Const.INVOKESPECIAL));
+        il.append(InstructionFactory.createReturn(Type.VOID));
+        method.setMaxStack();
+        method.setMaxLocals();
+        cg.addMethod(method.getMethod());
+        il.dispose();
+    }
+
+    private void createMainMethod() {
+        InstructionList il = factory.createPrintln("Hello World!");
+        MethodGen method = new MethodGen(Const.ACC_PUBLIC | Const.ACC_STATIC, Type.VOID, new Type[] { new ArrayType(Type.STRING, 1) }, new String[] { "arg0" }, "main",
+                ORG_APACHE_BCEL_HELLO_WORLD, il, cp);
+
+        il.append(InstructionFactory.createReturn(Type.VOID));
+        method.setMaxStack();
+        method.setMaxLocals();
+        cg.addMethod(method.getMethod());
+        il.dispose();
+    }
+
+    public static void main(String[] args) throws Exception {
+        org.apache.bcel.HelloWorldCreator creator = new org.apache.bcel.HelloWorldCreator();
+        Path path = Paths.get("target/test-classes/org/apache/bcel/HelloWorld.class");
+        Files.deleteIfExists(path);
+        try (OutputStream out = Files.newOutputStream(path)) {
+            creator.create(out);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/bcel/generic/EmptyVisitorTestCase.java b/src/test/java/org/apache/bcel/generic/EmptyVisitorTestCase.java
index d999851e..36ffcd5d 100644
--- a/src/test/java/org/apache/bcel/generic/EmptyVisitorTestCase.java
+++ b/src/test/java/org/apache/bcel/generic/EmptyVisitorTestCase.java
@@ -46,6 +46,7 @@ class EmptyVisitorTestCase {
         "java.lang.invoke.LambdaForm",                   // contains instruction MULTIANEWARRAY,
         "java.nio.Bits",                                 // contains instruction POP2,
         "java.nio.HeapShortBuffer",                      // contains instruction SALOAD, SASTORE
+        "Java8Example2",                                 // contains instruction FREM
         "java.awt.GradientPaintContext",                 // contains instruction DREM
         "java.util.concurrent.atomic.DoubleAccumulator", // contains instruction DUP2_X1
         "java.util.Hashtable",                           // contains instruction FNEG
diff --git a/src/test/java/org/apache/bcel/util/BCELifierTestCase.java b/src/test/java/org/apache/bcel/util/BCELifierTestCase.java
index ffef4ba4..0d3345a5 100644
--- a/src/test/java/org/apache/bcel/util/BCELifierTestCase.java
+++ b/src/test/java/org/apache/bcel/util/BCELifierTestCase.java
@@ -22,14 +22,22 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import java.io.BufferedInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
 import java.io.OutputStream;
-
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import org.apache.bcel.AbstractTestCase;
+import org.apache.bcel.HelloWorldCreator;
 import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Utility;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+public class BCELifierTestCase extends AbstractTestCase {
 
-public class BCELifierTestCase {
+    private static final String EOL = System.lineSeparator();
+    public static final String CLASSPATH = System.getProperty("java.class.path") + File.pathSeparator + ".";
 
     // Canonicalise the javap output so it compares better
     private String canonHashRef(String input) {
@@ -43,38 +51,49 @@ public class BCELifierTestCase {
         // System.err.println(java.util.Arrays.toString(args));
         final ProcessBuilder pb = new ProcessBuilder(args);
         pb.directory(workDir);
+        pb.redirectErrorStream(true);
         final Process proc = pb.start();
-        try (BufferedInputStream is = new BufferedInputStream(proc.getInputStream()); InputStream es = proc.getErrorStream()) {
-            proc.waitFor();
+        try (BufferedInputStream is = new BufferedInputStream(proc.getInputStream())) {
             final byte[] buff = new byte[2048];
             int len;
-            while ((len = es.read(buff)) != -1) {
-                System.err.print(new String(buff, 0, len));
-            }
 
             final StringBuilder sb = new StringBuilder();
             while ((len = is.read(buff)) != -1) {
                 sb.append(new String(buff, 0, len));
             }
-            return sb.toString();
+            String output = sb.toString();
+            assertEquals(0, proc.waitFor(), output);
+            return output;
         }
     }
 
     private void testClassOnPath(final String javaClassFileName) throws Exception {
-        // Get javap of the input class
-        final String initial = exec(null, "javap", "-p", "-c", javaClassFileName);
-
         final File workDir = new File("target");
         final File infile = new File(javaClassFileName);
         final JavaClass javaClass = BCELifier.getJavaClass(infile.getName().replace(JavaClass.EXTENSION, ""));
         assertNotNull(javaClass);
-        final File outfile = new File(workDir, infile.getName().replace(JavaClass.EXTENSION, "Creator.java"));
-        try (FileOutputStream fos = new FileOutputStream(outfile)) {
-            final BCELifier bcelifier = new BCELifier(javaClass, fos);
-            bcelifier.start();
+
+        // Get javap of the input class
+        final String initial = exec(null, "javap", "-cp", CLASSPATH, "-p", "-c", javaClass.getClassName());
+        String outFileName = javaClass.getSourceFilePath().replace(".java", "Creator.java");
+        final File outfile = new File(workDir, outFileName);
+        Files.createDirectories(outfile.getParentFile().toPath());
+        final String javaAgent = getJavaAgent();
+        String creatorSourceContents = null;
+        if (javaAgent == null) {
+            creatorSourceContents = exec(workDir, "java", "-cp", CLASSPATH, "org.apache.bcel.util.BCELifier", javaClass.getClassName());
+        } else {
+            String runtimeExecJavaAgent = javaAgent.replace("jacoco.exec", "jacoco_" + infile.getName() + ".exec");
+            creatorSourceContents = exec(workDir, "java", runtimeExecJavaAgent, "-cp", CLASSPATH, "org.apache.bcel.util.BCELifier", javaClass.getClassName());
+        }
+        Files.write(outfile.toPath(), creatorSourceContents.getBytes(StandardCharsets.UTF_8));
+        assertEquals("", exec(workDir, "javac", "-cp", CLASSPATH, outFileName.toString()));
+        if (javaAgent == null) {
+            assertEquals("", exec(workDir, "java", "-cp", CLASSPATH, javaClass.getClassName() + "Creator"));
+        } else {
+            String runtimeExecJavaAgent = javaAgent.replace("jacoco.exec", "jacoco_" + Utility.pathToPackage(outFileName) + ".exec");
+            assertEquals("", exec(workDir, "java", runtimeExecJavaAgent, "-cp", CLASSPATH, javaClass.getClassName() + "Creator"));
         }
-        exec(workDir, "javac", "-cp", "classes", outfile.getName(), "-source", "1.8", "-target", "1.8");
-        exec(workDir, "java", "-cp", "." + File.pathSeparator + "classes", outfile.getName().replace(".java", ""));
         final String output = exec(workDir, "javap", "-p", "-c", infile.getName());
         assertEquals(canonHashRef(initial), canonHashRef(output));
     }
@@ -83,9 +102,22 @@ public class BCELifierTestCase {
      * Dump a class using "javap" and compare with the same class recreated using BCELifier, "javac", "java" and dumped with
      * "javap" TODO: detect if JDK present and skip test if not
      */
-    @Test
-    public void testJavapCompare() throws Exception {
-        testClassOnPath("target/test-classes/Java8Example.class");
+    @ParameterizedTest
+    @ValueSource(strings = {
+    // @formatter:off
+        "org.apache.commons.lang.math.Fraction.class",
+        "org.apache.commons.lang.exception.NestableDelegate.class",
+        "org.apache.commons.lang.builder.CompareToBuilder.class",
+        "org.apache.commons.lang.builder.ToStringBuilder.class",
+        "org.apache.commons.lang.SerializationUtils.class",
+        "org.apache.commons.lang.ArrayUtils.class",
+        "target/test-classes/Java8Example.class",
+        "target/test-classes/Java8Example2.class",
+        "target/test-classes/Java4Example.class"
+    // @formatter:on
+    })
+    public void testJavapCompare(final String pathToClass) throws Exception {
+        testClassOnPath(pathToClass);
     }
 
     @Test
@@ -97,4 +129,30 @@ public class BCELifierTestCase {
         bcelifier.start();
     }
 
+    @Test
+    public void testMainNoArg() throws Exception {
+        final PrintStream sysout = System.out;
+        try {
+            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+            System.setOut(new PrintStream(out));
+            BCELifier.main(new String[0]);
+            final String outputNoArgs = new String(out.toByteArray());
+            assertEquals("Usage: BCELifier className" + EOL + "\tThe class must exist on the classpath" + EOL, outputNoArgs);
+        } finally {
+            System.setOut(sysout);
+        }
+    }
+
+    @Test
+    public void testHelloWorld() throws Exception {
+        HelloWorldCreator.main(new String[] {});
+        final File workDir = new File("target");
+        final String javaAgent = getJavaAgent();
+        if (javaAgent == null) {
+            assertEquals("Hello World!" + EOL, exec(workDir, "java", "-cp", CLASSPATH, "org.apache.bcel.HelloWorld"));
+        } else {
+            String runtimeExecJavaAgent = javaAgent.replace("jacoco.exec", "jacoco_org.apache.bcel.HelloWorld.exec");
+            assertEquals("Hello World!" + EOL, exec(workDir, "java", runtimeExecJavaAgent, "-cp", CLASSPATH, "org.apache.bcel.HelloWorld"));
+        }
+    }
 }
diff --git a/src/test/resources/Java4Example.class b/src/test/resources/Java4Example.class
new file mode 100644
index 00000000..2f905c63
Binary files /dev/null and b/src/test/resources/Java4Example.class differ
diff --git a/src/test/resources/Java4Example.java b/src/test/resources/Java4Example.java
new file mode 100644
index 00000000..3173b26c
--- /dev/null
+++ b/src/test/resources/Java4Example.java
@@ -0,0 +1,70 @@
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.*;
+
+public class Java4Example {
+
+    /*
+     * Example for RET instruction
+     */
+    public static void serialize(Serializable obj, OutputStream outputStream) throws IOException {
+        if (outputStream == null) {
+            throw new IllegalArgumentException("The OutputStream must not be null");
+        }
+        ObjectOutputStream out = null;
+        try {
+            // stream closed in the finally
+            out = new ObjectOutputStream(outputStream);
+            out.writeObject(obj);
+            
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+            } catch (Exception ex) {
+                // ignore
+            }
+        }
+    }
+
+    void tableSwitch(int inputValue) {
+        switch (inputValue) {
+            case 1:
+                System.out.println("One");
+                break;
+            case 2:
+                System.out.println("Two");
+                break;
+            case 3:
+                System.out.println("Three");
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+    }
+
+    void lookupSwitch(int inputValue) {
+        switch (inputValue) {
+            case 1:
+                System.out.println("One");
+                break;
+            case 1000:
+                System.out.println("One thousand");
+                break;
+            case 1000000:
+                System.out.println("One million");
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < args.length; i++) {
+            System.out.println(args[i]);
+        }
+    }
+}
diff --git a/src/test/resources/Java8Example2.class b/src/test/resources/Java8Example2.class
new file mode 100644
index 00000000..82ec0d15
Binary files /dev/null and b/src/test/resources/Java8Example2.class differ
diff --git a/src/test/resources/Java8Example2.java b/src/test/resources/Java8Example2.java
new file mode 100644
index 00000000..eacdd8b0
--- /dev/null
+++ b/src/test/resources/Java8Example2.java
@@ -0,0 +1,89 @@
+import java.io.Serializable;
+import java.util.*;
+import java.util.stream.*;
+
+public class Java8Example2 implements java.io.Serializable, Runnable {
+
+    private static final long serialVersionUID = 1234567891234567891L;
+    public static final float E = 2.7182818284590452354f;
+    public static final double PI = 3.14159265358979323846;
+    public static final char DOT = '.';
+    public static final short PORT = 22;
+    public static final int ICONST_M1 = -1;
+    public static final int ICONST_0 = 0;
+    public static final int ICONST_1 = 1;
+    public static final int ICONST_2 = 2;
+    public static final int ICONST_3 = 3;
+    public static final int ICONST_4 = 4;
+    public static final int ICONST_5 = 5;
+    public static final long LCONST_0 = 0;
+    public static final long LCONST_1 = 1;
+    public static final float FCONST_0 = 0f;
+    public static final float FCONST_1 = 1f;
+    public static final float FCONST_2 = 2f;
+    public static final double DCONST_0 = 0d;
+    public static final double DCONST_1 = 1d;
+    public static final Float FNULL = null;
+    public static final Double DNULL = null;
+    public static final Long LNULL = null;
+
+    public static final byte INVOKESTATIC = (byte) 184;
+    public static final int[][] MULTI_ARRAY = {{0}, {1}};
+    public static final int[][] MULTI_ARRAY2 = new int[2][2];
+
+    private static transient volatile StringBuffer STRING_BUFFER = new StringBuffer();
+
+    public synchronized void run() {
+        try {
+            hello("Hello", "World", "hi");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    void check(String... args) throws Exception {
+        if (!(args instanceof String[]) || args.getClass() != String[].class) {
+            throw new Exception();
+        }
+    }
+    
+    void hello(String... args) throws Exception {
+        check(args);
+        Arrays.stream(args).forEach(System.out::println);
+        STRING_BUFFER.append(ICONST_M1);
+        STRING_BUFFER.append(ICONST_0);
+        STRING_BUFFER.append(ICONST_1);
+        STRING_BUFFER.append(ICONST_2);
+        STRING_BUFFER.append(ICONST_3);
+        STRING_BUFFER.append(ICONST_4);
+        STRING_BUFFER.append(ICONST_5);
+        STRING_BUFFER.append(LCONST_0);
+        STRING_BUFFER.append(LCONST_1);
+        STRING_BUFFER.append(FCONST_0);
+        STRING_BUFFER.append(FCONST_1);
+        STRING_BUFFER.append(FCONST_2);
+        STRING_BUFFER.append(DCONST_0);
+        STRING_BUFFER.append(DCONST_1);
+        STRING_BUFFER.append(FNULL);
+        STRING_BUFFER.append(DNULL);
+        STRING_BUFFER.append(LNULL);
+        STRING_BUFFER.append(DOT);
+        STRING_BUFFER.append(INVOKESTATIC);
+        STRING_BUFFER.append(frem(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
+        STRING_BUFFER.append(frem(Float.NaN, Float.NaN));
+        STRING_BUFFER.append(frem(E, E));
+        STRING_BUFFER.append(drem(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+        STRING_BUFFER.append(drem(Double.NaN, Double.NaN));
+        STRING_BUFFER.append(drem(PI, PI));
+        STRING_BUFFER.append(serialVersionUID);
+        STRING_BUFFER.append(Arrays.deepToString(MULTI_ARRAY));
+    }
+
+    double drem(double a, double b) {
+        return a % b;
+    }
+    
+    float frem(float a, float b) {
+        return a % b;
+    }
+}