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