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

com.feilong.lib.javassist.bytecode.stackmap.BasicBlock Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.3.0
Show newest version
/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package com.feilong.lib.javassist.bytecode.stackmap;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.feilong.lib.javassist.bytecode.BadBytecode;
import com.feilong.lib.javassist.bytecode.CodeAttribute;
import com.feilong.lib.javassist.bytecode.CodeIterator;
import com.feilong.lib.javassist.bytecode.ExceptionTable;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.bytecode.Opcode;

/**
 * A basic block is a sequence of bytecode that does not contain jump/branch
 * instructions except at the last bytecode.
 * Since Java7 or later does not allow JSR, this class throws an exception when
 * it finds JSR.
 */
public class BasicBlock{

    static class JsrBytecode extends BadBytecode{

        /** default serialVersionUID */
        private static final long serialVersionUID = 1L;

        JsrBytecode(){
            super("JSR");
        }
    }

    protected int          position, length;

    protected int          incoming;        // the number of incoming branches.

    protected BasicBlock[] exit;            // null if the block is a leaf.

    protected boolean      stop;            // true if the block ends with an unconditional jump. 

    protected Catch        toCatch;

    protected BasicBlock(int pos){
        position = pos;
        length = 0;
        incoming = 0;
    }

    public static BasicBlock find(BasicBlock[] blocks,int pos) throws BadBytecode{
        for (BasicBlock b : blocks){
            if (b.position <= pos && pos < b.position + b.length){
                return b;
            }
        }

        throw new BadBytecode("no basic block at " + pos);
    }

    public static class Catch{

        public Catch      next;

        public BasicBlock body;

        public int        typeIndex;

        Catch(BasicBlock b, int i, Catch c){
            body = b;
            typeIndex = i;
            next = c;
        }
    }

    @Override
    public String toString(){
        StringBuffer sbuf = new StringBuffer();
        String cname = this.getClass().getName();
        int i = cname.lastIndexOf('.');
        sbuf.append(i < 0 ? cname : cname.substring(i + 1));
        sbuf.append("[");
        toString2(sbuf);
        sbuf.append("]");
        return sbuf.toString();
    }

    protected void toString2(StringBuffer sbuf){
        sbuf.append("pos=").append(position).append(", len=").append(length).append(", in=").append(incoming).append(", exit{");
        if (exit != null){
            for (BasicBlock b : exit){
                sbuf.append(b.position).append(",");
            }
        }

        sbuf.append("}, {");
        Catch th = toCatch;
        while (th != null){
            sbuf.append("(").append(th.body.position).append(", ").append(th.typeIndex).append("), ");
            th = th.next;
        }

        sbuf.append("}");
    }

    /**
     * A Mark indicates the position of a branch instruction
     * or a branch target.
     */
    static class Mark implements Comparable{

        int          position;

        BasicBlock   block;

        BasicBlock[] jump;

        boolean      alwaysJmp; // true if an unconditional branch.

        int          size;      // 0 unless the mark indicates RETURN etc. 

        Catch        catcher;

        Mark(int p){
            position = p;
            block = null;
            jump = null;
            alwaysJmp = false;
            size = 0;
            catcher = null;
        }

        @Override
        public int compareTo(Mark obj){
            if (null == obj){
                return -1;
            }
            return position - obj.position;
        }

        void setJump(BasicBlock[] bb,int s,boolean always){
            jump = bb;
            size = s;
            alwaysJmp = always;
        }
    }

    public static class Maker{

        /*
         * Override these two methods if a subclass of BasicBlock must be
         * instantiated.
         */
        protected BasicBlock makeBlock(int pos){
            return new BasicBlock(pos);
        }

        protected BasicBlock[] makeArray(int size){
            return new BasicBlock[size];
        }

        private BasicBlock[] makeArray(BasicBlock b){
            BasicBlock[] array = makeArray(1);
            array[0] = b;
            return array;
        }

        private BasicBlock[] makeArray(BasicBlock b1,BasicBlock b2){
            BasicBlock[] array = makeArray(2);
            array[0] = b1;
            array[1] = b2;
            return array;
        }

        public BasicBlock[] make(MethodInfo minfo) throws BadBytecode{
            CodeAttribute ca = minfo.getCodeAttribute();
            if (ca == null){
                return null;
            }

            CodeIterator ci = ca.iterator();
            return make(ci, 0, ci.getCodeLength(), ca.getExceptionTable());
        }

        public BasicBlock[] make(CodeIterator ci,int begin,int end,ExceptionTable et) throws BadBytecode{
            Map marks = makeMarks(ci, begin, end, et);
            BasicBlock[] bb = makeBlocks(marks);
            addCatchers(bb, et);
            return bb;
        }

        /*
         * Branch target
         */
        private Mark makeMark(Map table,int pos){
            return makeMark0(table, pos, true, true);
        }

        /*
         * Branch instruction.
         * size > 0
         */
        private Mark makeMark(Map table,int pos,BasicBlock[] jump,int size,boolean always){
            Mark m = makeMark0(table, pos, false, false);
            m.setJump(jump, size, always);
            return m;
        }

        private Mark makeMark0(Map table,int pos,boolean isBlockBegin,boolean isTarget){
            Integer p = pos;
            Mark m = table.get(p);
            if (m == null){
                m = new Mark(pos);
                table.put(p, m);
            }

            if (isBlockBegin){
                if (m.block == null){
                    m.block = makeBlock(pos);
                }

                if (isTarget){
                    m.block.incoming++;
                }
            }

            return m;
        }

        private Map makeMarks(CodeIterator ci,int begin,int end,ExceptionTable et) throws BadBytecode{
            ci.begin();
            ci.move(begin);
            Map marks = new HashMap<>();
            while (ci.hasNext()){
                int index = ci.next();
                if (index >= end){
                    break;
                }

                int op = ci.byteAt(index);
                if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE) || op == Opcode.IFNULL || op == Opcode.IFNONNULL){
                    Mark to = makeMark(marks, index + ci.s16bitAt(index + 1));
                    Mark next = makeMark(marks, index + 3);
                    makeMark(marks, index, makeArray(to.block, next.block), 3, false);
                }else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH){
                    switch (op) {
                        case Opcode.GOTO:
                            makeGoto(marks, index, index + ci.s16bitAt(index + 1), 3);
                            break;
                        case Opcode.JSR:
                            makeJsr(marks, index, index + ci.s16bitAt(index + 1), 3);
                            break;
                        case Opcode.RET:
                            makeMark(marks, index, null, 2, true);
                            break;
                        case Opcode.TABLESWITCH:{
                            int pos = (index & ~3) + 4;
                            int low = ci.s32bitAt(pos + 4);
                            int high = ci.s32bitAt(pos + 8);
                            int ncases = high - low + 1;
                            BasicBlock[] to = makeArray(ncases + 1);
                            to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target
                            int p = pos + 12;
                            int n = p + ncases * 4;
                            int k = 1;
                            while (p < n){
                                to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block;
                                p += 4;
                            }
                            makeMark(marks, index, to, n - index, true);
                            break;
                        }
                        case Opcode.LOOKUPSWITCH:{
                            int pos = (index & ~3) + 4;
                            int ncases = ci.s32bitAt(pos + 4);
                            BasicBlock[] to = makeArray(ncases + 1);
                            to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target
                            int p = pos + 8 + 4;
                            int n = p + ncases * 8 - 4;
                            int k = 1;
                            while (p < n){
                                to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block;
                                p += 8;
                            }
                            makeMark(marks, index, to, n - index, true);
                            break;
                        }
                    }
                }else if ((Opcode.IRETURN <= op && op <= Opcode.RETURN) || op == Opcode.ATHROW){
                    makeMark(marks, index, null, 1, true);
                }else if (op == Opcode.GOTO_W){
                    makeGoto(marks, index, index + ci.s32bitAt(index + 1), 5);
                }else if (op == Opcode.JSR_W){
                    makeJsr(marks, index, index + ci.s32bitAt(index + 1), 5);
                }else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET){
                    makeMark(marks, index, null, 4, true);
                }
            }

            if (et != null){
                int i = et.size();
                while (--i >= 0){
                    makeMark0(marks, et.startPc(i), true, false);
                    makeMark(marks, et.handlerPc(i));
                }
            }

            return marks;
        }

        private void makeGoto(Map marks,int pos,int target,int size){
            Mark to = makeMark(marks, target);
            BasicBlock[] jumps = makeArray(to.block);
            makeMark(marks, pos, jumps, size, true);
        }

        /*
         * We could ignore JSR since Java 7 or later does not allow it.
         * See The JVM Spec. Sec. 4.10.2.5.
         */
        protected void makeJsr(Map marks,int pos,int target,int size) throws BadBytecode{
            /*
             * Mark to = makeMark(marks, target);
             * Mark next = makeMark(marks, pos + size);
             * BasicBlock[] jumps = makeArray(to.block, next.block);
             * makeMark(marks, pos, jumps, size, false);
             */
            throw new JsrBytecode();
        }

        private BasicBlock[] makeBlocks(Map markTable){
            Mark[] marks = markTable.values().toArray(new Mark[markTable.size()]);
            Arrays.sort(marks);
            List blocks = new ArrayList<>();
            int i = 0;
            BasicBlock prev;
            if (marks.length > 0 && marks[0].position == 0 && marks[0].block != null){
                prev = getBBlock(marks[i++]);
            }else{
                prev = makeBlock(0);
            }

            blocks.add(prev);
            while (i < marks.length){
                Mark m = marks[i++];
                BasicBlock bb = getBBlock(m);
                if (bb == null){
                    // the mark indicates a branch instruction
                    if (prev.length > 0){
                        // the previous mark already has exits.
                        prev = makeBlock(prev.position + prev.length);
                        blocks.add(prev);
                    }

                    prev.length = m.position + m.size - prev.position;
                    prev.exit = m.jump;
                    prev.stop = m.alwaysJmp;
                }else{
                    // the mark indicates a branch target
                    if (prev.length == 0){
                        prev.length = m.position - prev.position;
                        bb.incoming++;
                        prev.exit = makeArray(bb);
                    }else{
                        // the previous mark already has exits.
                        if (prev.position + prev.length < m.position){
                            // dead code is found.
                            prev = makeBlock(prev.position + prev.length);
                            blocks.add(prev);
                            prev.length = m.position - prev.position;
                            // the incoming flow from dead code is not counted
                            // bb.incoming++;
                            prev.stop = true; // because the incoming flow is not counted.
                            prev.exit = makeArray(bb);
                        }
                    }

                    blocks.add(bb);
                    prev = bb;
                }
            }

            return blocks.toArray(makeArray(blocks.size()));
        }

        private static BasicBlock getBBlock(Mark m){
            BasicBlock b = m.block;
            if (b != null && m.size > 0){
                b.exit = m.jump;
                b.length = m.size;
                b.stop = m.alwaysJmp;
            }

            return b;
        }

        private void addCatchers(BasicBlock[] blocks,ExceptionTable et) throws BadBytecode{
            if (et == null){
                return;
            }

            int i = et.size();
            while (--i >= 0){
                BasicBlock handler = find(blocks, et.handlerPc(i));
                int start = et.startPc(i);
                int end = et.endPc(i);
                int type = et.catchType(i);
                handler.incoming--;
                for (BasicBlock block : blocks){
                    BasicBlock bb = block;
                    int iPos = bb.position;
                    if (start <= iPos && iPos < end){
                        bb.toCatch = new Catch(handler, type, bb.toCatch);
                        handler.incoming++;
                    }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy