edu.ubc.mirrors.holograms.FrameAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for edu.ubc.mirrors.holograms.FrameAnalyzer.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Robin Salkeld
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/
package edu.ubc.mirrors.holograms;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.Interpreter;

import edu.ubc.mirrors.holograms.FrameAnalyzer;
import edu.ubc.mirrors.holograms.FrameAnalyzerAdaptor;
import edu.ubc.mirrors.holograms.FrameValue;
import edu.ubc.mirrors.holograms.FrameVerifier;

public class FrameAnalyzer extends Analyzer<FrameValue> {

    public FrameAnalyzer(FrameVerifier verifier) {
        super(verifier);
    }

    private final Map<AbstractInsnNode, Integer> insnIndices = new HashMap<AbstractInsnNode, Integer>();

    private boolean[] jumpIn;
    private boolean[] stepIn;
    private AbstractInsnNode currentInsn;
    private MethodNode m;

    @Override
    protected void init(String owner, MethodNode m) throws AnalyzerException {
        super.init(owner, m);

        this.m = m;
        int n = m.instructions.size();
        this.jumpIn = new boolean[n];
        this.stepIn = new boolean[n];

        this.stepIn[0] = true;

        for (int i = 0; i < n; i++) {
            insnIndices.put(m.instructions.get(i), i);
        }

        if (m.name.equals("<init>")) {
            Frame<FrameValue>[] frames = getFrames();
            FrameValue initializedValue = frames[0].getLocal(0);
            FrameValue uninitializedThis = FrameValue.makeUninitializedThis(initializedValue.getBasicValue());
            frames[0].setLocal(0, uninitializedThis);
        }
    }

    @Override
    protected void newControlFlowEdge(int insn, int successor) {
        if (successor == insn + 1 && (currentInsn == null || (currentInsn.getOpcode() != Opcodes.TABLESWITCH
                && currentInsn.getOpcode() != Opcodes.LOOKUPSWITCH && currentInsn.getOpcode() != Opcodes.GOTO))) {
            stepIn[successor] = true;
        } else {
            jumpIn[successor] = true;
        }

        super.newControlFlowEdge(insn, successor);
    }

    @Override
    protected boolean newControlFlowExceptionEdge(int insn, int successor) {
        jumpIn[successor] = true;

        return super.newControlFlowExceptionEdge(insn, successor);
    }

    private class ContextFrame extends Frame<FrameValue> {

        public ContextFrame(Frame<? extends FrameValue> src) {
            super(src);
        }

        public ContextFrame(final int nLocals, final int nStack) {
            super(nLocals, nStack);
        }

        @Override
        public void execute(AbstractInsnNode insn, Interpreter<FrameValue> interpreter) throws AnalyzerException {
            ((FrameVerifier) interpreter).setContext(this);
            FrameAnalyzer.this.currentInsn = insn;
            super.execute(insn, interpreter);
        }
    }

    protected Frame<FrameValue> newFrame(final int nLocals, final int nStack) {
        return new ContextFrame(nLocals, nStack);
    }

    protected Frame<FrameValue> newFrame(final Frame<? extends FrameValue> src) {
        return new ContextFrame(src);
    }

    public Frame<FrameValue>[] insertFrames() {
        Frame<FrameValue>[] frames = getFrames();
        if (m == null || m.instructions == null) {
            return frames;
        }

        List<Frame<FrameValue>> newFrames = new ArrayList<Frame<FrameValue>>();
        AbstractInsnNode insnNode = m.instructions.getFirst();
        int n = m.instructions.size();
        for (int i = 0; i < n; i++) {
            int insnType = insnNode.getType();
            // Fetch the next node before possibly deleting this one,
            // since that will clear the links.
            AbstractInsnNode nextNode = insnNode.getNext();

            if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE
                    || insnType == AbstractInsnNode.FRAME) {
                if (i + 1 < n) {
                    jumpIn[i + 1] |= jumpIn[i];
                    stepIn[i + 1] = stepIn[i];
                }

                if (insnType == AbstractInsnNode.FRAME) {
                    m.instructions.remove(insnNode);
                } else {
                    newFrames.add(frames[i]);
                }
            } else {
                if (jumpIn[i] || !stepIn[i]) {
                    Frame<FrameValue> frame = frames[i];
                    // Unreachable code will have null frames
                    if (frame != null) {
                        FrameNode frameNode = toFrameNode(frame);
                        m.instructions.insertBefore(insnNode, frameNode);
                        newFrames.add(newFrame(frame));
                    }
                }
                newFrames.add(frames[i]);
            }

            insnNode = nextNode;
        }

        @SuppressWarnings("unchecked")
        Frame<FrameValue>[] result = (Frame<FrameValue>[]) newFrames.toArray(new Frame<?>[newFrames.size()]);
        return result;
    }

    private FrameNode toFrameNode(Frame<FrameValue> frame) {
        int numLocals = frame.getLocals();
        List<Object> localsList = new ArrayList<Object>(frame.getLocals());
        for (int i = 0; i < numLocals; i++) {
            FrameValue value = frame.getLocal(i);
            localsList.add(toFrameObject(value));
            if (value.getSize() == 2) {
                // Skip the next TOP - the visitor API represents locals
                // with one element instead.
                i++;
            }
        }
        Object[] locals = localsList.toArray();

        int stackSize = frame.getStackSize();
        Object[] stack = new Object[stackSize];
        for (int i = 0; i < stackSize; i++) {
            FrameValue value = frame.getStack(i);
            stack[i] = toFrameObject(value);
        }

        return new FrameNode(Opcodes.F_NEW, locals.length, locals, stack.length, stack);
    }

    public Object toFrameObject(FrameValue value) {
        if (value.isUninitializedThis()) {
            return Opcodes.UNINITIALIZED_THIS;
        } else if (value.isUninitialized()) {
            AbstractInsnNode newInsn = value.getNewInsn();
            // TODO: Could also search up to the previous actual instruction...
            AbstractInsnNode labelNode = newInsn.getPrevious();
            if (labelNode == null || !(labelNode instanceof LabelNode)) {
                labelNode = new LabelNode();
                m.instructions.insertBefore(newInsn, labelNode);
            }
            return ((LabelNode) labelNode).getLabel();
        } else {
            Type type = value.getBasicValue().getType();
            if (type == null) {
                return Opcodes.TOP;
            } else {
                switch (type.getSort()) {
                case Type.VOID:
                    // TODO: Not sure what to do here. Used for BasicValue.RETURNADDRESS_VALUE,
                    // but I can't figure out the correct verifier type for the return addresses
                    // used by JSR/RET.
                    return Opcodes.TOP;
                case Type.BOOLEAN:
                case Type.BYTE:
                case Type.CHAR:
                case Type.SHORT:
                case Type.INT:
                    return Opcodes.INTEGER;
                case Type.LONG:
                    return Opcodes.LONG;
                case Type.FLOAT:
                    return Opcodes.FLOAT;
                case Type.DOUBLE:
                    return Opcodes.DOUBLE;
                case Type.OBJECT:
                case Type.ARRAY:
                    return type.getInternalName();
                case Type.METHOD:
                    // Shouldn't happen
                default:
                    throw new RuntimeException("Bad sort: " + type.getSort());
                }
            }
        }
    }

    @Override
    public String toString() {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        FrameAnalyzerAdaptor.printAnalyzerResult(m, getFrames(), pw);
        return sw.toString();
    }
}