All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.dmlloyd.classfile.impl.RawBytecodeHelper Maven / Gradle / Ivy

There is a newer version: 24.cr2
Show newest version
/*
 * 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 java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.Objects;

public final class RawBytecodeHelper {

    public static final int
            ILLEGAL = -1,
            NOP = 0,
            ACONST_NULL = 1,
            ICONST_M1 = 2,
            ICONST_0 = 3,
            ICONST_1 = 4,
            ICONST_2 = 5,
            ICONST_3 = 6,
            ICONST_4 = 7,
            ICONST_5 = 8,
            LCONST_0 = 9,
            LCONST_1 = 10,
            FCONST_0 = 11,
            FCONST_1 = 12,
            FCONST_2 = 13,
            DCONST_0 = 14,
            DCONST_1 = 15,
            BIPUSH = 16,
            SIPUSH = 17,
            LDC = 18,
            LDC_W = 19,
            LDC2_W = 20,
            ILOAD = 21,
            LLOAD = 22,
            FLOAD = 23,
            DLOAD = 24,
            ALOAD = 25,
            ILOAD_0 = 26,
            ILOAD_1 = 27,
            ILOAD_2 = 28,
            ILOAD_3 = 29,
            LLOAD_0 = 30,
            LLOAD_1 = 31,
            LLOAD_2 = 32,
            LLOAD_3 = 33,
            FLOAD_0 = 34,
            FLOAD_1 = 35,
            FLOAD_2 = 36,
            FLOAD_3 = 37,
            DLOAD_0 = 38,
            DLOAD_1 = 39,
            DLOAD_2 = 40,
            DLOAD_3 = 41,
            ALOAD_0 = 42,
            ALOAD_1 = 43,
            ALOAD_2 = 44,
            ALOAD_3 = 45,
            IALOAD = 46,
            LALOAD = 47,
            FALOAD = 48,
            DALOAD = 49,
            AALOAD = 50,
            BALOAD = 51,
            CALOAD = 52,
            SALOAD = 53,
            ISTORE = 54,
            LSTORE = 55,
            FSTORE = 56,
            DSTORE = 57,
            ASTORE = 58,
            ISTORE_0 = 59,
            ISTORE_1 = 60,
            ISTORE_2 = 61,
            ISTORE_3 = 62,
            LSTORE_0 = 63,
            LSTORE_1 = 64,
            LSTORE_2 = 65,
            LSTORE_3 = 66,
            FSTORE_0 = 67,
            FSTORE_1 = 68,
            FSTORE_2 = 69,
            FSTORE_3 = 70,
            DSTORE_0 = 71,
            DSTORE_1 = 72,
            DSTORE_2 = 73,
            DSTORE_3 = 74,
            ASTORE_0 = 75,
            ASTORE_1 = 76,
            ASTORE_2 = 77,
            ASTORE_3 = 78,
            IASTORE = 79,
            LASTORE = 80,
            FASTORE = 81,
            DASTORE = 82,
            AASTORE = 83,
            BASTORE = 84,
            CASTORE = 85,
            SASTORE = 86,
            POP = 87,
            POP2 = 88,
            DUP = 89,
            DUP_X1 = 90,
            DUP_X2 = 91,
            DUP2 = 92,
            DUP2_X1 = 93,
            DUP2_X2 = 94,
            SWAP = 95,
            IADD = 96,
            LADD = 97,
            FADD = 98,
            DADD = 99,
            ISUB = 100,
            LSUB = 101,
            FSUB = 102,
            DSUB = 103,
            IMUL = 104,
            LMUL = 105,
            FMUL = 106,
            DMUL = 107,
            IDIV = 108,
            LDIV = 109,
            FDIV = 110,
            DDIV = 111,
            IREM = 112,
            LREM = 113,
            FREM = 114,
            DREM = 115,
            INEG = 116,
            LNEG = 117,
            FNEG = 118,
            DNEG = 119,
            ISHL = 120,
            LSHL = 121,
            ISHR = 122,
            LSHR = 123,
            IUSHR = 124,
            LUSHR = 125,
            IAND = 126,
            LAND = 127,
            IOR = 128,
            LOR = 129,
            IXOR = 130,
            LXOR = 131,
            IINC = 132,
            I2L = 133,
            I2F = 134,
            I2D = 135,
            L2I = 136,
            L2F = 137,
            L2D = 138,
            F2I = 139,
            F2L = 140,
            F2D = 141,
            D2I = 142,
            D2L = 143,
            D2F = 144,
            I2B = 145,
            I2C = 146,
            I2S = 147,
            LCMP = 148,
            FCMPL = 149,
            FCMPG = 150,
            DCMPL = 151,
            DCMPG = 152,
            IFEQ = 153,
            IFNE = 154,
            IFLT = 155,
            IFGE = 156,
            IFGT = 157,
            IFLE = 158,
            IF_ICMPEQ = 159,
            IF_ICMPNE = 160,
            IF_ICMPLT = 161,
            IF_ICMPGE = 162,
            IF_ICMPGT = 163,
            IF_ICMPLE = 164,
            IF_ACMPEQ = 165,
            IF_ACMPNE = 166,
            GOTO = 167,
            JSR = 168,
            RET = 169,
            TABLESWITCH = 170,
            LOOKUPSWITCH = 171,
            IRETURN = 172,
            LRETURN = 173,
            FRETURN = 174,
            DRETURN = 175,
            ARETURN = 176,
            RETURN = 177,
            GETSTATIC = 178,
            PUTSTATIC = 179,
            GETFIELD = 180,
            PUTFIELD = 181,
            INVOKEVIRTUAL = 182,
            INVOKESPECIAL = 183,
            INVOKESTATIC = 184,
            INVOKEINTERFACE = 185,
            INVOKEDYNAMIC = 186,
            NEW = 187,
            NEWARRAY = 188,
            ANEWARRAY = 189,
            ARRAYLENGTH = 190,
            ATHROW = 191,
            CHECKCAST = 192,
            INSTANCEOF = 193,
            MONITORENTER = 194,
            MONITOREXIT = 195,
            WIDE = 196,
            MULTIANEWARRAY = 197,
            IFNULL = 198,
            IFNONNULL = 199,
            GOTO_W = 200,
            JSR_W = 201;

    public record CodeRange(byte[] array, int length) {
        public RawBytecodeHelper start() {
            return new RawBytecodeHelper(this);
        }
    }

    /**
     * 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;

    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 & 0xFF;
    }

    /**
     * Returns whether the current functional opcode is in wide.
     */
    public boolean isWide() {
        return (opcode & (WIDE << 8)) != 0;
    }

    /**
     * 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;
        if (len <= 0) {
            len = checkSpecialInstruction(bci, end, code); // sets opcode
        }

        if ((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) {
        int len = -1;
        if (code == WIDE) {
            if (bci + 1 < end) {
                opcode = (WIDE << 8) | (code = getIndexU1());
                // Validated in UtilTest.testOpcodeLengthTable
                len = LENGTHS[code] * 2;
            }
        } else if (code == TABLESWITCH) {
            int alignedBci = align(bci + 1);
            if (alignedBci + 3 * 4 < end) {
                int lo = getIntUnchecked(alignedBci + 1 * 4);
                int hi = getIntUnchecked(alignedBci + 2 * 4);
                long l = alignedBci - bci + (3L + (long) hi - lo + 1L) * 4L;
                len = l > 0 && ((int) l == l) ? (int) l : -1;
            }
        } else if (code == LOOKUPSWITCH) {
            int alignedBci = align(bci + 1);
            if (alignedBci + 2 * 4 < end) {
                int npairs = getIntUnchecked(alignedBci + 4);
                if (npairs >= 0) {
                    long l = alignedBci - bci + (2L + 2L * npairs) * 4L;
                    len = l > 0 && ((int) l == l) ? (int) l : -1;
                }
            }
        }
        if (len <= 0) {
            opcode = ILLEGAL;
        }
        return len;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy