Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package io.github.dmlloyd.classfile.impl;
import static io.github.dmlloyd.classfile.ClassFile.*;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.Objects;
public final class RawBytecodeHelper {
public record CodeRange(byte[] array, int length) {
public RawBytecodeHelper start() {
return new RawBytecodeHelper(this);
}
}
public static final int ILLEGAL = -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 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) {
return (ISTORE <= code && code <= ASTORE_3);
}
public static int align(int n) {
return (n + 3) & ~3;
}
public final CodeRange code;
private int nextBci;
private int bci;
private int opcode;
private boolean isWide;
public static CodeRange of(byte[] array) {
return new CodeRange(array, array.length);
}
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) {
Objects.checkIndex(nextBci, endBci() + 1);
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) {
Objects.checkIndex(bci, endBci());
return getU1Unchecked(bci);
}
public int getU2(int bci) {
Objects.checkFromIndexSize(bci, 2, endBci());
return getU2Unchecked(bci);
}
public int getShort(int bci) {
Objects.checkFromIndexSize(bci, 2, endBci());
return getShortUnchecked(bci);
}
public int getInt(int bci) {
Objects.checkFromIndexSize(bci, 4, endBci());
return getIntUnchecked(bci);
}
// Unchecked accessors: only if opcode() is validated
public int getU1Unchecked(int bci) {
return Byte.toUnsignedInt(code.array[bci]);
}
private static final VarHandle U2BE = MethodHandles.byteArrayViewVarHandle(char[].class, ByteOrder.BIG_ENDIAN);
private static final VarHandle S2BE = MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.BIG_ENDIAN);
private static final VarHandle I4BE = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);
public int getU2Unchecked(int bci) {
return (char)U2BE.get(code.array, bci);
}
public int getShortUnchecked(int bci) {
return (short)S2BE.get(code.array, bci);
}
// used after switch validation
public int getIntUnchecked(int bci) {
return (int)I4BE.get(code.array, bci);
}
// non-wide branches
public int dest() {
return bci + getShortUnchecked(bci + 1);
}
// goto_w and jsr_w
public int destW() {
return bci + getIntUnchecked(bci + 1);
}
// *load, *store, iinc
public int getIndex() {
return isWide ? getU2Unchecked(bci + 2) : getIndexU1();
}
// ldc
public int getIndexU1() {
return getU1Unchecked(bci + 1);
}
// usually cp entry index
public int getIndexU2() {
return getU2Unchecked(bci + 1);
}
// 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;
}
}