diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index 0c317065162..2307af79dee 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -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) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java b/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java index 3e5d66245f0..860b7e74b4d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java @@ -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, 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; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java index 861bb509420..783ff4bf106 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java @@ -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 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 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()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 514c1d32f41..5825eb2f547 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -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 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 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; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 2d9e27b8d74..fee55249bbb 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -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 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 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])); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java index c9b076aa1f0..de97730aff5 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java @@ -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; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java index 74cdb881a74..9a0e781d145 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java @@ -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"); diff --git a/test/jdk/jdk/classfile/UtilTest.java b/test/jdk/jdk/classfile/UtilTest.java index 5ed04180919..d9d8240ae91 100644 --- a/test/jdk/jdk/classfile/UtilTest.java +++ b/test/jdk/jdk/classfile/UtilTest.java @@ -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); + } } diff --git a/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java b/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java index 4cf578889d1..4239f70504b 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java @@ -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 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())); }