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; package jdk.internal.classfile.impl;
import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.lang.classfile.BufWriter; import java.lang.classfile.BufWriter;
@ -247,8 +246,8 @@ public final class BufWriterImpl implements BufWriter {
return offset; return offset;
} }
public ByteBuffer asByteBuffer() { public RawBytecodeHelper.CodeRange bytecodeView() {
return ByteBuffer.wrap(elems, 0, offset).slice(); return RawBytecodeHelper.of(elems, offset);
} }
public void copyTo(byte[] array, int bufferOffset) { 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -24,23 +24,66 @@
*/ */
package jdk.internal.classfile.impl; package jdk.internal.classfile.impl;
import java.nio.ByteBuffer; import java.util.List;
import static java.lang.classfile.ClassFile.ASTORE_3; import java.util.function.BiFunction;
import static java.lang.classfile.ClassFile.ISTORE; import java.util.function.Function;
import static java.lang.classfile.ClassFile.LOOKUPSWITCH;
import static java.lang.classfile.ClassFile.TABLESWITCH; import jdk.internal.misc.Unsafe;
import static java.lang.classfile.ClassFile.WIDE; import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.Stable;
import static java.lang.classfile.ClassFile.*;
public final class RawBytecodeHelper { 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; 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, * The length of opcodes, or -1 for no fixed length.
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, * This is generated as if:
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, * {@snippet lang=java :
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, * var lengths = new byte[0x100];
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 * 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) { public static boolean isStoreIntoLocal(int code) {
@ -51,121 +94,200 @@ public final class RawBytecodeHelper {
return (n + 3) & ~3; return (n + 3) & ~3;
} }
private final ByteBuffer bytecode; private static final Unsafe UNSAFE = Unsafe.getUnsafe();
public int bci, nextBci, endBci; public final CodeRange code;
public int rawCode; private int nextBci;
public boolean isWide; private int bci;
private int opcode;
private boolean isWide;
public RawBytecodeHelper(ByteBuffer bytecode) { public static CodeRange of(byte[] array) {
this.bytecode = bytecode; return new CodeRange(array, array.length);
this.bci = 0;
this.nextBci = 0;
this.endBci = bytecode.capacity();
} }
public boolean isLastBytecode() { public static CodeRange of(byte[] array, int limit) {
return nextBci >= endBci; 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) { public int getShort(int bci) {
return bytecode.getShort(bci); Preconditions.checkFromIndexSize(bci, 2, endBci(), IAE_FORMATTER);
} return getShortUnchecked(bci);
public int dest() {
return bci + getShort(bci + 1);
} }
public int getInt(int 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() { public int destW() {
return bci + getInt(bci + 1); return bci + getIntUnchecked(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;
}
} }
// *load, *store, iinc
public int getIndex() { 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() { public int getIndexU2() {
return getIndexU2Raw(bci + 1); return getU2Unchecked(bci + 1);
} }
public int getIndexU2Raw(int bci) { // Transition methods
return bytecode.getShort(bci) & 0xffff;
/**
* 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.classfile.constantpool.MemberRefEntry;
import java.lang.constant.ClassDesc; import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc; import java.lang.constant.MethodTypeDesc;
import java.nio.ByteBuffer;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.BitSet; import java.util.BitSet;
import java.util.List; import java.util.List;
@ -55,7 +54,7 @@ public final class StackCounter {
dcb.methodInfo.methodName().stringValue(), dcb.methodInfo.methodName().stringValue(),
dcb.methodInfo.methodTypeSymbol(), dcb.methodInfo.methodTypeSymbol(),
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
dcb.bytecodesBufWriter.asByteBuffer(), dcb.bytecodesBufWriter.bytecodeView(),
dcb.constantPool, dcb.constantPool,
dcb.handlers); dcb.handlers);
} }
@ -67,7 +66,6 @@ public final class StackCounter {
private final String methodName; private final String methodName;
private final MethodTypeDesc methodDesc; private final MethodTypeDesc methodDesc;
private final boolean isStatic; private final boolean isStatic;
private final ByteBuffer bytecode;
private final SplitConstantPool cp; private final SplitConstantPool cp;
private final Queue<Target> targets; private final Queue<Target> targets;
private final BitSet visited; private final BitSet visited;
@ -91,12 +89,12 @@ public final class StackCounter {
Target en; Target en;
while ((en = targets.poll()) != null) { while ((en = targets.poll()) != null) {
if (!visited.get(en.bci)) { if (!visited.get(en.bci)) {
bcs.nextBci = en.bci; bcs.reset(en.bci);
stack = en.stack; stack = en.stack;
return true; return true;
} }
} }
bcs.nextBci = bcs.endBci; bcs.reset(bcs.endBci());
return false; return false;
} }
@ -106,14 +104,13 @@ public final class StackCounter {
String methodName, String methodName,
MethodTypeDesc methodDesc, MethodTypeDesc methodDesc,
boolean isStatic, boolean isStatic,
ByteBuffer bytecode, RawBytecodeHelper.CodeRange bytecode,
SplitConstantPool cp, SplitConstantPool cp,
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) { List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
this.thisClass = thisClass; this.thisClass = thisClass;
this.methodName = methodName; this.methodName = methodName;
this.methodDesc = methodDesc; this.methodDesc = methodDesc;
this.isStatic = isStatic; this.isStatic = isStatic;
this.bytecode = bytecode;
this.cp = cp; this.cp = cp;
targets = new ArrayDeque<>(); targets = new ArrayDeque<>();
stack = rets = 0; stack = rets = 0;
@ -132,14 +129,13 @@ public final class StackCounter {
} }
maxLocals = isStatic ? 0 : 1; maxLocals = isStatic ? 0 : 1;
maxLocals += Util.parameterSlots(methodDesc); maxLocals += Util.parameterSlots(methodDesc);
bcs = new RawBytecodeHelper(bytecode); bcs = bytecode.start();
visited = new BitSet(bcs.endBci); visited = new BitSet(bcs.endBci());
targets.add(new Target(0, 0)); targets.add(new Target(0, 0));
while (next()) { while (next()) {
while (!bcs.isLastBytecode()) { while (bcs.next()) {
bcs.rawNext(); int opcode = bcs.opcode();
int opcode = bcs.rawCode; int bci = bcs.bci();
int bci = bcs.bci;
visited.set(bci); visited.set(bci);
switch (opcode) { switch (opcode) {
case NOP, LALOAD, DALOAD, SWAP, INEG, ARRAYLENGTH, INSTANCEOF, LNEG, FNEG, DNEG, I2F, L2D, F2I, D2L, I2B, I2C, I2S, 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 -> { case TABLESWITCH, LOOKUPSWITCH -> {
int alignedBci = RawBytecodeHelper.align(bci + 1); int alignedBci = RawBytecodeHelper.align(bci + 1);
int defaultOfset = bcs.getInt(alignedBci); int defaultOffset = bcs.getIntUnchecked(alignedBci);
int keys, delta; int keys, delta;
addStackSlot(-1); addStackSlot(-1);
if (bcs.rawCode == TABLESWITCH) { if (bcs.opcode() == TABLESWITCH) {
int low = bcs.getInt(alignedBci + 4); int low = bcs.getIntUnchecked(alignedBci + 4);
int high = bcs.getInt(alignedBci + 2 * 4); int high = bcs.getIntUnchecked(alignedBci + 2 * 4);
if (low > high) { if (low > high) {
throw error("low must be less than or equal to high in tableswitch"); throw error("low must be less than or equal to high in tableswitch");
} }
@ -282,24 +278,23 @@ public final class StackCounter {
} }
delta = 1; delta = 1;
} else { } else {
keys = bcs.getInt(alignedBci + 4); keys = bcs.getIntUnchecked(alignedBci + 4);
if (keys < 0) { if (keys < 0) {
throw error("number of keys in lookupswitch less than 0"); throw error("number of keys in lookupswitch less than 0");
} }
delta = 2; delta = 2;
for (int i = 0; i < (keys - 1); i++) { for (int i = 0; i < (keys - 1); i++) {
int this_key = bcs.getInt(alignedBci + (2 + 2 * i) * 4); int this_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i) * 4);
int next_key = bcs.getInt(alignedBci + (2 + 2 * i + 2) * 4); int next_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i + 2) * 4);
if (this_key >= next_key) { if (this_key >= next_key) {
throw error("Bad lookupswitch instruction"); throw error("Bad lookupswitch instruction");
} }
} }
} }
int target = bci + defaultOfset; int target = bci + defaultOffset;
jump(target); jump(target);
for (int i = 0; i < keys; i++) { for (int i = 0; i < keys; i++) {
alignedBci = RawBytecodeHelper.align(bcs.bci + 1); target = bci + bcs.getIntUnchecked(alignedBci + (3 + i * delta) * 4);
target = bci + bcs.getInt(alignedBci + (3 + i * delta) * 4);
jump(target); jump(target);
} }
next(); next();
@ -314,7 +309,7 @@ public final class StackCounter {
} }
case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> { case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> {
var tk = TypeKind.fromDescriptor(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType().type()); var tk = TypeKind.fromDescriptor(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType().type());
switch (bcs.rawCode) { switch (bcs.opcode()) {
case GETSTATIC -> case GETSTATIC ->
addStackSlot(tk.slotSize()); addStackSlot(tk.slotSize());
case PUTSTATIC -> case PUTSTATIC ->
@ -337,7 +332,7 @@ public final class StackCounter {
addStackSlot(delta); addStackSlot(delta);
} }
case MULTIANEWARRAY -> case MULTIANEWARRAY ->
addStackSlot(1 - bcs.getU1(bcs.bci + 3)); addStackSlot(1 - bcs.getU1Unchecked(bcs.bci() + 3));
case JSR -> { case JSR -> {
addStackSlot(+1); addStackSlot(+1);
jump(bcs.dest()); //here we lost track of the exact stack size after return from subroutine 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) { private IllegalArgumentException error(String msg) {
var sb = new StringBuilder("%s at bytecode offset %d of method %s(%s)".formatted( var sb = new StringBuilder("%s at bytecode offset %d of method %s(%s)".formatted(
msg, msg,
bcs.bci, bcs.bci(),
methodName, methodName,
methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(",")))); 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()); 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.classfile.constantpool.MemberRefEntry;
import java.lang.constant.ClassDesc; import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc; import java.lang.constant.MethodTypeDesc;
import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
@ -46,6 +45,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jdk.internal.constant.ReferenceClassDescImpl; import jdk.internal.constant.ReferenceClassDescImpl;
import jdk.internal.util.Preconditions;
import static java.lang.classfile.ClassFile.*; import static java.lang.classfile.ClassFile.*;
import static java.lang.constant.ConstantDescs.*; import static java.lang.constant.ConstantDescs.*;
@ -152,7 +152,7 @@ public final class StackMapGenerator {
dcb.methodInfo.methodName().stringValue(), dcb.methodInfo.methodName().stringValue(),
dcb.methodInfo.methodTypeSymbol(), dcb.methodInfo.methodTypeSymbol(),
(dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0,
dcb.bytecodesBufWriter.asByteBuffer(), dcb.bytecodesBufWriter.bytecodeView(),
dcb.constantPool, dcb.constantPool,
dcb.context, dcb.context,
dcb.handlers); dcb.handlers);
@ -188,7 +188,7 @@ public final class StackMapGenerator {
private final Type thisType; private final Type thisType;
private final String methodName; private final String methodName;
private final MethodTypeDesc methodDesc; private final MethodTypeDesc methodDesc;
private final ByteBuffer bytecode; private final RawBytecodeHelper.CodeRange bytecode;
private final SplitConstantPool cp; private final SplitConstantPool cp;
private final boolean isStatic; private final boolean isStatic;
private final LabelContext labelContext; private final LabelContext labelContext;
@ -222,7 +222,7 @@ public final class StackMapGenerator {
String methodName, String methodName,
MethodTypeDesc methodDesc, MethodTypeDesc methodDesc,
boolean isStatic, boolean isStatic,
ByteBuffer bytecode, RawBytecodeHelper.CodeRange bytecode,
SplitConstantPool cp, SplitConstantPool cp,
ClassFileImpl context, ClassFileImpl context,
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) { List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {
@ -289,7 +289,7 @@ public final class StackMapGenerator {
} }
private void generate() { private void generate() {
exMin = bytecode.capacity(); exMin = bytecode.length();
exMax = -1; exMax = -1;
for (var exhandler : handlers) { for (var exhandler : handlers) {
int start_pc = labelContext.labelToBci(exhandler.tryStart()); int start_pc = labelContext.labelToBci(exhandler.tryStart());
@ -326,15 +326,13 @@ public final class StackMapGenerator {
//patch frame //patch frame
frame.pushStack(Type.THROWABLE_TYPE); frame.pushStack(Type.THROWABLE_TYPE);
if (maxStack < 1) maxStack = 1; 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 //patch bytecode
bytecode.position(frame.offset); var arr = bytecode.array();
for (int n=1; n<blockSize; n++) { Arrays.fill(arr, frame.offset, end, (byte) NOP);
bytecode.put((byte) NOP); arr[end] = (byte) ATHROW;
}
bytecode.put((byte) ATHROW);
//patch handlers //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.flags = 0;
currentFrame.offset = -1; currentFrame.offset = -1;
int stackmapIndex = 0; int stackmapIndex = 0;
RawBytecodeHelper bcs = new RawBytecodeHelper(bytecode); var bcs = bytecode.start();
boolean ncf = false; boolean ncf = false;
while (!bcs.isLastBytecode()) { while (bcs.next()) {
bcs.rawNext(); currentFrame.offset = bcs.bci();
currentFrame.offset = bcs.bci;
if (stackmapIndex < frames.size()) { if (stackmapIndex < frames.size()) {
int thisOffset = frames.get(stackmapIndex).offset; int thisOffset = frames.get(stackmapIndex).offset;
if (ncf && thisOffset > bcs.bci) { if (ncf && thisOffset > bcs.bci()) {
throw generatorError("Expecting a stack map frame"); throw generatorError("Expecting a stack map frame");
} }
if (thisOffset == bcs.bci) { if (thisOffset == bcs.bci()) {
if (!ncf) { if (!ncf) {
currentFrame.checkAssignableTo(frames.get(stackmapIndex)); currentFrame.checkAssignableTo(frames.get(stackmapIndex));
} }
@ -426,11 +423,12 @@ public final class StackMapGenerator {
if (stackmapIndex == frames.size()) return; //skip the rest of this round if (stackmapIndex == frames.size()) return; //skip the rest of this round
nextFrame = frames.get(stackmapIndex++); nextFrame = frames.get(stackmapIndex++);
} }
bcs.rawNext(nextFrame.offset); //skip code up-to the next frame bcs.reset(nextFrame.offset); //skip code up-to the next frame
currentFrame.offset = bcs.bci; bcs.next();
currentFrame.offset = bcs.bci();
currentFrame.copyFrom(nextFrame); currentFrame.copyFrom(nextFrame);
nextFrame.dirty = false; nextFrame.dirty = false;
} else if (thisOffset < bcs.bci) { } else if (thisOffset < bcs.bci()) {
throw new ClassFormatError(String.format("Bad stack map offset %d", thisOffset)); throw new ClassFormatError(String.format("Bad stack map offset %d", thisOffset));
} }
} else if (ncf) { } else if (ncf) {
@ -441,11 +439,11 @@ public final class StackMapGenerator {
} }
private boolean processBlock(RawBytecodeHelper bcs) { private boolean processBlock(RawBytecodeHelper bcs) {
int opcode = bcs.rawCode; int opcode = bcs.opcode();
boolean ncf = false; boolean ncf = false;
boolean this_uninit = false; boolean this_uninit = false;
boolean verified_exc_handlers = false; boolean verified_exc_handlers = false;
int bci = bcs.bci; int bci = bcs.bci();
Type type1, type2, type3, type4; Type type1, type2, type3, type4;
if (RawBytecodeHelper.isStoreIntoLocal(opcode) && bci >= exMin && bci < exMax) { if (RawBytecodeHelper.isStoreIntoLocal(opcode) && bci >= exMin && bci < exMax) {
processExceptionHandlerTargets(bci, this_uninit); processExceptionHandlerTargets(bci, this_uninit);
@ -651,7 +649,7 @@ public final class StackMapGenerator {
currentFrame.decStack(1).pushStack(cpIndexToType(bcs.getIndexU2(), cp)); currentFrame.decStack(1).pushStack(cpIndexToType(bcs.getIndexU2(), cp));
case MULTIANEWARRAY -> { case MULTIANEWARRAY -> {
type1 = cpIndexToType(bcs.getIndexU2(), cp); 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++) { for (int i = 0; i < dim; i++) {
currentFrame.popStack(); currentFrame.popStack();
} }
@ -708,14 +706,14 @@ public final class StackMapGenerator {
} }
private void processSwitch(RawBytecodeHelper bcs) { private void processSwitch(RawBytecodeHelper bcs) {
int bci = bcs.bci; int bci = bcs.bci();
int alignedBci = RawBytecodeHelper.align(bci + 1); int alignedBci = RawBytecodeHelper.align(bci + 1);
int defaultOfset = bcs.getInt(alignedBci); int defaultOffset = bcs.getIntUnchecked(alignedBci);
int keys, delta; int keys, delta;
currentFrame.popStack(); currentFrame.popStack();
if (bcs.rawCode == TABLESWITCH) { if (bcs.opcode() == TABLESWITCH) {
int low = bcs.getInt(alignedBci + 4); int low = bcs.getIntUnchecked(alignedBci + 4);
int high = bcs.getInt(alignedBci + 2 * 4); int high = bcs.getIntUnchecked(alignedBci + 2 * 4);
if (low > high) { if (low > high) {
throw generatorError("low must be less than or equal to high in tableswitch"); throw generatorError("low must be less than or equal to high in tableswitch");
} }
@ -725,31 +723,30 @@ public final class StackMapGenerator {
} }
delta = 1; delta = 1;
} else { } else {
keys = bcs.getInt(alignedBci + 4); keys = bcs.getIntUnchecked(alignedBci + 4);
if (keys < 0) { if (keys < 0) {
throw generatorError("number of keys in lookupswitch less than 0"); throw generatorError("number of keys in lookupswitch less than 0");
} }
delta = 2; delta = 2;
for (int i = 0; i < (keys - 1); i++) { for (int i = 0; i < (keys - 1); i++) {
int this_key = bcs.getInt(alignedBci + (2 + 2 * i) * 4); int this_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i) * 4);
int next_key = bcs.getInt(alignedBci + (2 + 2 * i + 2) * 4); int next_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i + 2) * 4);
if (this_key >= next_key) { if (this_key >= next_key) {
throw generatorError("Bad lookupswitch instruction"); throw generatorError("Bad lookupswitch instruction");
} }
} }
} }
int target = bci + defaultOfset; int target = bci + defaultOffset;
checkJumpTarget(currentFrame, target); checkJumpTarget(currentFrame, target);
for (int i = 0; i < keys; i++) { for (int i = 0; i < keys; i++) {
alignedBci = RawBytecodeHelper.align(bcs.bci + 1); target = bci + bcs.getIntUnchecked(alignedBci + (3 + i * delta) * 4);
target = bci + bcs.getInt(alignedBci + (3 + i * delta) * 4);
checkJumpTarget(currentFrame, target); checkJumpTarget(currentFrame, target);
} }
} }
private void processFieldInstructions(RawBytecodeHelper bcs) { private void processFieldInstructions(RawBytecodeHelper bcs) {
var desc = Util.fieldTypeSymbol(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType()); var desc = Util.fieldTypeSymbol(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType());
switch (bcs.rawCode) { switch (bcs.opcode()) {
case GETSTATIC -> case GETSTATIC ->
currentFrame.pushStack(desc); currentFrame.pushStack(desc);
case PUTSTATIC -> { case PUTSTATIC -> {
@ -771,13 +768,13 @@ public final class StackMapGenerator {
private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit) { private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit) {
int index = bcs.getIndexU2(); int index = bcs.getIndexU2();
int opcode = bcs.rawCode; int opcode = bcs.opcode();
var nameAndType = opcode == INVOKEDYNAMIC var nameAndType = opcode == INVOKEDYNAMIC
? cp.entryByIndex(index, InvokeDynamicEntry.class).nameAndType() ? cp.entryByIndex(index, InvokeDynamicEntry.class).nameAndType()
: cp.entryByIndex(index, MemberRefEntry.class).nameAndType(); : cp.entryByIndex(index, MemberRefEntry.class).nameAndType();
String invokeMethodName = nameAndType.name().stringValue(); String invokeMethodName = nameAndType.name().stringValue();
var mDesc = Util.methodTypeSymbol(nameAndType); var mDesc = Util.methodTypeSymbol(nameAndType);
int bci = bcs.bci; int bci = bcs.bci();
currentFrame.decStack(Util.parameterSlots(mDesc)); currentFrame.decStack(Util.parameterSlots(mDesc));
if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) { if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
if (OBJECT_INITIALIZER_NAME.equals(invokeMethodName)) { if (OBJECT_INITIALIZER_NAME.equals(invokeMethodName)) {
@ -790,7 +787,7 @@ public final class StackMapGenerator {
thisUninit = true; thisUninit = true;
} else if (type.tag == ITEM_UNINITIALIZED) { } else if (type.tag == ITEM_UNINITIALIZED) {
int new_offset = type.bci; 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); Type new_class_type = cpIndexToType(new_class_index, cp);
if (inTryBlock) { if (inTryBlock) {
processExceptionHandlerTargets(bci, thisUninit); processExceptionHandlerTargets(bci, thisUninit);
@ -849,16 +846,16 @@ public final class StackMapGenerator {
var offsets = new BitSet() { var offsets = new BitSet() {
@Override @Override
public void set(int i) { 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); super.set(i);
} }
}; };
RawBytecodeHelper bcs = new RawBytecodeHelper(bytecode); var bcs = bytecode.start();
boolean no_control_flow = false; boolean no_control_flow = false;
int opcode, bci = 0; int opcode, bci = 0;
while (!bcs.isLastBytecode()) try { while (bcs.next()) try {
opcode = bcs.rawNext(); opcode = bcs.opcode();
bci = bcs.bci; bci = bcs.bci();
if (no_control_flow) { if (no_control_flow) {
offsets.set(bci); offsets.set(bci);
} }
@ -880,20 +877,20 @@ public final class StackMapGenerator {
} }
case TABLESWITCH, LOOKUPSWITCH -> { case TABLESWITCH, LOOKUPSWITCH -> {
int aligned_bci = RawBytecodeHelper.align(bci + 1); int aligned_bci = RawBytecodeHelper.align(bci + 1);
int default_ofset = bcs.getInt(aligned_bci); int default_ofset = bcs.getIntUnchecked(aligned_bci);
int keys, delta; int keys, delta;
if (bcs.rawCode == TABLESWITCH) { if (bcs.opcode() == TABLESWITCH) {
int low = bcs.getInt(aligned_bci + 4); int low = bcs.getIntUnchecked(aligned_bci + 4);
int high = bcs.getInt(aligned_bci + 2 * 4); int high = bcs.getIntUnchecked(aligned_bci + 2 * 4);
keys = high - low + 1; keys = high - low + 1;
delta = 1; delta = 1;
} else { } else {
keys = bcs.getInt(aligned_bci + 4); keys = bcs.getIntUnchecked(aligned_bci + 4);
delta = 2; delta = 2;
} }
offsets.set(bci + default_ofset); offsets.set(bci + default_ofset);
for (int i = 0; i < keys; i++) { 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; yield true;
} }

View File

@ -53,7 +53,6 @@ import jdk.internal.access.SharedSecrets;
import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.classfile.ClassFile.ACC_STATIC;
import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.attribute.CodeAttribute;
import java.lang.classfile.components.ClassPrinter; import java.lang.classfile.components.ClassPrinter;
import java.nio.ByteBuffer;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@ -270,7 +269,7 @@ public class Util {
String methodName, String methodName,
MethodTypeDesc methodDesc, MethodTypeDesc methodDesc,
int acc, int acc,
ByteBuffer bytecode, RawBytecodeHelper.CodeRange bytecode,
Consumer<String> dump) { Consumer<String> dump) {
// try to dump debug info about corrupted bytecode // try to dump debug info about corrupted bytecode
@ -283,8 +282,8 @@ public class Util {
public void writeBody(BufWriterImpl b) { public void writeBody(BufWriterImpl b) {
b.writeU2(-1);//max stack b.writeU2(-1);//max stack
b.writeU2(-1);//max locals b.writeU2(-1);//max locals
b.writeInt(bytecode.limit()); b.writeInt(bytecode.length());
b.writeBytes(bytecode.array(), 0, bytecode.limit()); b.writeBytes(bytecode.array(), 0, bytecode.length());
b.writeU2(0);//exception handlers b.writeU2(0);//exception handlers
b.writeU2(0);//attributes b.writeU2(0);//attributes
} }
@ -292,13 +291,16 @@ public class Util {
ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump); ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump);
} catch (Error | Exception _) { } catch (Error | Exception _) {
// fallback to bytecode hex dump // fallback to bytecode hex dump
bytecode.rewind(); dumpBytesHex(dump, bytecode.array(), bytecode.length());
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()));
} }
} }
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; package jdk.internal.classfile.impl.verifier;
import java.nio.ByteBuffer;
import java.lang.classfile.ClassFile; import java.lang.classfile.ClassFile;
import jdk.internal.classfile.impl.verifier.VerificationSignature.BasicType; import jdk.internal.classfile.impl.verifier.VerificationSignature.BasicType;
import static 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; 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) { static boolean is_store_into_local(int code) {
return (ClassFile.ISTORE <= code && code <= ClassFile.ASTORE_3); return (ClassFile.ISTORE <= code && code <= ClassFile.ASTORE_3);
} }
static final int _lengths[] = new int[number_of_codes]; 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) { static int align(int n) {
return (n + 3) & ~3; return (n + 3) & ~3;
} }

View File

@ -24,7 +24,6 @@
*/ */
package jdk.internal.classfile.impl.verifier; package jdk.internal.classfile.impl.verifier;
import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -316,20 +315,20 @@ public final class VerifierImpl {
if (code_length < 1 || code_length > MAX_CODE_SIZE) { if (code_length < 1 || code_length > MAX_CODE_SIZE) {
verifyError(String.format("Invalid method Code length %d", code_length)); verifyError(String.format("Invalid method Code length %d", code_length));
} }
var code = ByteBuffer.wrap(codeArray, 0, _method.codeLength()); var code = RawBytecodeHelper.of(codeArray);
byte[] code_data = generate_code_data(code, code_length); byte[] code_data = generate_code_data(code);
int ex_minmax[] = new int[] {code_length, -1}; int ex_minmax[] = new int[] {code_length, -1};
verify_exception_handler_table(code_length, code_data, ex_minmax); verify_exception_handler_table(code_length, code_data, ex_minmax);
verify_local_variable_table(code_length, code_data); 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); 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; boolean no_control_flow = false;
int opcode; int opcode;
while (!bcs.isLastBytecode()) { while (bcs.next()) {
opcode = bcs.rawNext(); opcode = bcs.opcode();
bci = bcs.bci; bci = bcs.bci();
current_frame.set_offset(bci); current_frame.set_offset(bci);
current_frame.set_mark(); current_frame.set_mark();
stackmap_index = verify_stackmap_table(stackmap_index, bci, current_frame, stackmap_table, no_control_flow); 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; int target;
VerificationType type, type2 = null; VerificationType type, type2 = null;
VerificationType atype; VerificationType atype;
if (bcs.isWide) { if (bcs.isWide()) {
if (opcode != ClassFile.IINC && opcode != ClassFile.ILOAD if (opcode != ClassFile.IINC && opcode != ClassFile.ILOAD
&& opcode != ClassFile.ALOAD && opcode != ClassFile.LLOAD && opcode != ClassFile.ALOAD && opcode != ClassFile.LLOAD
&& opcode != ClassFile.ISTORE && opcode != ClassFile.ASTORE && opcode != ClassFile.ISTORE && opcode != ClassFile.ASTORE
@ -1195,7 +1194,7 @@ public final class VerifierImpl {
case ClassFile.MULTIANEWARRAY : case ClassFile.MULTIANEWARRAY :
{ {
index = bcs.getIndexU2(); 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); verify_cp_class_type(bci, index, cp);
VerificationType new_array_type = VerificationType new_array_type =
cp_index_to_type(index, cp); cp_index_to_type(index, cp);
@ -1230,13 +1229,13 @@ public final class VerifierImpl {
} }
} }
private byte[] generate_code_data(ByteBuffer code, int code_length) { private byte[] generate_code_data(RawBytecodeHelper.CodeRange code) {
byte code_data[] = new byte[code_length]; byte[] code_data = new byte[code.length()];
var bcs = new RawBytecodeHelper(code); var bcs = code.start();
while (!bcs.isLastBytecode()) { while (bcs.next()) {
if (bcs.rawNext() != ILLEGAL) { if (bcs.opcode() != ILLEGAL) {
int bci = bcs.bci; int bci = bcs.bci();
if (bcs.rawCode == ClassFile.NEW) { if (bcs.opcode() == ClassFile.NEW) {
code_data[bci] = NEW_OFFSET; code_data[bci] = NEW_OFFSET;
} else { } else {
code_data[bci] = BYTECODE_OFFSET; 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) { 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); int aligned_bci = VerificationBytecodes.align(bci + 1);
// 4639449 & 4647081: padding bytes must be 0 // 4639449 & 4647081: padding bytes must be 0
if (_klass.majorVersion() < NONZERO_PADDING_BYTES_IN_SWITCH_MAJOR_VERSION) { if (_klass.majorVersion() < NONZERO_PADDING_BYTES_IN_SWITCH_MAJOR_VERSION) {
@ -1422,12 +1421,12 @@ public final class VerifierImpl {
padding_offset++; padding_offset++;
} }
} }
int default_ofset = bcs.getInt(aligned_bci); int default_offset = bcs.getIntUnchecked(aligned_bci);
int keys, delta; int keys, delta;
current_frame.pop_stack(VerificationType.integer_type); current_frame.pop_stack(VerificationType.integer_type);
if (bcs.rawCode == ClassFile.TABLESWITCH) { if (bcs.opcode() == ClassFile.TABLESWITCH) {
int low = bcs.getInt(aligned_bci + 4); int low = bcs.getIntUnchecked(aligned_bci + 4);
int high = bcs.getInt(aligned_bci + 2*4); int high = bcs.getIntUnchecked(aligned_bci + 2*4);
if (low > high) { if (low > high) {
verifyError("low must be less than or equal to high in tableswitch"); verifyError("low must be less than or equal to high in tableswitch");
} }
@ -1438,31 +1437,31 @@ public final class VerifierImpl {
delta = 1; delta = 1;
} else { } else {
// Make sure that the lookupswitch items are sorted // Make sure that the lookupswitch items are sorted
keys = bcs.getInt(aligned_bci + 4); keys = bcs.getIntUnchecked(aligned_bci + 4);
if (keys < 0) { if (keys < 0) {
verifyError("number of keys in lookupswitch less than 0"); verifyError("number of keys in lookupswitch less than 0");
} }
delta = 2; delta = 2;
for (int i = 0; i < (keys - 1); i++) { for (int i = 0; i < (keys - 1); i++) {
int this_key = bcs.getInt(aligned_bci + (2+2*i)*4); int this_key = bcs.getIntUnchecked(aligned_bci + (2+2*i)*4);
int next_key = bcs.getInt(aligned_bci + (2+2*i+2)*4); int next_key = bcs.getIntUnchecked(aligned_bci + (2+2*i+2)*4);
if (this_key >= next_key) { if (this_key >= next_key) {
verifyError("Bad lookupswitch instruction"); verifyError("Bad lookupswitch instruction");
} }
} }
} }
int target = bci + default_ofset; int target = bci + default_offset;
stackmap_table.check_jump_target(current_frame, target); stackmap_table.check_jump_target(current_frame, target);
for (int i = 0; i < keys; i++) { for (int i = 0; i < keys; i++) {
aligned_bci = VerificationBytecodes.align(bcs.bci + 1); aligned_bci = VerificationBytecodes.align(bcs.bci() + 1);
target = bci + bcs.getInt(aligned_bci + (3+i*delta)*4); target = bci + bcs.getIntUnchecked(aligned_bci + (3+i*delta)*4);
stackmap_table.check_jump_target(current_frame, target); stackmap_table.check_jump_target(current_frame, target);
} }
} }
void verify_field_instructions(RawBytecodeHelper bcs, VerificationFrame current_frame, ConstantPoolWrapper cp, boolean allow_arrays) { void verify_field_instructions(RawBytecodeHelper bcs, VerificationFrame current_frame, ConstantPoolWrapper cp, boolean allow_arrays) {
int index = bcs.getIndexU2(); 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_name = cp.refNameAt(index);
String field_sig = cp.refSignatureAt(index); String field_sig = cp.refSignatureAt(index);
if (!VerificationSignature.isValidTypeSignature(field_sig)) verifyError("Invalid field signature"); if (!VerificationSignature.isValidTypeSignature(field_sig)) verifyError("Invalid field signature");
@ -1477,7 +1476,7 @@ public final class VerifierImpl {
VerificationType stack_object_type = null; VerificationType stack_object_type = null;
int n = change_sig_to_verificationType(sig_stream, field_type, 0); int n = change_sig_to_verificationType(sig_stream, field_type, 0);
boolean is_assignable; boolean is_assignable;
switch (bcs.rawCode) { switch (bcs.opcode()) {
case ClassFile.GETSTATIC -> { case ClassFile.GETSTATIC -> {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
current_frame.push_stack(field_type[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, boolean verify_invoke_init(RawBytecodeHelper bcs, int ref_class_index, VerificationType ref_class_type,
VerificationFrame current_frame, int code_length, boolean in_try_block, VerificationFrame current_frame, int code_length, boolean in_try_block,
boolean this_uninit, ConstantPoolWrapper cp, VerificationTable stackmap_table) { 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); VerificationType type = current_frame.pop_stack(VerificationType.reference_check);
if (type.is_uninitialized_this(this)) { if (type.is_uninitialized_this(this)) {
String superk_name = current_class().superclassName(); 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) { if (new_offset > (code_length - 3) || (_method.codeArray()[new_offset] & 0xff) != ClassFile.NEW) {
verifyError("Expecting new instruction"); 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); verify_cp_class_type(bci, new_class_index, cp);
VerificationType new_class_type = cp_index_to_type( VerificationType new_class_type = cp_index_to_type(
new_class_index, cp); 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) { 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 // Make sure the constant pool item is the right type
int index = bcs.getIndexU2(); int index = bcs.getIndexU2();
int opcode = bcs.rawCode; int opcode = bcs.opcode();
int types = 0; int types = 0;
switch (opcode) { switch (opcode) {
case ClassFile.INVOKEINTERFACE: case ClassFile.INVOKEINTERFACE:
@ -1601,7 +1600,7 @@ public final class VerifierImpl {
default: default:
types = 1 << JVM_CONSTANT_Methodref; 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_name = cp.refNameAt(index);
String method_sig = cp.refSignatureAt(index); String method_sig = cp.refSignatureAt(index);
if (!VerificationSignature.isValidMethodSignature(method_sig)) verifyError("Invalid method signature"); 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); mth_sig_verif_types = new sig_as_verification_types(verif_types);
create_method_sig_entry(mth_sig_verif_types, sig); create_method_sig_entry(mth_sig_verif_types, sig);
int nargs = mth_sig_verif_types.num_args(); int nargs = mth_sig_verif_types.num_args();
int bci = bcs.bci; int bci = bcs.bci();
if (opcode == ClassFile.INVOKEINTERFACE) { if (opcode == ClassFile.INVOKEINTERFACE) {
if ((_method.codeArray()[bci+3] & 0xff) != (nargs+1)) { if ((_method.codeArray()[bci+3] & 0xff) != (nargs+1)) {
verifyError("Inconsistent args count operand in invokeinterface"); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,14 @@
* @summary Testing ClassFile Util. * @summary Testing ClassFile Util.
* @run junit UtilTest * @run junit UtilTest
*/ */
import java.lang.classfile.ClassFile;
import java.lang.classfile.Opcode;
import java.lang.constant.MethodTypeDesc; 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 jdk.internal.classfile.impl.Util;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
@ -76,4 +83,21 @@ class UtilTest {
private void assertSlots(String methodDesc, int slots) { private void assertSlots(String methodDesc, int slots) {
assertEquals(Util.parameterSlots(MethodTypeDesc.ofDescriptor(methodDesc)), 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; package org.openjdk.bench.jdk.classfile;
import java.io.IOException; import java.io.IOException;
import java.lang.classfile.Attributes;
import java.lang.constant.ClassDesc; import java.lang.constant.ClassDesc;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Iterator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.lang.classfile.ClassFile; import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassReader; 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.classfile.constantpool.ConstantPoolBuilder;
import java.lang.constant.MethodTypeDesc; import java.lang.constant.MethodTypeDesc;
import jdk.internal.classfile.impl.AbstractPseudoInstruction; import jdk.internal.classfile.impl.AbstractPseudoInstruction;
import jdk.internal.classfile.impl.CodeImpl;
import jdk.internal.classfile.impl.LabelContext; import jdk.internal.classfile.impl.LabelContext;
import jdk.internal.classfile.impl.ClassFileImpl; import jdk.internal.classfile.impl.ClassFileImpl;
import jdk.internal.classfile.impl.RawBytecodeHelper;
import jdk.internal.classfile.impl.SplitConstantPool; import jdk.internal.classfile.impl.SplitConstantPool;
import jdk.internal.classfile.impl.StackCounter; import jdk.internal.classfile.impl.StackCounter;
import jdk.internal.classfile.impl.StackMapGenerator; import jdk.internal.classfile.impl.StackMapGenerator;
@ -70,7 +67,7 @@ public class CodeAttributeTools {
String methodName, String methodName,
MethodTypeDesc methodDesc, MethodTypeDesc methodDesc,
boolean isStatic, boolean isStatic,
ByteBuffer bytecode, RawBytecodeHelper.CodeRange bytecode,
ConstantPoolBuilder constantPool, ConstantPoolBuilder constantPool,
List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {} List<AbstractPseudoInstruction.ExceptionCatchImpl> handlers) {}
@ -85,15 +82,14 @@ public class CodeAttributeTools {
var thisCls = clm.thisClass().asSymbol(); var thisCls = clm.thisClass().asSymbol();
var cp = new SplitConstantPool((ClassReader)clm.constantPool()); var cp = new SplitConstantPool((ClassReader)clm.constantPool());
for (var m : clm.methods()) { for (var m : clm.methods()) {
m.code().ifPresent(com -> { m.findAttribute(Attributes.code()).ifPresent(com -> {
var bb = ByteBuffer.wrap(((CodeImpl)com).contents());
data.add(new GenData( data.add(new GenData(
(LabelContext)com, (LabelContext)com,
thisCls, thisCls,
m.methodName().stringValue(), m.methodName().stringValue(),
m.methodTypeSymbol(), m.methodTypeSymbol(),
(m.flags().flagsMask() & ClassFile.ACC_STATIC) != 0, (m.flags().flagsMask() & ClassFile.ACC_STATIC) != 0,
bb.slice(8, bb.getInt(4)), RawBytecodeHelper.of(com.codeArray()),
cp, cp,
com.exceptionHandlers().stream().map(eh -> (AbstractPseudoInstruction.ExceptionCatchImpl)eh).toList())); com.exceptionHandlers().stream().map(eh -> (AbstractPseudoInstruction.ExceptionCatchImpl)eh).toList()));
}); });
@ -112,7 +108,7 @@ public class CodeAttributeTools {
d.methodName(), d.methodName(),
d.methodDesc(), d.methodDesc(),
d.isStatic(), d.isStatic(),
d.bytecode().rewind(), d.bytecode(),
(SplitConstantPool)d.constantPool(), (SplitConstantPool)d.constantPool(),
(ClassFileImpl)ClassFile.of(), (ClassFileImpl)ClassFile.of(),
d.handlers())); d.handlers()));
@ -127,7 +123,7 @@ public class CodeAttributeTools {
d.methodName(), d.methodName(),
d.methodDesc(), d.methodDesc(),
d.isStatic(), d.isStatic(),
d.bytecode().rewind(), d.bytecode(),
(SplitConstantPool)d.constantPool(), (SplitConstantPool)d.constantPool(),
d.handlers())); d.handlers()));
} }