From dcad6633da62241402f06077e26f7f7742077759 Mon Sep 17 00:00:00 2001 From: Hippo Date: Sun, 9 Aug 2020 02:39:10 -0500 Subject: [PATCH 1/4] BCEL-341: Oak class file patch --- pom.xml | 5 ++ .../org/apache/bcel/classfile/Attribute.java | 25 +++++++++- .../apache/bcel/classfile/ClassParser.java | 4 +- .../java/org/apache/bcel/classfile/Code.java | 50 ++++++++++++++++--- .../apache/bcel/classfile/FieldOrMethod.java | 13 ++++- .../org/apache/bcel/classfile/Method.java | 14 +++++- 6 files changed, 101 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 7f25bd83f8..b6871e1dd1 100644 --- a/pom.xml +++ b/pom.xml @@ -184,6 +184,11 @@ Sam Yoon + + + Hippo + hippah at protonmail.com + diff --git a/src/main/java/org/apache/bcel/classfile/Attribute.java b/src/main/java/org/apache/bcel/classfile/Attribute.java index 72e5c4e897..feac9d5e4e 100644 --- a/src/main/java/org/apache/bcel/classfile/Attribute.java +++ b/src/main/java/org/apache/bcel/classfile/Attribute.java @@ -86,6 +86,7 @@ protected static void println(final String msg) { } } + /** * Class method reads one attribute from the input data stream. This method * must not be accessible from the outside. It is called by the Field and @@ -103,6 +104,28 @@ protected static void println(final String msg) { */ public static Attribute readAttribute(final DataInput file, final ConstantPool constant_pool) throws IOException, ClassFormatException + { + return readAttribute(file, constant_pool, false); + } + + /** + * Class method reads one attribute from the input data stream. This method + * must not be accessible from the outside. It is called by the Field and + * Method constructor methods. + * + * @see Field + * @see Method + * + * @param file Input stream + * @param constant_pool Array of constants + * @param isOak If the the class file is oak + * @return Attribute + * @throws IOException + * @throws ClassFormatException + * @since 6.0 + */ + public static Attribute readAttribute(final DataInput file, final ConstantPool constant_pool, final boolean isOak) + throws IOException, ClassFormatException { byte tag = Const.ATTR_UNKNOWN; // Unknown attribute // Get class name from constant pool via `name_index' indirection @@ -138,7 +161,7 @@ public static Attribute readAttribute(final DataInput file, final ConstantPool c case Const.ATTR_SOURCE_FILE: return new SourceFile(name_index, length, file, constant_pool); case Const.ATTR_CODE: - return new Code(name_index, length, file, constant_pool); + return new Code(name_index, length, file, constant_pool, isOak); case Const.ATTR_EXCEPTIONS: return new ExceptionTable(name_index, length, file, constant_pool); case Const.ATTR_LINE_NUMBER_TABLE: diff --git a/src/main/java/org/apache/bcel/classfile/ClassParser.java b/src/main/java/org/apache/bcel/classfile/ClassParser.java index a592bfed06..79a3b29174 100644 --- a/src/main/java/org/apache/bcel/classfile/ClassParser.java +++ b/src/main/java/org/apache/bcel/classfile/ClassParser.java @@ -58,6 +58,7 @@ public final class ClassParser { private Attribute[] attributes; // attributes defined in the class private final boolean isZip; // Loaded from zip file private static final int BUFSIZE = 8192; + private boolean isOak; // Is oak class file /** @@ -290,7 +291,7 @@ private void readMethods() throws IOException, ClassFormatException { final int methods_count = dataInputStream.readUnsignedShort(); methods = new Method[methods_count]; for (int i = 0; i < methods_count; i++) { - methods[i] = new Method(dataInputStream, constantPool); + methods[i] = new Method(dataInputStream, constantPool, isOak); } } @@ -303,5 +304,6 @@ private void readMethods() throws IOException, ClassFormatException { private void readVersion() throws IOException, ClassFormatException { minor = dataInputStream.readUnsignedShort(); major = dataInputStream.readUnsignedShort(); + isOak = major < Const.MAJOR_1_1 || (major == Const.MAJOR_1_1 && minor < 3); } } diff --git a/src/main/java/org/apache/bcel/classfile/Code.java b/src/main/java/org/apache/bcel/classfile/Code.java index 5e81d21f3f..224c6990af 100644 --- a/src/main/java/org/apache/bcel/classfile/Code.java +++ b/src/main/java/org/apache/bcel/classfile/Code.java @@ -47,6 +47,7 @@ public final class Code extends Attribute { private byte[] code; // Actual byte code private CodeException[] exceptionTable; // Table of handled exceptions private Attribute[] attributes; // or LocalVariable + private boolean isOak; // If the class file follows oak format /** @@ -59,6 +60,7 @@ public Code(final Code c) { } + /** * @param name_index Index pointing to the name Code * @param length Content length in bytes @@ -67,10 +69,23 @@ public Code(final Code c) { */ Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool) throws IOException { + this(name_index, length, file, constant_pool, false); + } + + /** + * @param name_index Index pointing to the name Code + * @param length Content length in bytes + * @param file Input stream + * @param constant_pool Array of constants + * @param isOak If the class file is oak + */ + Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool, final boolean isOak) + throws IOException { // Initialize with some default values which will be overwritten later - this(name_index, length, file.readUnsignedShort(), file.readUnsignedShort(), (byte[]) null, + this(name_index, length, isOak ? file.readUnsignedByte() : file.readUnsignedShort(), isOak ? file.readUnsignedByte() : file.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null, constant_pool); - final int code_length = file.readInt(); + this.isOak = isOak; + final int code_length = isOak ? file.readUnsignedShort() : file.readInt(); code = new byte[code_length]; // Read byte code file.readFully(code); /* Read exception table that contains all regions where an exception @@ -108,13 +123,30 @@ public Code(final Code c) { * @param constant_pool Array of constants */ public Code(final int name_index, final int length, final int maxStack, final int maxLocals, final byte[] code, - final CodeException[] exceptionTable, final Attribute[] attributes, final ConstantPool constant_pool) { + final CodeException[] exceptionTable, final Attribute[] attributes, final ConstantPool constant_pool) { + this(name_index, length, maxStack, maxLocals, code, exceptionTable, attributes, constant_pool, false); + } + + /** + * @param name_index Index pointing to the name Code + * @param length Content length in bytes + * @param maxStack Maximum size of stack + * @param maxLocals Number of local variables + * @param code Actual byte code + * @param exceptionTable of handled exceptions + * @param attributes Attributes of code: LineNumber or LocalVariable + * @param constant_pool Array of constants + * @param isOak If the class file follows the oak format + */ + public Code(final int name_index, final int length, final int maxStack, final int maxLocals, final byte[] code, + final CodeException[] exceptionTable, final Attribute[] attributes, final ConstantPool constant_pool, final boolean isOak) { super(Const.ATTR_CODE, name_index, length, constant_pool); this.maxStack = maxStack; this.maxLocals = maxLocals; this.code = code != null ? code : new byte[0]; this.exceptionTable = exceptionTable != null ? exceptionTable : new CodeException[0]; this.attributes = attributes != null ? attributes : new Attribute[0]; + this.isOak = isOak; super.setLength(calculateLength()); // Adjust length } @@ -141,9 +173,15 @@ public void accept( final Visitor v ) { @Override public void dump( final DataOutputStream file ) throws IOException { super.dump(file); - file.writeShort(maxStack); - file.writeShort(maxLocals); - file.writeInt(code.length); + if (isOak) { + file.writeByte(maxStack); + file.writeByte(maxLocals); + file.writeShort(code.length); + } else { + file.writeShort(maxStack); + file.writeShort(maxLocals); + file.writeInt(code.length); + } file.write(code, 0, code.length); file.writeShort(exceptionTable.length); for (final CodeException exception : exceptionTable) { diff --git a/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java b/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java index 6b70942185..8ed8e82d67 100644 --- a/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java +++ b/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java @@ -100,12 +100,23 @@ protected FieldOrMethod(final DataInputStream file, final ConstantPool constant_ * @throws ClassFormatException */ protected FieldOrMethod(final DataInput file, final ConstantPool constant_pool) throws IOException, ClassFormatException { + this(file, constant_pool, false); + } + + /** + * Construct object from file stream. + * @param file Input stream + * @param isOak If the class file is oak + * @throws IOException + * @throws ClassFormatException + */ + protected FieldOrMethod(final DataInput file, final ConstantPool constant_pool, final boolean isOak) throws IOException, ClassFormatException { this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), null, constant_pool); final int attributes_count = file.readUnsignedShort(); attributes = new Attribute[attributes_count]; for (int i = 0; i < attributes_count; i++) { - attributes[i] = Attribute.readAttribute(file, constant_pool); + attributes[i] = Attribute.readAttribute(file, constant_pool, isOak); } this.attributes_count = attributes_count; // init deprecated field } diff --git a/src/main/java/org/apache/bcel/classfile/Method.java b/src/main/java/org/apache/bcel/classfile/Method.java index 8f9b8d6b5a..4a94d92af4 100644 --- a/src/main/java/org/apache/bcel/classfile/Method.java +++ b/src/main/java/org/apache/bcel/classfile/Method.java @@ -79,7 +79,19 @@ public Method(final Method c) { */ Method(final DataInput file, final ConstantPool constant_pool) throws IOException, ClassFormatException { - super(file, constant_pool); + this(file, constant_pool, false); + } + + /** + * Construct object from file stream. + * @param file Input stream + * @param isOak If the class file is oak + * @throws IOException + * @throws ClassFormatException + */ + Method(final DataInput file, final ConstantPool constant_pool, final boolean isOak) throws IOException, + ClassFormatException { + super(file, constant_pool, isOak); } From a049c5d89e938c9b3f0dcffd28d6c40936c871e5 Mon Sep 17 00:00:00 2001 From: Hippo Date: Mon, 10 Aug 2020 16:44:17 -0500 Subject: [PATCH 2/4] Checkstyle error fix --- src/main/java/org/apache/bcel/classfile/Code.java | 4 ++-- src/test/java/org/apache/bcel/BCELBenchmark.java | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/bcel/classfile/Code.java b/src/main/java/org/apache/bcel/classfile/Code.java index 224c6990af..c7504d5d8d 100644 --- a/src/main/java/org/apache/bcel/classfile/Code.java +++ b/src/main/java/org/apache/bcel/classfile/Code.java @@ -82,8 +82,8 @@ public Code(final Code c) { Code(final int name_index, final int length, final DataInput file, final ConstantPool constant_pool, final boolean isOak) throws IOException { // Initialize with some default values which will be overwritten later - this(name_index, length, isOak ? file.readUnsignedByte() : file.readUnsignedShort(), isOak ? file.readUnsignedByte() : file.readUnsignedShort(), (byte[]) null, - (CodeException[]) null, (Attribute[]) null, constant_pool); + this(name_index, length, isOak ? file.readUnsignedByte() : file.readUnsignedShort(), isOak ? file.readUnsignedByte() : file.readUnsignedShort(), + (byte[]) null, (CodeException[]) null, (Attribute[]) null, constant_pool); this.isOak = isOak; final int code_length = isOak ? file.readUnsignedShort() : file.readInt(); code = new byte[code_length]; // Read byte code diff --git a/src/test/java/org/apache/bcel/BCELBenchmark.java b/src/test/java/org/apache/bcel/BCELBenchmark.java index ad48cee6a3..8538209dc5 100644 --- a/src/test/java/org/apache/bcel/BCELBenchmark.java +++ b/src/test/java/org/apache/bcel/BCELBenchmark.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.bcel; +/*package org.apache.bcel; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -69,6 +69,7 @@ public boolean evaluate(JarEntry entry) { /** * Baseline benchmark. Read the classes but don't parse them. */ +/* @Benchmark public void baseline(Blackhole bh) throws IOException { JarFile jar = getJarFile(); @@ -123,4 +124,4 @@ public void generator(Blackhole bh) throws IOException { jar.close(); } -} +}*/ From 089d0ae6c1b5c55f04a81ac3af366582d62eae4e Mon Sep 17 00:00:00 2001 From: Hippo Date: Mon, 10 Aug 2020 16:44:47 -0500 Subject: [PATCH 3/4] Checkstyle error fix --- src/test/java/org/apache/bcel/BCELBenchmark.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/apache/bcel/BCELBenchmark.java b/src/test/java/org/apache/bcel/BCELBenchmark.java index 8538209dc5..ad48cee6a3 100644 --- a/src/test/java/org/apache/bcel/BCELBenchmark.java +++ b/src/test/java/org/apache/bcel/BCELBenchmark.java @@ -15,7 +15,7 @@ * limitations under the License. */ -/*package org.apache.bcel; +package org.apache.bcel; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -69,7 +69,6 @@ public boolean evaluate(JarEntry entry) { /** * Baseline benchmark. Read the classes but don't parse them. */ -/* @Benchmark public void baseline(Blackhole bh) throws IOException { JarFile jar = getJarFile(); @@ -124,4 +123,4 @@ public void generator(Blackhole bh) throws IOException { jar.close(); } -}*/ +} From b40402d2cecaeb4c62c000d16bab0efaf84848a3 Mon Sep 17 00:00:00 2001 From: Hippo Date: Mon, 10 Aug 2020 22:55:03 -0500 Subject: [PATCH 4/4] Fixed calculation error on Code#getInternalLength() --- src/main/java/org/apache/bcel/classfile/Code.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/bcel/classfile/Code.java b/src/main/java/org/apache/bcel/classfile/Code.java index c7504d5d8d..ad968e4f53 100644 --- a/src/main/java/org/apache/bcel/classfile/Code.java +++ b/src/main/java/org/apache/bcel/classfile/Code.java @@ -267,7 +267,19 @@ public int getMaxStack() { * and excluding all its attributes */ private int getInternalLength() { - return 2 /*maxStack*/+ 2 /*maxLocals*/+ 4 /*code length*/ + final int maxStack; + final int maxLocals; + final int codeLength; + if (isOak) { + maxStack = 1; + maxLocals = 1; + codeLength = 2; + } else { + maxStack = 2; + maxLocals = 2; + codeLength = 4; + } + return maxStack + maxLocals + codeLength + code.length /*byte-code*/ + 2 /*exception-table length*/ + 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */