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

org.netbeans.lib.profiler.classfile.ClassInfo Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.lib.profiler.classfile;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.netbeans.lib.profiler.classfile.ClassInfo.StackMapFrame.FrameType;
import org.netbeans.lib.profiler.global.CommonConstants;
import org.netbeans.lib.profiler.instrumentation.JavaClassConstants;


/**
 * A representation of a binary Java class, that is relatively compact - it does not contain method bodies,
 * and contains only a subset of information from the constant pool. Method bodies (or, more precisely, byte
 * arrays representing either full MethodInfos as defined in JVM Specification, or just method bodies), can
 * be obtained individually on demand.
 *
 * This class is abstract, since it contains a single abstract method that actually returns the class file bytes
 * for the whole class. Concrete subclasses of this class may choose to simply store this byte array, or retrieve
 * it e.g. from disk on demand.
 *
 * @author Misha Dmitirev
 * @author Tomas Hurka
 */
public abstract class ClassInfo extends BaseClassInfo implements JavaClassConstants, CommonConstants {
    //~ Inner Classes ------------------------------------------------------------------------------------------------------------

    public static class LineNumberTables {
        //~ Instance fields ------------------------------------------------------------------------------------------------------

        private char[][] lineNumbers;
        private char[][] startPCs;
        private boolean hasTable;

        //~ Constructors -------------------------------------------------------------------------------------------------------------

        LineNumberTables(ClassInfo ci) {
            byte[] classBuf = null;

            try {
                classBuf = ci.getClassFileBytes();
            } catch (IOException ex1) { // Should not happen - class file already loaded once by this time
            } catch (ClassNotFoundException ex2) {
            } // Ditto

            int nMethods = ci.getMethodNames().length;
            startPCs = new char[nMethods][];
            lineNumbers = new char[nMethods][];

            for (int i = 0; i < nMethods; i++) {
                int ofs = ci.methodInfoOffsets[i] + ci.lineNumberTablesOffsets[i];

                if (ofs == -1) {
                    continue; // Abstract or native method, or no line number tables in this class
                }

                hasTable = true;

                int tableLen = ci.lineNumberTablesLengths[i];
                char[] startPC = startPCs[i] = new char[tableLen];
                char[] lineNumber = lineNumbers[i] = new char[tableLen];

                for (int j = 0; j < tableLen; j++) {
                    startPC[j] = (char) (((classBuf[ofs++] & 255) << 8) + (classBuf[ofs++] & 255));
                    lineNumber[j] = (char) (((classBuf[ofs++] & 255) << 8) + (classBuf[ofs++] & 255));
                }
            }
        }
        
        //~ Methods --------------------------------------------------------------------------------------------------------------

        public char[][] getStartPCs() {
            return startPCs;
        }

        int[] getMinAndMaxLinesForMethod(int methodIdx) {
            int[] lines = new int[2];

            if (startPCs[methodIdx] == null) { // No line number table for this method - return special value
                lines[0] = lines[1] = -1;

                return lines;
            }

            lines[0] = 10000000;
            lines[1] = -10000000;

            char[] lns = lineNumbers[methodIdx];

            for (int i = 0; i < lns.length; i++) {
                if (lns[i] < lines[0]) {
                    lines[0] = lns[i];
                }

                if (lns[i] > lines[1]) {
                    lines[1] = lns[i];
                }
            }

            return lines;
        }

        int bciForLineNo(int methodIdx, int lineNo) {
            char[] spcs = startPCs[methodIdx];

            if (spcs == null) {
                return -1;
            }

            int tableLen = spcs.length;
            char[] lns = lineNumbers[methodIdx];

            int minLine = 100000000;
            int bestLine = 100000000;
            int maxLine = 0;

            int curLine = -1;
            int bestBCI = 100000000;

            for (int i = 0; i < tableLen; i++) {
                curLine = lns[i];

                if (curLine > maxLine) {
                    maxLine = curLine;
                }

                if (curLine < minLine) {
                    minLine = curLine;
                }

                if (curLine == lineNo) { // Perfect match
                    bestBCI = spcs[i];

                    break;
                } else if ((curLine > lineNo) && (curLine <= bestLine)) { // Update bci/line

                    if (spcs[i] < bestBCI) { // ..but check first if it's the smallest bci for this line.
                                             // The whole issue is due to 'while() { }' effectively compiled as 'do { } while()', where for the actual
                                             // line of the 'while' statementwe get two different bci's in the line number table:
                                             // 1. the one for the initial 'goto' that transfers us to the condition check block in the end of the loop body
                                             // 2. the first bci of that condition check block.
                                             // Whether we hit this line as the first or the last line of our code fragment, the smallest bci is a correct answer.
                        bestBCI = spcs[i];
                        bestLine = curLine;
                    }
                }
            }

            // Found a valid matching line if there is a perfect match or at least the specified
            // line is within this method's line number table.
            if ((curLine == lineNo) || ((lineNo >= minLine) && (lineNo <= maxLine))) {
                return bestBCI;
            } else {
                return -1;
            }
        }

        int lineNoForBci(int methodIdx, int bci) {
            char[] spcs = startPCs[methodIdx];

            if (spcs == null) {
                return -1;
            }

            int tableLen = spcs.length;
            char[] lns = lineNumbers[methodIdx];

            int bestLine = -1;

            for (int i = 0; i < tableLen; i++) {
                if (spcs[i] > bci) {
                    break; // reached in last cycle
                }

                bestLine = lns[i];
            }

            return bestLine;
        }

        private boolean hasTable() {
            return hasTable;
        }
    }

    public static class LocalVariableTables {

        public static final int ATTR_SIZE = 10;

        //~ Instance fields ------------------------------------------------------------------------------------------------------

        private char[][] lengths;
        private char[][] startPCs;
        private boolean hasTable;

        //~ Constructors -------------------------------------------------------------------------------------------------------------

        LocalVariableTables(ClassInfo ci) {
            this(ci, ci.localVariableTablesOffsets, ci.localVariableTablesLengths);
        }
        
        private LocalVariableTables(ClassInfo ci, int[] tablesOffsets, char[] tablesLengths) {
            byte[] classBuf = null;

            try {
                classBuf = ci.getClassFileBytes();
            } catch (IOException ex1) { // Should not happen - class file already loaded once by this time
            } catch (ClassNotFoundException ex2) {
            } // Ditto

            int nMethods = ci.getMethodNames().length;
            startPCs = new char[nMethods][];
            lengths = new char[nMethods][];

            for (int i = 0; i < nMethods; i++) {
                int tableLen = tablesLengths[i];

                if (tableLen == 0) {
                    continue;
                }
                int ofs = ci.methodInfoOffsets[i] + tablesOffsets[i];
                char[] startPC = startPCs[i] = new char[tableLen];
                char[] length = lengths[i] = new char[tableLen];

                for (int j = 0; j < tableLen; j++, ofs+=ATTR_SIZE ) {
                    int offset = ofs;
                    startPC[j] = (char) (((classBuf[offset++] & 255) << 8) + (classBuf[offset++] & 255));
                    length[j] = (char) (((classBuf[offset++] & 255) << 8) + (classBuf[offset++] & 255));
                }
                hasTable = true;
            }
        }
        
        //~ Methods --------------------------------------------------------------------------------------------------------------

        char[][] getStartPCs() {
            return startPCs;
        }

        char[][] getLengts() {
            return lengths;
        }

        public boolean hasTable() {
            return hasTable;
        }
        
        public void updateTable(int injectionPos, int injectedBytesCount, int methodIdx) {
            if (hasTable()) {
                char[] startPC = getStartPCs()[methodIdx];
                char[] lengths = getLengts()[methodIdx];

                if (startPC != null) {
                    for (int i = 0; i < startPC.length; i++) {
                        char currentBCI = startPC[i];
                        if (currentBCI >= injectionPos) {
                            startPC[i] = (char)(currentBCI + injectedBytesCount);
                        } else {
                            char currentLength = lengths[i];
                            if (currentBCI + currentLength > injectionPos) {
                                 lengths[i] = (char)(currentLength + injectedBytesCount);
                            }
                        }
                    }
                }
            }
        }
    
        public void writeTable(final byte[] buffer, int locVarTablePtr, int methodIdx) {
            char[] startPC = getStartPCs()[methodIdx];
            char[] lengths = getLengts()[methodIdx];

            if (startPC != null) {
                for (int i = 0; i < startPC.length; i++, locVarTablePtr+=ATTR_SIZE) {
                    putU2(buffer, locVarTablePtr, startPC[i]);
                    putU2(buffer, locVarTablePtr + 2, lengths[i]);
                }
            }
        }  
    }
        
    public static class LocalVariableTypeTables extends LocalVariableTables {

        LocalVariableTypeTables(ClassInfo ci) {
            super(ci, ci.localVariableTypeTablesOffsets, ci.localVariableTypeTablesLengths);
        }
    }
//    public static Logger LOG = Logger.getLogger(ClassInfo.class.getName());   
    public class StackMapTables {
        private StackMapFrame[][] frames;
        private byte[][] framesBytes;
        private boolean hasTable;
         
        StackMapTables() {
            byte[] classBuf = null;

            try {
                classBuf = ClassInfo.this.getClassFileBytes();
            } catch (IOException ex1) { // Should not happen - class file already loaded once by this time
            } catch (ClassNotFoundException ex2) {
            } // Ditto

            int nMethods = ClassInfo.this.getMethodNames().length;
            frames = new StackMapFrame[nMethods][];
            framesBytes = new byte[nMethods][];
            for (int i = 0; i < nMethods; i++) {
                int tableLen = ClassInfo.this.stackMapTablesLengths[i];

                if (tableLen == 0) {
                    continue;
                }
                int startOfs = ClassInfo.this.methodInfoOffsets[i] + ClassInfo.this.stackMapTablesOffsets[i];
                int ofs = startOfs;
                StackMapFrame[] frms = frames[i] = new StackMapFrame[tableLen];
//                LOG.finer("Class "+ClassInfo.this.name+" method "+ClassInfo.this.getMethodName(i));
                for (int j = 0; j < tableLen; j++) {
                    frms[j] = new StackMapFrame(classBuf,ofs);
                    ofs+=frms[j].getSize();
//                    LOG.finer(frms[j].toString());
                }
                int len = ofs - startOfs;
                framesBytes[i] = new byte[len+tableLen+2*(StackMapFrame.FrameType.FULL_FRAME.size()+2*3)];
                System.arraycopy(classBuf,startOfs,framesBytes[i],0,len);
                hasTable = true;
            }
        }

        public boolean hasTable() {
            return hasTable;
        }

        public void updateTable(int injectionPos, int injectedBytesCount, int methodIdx, boolean changeTypeIsInjectNewInstr, boolean injectionBindsToFollowingInstruction) {
            String method = getMethodName(methodIdx);
            if (hasTable()) {
                StackMapFrame[] frms = frames[methodIdx];
                
                if (frms != null) {
                    int bciIter = -1;
                    boolean  offsetAdjusted = false; // only need to adjust one offset
                    
                    for (StackMapFrame frame : frms) {
                        int offsetDelta = frame.getOffsetDelta();
                        bciIter += offsetDelta;

                        if (!offsetAdjusted) {
                            if (adjustOffset(bciIter, injectionPos, changeTypeIsInjectNewInstr, injectionBindsToFollowingInstruction)) {
                                setOffsetDelta(methodIdx, frame, offsetDelta + injectedBytesCount);
                                offsetAdjusted = true;
                            }
                        }
                        frame.updateUnitilializedList(injectionPos, injectedBytesCount, changeTypeIsInjectNewInstr, injectionBindsToFollowingInstruction);
                    }
                }                
            }
        }
        
        public int getNumberOfFrames(int methodIdx) {
            StackMapFrame[] frms = frames[methodIdx];
            
            if (frms != null) {
                return frms.length;
            }
            return 0;
        }

        public byte[] getAttributeHeader(int methodIdx) {
            byte[] header = new byte[8];
            putU2(header,0,ClassInfo.this.stackMapTableCPindex);
            return header;
        }
        
        public byte[] writeTable(int methodIdx) {
            StackMapFrame[] frms = frames[methodIdx];
            byte frameBytes[] = framesBytes[methodIdx];
            
            if (frms != null) {
                byte[] ret;
                int offset = 0;
                
                for (StackMapFrame frame : frms) {
                    frame.writeFrame(frameBytes, offset);
                    offset+=frame.getSize();
                }
                ret = new byte[offset];
                System.arraycopy(frameBytes,0,ret,0,offset);
                return ret;
            }
            return null;
        }

        void addFullStackMapFrameEntry(int methodIdx, int endPC, int[] locals, int[] stacks) {
            StackMapFrame[] frms = frames[methodIdx];
            StackMapFrame[] newFrms;

            if (frms != null) {
                int bciIter = -1;

                for (StackMapFrame frame : frms) {
                    bciIter += frame.getOffsetDelta();
                }
                newFrms = frames[methodIdx] = new StackMapFrame[frms.length+1];
                System.arraycopy(frms,0,newFrms,0,frms.length);
                newFrms[frms.length] = new FullStackMapFrame(endPC - bciIter, locals, stacks);
            } else {
                newFrms = frames[methodIdx] = new StackMapFrame[1];
                newFrms[0] = new FullStackMapFrame(endPC + 1, locals, stacks);
                framesBytes[methodIdx] = new byte[newFrms[0].getSize()];
                hasTable = true;
            }
        }

        private void setOffsetDelta(int methodIdx, StackMapFrame frame, int newOffsetDelta) {
            FrameType frameType = frame.frameType;
            
            if (frameType == FrameType.SAME && newOffsetDelta > 63) {
                 extendFrame(methodIdx,frame,2);
                frame.setFrameType(FrameType.SAME_FRAME_EXTENDED);
            }
            if (frameType == FrameType.SAME_LOCALS_1_STACK_ITEM && newOffsetDelta > 63) {
                extendFrame(methodIdx,frame,2);
                frame.setFrameType(FrameType.SAME_LOCALS_1_STACK_ITEM_EXTENDED);
            }
            frame.setOffsetDelta(newOffsetDelta);
        }

        private void extendFrame(int methodIdx, StackMapFrame frame, int addBytes) {
             StackMapFrame[] frms = frames[methodIdx];
             byte[] data = framesBytes[methodIdx];
             int offset = 0;
             
             for (StackMapFrame f : frms) {
                 if (f == frame) {
                     break;
                 }
                 offset += f.getSize();
             }
             System.arraycopy(data,offset,data,offset+addBytes,data.length-offset-addBytes);
        }
    }
    
    static class StackMapFrame {
        enum FrameType {
            SAME(1),
            SAME_LOCALS_1_STACK_ITEM(1),
            SAME_LOCALS_1_STACK_ITEM_EXTENDED(3),
            CHOP(3),
            SAME_FRAME_EXTENDED(3),
            APPEND(3),
            FULL_FRAME(7);
                    
            private int frameSize;

            int size() {return frameSize;}

            FrameType(int size){
               frameSize = size;
            }
        }
        
        FrameType frameType;
        int storedOffsetDelta;
        int size;
        boolean modified;
        boolean frameModified;
        boolean uninitializedListModified;
        List uninitializedList;
        
        StackMapFrame(FrameType type, int offset, int s) {
            frameType = type;
            storedOffsetDelta = offset - 1;
            size = type.size()+s;
        }
        
        StackMapFrame(byte[] buffer, int offset) {
            int type;
            
            type = buffer[offset++] & 0xff;
            if (type <= 63) {
                frameType = FrameType.SAME;
                storedOffsetDelta = type;
            } else if (type <= 127) {
                frameType = FrameType.SAME_LOCALS_1_STACK_ITEM;
                storedOffsetDelta = type - 64;
                size = getVerificationTypeInfoSize(buffer[offset]);
                storeUninitializedVariableInfo(buffer,offset,0);
            } else if (type <= 246) {
                throw new IllegalArgumentException("Type: "+type);
            } else if (type == 247) {
                frameType = FrameType.SAME_LOCALS_1_STACK_ITEM_EXTENDED;
                storedOffsetDelta = getU2(buffer,offset);
                offset+=2;
                size = getVerificationTypeInfoSize(buffer[offset]);
                storeUninitializedVariableInfo(buffer,offset,0);
            } else if (type <= 250) {
                frameType = FrameType.CHOP;
                storedOffsetDelta = getU2(buffer,offset);
            } else if (type == 251) {
                frameType = FrameType.SAME_FRAME_EXTENDED;
                storedOffsetDelta = getU2(buffer,offset);
            } else if (type <= 254) {
                frameType = FrameType.APPEND;
                storedOffsetDelta = getU2(buffer,offset);
                offset+=2;
                int locals = type - 251;
                for (int i=0; i();
                }
                while (uninitializedList.size() < listIndex+1) {
                    uninitializedList.add(null);
                }
                uninitializedList.set(listIndex,Integer.valueOf(getU2(buffer,offset)));
//                LOG.finer("ITEM_Unitialized "+Integer.valueOf(getU2(buffer,offset)));
            }
        }

        void writeFrame(byte[] ret, int newFrameOffset) {
            if (modified) {
//                LOG.finer("Updating "+frameType+" new offset "+getOffsetDelta()+" old type "+Integer.valueOf(ret[newFrameOffset]&0xff));
                if (frameModified) {
                    switch (frameType) {
                        case SAME_LOCALS_1_STACK_ITEM_EXTENDED:
                            ret[newFrameOffset] = (byte)247;
                            break;
                        case SAME_FRAME_EXTENDED:
                            ret[newFrameOffset] = (byte)251;
                            break;
                    }
                }
                switch (frameType) {
                    case SAME:
                        ret[newFrameOffset] = (byte)(storedOffsetDelta & 0x3F);
                        break;
                    case SAME_LOCALS_1_STACK_ITEM:
                        ret[newFrameOffset] = (byte)(64 + (storedOffsetDelta & 0x3F));
                        break;
                    case SAME_LOCALS_1_STACK_ITEM_EXTENDED:
                    case CHOP:
                    case SAME_FRAME_EXTENDED:
                    case APPEND:
                    case FULL_FRAME:
                        putU2(ret, newFrameOffset+1, storedOffsetDelta);
                        break;
                }
            }
            if (uninitializedListModified) {
                switch (frameType) {
                    case SAME_LOCALS_1_STACK_ITEM:
                        putU2(ret,newFrameOffset+2,uninitializedList.get(0).intValue());
                        break;
                    case SAME_LOCALS_1_STACK_ITEM_EXTENDED:
                        putU2(ret,newFrameOffset+3,uninitializedList.get(0).intValue());
                        break;
                    case APPEND: {
                        int offset = newFrameOffset+3;
                        for (Integer off : uninitializedList) {
                            byte type = ret[offset];
                            int typeInfoSize = getVerificationTypeInfoSize(type);

                            if (type == 8) { // ITEM_Unitialized
                                putU2(ret,offset+1,off.intValue());
                            }
                            offset += typeInfoSize;
                        }
                        break;
                    }
                    case FULL_FRAME: {
                        int offset = newFrameOffset+3;
                        int locals = getU2(ret,offset);
                        
                        offset+=2;
//                        LOG.finer("Locals: "+locals);
                        for (int i=0; i0 && locals[0] == 0)?locals.length:3*locals.length));
            localsCPIdx = locals;
            stacksCPIdx = stacks;
        }
        
        void writeFrame(byte[] ret, int offset) {
            ret[offset++] = (byte)255; // FULL_FRAME
            putU2(ret,offset,storedOffsetDelta); // offset_delta
            offset+=2;
            putU2(ret,offset,localsCPIdx.length); // locals
            offset+=2;
            for (int i=0; i= opc_iload) && (opcode <= opc_aload)) || ((opcode >= opc_istore) && (opcode <= opc_astore))
                        || (opcode == opc_ret)) {
                    offset += 4;
                } else if (opcode == opc_iinc) {
                    offset += 6;
                } else {
                    offset++;
                }
            } else {
                switch (opcode) {
                    case opc_tableswitch: {
                        int tbl = (offset + 1 + 3) & (~3); // four byte boundry
                        long default_skip = intAt(codeBytes, tbl, 0);
                        long low = intAt(codeBytes, tbl, 1);
                        long high = intAt(codeBytes, tbl, 2);
                        tbl += (3 << 2); // three int header
                        offset = tbl + (int) ((high - low + 1) << 2);

                        break;
                    }
                    case opc_lookupswitch: {
                        int tbl = (offset + 1 + 3) & (~3); // four byte boundry
                        long default_skip = intAt(codeBytes, tbl, 0);
                        int npairs = (int) intAt(codeBytes, tbl, 1);
                        int nints = npairs * 2;
                        tbl += (2 << 2); // two int header
                        offset = tbl + (nints << 2);

                        break;
                    }
                    default:
                        offset += opc_length[opcode];

                        break;
                }
            }
        }

        return prev_offset;
    }

    public int lineNoForMethodAndBci(int methodIdx, int bci) { // TODO CHECK: unused method
        initLineNumberTables();

        return lineNumberTables.lineNoForBci(methodIdx, bci);
    }

    /**
     * Returns a {method idx, best BCI} pair for the given source line number in this class. If no suitable method is
     * found, returns {-1, -1}. If this class doesn't have any line number tables (because it's abstract or because
     * it was compiled without tables), returns {-2, -2}.
     */
    public int[] methodIdxAndBestBCIForLineNo(int lineNo) {
        initLineNumberTables();

        if (!lineNumberTables.hasTable()) {
            return new int[] { -2, -2 };
        }

        int nMethods = methodNames.length;

        // We need to take into account the fact that for a constructor/class initializer the line numbers may span
        // a much larger range than the constructor body. That's due to instance/static initialization statements
        // that can be scattered about the whole class. If we put the cursor into a method that is between two
        // initializers, we may well get a constructor as the "best match" for the given line. Thus, we first
        // search normal methods, and only if this fails - constructors and class initializer.
        for (int i = 0; i < nMethods; i++) {
            if ((methodNames[i] == "") || (methodNames[i] == "")) { // NOI18N
                continue;
            }

            int bestBCI = lineNumberTables.bciForLineNo(i, lineNo);

            if (bestBCI != -1) {
                return new int[] { i, bestBCI };
            }
        }

        // No success with ordinary methods - try constructors now
        for (int i = 0; i < nMethods; i++) {
            if ((methodNames[i] != "") && (methodNames[i] != "")) { // NOI18N
                continue;
            }

            int bestBCI = lineNumberTables.bciForLineNo(i, lineNo);

            if (bestBCI != -1) {
                return new int[] { i, bestBCI };
            }
        }

        return new int[] { -1, -1 };
    }

    // WARNING: this call doesn't check if the method in superClass is not private, final, static or constructor. This is done for
    // speedup, since we call it only in the context when it is already known that the above is true.
    public int overridesVirtualMethod(ClassInfo superClass, int superMethodIdx) { // TODO CHECK: unused method

        int idx = getMethodIndex(superClass.methodNames[superMethodIdx], superClass.methodSignatures[superMethodIdx]);

        if (idx == -1) {
            return -1;
        }

        if (superClass.isMethodPublic(superMethodIdx) || superClass.isMethodProtected(superMethodIdx)) {
            return idx;
        } else if (superClass.packageName == this.packageName) {
            return idx;
        } else {
            return -1;
        }
    }

    //-------------------------------------- Protected methods -------------------------------------------

    /** Returns the class file bytes for this class. */
    protected abstract byte[] getClassFileBytes() throws IOException, ClassNotFoundException;

    /**
     * Returns package name for the given class. In case of no package, returns an
     * empty, but non-null string. Returned string is interned.
     */
    protected static String getPackageName(String clazzName) {
        int ldi = clazzName.lastIndexOf('/'); // For convenience, we use system-internal slashes, not dots

        if (ldi == -1) {
            return ""; // NOI18N
        } else {
            return clazzName.substring(0, ldi).intern();
        }
    }

    /** Given the table at the specified index, return the specified entry */
    static long intAt(byte[] codeBytes, int tbl, int entry) { // TODO CHECK: unused method

        int base = tbl + (entry << 2);

        return (codeBytes[base] << 24) | ((codeBytes[base + 1] & 0xFF) << 16) | ((codeBytes[base + 2] & 0xFF) << 8)
               | (codeBytes[base + 3] & 0xFF);
    }

    static void putU2(byte[] buf, int pos, int value) {
        buf[pos] = (byte) ((value >> 8) & 0xFF);
        buf[pos + 1] = (byte) (value & 0xFF);
    }

    static int getU2(byte[] buf, int pos) {
        return ((buf[pos] & 0xFF) << 8) + (buf[pos + 1] & 0xFF);
    }

    private static boolean adjustOffset(int bciIter, int injectionPos, boolean changeTypeIsInjectNewInstr, boolean injectionBindsToFollowingInstruction) {
        boolean adjustOffset = false;
        if (bciIter > injectionPos) {
            adjustOffset = true;
        } else if (changeTypeIsInjectNewInstr) {
            if (injectionPos == 0 && bciIter == 0) {
                adjustOffset = true;
            } else if (!injectionBindsToFollowingInstruction && bciIter >= injectionPos) {
                adjustOffset = true;
            }
        }
        return adjustOffset;
    }    
    //----------------------------------------- Private implementation -----------------------------------

    private synchronized void initLineNumberTables() {
        if (lineNumberTables == null) {
            lineNumberTables = new LineNumberTables(this);
        }
    }

    private synchronized void initLocalVariableTables() {
        if (localVariableTables == null) {
            localVariableTables = new LocalVariableTables(this);
        }
    }

    private synchronized void initLocalVariableTypeTables() {
        if (localVariableTypeTables == null) {
            localVariableTypeTables = new LocalVariableTypeTables(this);
        }
    }

    private synchronized void initStackMapTables() {
        if (stackMapTables == null) {
            stackMapTables = new StackMapTables();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy