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:
parent
a35fd38610
commit
a1eebbdf8a
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user