8339576: Speed up raw bytecode processing in ClassFile API

Co-authored-by: Shaojin Wen <swen@openjdk.org>
Reviewed-by: asotona, redestad
This commit is contained in:
Chen Liang 2024-09-06 11:42:50 +00:00
parent a35fd38610
commit a1eebbdf8a
9 changed files with 380 additions and 283 deletions

View File

@ -26,7 +26,6 @@
package jdk.internal.classfile.impl;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.lang.classfile.BufWriter;
@ -247,8 +246,8 @@ public final class BufWriterImpl implements BufWriter {
return offset;
}
public ByteBuffer asByteBuffer() {
return ByteBuffer.wrap(elems, 0, offset).slice();
public RawBytecodeHelper.CodeRange bytecodeView() {
return RawBytecodeHelper.of(elems, offset);
}
public void copyTo(byte[] array, int bufferOffset) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -24,23 +24,66 @@
*/
package jdk.internal.classfile.impl;
import java.nio.ByteBuffer;
import static java.lang.classfile.ClassFile.ASTORE_3;
import static java.lang.classfile.ClassFile.ISTORE;
import static java.lang.classfile.ClassFile.LOOKUPSWITCH;
import static java.lang.classfile.ClassFile.TABLESWITCH;
import static java.lang.classfile.ClassFile.WIDE;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.Stable;
import static java.lang.classfile.ClassFile.*;
public final class RawBytecodeHelper {
public static final BiFunction<String, List<Number>, IllegalArgumentException>
IAE_FORMATTER = Preconditions.outOfBoundsExceptionFormatter(new Function<>() {
@Override
public IllegalArgumentException apply(String s) {
return new IllegalArgumentException(s);
}
});
public record CodeRange(byte[] array, int length) {
public RawBytecodeHelper start() {
return new RawBytecodeHelper(this);
}
}
public static final int ILLEGAL = -1;
private static final byte[] LENGTHS = new byte[] {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 3, 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3 | (6 << 4), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2 | (4 << 4), 0, 0, 1, 1, 1,
1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, 5, 5, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 4, 4, 4, 2, 4, 3, 3, 0, 0, 1, 3, 2, 3, 3, 3, 1, 2, 1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
/**
* The length of opcodes, or -1 for no fixed length.
* This is generated as if:
* {@snippet lang=java :
* var lengths = new byte[0x100];
* Arrays.fill(lengths, (byte) -1);
* for (var op : Opcode.values()) {
* if (!op.isWide()) {
* lengths[op.bytecode()] = (byte) op.sizeIfFixed();
* }
* }
* }
* Tested in UtilTest::testOpcodeLengthTable.
*/
// Note: Consider distinguishing non-opcode and non-fixed-length opcode
public static final @Stable byte[] LENGTHS = new byte[] {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 2, -1, -1, 1, 1, 1, 1,
1, 1, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1,
3, 3, 1, 1, -1, 4, 3, 3, 5, 5, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
public static boolean isStoreIntoLocal(int code) {
@ -51,121 +94,200 @@ public final class RawBytecodeHelper {
return (n + 3) & ~3;
}
private final ByteBuffer bytecode;
public int bci, nextBci, endBci;
public int rawCode;
public boolean isWide;
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
public final CodeRange code;
private int nextBci;
private int bci;
private int opcode;
private boolean isWide;
public RawBytecodeHelper(ByteBuffer bytecode) {
this.bytecode = bytecode;
this.bci = 0;
this.nextBci = 0;
this.endBci = bytecode.capacity();
public static CodeRange of(byte[] array) {
return new CodeRange(array, array.length);
}
public boolean isLastBytecode() {
return nextBci >= endBci;
public static CodeRange of(byte[] array, int limit) {
return new CodeRange(array, limit);
}
private RawBytecodeHelper(CodeRange range) {
this.code = range;
}
// immutable states
/** {@return the end of the code array} */
public int endBci() {
return code.length;
}
// setup
/**
* Sets the starting bci for bytecode reading. Can be set to
* {@link #endBci} to end scanning. Must be followed by a
* {@link #next} before getter access.
*/
public void reset(int nextBci) {
Preconditions.checkIndex(nextBci, endBci() + 1, IAE_FORMATTER);
this.nextBci = nextBci;
}
// getters after transition
/**
* Returns the current functional opcode, or {@link #ILLEGAL} if
* the next instruction is invalid in format.
* If this returns a valid opcode, that instruction's format must
* be valid and can be accessed unchecked.
*/
public int opcode() {
return opcode;
}
/**
* Returns whether the current functional opcode is in wide.
*/
public boolean isWide() {
return isWide;
}
/**
* Returns the last validated instruction's index.
*/
public int bci() {
return bci;
}
// general utilities
public int getU1(int bci) {
Preconditions.checkIndex(bci, endBci(), IAE_FORMATTER);
return getU1Unchecked(bci);
}
public int getU2(int bci) {
Preconditions.checkFromIndexSize(bci, 2, endBci(), IAE_FORMATTER);
return getU2Unchecked(bci);
}
public int getShort(int bci) {
return bytecode.getShort(bci);
}
public int dest() {
return bci + getShort(bci + 1);
Preconditions.checkFromIndexSize(bci, 2, endBci(), IAE_FORMATTER);
return getShortUnchecked(bci);
}
public int getInt(int bci) {
return bytecode.getInt(bci);
Preconditions.checkFromIndexSize(bci, 4, endBci(), IAE_FORMATTER);
return getIntUnchecked(bci);
}
// Unchecked accessors: only if opcode() is validated
public int getU1Unchecked(int bci) {
return Byte.toUnsignedInt(code.array[bci]);
}
public int getU2Unchecked(int bci) {
return UNSAFE.getCharUnaligned(code.array, (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + bci, true);
}
public int getShortUnchecked(int bci) {
return UNSAFE.getShortUnaligned(code.array, (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + bci, true);
}
// used after switch validation
public int getIntUnchecked(int bci) {
return UNSAFE.getIntUnaligned(code.array, (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + bci, true);
}
// non-wide branches
public int dest() {
return bci + getShortUnchecked(bci + 1);
}
// goto_w and jsr_w
public int destW() {
return bci + getInt(bci + 1);
}
public int getIndexU1() {
return bytecode.get(bci + 1) & 0xff;
}
public int getU1(int bci) {
return bytecode.get(bci) & 0xff;
}
public int rawNext(int jumpTo) {
this.nextBci = jumpTo;
return rawNext();
}
public int rawNext() {
bci = nextBci;
int code = bytecode.get(bci) & 0xff;
int len = LENGTHS[code] & 0xf;
if (len > 0 && (bci <= endBci - len)) {
isWide = false;
nextBci += len;
if (nextBci <= bci) {
code = ILLEGAL;
}
rawCode = code;
return code;
} else {
len = switch (bytecode.get(bci) & 0xff) {
case WIDE -> {
if (bci + 1 >= endBci) {
yield -1;
}
yield LENGTHS[bytecode.get(bci + 1) & 0xff] >> 4;
}
case TABLESWITCH -> {
int aligned_bci = align(bci + 1);
if (aligned_bci + 3 * 4 >= endBci) {
yield -1;
}
int lo = bytecode.getInt(aligned_bci + 1 * 4);
int hi = bytecode.getInt(aligned_bci + 2 * 4);
int l = aligned_bci - bci + (3 + hi - lo + 1) * 4;
if (l > 0) yield l; else yield -1;
}
case LOOKUPSWITCH -> {
int aligned_bci = align(bci + 1);
if (aligned_bci + 2 * 4 >= endBci) {
yield -1;
}
int npairs = bytecode.getInt(aligned_bci + 4);
int l = aligned_bci - bci + (2 + 2 * npairs) * 4;
if (l > 0) yield l; else yield -1;
}
default ->
0;
};
if (len <= 0 || (bci > endBci - len) || (bci - len >= nextBci)) {
code = ILLEGAL;
} else {
nextBci += len;
isWide = false;
if (code == WIDE) {
if (bci + 1 >= endBci) {
code = ILLEGAL;
} else {
code = bytecode.get(bci + 1) & 0xff;
isWide = true;
}
}
}
rawCode = code;
return code;
}
return bci + getIntUnchecked(bci + 1);
}
// *load, *store, iinc
public int getIndex() {
return (isWide) ? getIndexU2Raw(bci + 2) : getIndexU1();
return isWide ? getU2Unchecked(bci + 2) : getIndexU1();
}
// ldc
public int getIndexU1() {
return getU1Unchecked(bci + 1);
}
// usually cp entry index
public int getIndexU2() {
return getIndexU2Raw(bci + 1);
return getU2Unchecked(bci + 1);
}
public int getIndexU2Raw(int bci) {
return bytecode.getShort(bci) & 0xffff;
// Transition methods
/**
* Transitions to the next instruction and returns whether scanning should
* continue. If the next instruction is malformed, {@link #opcode()} returns
* {@link #ILLEGAL}, so we can perform value access without bound checks if
* we have a valid opcode.
*/
public boolean next() {
var bci = nextBci;
var end = endBci();
if (bci >= end) {
return false;
}
int code = getU1Unchecked(bci);
int len = LENGTHS[code & 0xFF]; // & 0xFF eliminates bound check
this.bci = bci;
opcode = code;
isWide = false;
if (len <= 0) {
len = checkSpecialInstruction(bci, end, code); // sets opcode
}
if (len <= 0 || (nextBci += len) > end) {
opcode = ILLEGAL;
}
return true;
}
// Put rarely used code in another method to reduce code size
private int checkSpecialInstruction(int bci, int end, int code) {
if (code == WIDE) {
if (bci + 1 >= end) {
return -1;
}
opcode = code = getIndexU1();
isWide = true;
// Validated in UtilTest.testOpcodeLengthTable
return LENGTHS[code] * 2;
}
if (code == TABLESWITCH) {
int alignedBci = align(bci + 1);
if (alignedBci + 3 * 4 >= end) {
return -1;
}
int lo = getIntUnchecked(alignedBci + 1 * 4);
int hi = getIntUnchecked(alignedBci + 2 * 4);
long l = alignedBci - bci + (3L + (long) hi - lo + 1L) * 4L;
return l > 0 && ((int) l == l) ? (int) l : -1;
}
if (code == LOOKUPSWITCH) {
int alignedBci = align(bci + 1);
if (alignedBci + 2 * 4 >= end) {
return -1;
}
int npairs = getIntUnchecked(alignedBci + 4);
if (npairs < 0) {
return -1;
}
long l = alignedBci - bci + (2L + 2L * npairs) * 4L;
return l > 0 && ((int) l == l) ? (int) l : -1;
}
return -1;
}
}

View File

@ -34,7 +34,6 @@ import java.lang.classfile.constantpool.DynamicConstantPoolEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.BitSet;
import java.util.List;
@ -55,7 +54,7 @@ public final class StackCounter {
dcb.methodInfo.methodName().stringValue(),
dcb.methodInfo.methodTypeSymbol(),
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
dcb.bytecodesBufWriter.asByteBuffer(),
dcb.bytecodesBufWriter.bytecodeView(),
dcb.constantPool,
dcb.handlers);
}
@ -67,7 +66,6 @@ public final class StackCounter {
private final String methodName;
private final MethodTypeDesc methodDesc;
private final boolean isStatic;
private final ByteBuffer bytecode;
private final SplitConstantPool cp;
private final Queue<Target> targets;
private final BitSet visited;
@ -91,12 +89,12 @@ public final class StackCounter {
Target en;
while ((en = targets.poll()) != null) {
if (!visited.get(en.bci)) {
bcs.nextBci = en.bci;
bcs.reset(en.bci);
stack = en.stack;
return true;
}
}
bcs.nextBci = bcs.endBci;
bcs.reset(bcs.endBci());
return false;
}
@ -106,14 +104,13 @@ public final class StackCounter {
String methodName,
MethodTypeDesc methodDesc,
boolean isStatic,
ByteBuffer bytecode,
RawBytecodeHelper.CodeRange bytecode,
SplitConstantPool cp,
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
this.thisClass = thisClass;
this.methodName = methodName;
this.methodDesc = methodDesc;
this.isStatic = isStatic;
this.bytecode = bytecode;
this.cp = cp;
targets = new ArrayDeque<>();
stack = rets = 0;
@ -132,14 +129,13 @@ public final class StackCounter {
}
maxLocals = isStatic ? 0 : 1;
maxLocals += Util.parameterSlots(methodDesc);
bcs = new RawBytecodeHelper(bytecode);
visited = new BitSet(bcs.endBci);
bcs = bytecode.start();
visited = new BitSet(bcs.endBci());
targets.add(new Target(0, 0));
while (next()) {
while (!bcs.isLastBytecode()) {
bcs.rawNext();
int opcode = bcs.rawCode;
int bci = bcs.bci;
while (bcs.next()) {
int opcode = bcs.opcode();
int bci = bcs.bci();
visited.set(bci);
switch (opcode) {
case NOP, LALOAD, DALOAD, SWAP, INEG, ARRAYLENGTH, INSTANCEOF, LNEG, FNEG, DNEG, I2F, L2D, F2I, D2L, I2B, I2C, I2S,
@ -267,12 +263,12 @@ public final class StackCounter {
}
case TABLESWITCH, LOOKUPSWITCH -> {
int alignedBci = RawBytecodeHelper.align(bci + 1);
int defaultOfset = bcs.getInt(alignedBci);
int defaultOffset = bcs.getIntUnchecked(alignedBci);
int keys, delta;
addStackSlot(-1);
if (bcs.rawCode == TABLESWITCH) {
int low = bcs.getInt(alignedBci + 4);
int high = bcs.getInt(alignedBci + 2 * 4);
if (bcs.opcode() == TABLESWITCH) {
int low = bcs.getIntUnchecked(alignedBci + 4);
int high = bcs.getIntUnchecked(alignedBci + 2 * 4);
if (low > high) {
throw error("low must be less than or equal to high in tableswitch");
}
@ -282,24 +278,23 @@ public final class StackCounter {
}
delta = 1;
} else {
keys = bcs.getInt(alignedBci + 4);
keys = bcs.getIntUnchecked(alignedBci + 4);
if (keys < 0) {
throw error("number of keys in lookupswitch less than 0");
}
delta = 2;
for (int i = 0; i < (keys - 1); i++) {
int this_key = bcs.getInt(alignedBci + (2 + 2 * i) * 4);
int next_key = bcs.getInt(alignedBci + (2 + 2 * i + 2) * 4);
int this_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i) * 4);
int next_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i + 2) * 4);
if (this_key >= next_key) {
throw error("Bad lookupswitch instruction");
}
}
}
int target = bci + defaultOfset;
int target = bci + defaultOffset;
jump(target);
for (int i = 0; i < keys; i++) {
alignedBci = RawBytecodeHelper.align(bcs.bci + 1);
target = bci + bcs.getInt(alignedBci + (3 + i * delta) * 4);
target = bci + bcs.getIntUnchecked(alignedBci + (3 + i * delta) * 4);
jump(target);
}
next();
@ -314,7 +309,7 @@ public final class StackCounter {
}
case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> {
var tk = TypeKind.fromDescriptor(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType().type());
switch (bcs.rawCode) {
switch (bcs.opcode()) {
case GETSTATIC ->
addStackSlot(tk.slotSize());
case PUTSTATIC ->
@ -337,7 +332,7 @@ public final class StackCounter {
addStackSlot(delta);
}
case MULTIANEWARRAY ->
addStackSlot(1 - bcs.getU1(bcs.bci + 3));
addStackSlot(1 - bcs.getU1Unchecked(bcs.bci() + 3));
case JSR -> {
addStackSlot(+1);
jump(bcs.dest()); //here we lost track of the exact stack size after return from subroutine
@ -395,10 +390,10 @@ public final class StackCounter {
private IllegalArgumentException error(String msg) {
var sb = new StringBuilder("%s at bytecode offset %d of method %s(%s)".formatted(
msg,
bcs.bci,
bcs.bci(),
methodName,
methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(","))));
Util.dumpMethod(cp, thisClass, methodName, methodDesc, isStatic ? ACC_STATIC : 0, bytecode, sb::append);
Util.dumpMethod(cp, thisClass, methodName, methodDesc, isStatic ? ACC_STATIC : 0, bcs.code, sb::append);
return new IllegalArgumentException(sb.toString());
}
}

View File

@ -38,7 +38,6 @@ import java.lang.classfile.constantpool.InvokeDynamicEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@ -46,6 +45,7 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import jdk.internal.constant.ReferenceClassDescImpl;
import jdk.internal.util.Preconditions;
import static java.lang.classfile.ClassFile.*;
import static java.lang.constant.ConstantDescs.*;
@ -152,7 +152,7 @@ public final class StackMapGenerator {
dcb.methodInfo.methodName().stringValue(),
dcb.methodInfo.methodTypeSymbol(),
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
dcb.bytecodesBufWriter.asByteBuffer(),
dcb.bytecodesBufWriter.bytecodeView(),
dcb.constantPool,
dcb.context,
dcb.handlers);
@ -188,7 +188,7 @@ public final class StackMapGenerator {
private final Type thisType;
private final String methodName;
private final MethodTypeDesc methodDesc;
private final ByteBuffer bytecode;
private final RawBytecodeHelper.CodeRange bytecode;
private final SplitConstantPool cp;
private final boolean isStatic;
private final LabelContext labelContext;
@ -222,7 +222,7 @@ public final class StackMapGenerator {
String methodName,
MethodTypeDesc methodDesc,
boolean isStatic,
ByteBuffer bytecode,
RawBytecodeHelper.CodeRange bytecode,
SplitConstantPool cp,
ClassFileImpl context,
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
@ -289,7 +289,7 @@ public final class StackMapGenerator {
}
private void generate() {
exMin = bytecode.capacity();
exMin = bytecode.length();
exMax = -1;
for (var exhandler : handlers) {
int start_pc = labelContext.labelToBci(exhandler.tryStart());
@ -326,15 +326,13 @@ public final class StackMapGenerator {
//patch frame
frame.pushStack(Type.THROWABLE_TYPE);
if (maxStack < 1) maxStack = 1;
int blockSize = (i < framesCount - 1 ? frames.get(i + 1).offset : bytecode.limit()) - frame.offset;
int end = (i < framesCount - 1 ? frames.get(i + 1).offset : bytecode.length()) - 1;
//patch bytecode
bytecode.position(frame.offset);
for (int n=1; n<blockSize; n++) {
bytecode.put((byte) NOP);
}
bytecode.put((byte) ATHROW);
var arr = bytecode.array();
Arrays.fill(arr, frame.offset, end, (byte) NOP);
arr[end] = (byte) ATHROW;
//patch handlers
removeRangeFromExcTable(frame.offset, frame.offset + blockSize);
removeRangeFromExcTable(frame.offset, end + 1);
}
}
}
@ -407,17 +405,16 @@ public final class StackMapGenerator {
currentFrame.flags = 0;
currentFrame.offset = -1;
int stackmapIndex = 0;
RawBytecodeHelper bcs = new RawBytecodeHelper(bytecode);
var bcs = bytecode.start();
boolean ncf = false;
while (!bcs.isLastBytecode()) {
bcs.rawNext();
currentFrame.offset = bcs.bci;
while (bcs.next()) {
currentFrame.offset = bcs.bci();
if (stackmapIndex < frames.size()) {
int thisOffset = frames.get(stackmapIndex).offset;
if (ncf && thisOffset > bcs.bci) {
if (ncf && thisOffset > bcs.bci()) {
throw generatorError("Expecting a stack map frame");
}
if (thisOffset == bcs.bci) {
if (thisOffset == bcs.bci()) {
if (!ncf) {
currentFrame.checkAssignableTo(frames.get(stackmapIndex));
}
@ -426,11 +423,12 @@ public final class StackMapGenerator {
if (stackmapIndex == frames.size()) return; //skip the rest of this round
nextFrame = frames.get(stackmapIndex++);
}
bcs.rawNext(nextFrame.offset); //skip code up-to the next frame
currentFrame.offset = bcs.bci;
bcs.reset(nextFrame.offset); //skip code up-to the next frame
bcs.next();
currentFrame.offset = bcs.bci();
currentFrame.copyFrom(nextFrame);
nextFrame.dirty = false;
} else if (thisOffset < bcs.bci) {
} else if (thisOffset < bcs.bci()) {
throw new ClassFormatError(String.format("Bad stack map offset %d", thisOffset));
}
} else if (ncf) {
@ -441,11 +439,11 @@ public final class StackMapGenerator {
}
private boolean processBlock(RawBytecodeHelper bcs) {
int opcode = bcs.rawCode;
int opcode = bcs.opcode();
boolean ncf = false;
boolean this_uninit = false;
boolean verified_exc_handlers = false;
int bci = bcs.bci;
int bci = bcs.bci();
Type type1, type2, type3, type4;
if (RawBytecodeHelper.isStoreIntoLocal(opcode) && bci >= exMin && bci < exMax) {
processExceptionHandlerTargets(bci, this_uninit);
@ -651,7 +649,7 @@ public final class StackMapGenerator {
currentFrame.decStack(1).pushStack(cpIndexToType(bcs.getIndexU2(), cp));
case MULTIANEWARRAY -> {
type1 = cpIndexToType(bcs.getIndexU2(), cp);
int dim = bcs.getU1(bcs.bci + 3);
int dim = bcs.getU1Unchecked(bcs.bci() + 3);
for (int i = 0; i < dim; i++) {
currentFrame.popStack();
}
@ -708,14 +706,14 @@ public final class StackMapGenerator {
}
private void processSwitch(RawBytecodeHelper bcs) {
int bci = bcs.bci;
int bci = bcs.bci();
int alignedBci = RawBytecodeHelper.align(bci + 1);
int defaultOfset = bcs.getInt(alignedBci);
int defaultOffset = bcs.getIntUnchecked(alignedBci);
int keys, delta;
currentFrame.popStack();
if (bcs.rawCode == TABLESWITCH) {
int low = bcs.getInt(alignedBci + 4);
int high = bcs.getInt(alignedBci + 2 * 4);
if (bcs.opcode() == TABLESWITCH) {
int low = bcs.getIntUnchecked(alignedBci + 4);
int high = bcs.getIntUnchecked(alignedBci + 2 * 4);
if (low > high) {
throw generatorError("low must be less than or equal to high in tableswitch");
}
@ -725,31 +723,30 @@ public final class StackMapGenerator {
}
delta = 1;
} else {
keys = bcs.getInt(alignedBci + 4);
keys = bcs.getIntUnchecked(alignedBci + 4);
if (keys < 0) {
throw generatorError("number of keys in lookupswitch less than 0");
}
delta = 2;
for (int i = 0; i < (keys - 1); i++) {
int this_key = bcs.getInt(alignedBci + (2 + 2 * i) * 4);
int next_key = bcs.getInt(alignedBci + (2 + 2 * i + 2) * 4);
int this_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i) * 4);
int next_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i + 2) * 4);
if (this_key >= next_key) {
throw generatorError("Bad lookupswitch instruction");
}
}
}
int target = bci + defaultOfset;
int target = bci + defaultOffset;
checkJumpTarget(currentFrame, target);
for (int i = 0; i < keys; i++) {
alignedBci = RawBytecodeHelper.align(bcs.bci + 1);
target = bci + bcs.getInt(alignedBci + (3 + i * delta) * 4);
target = bci + bcs.getIntUnchecked(alignedBci + (3 + i * delta) * 4);
checkJumpTarget(currentFrame, target);
}
}
private void processFieldInstructions(RawBytecodeHelper bcs) {
var desc = Util.fieldTypeSymbol(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType());
switch (bcs.rawCode) {
switch (bcs.opcode()) {
case GETSTATIC ->
currentFrame.pushStack(desc);
case PUTSTATIC -> {
@ -771,13 +768,13 @@ public final class StackMapGenerator {
private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit) {
int index = bcs.getIndexU2();
int opcode = bcs.rawCode;
int opcode = bcs.opcode();
var nameAndType = opcode == INVOKEDYNAMIC
? cp.entryByIndex(index, InvokeDynamicEntry.class).nameAndType()
: cp.entryByIndex(index, MemberRefEntry.class).nameAndType();
String invokeMethodName = nameAndType.name().stringValue();
var mDesc = Util.methodTypeSymbol(nameAndType);
int bci = bcs.bci;
int bci = bcs.bci();
currentFrame.decStack(Util.parameterSlots(mDesc));
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
if (OBJECT_INITIALIZER_NAME.equals(invokeMethodName)) {
@ -790,7 +787,7 @@ public final class StackMapGenerator {
thisUninit = true;
} else if (type.tag == ITEM_UNINITIALIZED) {
int new_offset = type.bci;
int new_class_index = bcs.getIndexU2Raw(new_offset + 1);
int new_class_index = bcs.getU2(new_offset + 1);
Type new_class_type = cpIndexToType(new_class_index, cp);
if (inTryBlock) {
processExceptionHandlerTargets(bci, thisUninit);
@ -849,16 +846,16 @@ public final class StackMapGenerator {
var offsets = new BitSet() {
@Override
public void set(int i) {
if (i < 0 || i >= bytecode.capacity()) throw new IllegalArgumentException();
Preconditions.checkIndex(i, bytecode.length(), RawBytecodeHelper.IAE_FORMATTER);
super.set(i);
}
};
RawBytecodeHelper bcs = new RawBytecodeHelper(bytecode);
var bcs = bytecode.start();
boolean no_control_flow = false;
int opcode, bci = 0;
while (!bcs.isLastBytecode()) try {
opcode = bcs.rawNext();
bci = bcs.bci;
while (bcs.next()) try {
opcode = bcs.opcode();
bci = bcs.bci();
if (no_control_flow) {
offsets.set(bci);
}
@ -880,20 +877,20 @@ public final class StackMapGenerator {
}
case TABLESWITCH, LOOKUPSWITCH -> {
int aligned_bci = RawBytecodeHelper.align(bci + 1);
int default_ofset = bcs.getInt(aligned_bci);
int default_ofset = bcs.getIntUnchecked(aligned_bci);
int keys, delta;
if (bcs.rawCode == TABLESWITCH) {
int low = bcs.getInt(aligned_bci + 4);
int high = bcs.getInt(aligned_bci + 2 * 4);
if (bcs.opcode() == TABLESWITCH) {
int low = bcs.getIntUnchecked(aligned_bci + 4);
int high = bcs.getIntUnchecked(aligned_bci + 2 * 4);
keys = high - low + 1;
delta = 1;
} else {
keys = bcs.getInt(aligned_bci + 4);
keys = bcs.getIntUnchecked(aligned_bci + 4);
delta = 2;
}
offsets.set(bci + default_ofset);
for (int i = 0; i < keys; i++) {
offsets.set(bci + bcs.getInt(aligned_bci + (3 + i * delta) * 4));
offsets.set(bci + bcs.getIntUnchecked(aligned_bci + (3 + i * delta) * 4));
}
yield true;
}

View File

@ -53,7 +53,6 @@ import jdk.internal.access.SharedSecrets;
import static java.lang.classfile.ClassFile.ACC_STATIC;
import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.components.ClassPrinter;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
/**
@ -270,7 +269,7 @@ public class Util {
String methodName,
MethodTypeDesc methodDesc,
int acc,
ByteBuffer bytecode,
RawBytecodeHelper.CodeRange bytecode,
Consumer<String> dump) {
// try to dump debug info about corrupted bytecode
@ -283,8 +282,8 @@ public class Util {
public void writeBody(BufWriterImpl b) {
b.writeU2(-1);//max stack
b.writeU2(-1);//max locals
b.writeInt(bytecode.limit());
b.writeBytes(bytecode.array(), 0, bytecode.limit());
b.writeInt(bytecode.length());
b.writeBytes(bytecode.array(), 0, bytecode.length());
b.writeU2(0);//exception handlers
b.writeU2(0);//attributes
}
@ -292,13 +291,16 @@ public class Util {
ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump);
} catch (Error | Exception _) {
// fallback to bytecode hex dump
bytecode.rewind();
while (bytecode.position() < bytecode.limit()) {
dump.accept("%n%04x:".formatted(bytecode.position()));
for (int i = 0; i < 16 && bytecode.position() < bytecode.limit(); i++) {
dump.accept(" %02x".formatted(bytecode.get()));
}
dumpBytesHex(dump, bytecode.array(), bytecode.length());
}
}
public static void dumpBytesHex(Consumer<String> dump, byte[] bytes, int length) {
for (int i = 0; i < length; i++) {
if (i % 16 == 0) {
dump.accept("%n%04x:".formatted(i));
}
dump.accept(" %02x".formatted(bytes[i]));
}
}

View File

@ -24,9 +24,8 @@
*/
package jdk.internal.classfile.impl.verifier;
import java.nio.ByteBuffer;
import java.lang.classfile.ClassFile;
import jdk.internal.classfile.impl.verifier.VerificationSignature.BasicType;
import static jdk.internal.classfile.impl.verifier.VerificationSignature.BasicType.*;
@ -83,48 +82,12 @@ final class VerificationBytecodes {
return 0 <= code && code < number_of_codes;
}
static int wide_length_for(int code) {
return is_valid(code) ? _lengths[code] >> 4 : -1;
}
static boolean is_store_into_local(int code) {
return (ClassFile.ISTORE <= code && code <= ClassFile.ASTORE_3);
}
static final int _lengths[] = new int[number_of_codes];
static int special_length_at(int code, byte bytecode[], int bci, int end) {
switch (code) {
case ClassFile.WIDE:
if (bci + 1 >= end) {
return -1;
}
return wide_length_for(bytecode[bci + 1] & 0xff);
case ClassFile.TABLESWITCH:
int aligned_bci = align(bci + 1);
if (aligned_bci + 3 * 4 >= end) {
return -1;
}
ByteBuffer bb = ByteBuffer.wrap(bytecode, aligned_bci + 1 * 4, 2 * 4);
int lo = bb.getInt();
int hi = bb.getInt();
int len = aligned_bci - bci + (3 + hi - lo + 1) * 4;
return len > 0 ? len : -1;
case ClassFile.LOOKUPSWITCH:
case _fast_binaryswitch:
case _fast_linearswitch:
aligned_bci = align(bci + 1);
if (aligned_bci + 2 * 4 >= end) {
return -1;
}
int npairs = ByteBuffer.wrap(bytecode, aligned_bci + 4, 4).getInt();
len = aligned_bci - bci + (2 + 2 * npairs) * 4;
return len > 0 ? len : -1;
default:
return 0;
}
}
static int align(int n) {
return (n + 3) & ~3;
}

View File

@ -24,7 +24,6 @@
*/
package jdk.internal.classfile.impl.verifier;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -316,20 +315,20 @@ public final class VerifierImpl {
if (code_length < 1 || code_length > MAX_CODE_SIZE) {
verifyError(String.format("Invalid method Code length %d", code_length));
}
var code = ByteBuffer.wrap(codeArray, 0, _method.codeLength());
byte[] code_data = generate_code_data(code, code_length);
var code = RawBytecodeHelper.of(codeArray);
byte[] code_data = generate_code_data(code);
int ex_minmax[] = new int[] {code_length, -1};
verify_exception_handler_table(code_length, code_data, ex_minmax);
verify_local_variable_table(code_length, code_data);
VerificationTable stackmap_table = new VerificationTable(stackmap_data, current_frame, max_locals, max_stack, code_data, code_length, cp, this);
var bcs = new RawBytecodeHelper(code);
var bcs = code.start();
boolean no_control_flow = false;
int opcode;
while (!bcs.isLastBytecode()) {
opcode = bcs.rawNext();
bci = bcs.bci;
while (bcs.next()) {
opcode = bcs.opcode();
bci = bcs.bci();
current_frame.set_offset(bci);
current_frame.set_mark();
stackmap_index = verify_stackmap_table(stackmap_index, bci, current_frame, stackmap_table, no_control_flow);
@ -340,7 +339,7 @@ public final class VerifierImpl {
int target;
VerificationType type, type2 = null;
VerificationType atype;
if (bcs.isWide) {
if (bcs.isWide()) {
if (opcode != ClassFile.IINC && opcode != ClassFile.ILOAD
&& opcode != ClassFile.ALOAD && opcode != ClassFile.LLOAD
&& opcode != ClassFile.ISTORE && opcode != ClassFile.ASTORE
@ -1195,7 +1194,7 @@ public final class VerifierImpl {
case ClassFile.MULTIANEWARRAY :
{
index = bcs.getIndexU2();
int dim = _method.codeArray()[bcs.bci+3] & 0xff;
int dim = _method.codeArray()[bcs.bci() +3] & 0xff;
verify_cp_class_type(bci, index, cp);
VerificationType new_array_type =
cp_index_to_type(index, cp);
@ -1230,13 +1229,13 @@ public final class VerifierImpl {
}
}
private byte[] generate_code_data(ByteBuffer code, int code_length) {
byte code_data[] = new byte[code_length];
var bcs = new RawBytecodeHelper(code);
while (!bcs.isLastBytecode()) {
if (bcs.rawNext() != ILLEGAL) {
int bci = bcs.bci;
if (bcs.rawCode == ClassFile.NEW) {
private byte[] generate_code_data(RawBytecodeHelper.CodeRange code) {
byte[] code_data = new byte[code.length()];
var bcs = code.start();
while (bcs.next()) {
if (bcs.opcode() != ILLEGAL) {
int bci = bcs.bci();
if (bcs.opcode() == ClassFile.NEW) {
code_data[bci] = NEW_OFFSET;
} else {
code_data[bci] = BYTECODE_OFFSET;
@ -1410,7 +1409,7 @@ public final class VerifierImpl {
}
void verify_switch(RawBytecodeHelper bcs, int code_length, byte[] code_data, VerificationFrame current_frame, VerificationTable stackmap_table) {
int bci = bcs.bci;
int bci = bcs.bci();
int aligned_bci = VerificationBytecodes.align(bci + 1);
// 4639449 & 4647081: padding bytes must be 0
if (_klass.majorVersion() < NONZERO_PADDING_BYTES_IN_SWITCH_MAJOR_VERSION) {
@ -1422,12 +1421,12 @@ public final class VerifierImpl {
padding_offset++;
}
}
int default_ofset = bcs.getInt(aligned_bci);
int default_offset = bcs.getIntUnchecked(aligned_bci);
int keys, delta;
current_frame.pop_stack(VerificationType.integer_type);
if (bcs.rawCode == ClassFile.TABLESWITCH) {
int low = bcs.getInt(aligned_bci + 4);
int high = bcs.getInt(aligned_bci + 2*4);
if (bcs.opcode() == ClassFile.TABLESWITCH) {
int low = bcs.getIntUnchecked(aligned_bci + 4);
int high = bcs.getIntUnchecked(aligned_bci + 2*4);
if (low > high) {
verifyError("low must be less than or equal to high in tableswitch");
}
@ -1438,31 +1437,31 @@ public final class VerifierImpl {
delta = 1;
} else {
// Make sure that the lookupswitch items are sorted
keys = bcs.getInt(aligned_bci + 4);
keys = bcs.getIntUnchecked(aligned_bci + 4);
if (keys < 0) {
verifyError("number of keys in lookupswitch less than 0");
}
delta = 2;
for (int i = 0; i < (keys - 1); i++) {
int this_key = bcs.getInt(aligned_bci + (2+2*i)*4);
int next_key = bcs.getInt(aligned_bci + (2+2*i+2)*4);
int this_key = bcs.getIntUnchecked(aligned_bci + (2+2*i)*4);
int next_key = bcs.getIntUnchecked(aligned_bci + (2+2*i+2)*4);
if (this_key >= next_key) {
verifyError("Bad lookupswitch instruction");
}
}
}
int target = bci + default_ofset;
int target = bci + default_offset;
stackmap_table.check_jump_target(current_frame, target);
for (int i = 0; i < keys; i++) {
aligned_bci = VerificationBytecodes.align(bcs.bci + 1);
target = bci + bcs.getInt(aligned_bci + (3+i*delta)*4);
aligned_bci = VerificationBytecodes.align(bcs.bci() + 1);
target = bci + bcs.getIntUnchecked(aligned_bci + (3+i*delta)*4);
stackmap_table.check_jump_target(current_frame, target);
}
}
void verify_field_instructions(RawBytecodeHelper bcs, VerificationFrame current_frame, ConstantPoolWrapper cp, boolean allow_arrays) {
int index = bcs.getIndexU2();
verify_cp_type(bcs.bci, index, cp, 1 << JVM_CONSTANT_Fieldref);
verify_cp_type(bcs.bci(), index, cp, 1 << JVM_CONSTANT_Fieldref);
String field_name = cp.refNameAt(index);
String field_sig = cp.refSignatureAt(index);
if (!VerificationSignature.isValidTypeSignature(field_sig)) verifyError("Invalid field signature");
@ -1477,7 +1476,7 @@ public final class VerifierImpl {
VerificationType stack_object_type = null;
int n = change_sig_to_verificationType(sig_stream, field_type, 0);
boolean is_assignable;
switch (bcs.rawCode) {
switch (bcs.opcode()) {
case ClassFile.GETSTATIC -> {
for (int i = 0; i < n; i++) {
current_frame.push_stack(field_type[i]);
@ -1524,7 +1523,7 @@ public final class VerifierImpl {
boolean verify_invoke_init(RawBytecodeHelper bcs, int ref_class_index, VerificationType ref_class_type,
VerificationFrame current_frame, int code_length, boolean in_try_block,
boolean this_uninit, ConstantPoolWrapper cp, VerificationTable stackmap_table) {
int bci = bcs.bci;
int bci = bcs.bci();
VerificationType type = current_frame.pop_stack(VerificationType.reference_check);
if (type.is_uninitialized_this(this)) {
String superk_name = current_class().superclassName();
@ -1552,7 +1551,7 @@ public final class VerifierImpl {
if (new_offset > (code_length - 3) || (_method.codeArray()[new_offset] & 0xff) != ClassFile.NEW) {
verifyError("Expecting new instruction");
}
int new_class_index = bcs.getIndexU2Raw(new_offset + 1);
int new_class_index = bcs.getU2(new_offset + 1);
verify_cp_class_type(bci, new_class_index, cp);
VerificationType new_class_type = cp_index_to_type(
new_class_index, cp);
@ -1583,7 +1582,7 @@ public final class VerifierImpl {
boolean verify_invoke_instructions(RawBytecodeHelper bcs, int code_length, VerificationFrame current_frame, boolean in_try_block, boolean this_uninit, VerificationType return_type, ConstantPoolWrapper cp, VerificationTable stackmap_table) {
// Make sure the constant pool item is the right type
int index = bcs.getIndexU2();
int opcode = bcs.rawCode;
int opcode = bcs.opcode();
int types = 0;
switch (opcode) {
case ClassFile.INVOKEINTERFACE:
@ -1601,7 +1600,7 @@ public final class VerifierImpl {
default:
types = 1 << JVM_CONSTANT_Methodref;
}
verify_cp_type(bcs.bci, index, cp, types);
verify_cp_type(bcs.bci(), index, cp, types);
String method_name = cp.refNameAt(index);
String method_sig = cp.refSignatureAt(index);
if (!VerificationSignature.isValidMethodSignature(method_sig)) verifyError("Invalid method signature");
@ -1619,7 +1618,7 @@ public final class VerifierImpl {
mth_sig_verif_types = new sig_as_verification_types(verif_types);
create_method_sig_entry(mth_sig_verif_types, sig);
int nargs = mth_sig_verif_types.num_args();
int bci = bcs.bci;
int bci = bcs.bci();
if (opcode == ClassFile.INVOKEINTERFACE) {
if ((_method.codeArray()[bci+3] & 0xff) != (nargs+1)) {
verifyError("Inconsistent args count operand in invokeinterface");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,14 @@
* @summary Testing ClassFile Util.
* @run junit UtilTest
*/
import java.lang.classfile.ClassFile;
import java.lang.classfile.Opcode;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.BitSet;
import jdk.internal.classfile.impl.RawBytecodeHelper;
import jdk.internal.classfile.impl.Util;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@ -76,4 +83,21 @@ class UtilTest {
private void assertSlots(String methodDesc, int slots) {
assertEquals(Util.parameterSlots(MethodTypeDesc.ofDescriptor(methodDesc)), slots);
}
@Test
void testOpcodeLengthTable() {
var lengths = new byte[0x100];
Arrays.fill(lengths, (byte) -1);
for (var op : Opcode.values()) {
if (!op.isWide()) {
lengths[op.bytecode()] = (byte) op.sizeIfFixed();
} else {
// Wide pseudo-opcodes have double the length as normal variants
// Must match logic in checkSpecialInstruction()
assertEquals(op.sizeIfFixed(), lengths[op.bytecode() & 0xFF] * 2, op + " size");
}
}
assertArrayEquals(lengths, RawBytecodeHelper.LENGTHS);
}
}

View File

@ -23,24 +23,21 @@
package org.openjdk.bench.jdk.classfile;
import java.io.IOException;
import java.lang.classfile.Attributes;
import java.lang.constant.ClassDesc;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassReader;
import java.lang.classfile.MethodModel;
import java.lang.classfile.constantpool.ConstantPool;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.constant.MethodTypeDesc;
import jdk.internal.classfile.impl.AbstractPseudoInstruction;
import jdk.internal.classfile.impl.CodeImpl;
import jdk.internal.classfile.impl.LabelContext;
import jdk.internal.classfile.impl.ClassFileImpl;
import jdk.internal.classfile.impl.RawBytecodeHelper;
import jdk.internal.classfile.impl.SplitConstantPool;
import jdk.internal.classfile.impl.StackCounter;
import jdk.internal.classfile.impl.StackMapGenerator;
@ -70,7 +67,7 @@ public class CodeAttributeTools {
String methodName,
MethodTypeDesc methodDesc,
boolean isStatic,
ByteBuffer bytecode,
RawBytecodeHelper.CodeRange bytecode,
ConstantPoolBuilder constantPool,
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {}
@ -85,15 +82,14 @@ public class CodeAttributeTools {
var thisCls = clm.thisClass().asSymbol();
var cp = new SplitConstantPool((ClassReader)clm.constantPool());
for (var m : clm.methods()) {
m.code().ifPresent(com -> {
var bb = ByteBuffer.wrap(((CodeImpl)com).contents());
m.findAttribute(Attributes.code()).ifPresent(com -> {
data.add(new GenData(
(LabelContext)com,
thisCls,
m.methodName().stringValue(),
m.methodTypeSymbol(),
(m.flags().flagsMask() & ClassFile.ACC_STATIC) != 0,
bb.slice(8, bb.getInt(4)),
RawBytecodeHelper.of(com.codeArray()),
cp,
com.exceptionHandlers().stream().map(eh -> (AbstractPseudoInstruction.ExceptionCatchImpl)eh).toList()));
});
@ -112,7 +108,7 @@ public class CodeAttributeTools {
d.methodName(),
d.methodDesc(),
d.isStatic(),
d.bytecode().rewind(),
d.bytecode(),
(SplitConstantPool)d.constantPool(),
(ClassFileImpl)ClassFile.of(),
d.handlers()));
@ -127,7 +123,7 @@ public class CodeAttributeTools {
d.methodName(),
d.methodDesc(),
d.isStatic(),
d.bytecode().rewind(),
d.bytecode(),
(SplitConstantPool)d.constantPool(),
d.handlers()));
}