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 d0996540 [Bcelifier] stackmap support to pass JDK verifier (#177)
d0996540 is described below

commit d0996540e2574d6c8c537ee6f493d5f35e25b206
Author: nbauma109 <nbauma...@users.noreply.github.com>
AuthorDate: Tue Apr 25 19:43:06 2023 +0200

    [Bcelifier] stackmap support to pass JDK verifier (#177)
    
    * Bcelifier stackmap support verifier
    
    * Bcelifier stackmap support
    
    * BCELifier generates the stack map table so that the generated class
    passes the Java verifier checks.
    
    * added some test inputs StackMapExample and StackMapExample2 compiled in 
JDK8
    
    * Updated BCELifierTestCase.java: added testStackMap
    
    * StackMapType.EMPTY_ARRAY in package visibility and updated 
BinaryOpCreator.java accordingly
    
    Co-authored-by: nbauma109 <nbauma...@github.com>
    
    * Re-integrate lost fix on possible null pointer
    
    * Fixing visibility of EMPTY_ARRAY
    
    * Comment for StackMapExample2
    
    * Javadoc fix
    
    * Add StackMapTest
    
    * Fix Javadoc since tag
    
    * Fix Javadoc since tag
    
    * Fix Javadoc since tag
    
    * Fix Javadoc since tag
    
    * Fix Javadoc since tag
    
    * Don't break BC
    
    * Whitespace
    
    * Javadoc
    
    ---------
    
    Co-authored-by: nbauma109 <nbauma...@github.com>
    Co-authored-by: Gary David Gregory (Code signing key) <ggreg...@apache.org>
    Co-authored-by: Gary Gregory <garydgreg...@users.noreply.github.com>
---
 src/main/java/org/apache/bcel/classfile/Code.java  |  14 ++++
 .../apache/bcel/classfile/DescendingVisitor.java   |  14 ++++
 .../org/apache/bcel/classfile/EmptyVisitor.java    |   9 +++
 .../org/apache/bcel/classfile/StackMapType.java    |  27 ++++++-
 .../java/org/apache/bcel/classfile/Visitor.java    |   9 +++
 src/main/java/org/apache/bcel/util/BCELifier.java  |  86 +++++++++++++++++++++
 .../org/apache/bcel/CounterVisitorTestCase.java    |   5 ++
 .../org/apache/bcel/generic/BinaryOpCreator.java   |  12 +--
 .../org/apache/bcel/util/BCELifierTestCase.java    |  10 ++-
 .../org/apache/bcel/visitors/CountingVisitor.java  |  11 +++
 src/test/resources/StackMapExample.class           | Bin 0 -> 790 bytes
 src/test/resources/StackMapExample.java            |  18 +++++
 src/test/resources/StackMapExample2.class          | Bin 0 -> 673 bytes
 src/test/resources/StackMapExample2.java           |  12 +++
 14 files changed, 218 insertions(+), 9 deletions(-)

diff --git a/src/main/java/org/apache/bcel/classfile/Code.java 
b/src/main/java/org/apache/bcel/classfile/Code.java
index 5e12d00d..4d2dea39 100644
--- a/src/main/java/org/apache/bcel/classfile/Code.java
+++ b/src/main/java/org/apache/bcel/classfile/Code.java
@@ -247,6 +247,20 @@ public final class Code extends Attribute {
         return null;
     }
 
+    /**
+     * Finds the attribute of {@link StackMap} instance.
+     * @return StackMap of Code, if it has one, else null.
+     * @since 6.8.0
+     */
+    public StackMap getStackMap() {
+        for (final Attribute attribute : attributes) {
+            if (attribute instanceof StackMap) {
+                return (StackMap) attribute;
+            }
+        }
+        return null;
+    }
+
     /**
      * @return LocalVariableTable of Code, if it has one
      */
diff --git a/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java 
b/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java
index 5197f2ef..c893a394 100644
--- a/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java
+++ b/src/main/java/org/apache/bcel/classfile/DescendingVisitor.java
@@ -526,6 +526,20 @@ public class DescendingVisitor implements Visitor {
 
     @Override
     public void visitStackMapEntry(final StackMapEntry var) {
+        stack.push(var);
+        var.accept(visitor);
+        accept(var.getTypesOfLocals());
+        accept(var.getTypesOfStackItems());
+        stack.pop();
+    }
+
+    /**
+     * Visits a {@link StackMapType} object.
+     * @param var object to visit
+     * @since 6.8.0
+     */
+    @Override
+    public void visitStackMapType(final StackMapType var) {
         stack.push(var);
         var.accept(visitor);
         stack.pop();
diff --git a/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java 
b/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java
index b817ad9f..5eaaa968 100644
--- a/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java
+++ b/src/main/java/org/apache/bcel/classfile/EmptyVisitor.java
@@ -310,6 +310,15 @@ public class EmptyVisitor implements Visitor {
     public void visitStackMapEntry(final StackMapEntry obj) {
     }
 
+    /**
+     * Visits a {@link StackMapType} object.
+     * @param obj object to visit
+     * @since 6.8.0
+     */
+    @Override
+    public void visitStackMapType(final StackMapType obj) {
+    }
+
     @Override
     public void visitSynthetic(final Synthetic obj) {
     }
diff --git a/src/main/java/org/apache/bcel/classfile/StackMapType.java 
b/src/main/java/org/apache/bcel/classfile/StackMapType.java
index e43fd386..3f3fc50c 100644
--- a/src/main/java/org/apache/bcel/classfile/StackMapType.java
+++ b/src/main/java/org/apache/bcel/classfile/StackMapType.java
@@ -29,9 +29,9 @@ import org.apache.bcel.Const;
  * @see StackMap
  * @see Const
  */
-public final class StackMapType implements Cloneable {
+public final class StackMapType implements Node, Cloneable {
 
-    public static final StackMapType[] EMPTY_ARRAY = {}; // must be public 
because BCELifier code generator writes calls to it
+    public static final StackMapType[] EMPTY_ARRAY = {}; // BCELifier code 
generator writes calls to constructor translating null to EMPTY_ARRAY
 
     private byte type;
     private int index = -1; // Index to CONSTANT_Class or offset
@@ -61,6 +61,18 @@ public final class StackMapType implements Cloneable {
         this.constantPool = constantPool;
     }
 
+    /**
+     * Called by objects that are traversing the nodes of the tree implicitly 
defined by the contents of a Java class.
+     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree 
of objects.
+     *
+     * @param v Visitor object
+     * @since 6.8.0
+     */
+    @Override
+    public void accept(final Visitor v) {
+        v.visitStackMapType(this);
+    }
+
     private byte checkType(final byte type) {
         if (type < Const.ITEM_Bogus || type > Const.ITEM_NewObject) {
             throw new ClassFormatException("Illegal type for StackMapType: " + 
type);
@@ -124,7 +136,7 @@ public final class StackMapType implements Cloneable {
             if (index < 0) {
                 return ", class=<unknown>";
             }
-            return ", class=" + constantPool.constantToString(index, 
Const.CONSTANT_Class);
+            return ", class=" + getClassName();
         }
         if (type == Const.ITEM_NewObject) {
             return ", offset=" + index;
@@ -132,6 +144,15 @@ public final class StackMapType implements Cloneable {
         return "";
     }
 
+    /**
+     * Gets the class name of this StackMapType from the constant pool at 
index position.
+     * @return the fully qualified name of the class for this StackMapType.
+     * @since 6.8.0
+     */
+    public String getClassName() {
+        return constantPool.constantToString(index, Const.CONSTANT_Class);
+    }
+
     /**
      * @param constantPool Constant pool to be used for this object.
      */
diff --git a/src/main/java/org/apache/bcel/classfile/Visitor.java 
b/src/main/java/org/apache/bcel/classfile/Visitor.java
index b4670705..9a3388cf 100644
--- a/src/main/java/org/apache/bcel/classfile/Visitor.java
+++ b/src/main/java/org/apache/bcel/classfile/Visitor.java
@@ -225,6 +225,15 @@ public interface Visitor {
 
     void visitStackMapEntry(StackMapEntry obj);
 
+    /**
+     * Visits a {@link StackMapType} object.
+     * @param obj object to visit
+     * @since 6.8.0
+     */
+    default void visitStackMapType(StackMapType obj) {
+      // empty
+    }
+
     void visitSynthetic(Synthetic obj);
 
     void visitUnknown(Unknown obj);
diff --git a/src/main/java/org/apache/bcel/util/BCELifier.java 
b/src/main/java/org/apache/bcel/util/BCELifier.java
index f719146e..bdf8d57e 100644
--- a/src/main/java/org/apache/bcel/util/BCELifier.java
+++ b/src/main/java/org/apache/bcel/util/BCELifier.java
@@ -26,16 +26,21 @@ import java.util.Locale;
 import org.apache.bcel.Const;
 import org.apache.bcel.Repository;
 import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.Code;
 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;
+import org.apache.bcel.classfile.StackMap;
+import org.apache.bcel.classfile.StackMapEntry;
+import org.apache.bcel.classfile.StackMapType;
 import org.apache.bcel.classfile.Utility;
 import org.apache.bcel.generic.ArrayType;
 import org.apache.bcel.generic.ConstantPoolGen;
 import org.apache.bcel.generic.MethodGen;
 import org.apache.bcel.generic.Type;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 
 /**
@@ -306,6 +311,13 @@ public class BCELifier extends 
org.apache.bcel.classfile.EmptyVisitor {
                 printWriter.println("\");");
             }
         }
+        final Code code = method.getCode();
+        if (code != null) {
+            final StackMap stackMap = code.getStackMap();
+            if (stackMap != null) {
+                stackMap.accept(this);
+            }
+        }
         printWriter.println();
         final BCELFactory factory = new BCELFactory(mg, printWriter);
         factory.start();
@@ -314,4 +326,78 @@ public class BCELifier extends 
org.apache.bcel.classfile.EmptyVisitor {
         printWriter.println("    _cg.addMethod(method.getMethod());");
         printWriter.println("    il.dispose();");
     }
+
+    @Override
+    public void visitStackMap(final StackMap stackMap) {
+        super.visitStackMap(stackMap);
+        printWriter.print("    method.addCodeAttribute(");
+        printWriter.print("new StackMap(_cp.addUtf8(\"");
+        printWriter.print(stackMap.getName());
+        printWriter.print("\"), ");
+        printWriter.print(stackMap.getLength());
+        printWriter.print(", ");
+        printWriter.print("new StackMapEntry[] {");
+        final StackMapEntry[] table = stackMap.getStackMap();
+        for (int i = 0; i < table.length; i++) {
+            table[i].accept(this);
+            if (i < table.length - 1) {
+                printWriter.print(", ");
+            } else {
+                printWriter.print(" }");
+            }
+        }
+        printWriter.print(", _cp.getConstantPool())");
+        printWriter.println(");");
+    }
+
+    @Override
+    public void visitStackMapEntry(final StackMapEntry stackMapEntry) {
+        super.visitStackMapEntry(stackMapEntry);
+        printWriter.print("new StackMapEntry(");
+        printWriter.print(stackMapEntry.getFrameType());
+        printWriter.print(", ");
+        printWriter.print(stackMapEntry.getByteCodeOffset());
+        printWriter.print(", ");
+        visitStackMapTypeArray(stackMapEntry.getTypesOfLocals());
+        printWriter.print(", ");
+        visitStackMapTypeArray(stackMapEntry.getTypesOfStackItems());
+        printWriter.print(", _cp.getConstantPool())");
+    }
+
+    /**
+     * Visits a {@link StackMapType} object.
+     * @param stackMapType object to visit
+     * @since 6.7.1
+     */
+    @Override
+    public void visitStackMapType(final StackMapType stackMapType) {
+        super.visitStackMapType(stackMapType);
+        printWriter.print("new StackMapType((byte)");
+        printWriter.print(stackMapType.getType());
+        printWriter.print(", ");
+        if (stackMapType.hasIndex()) {
+            printWriter.print("_cp.addClass(\"");
+            printWriter.print(stackMapType.getClassName());
+            printWriter.print("\")");
+        } else {
+            printWriter.print("-1");
+        }
+        printWriter.print(", _cp.getConstantPool())");
+    }
+
+    private void visitStackMapTypeArray(final StackMapType[] types) {
+        if (ArrayUtils.isEmpty(types)) {
+            printWriter.print("null"); // null translates to 
StackMapType.EMPTY_ARRAY
+        } else {
+            printWriter.print("new StackMapType[] {");
+            for (int i = 0; i < types.length; i++) {
+                types[i].accept(this);
+                if (i < types.length -1) {
+                    printWriter.print(", ");
+                } else {
+                    printWriter.print(" }");
+                }
+            }
+        }
+    }
 }
diff --git a/src/test/java/org/apache/bcel/CounterVisitorTestCase.java 
b/src/test/java/org/apache/bcel/CounterVisitorTestCase.java
index 0cdc69f1..d1fcfa5e 100644
--- a/src/test/java/org/apache/bcel/CounterVisitorTestCase.java
+++ b/src/test/java/org/apache/bcel/CounterVisitorTestCase.java
@@ -203,6 +203,11 @@ public class CounterVisitorTestCase extends 
AbstractCounterVisitorTestCase {
         assertEquals(0, getVisitor().stackMapEntryCount, "stackMapEntryCount");
     }
 
+    @Test
+    public void testStackMapTypeCount() {
+        assertEquals(0, getVisitor().stackMapTypeCount, "stackMapTypeCount");
+    }
+
     @Test
     public void testSyntheticCount() {
         assertEquals(0, getVisitor().syntheticCount, "syntheticCount");
diff --git a/src/test/java/org/apache/bcel/generic/BinaryOpCreator.java 
b/src/test/java/org/apache/bcel/generic/BinaryOpCreator.java
index 3d372b8f..6450445b 100644
--- a/src/test/java/org/apache/bcel/generic/BinaryOpCreator.java
+++ b/src/test/java/org/apache/bcel/generic/BinaryOpCreator.java
@@ -115,11 +115,13 @@ public class BinaryOpCreator {
                 "calculate", ORG_APACHE_BCEL_GENERIC_BINARY_OP, il, cp);
         method.addException("java.lang.Exception");
         final StackMapType[] typesOfLocals = { new StackMapType((byte) 7, 
cp.addClass("java.lang.String"), cp.getConstantPool()) };
-        final StackMapEntry[] table = { new StackMapEntry(252, 70, 
typesOfLocals, StackMapType.EMPTY_ARRAY, cp.getConstantPool()),
-                new StackMapEntry(251, 65, StackMapType.EMPTY_ARRAY, 
StackMapType.EMPTY_ARRAY, cp.getConstantPool()),
-                new StackMapEntry(251, 65, StackMapType.EMPTY_ARRAY, 
StackMapType.EMPTY_ARRAY, cp.getConstantPool()),
-                new StackMapEntry(251, 65, StackMapType.EMPTY_ARRAY, 
StackMapType.EMPTY_ARRAY, cp.getConstantPool()) };
-        method.addCodeAttribute(new StackMap(cp.addUtf8("StackMapTable"), 17, 
table, cp.getConstantPool()));
+        final StackMapEntry[] table = { new StackMapEntry(252, 70, 
typesOfLocals, null, cp.getConstantPool()),
+                new StackMapEntry(251, 65, null, null, cp.getConstantPool()),
+                new StackMapEntry(251, 65, null, null, cp.getConstantPool()),
+                new StackMapEntry(251, 65, null, null, cp.getConstantPool()) };
+        final StackMap stackMap = new StackMap(cp.addUtf8("StackMapTable"), 
17, table, cp.getConstantPool());
+        stackMap.setStackMap(table);
+        method.addCodeAttribute(stackMap);
 
         il.append(InstructionFactory.createLoad(Type.OBJECT, 1));
         il.append(new PUSH(cp, 0));
diff --git a/src/test/java/org/apache/bcel/util/BCELifierTestCase.java 
b/src/test/java/org/apache/bcel/util/BCELifierTestCase.java
index 24d2eb2e..a856c2cd 100644
--- a/src/test/java/org/apache/bcel/util/BCELifierTestCase.java
+++ b/src/test/java/org/apache/bcel/util/BCELifierTestCase.java
@@ -42,7 +42,7 @@ import org.junit.jupiter.params.provider.ValueSource;
 public class BCELifierTestCase extends AbstractTestCase {
 
     private static final String EOL = System.lineSeparator();
-    public static final String CLASSPATH = 
System.getProperty("java.class.path") + File.pathSeparator + ".";
+    public static final String CLASSPATH = "." + File.pathSeparator + 
System.getProperty("java.class.path");
 
     // Canonicalise the javap output so it compares better
     private String canonHashRef(String input) {
@@ -216,4 +216,12 @@ public class BCELifierTestCase extends AbstractTestCase {
         final BCELifier bcelifier = new BCELifier(javaClass, os);
         bcelifier.start();
     }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "StackMapExample", "StackMapExample2" })
+    public void testStackMap(final String className) throws Exception {
+        testJavapCompare(className);
+        final File workDir = new File("target");
+        assertEquals("Hello World" + EOL, exec(workDir, "java", "-cp", 
CLASSPATH, className, "Hello"));
+    }
 }
diff --git a/src/test/java/org/apache/bcel/visitors/CountingVisitor.java 
b/src/test/java/org/apache/bcel/visitors/CountingVisitor.java
index 8a350385..3522df26 100644
--- a/src/test/java/org/apache/bcel/visitors/CountingVisitor.java
+++ b/src/test/java/org/apache/bcel/visitors/CountingVisitor.java
@@ -72,6 +72,7 @@ import org.apache.bcel.classfile.Signature;
 import org.apache.bcel.classfile.SourceFile;
 import org.apache.bcel.classfile.StackMap;
 import org.apache.bcel.classfile.StackMapEntry;
+import org.apache.bcel.classfile.StackMapType;
 import org.apache.bcel.classfile.Synthetic;
 import org.apache.bcel.classfile.Unknown;
 import org.apache.bcel.classfile.Visitor;
@@ -84,6 +85,8 @@ public class CountingVisitor implements Visitor {
 
     public int stackMapEntryCount;
 
+    public int stackMapTypeCount;
+
     public int stackMapCount;
 
     public int sourceFileCount;
@@ -497,6 +500,14 @@ public class CountingVisitor implements Visitor {
         stackMapEntryCount++;
     }
 
+    /**
+     * @since 6.8.0
+     */
+    @Override
+    public void visitStackMapType(final StackMapType obj) {
+        stackMapTypeCount++;
+    }
+
     @Override
     public void visitSynthetic(final Synthetic obj) {
         syntheticCount++;
diff --git a/src/test/resources/StackMapExample.class 
b/src/test/resources/StackMapExample.class
new file mode 100644
index 00000000..62066fd6
Binary files /dev/null and b/src/test/resources/StackMapExample.class differ
diff --git a/src/test/resources/StackMapExample.java 
b/src/test/resources/StackMapExample.java
new file mode 100644
index 00000000..f4fc3168
--- /dev/null
+++ b/src/test/resources/StackMapExample.java
@@ -0,0 +1,18 @@
+/*
+ * Example to check if BCELifier generates the stack map table
+ * so that the generated class passes the Java verifier checks.
+ */
+public abstract class StackMapExample {
+
+    protected abstract void someAbstractMethod();
+
+    public static void main(final String[] args) {
+        switch (args[0]) {
+        case "Hello":
+            System.out.println("Hello World");
+            break;
+        default:
+            break;
+        }
+    }
+}
diff --git a/src/test/resources/StackMapExample2.class 
b/src/test/resources/StackMapExample2.class
new file mode 100644
index 00000000..c449a193
Binary files /dev/null and b/src/test/resources/StackMapExample2.class differ
diff --git a/src/test/resources/StackMapExample2.java 
b/src/test/resources/StackMapExample2.java
new file mode 100644
index 00000000..441f678e
--- /dev/null
+++ b/src/test/resources/StackMapExample2.java
@@ -0,0 +1,12 @@
+/*
+ * Another version of StackMapExample using 2 types of locals String and int
+ * instead of just String. 
+ */
+public class StackMapExample2 {
+
+    public static void main(String[] args) {
+        if (args.length == 1 && "Hello".equals(args[0])) {
+            System.out.println("Hello World");
+        }
+    }
+}

Reply via email to