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;
+ }
+}