Package org.freud.analysed.classbytecode.parser.asm

Source Code of org.freud.analysed.classbytecode.parser.asm.AsmMethod

/*
* Copyright 2013 LMAX Ltd.
*
* Licensed 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.freud.analysed.classbytecode.parser.asm;

import org.freud.analysed.classbytecode.ClassByteCodeInnerClass;
import org.freud.analysed.classbytecode.method.ClassByteCodeMethod;
import org.freud.analysed.classbytecode.method.LocalVariable;
import org.freud.analysed.classbytecode.method.instruction.AbstractOperandStack;
import org.freud.analysed.classbytecode.method.instruction.ConstInstruction;
import org.freud.analysed.classbytecode.method.instruction.FieldInstruction;
import org.freud.analysed.classbytecode.method.instruction.Instruction;
import org.freud.analysed.classbytecode.method.instruction.InstructionVisitor;
import org.freud.analysed.classbytecode.method.instruction.IntOperandInstruction;
import org.freud.analysed.classbytecode.method.instruction.JumpInstruction;
import org.freud.analysed.classbytecode.method.instruction.Label;
import org.freud.analysed.classbytecode.method.instruction.MethodInvocationInstruction;
import org.freud.analysed.classbytecode.method.instruction.Opcode;
import org.freud.analysed.classbytecode.method.instruction.OperandStack;
import org.freud.analysed.classbytecode.method.instruction.ReferenceOperandInstruction;
import org.freud.analysed.classbytecode.method.instruction.StaticOperandStack;
import org.freud.analysed.classbytecode.method.instruction.VarInstruction;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

final class AsmMethod extends AsmElement implements MethodVisitor, ClassByteCodeMethod {
    private static final Pattern METHOD_DESC_PATTERN = Pattern.compile("\\((.*)\\)(.+)");
    private static final Opcode[] OPCODES_ARRAY = Opcode.values();
    private static final String[] NEWARRAY_TYPES =
            {
                    null, null, null, null,
                    boolean.class.getCanonicalName(), char.class.getCanonicalName(),
                    float.class.getCanonicalName(), double.class.getCanonicalName(),
                    byte.class.getCanonicalName(), short.class.getCanonicalName(),
                    int.class.getCanonicalName(), long.class.getCanonicalName(),
            };

    private final String name;
    private final String desc;
    private final String signature;
    private final String[] exceptions;
    private final List<ClassByteCodeInnerClass> innerClassList;
    private final List<Instruction> instructionList;
    private final LinkedHashMap<String, LocalVariable> variableByNameMap;
    private final Map<org.objectweb.asm.Label, Label> labelByAsmLabelMap;

    private int currentLineNumber;
    private OperandStack currentOperandStack = AbstractOperandStack.EMPTY_STACK;
    private List<String> currentLocals;
    private String returnType;

    public AsmMethod(final AsmClassByteCode classByteCode, final int access, final String name, final String desc, final String signature, final String... exceptions) {
        super(access);
        this.signature = signature;
        this.exceptions = exceptions;
        this.name = name;
        this.desc = desc;
        this.innerClassList = new LinkedList<ClassByteCodeInnerClass>();
        this.instructionList = new ArrayList<Instruction>();
        this.labelByAsmLabelMap = new HashMap<org.objectweb.asm.Label, Label>();
        this.variableByNameMap = new LinkedHashMap<String, LocalVariable>();
        for (ClassByteCodeInnerClass innerClass : classByteCode.getInnerClassList()) {
            if (innerClass.isAnonymous() && name.equals(innerClass.getOuterName()) && desc.equals(innerClass.getOuterDesc())) {
                innerClassList.add(innerClass);
            }
        }
        classByteCode.addMethod(this);
        currentLocals = new ArrayList<String>();
        initLocals(desc);
        this.currentLineNumber = -1;
    }

    @Override
    public void findInstruction(final InstructionVisitor instructionVisitor) {
        for (final Instruction instruction : instructionList) {
            instruction.visit(instructionVisitor);
        }
    }

    @Override
    public String getReturnType() {
        return returnType;
    }

    @Override
    public Instruction getInstruction(final int index) {
        return instructionList.get(index);
    }

    @Override
    public List<ClassByteCodeInnerClass> getAnonymousClassList() {
        return innerClassList;
    }

    @Override
    public LocalVariable getLocalVariable(final String name) {
        return variableByNameMap.get(name);
    }

    @Override
    public LocalVariable getLocalVariable(final int index) {
        int i = 0;
        for (LocalVariable variable : variableByNameMap.values()) {
            if (index == i++) {
                return variable;
            }
        }
        return null;
    }

    @Override
    public String getLocalVariableType(final int index) {
        return currentLocals.get(index);
    }

    @Override
    public boolean isStatic() {
        return isAccessModifier(Opcodes.ACC_STATIC);
    }

    @Override
    public boolean isSynchronized() {
        return isAccessModifier(Opcodes.ACC_SYNCHRONIZED);
    }

    @Override
    public boolean isBridge() {
        return isAccessModifier(Opcodes.ACC_BRIDGE);
    }

    @Override
    public boolean isVarargs() {
        return isAccessModifier(Opcodes.ACC_VARARGS);
    }

    @Override
    public boolean isNative() {
        return isAccessModifier(Opcodes.ACC_NATIVE);
    }

    @Override
    public boolean isStrict() {
        return isAccessModifier(Opcodes.ACC_STRICT);
    }

    @Override
    public boolean isAbstract() {
        return isAccessModifier(Opcodes.ACC_ABSTRACT);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDesc() {
        return desc;
    }

    @Override
    public String getSignature() {
        return signature;
    }

    @Override
    public String[] getExceptions() {
        return exceptions;
    }

    @Override
    public AnnotationVisitor visitAnnotationDefault() {
        return new AsmAnnotation(this);
    }

    @Override
    public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
        return new AsmAnnotation(this, desc, visible);
    }

    @Override
    public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) {
        return new AsmAnnotation(this, parameter, desc, visible);
    }

    @Override
    public void visitAttribute(final Attribute attr) {
        //  no op
    }


    public void visitCode() {
        // no op
    }

    public void visitInsn(final int opcode) {
        final Instruction instruction = new Instruction(instructionList.size(), OPCODES_ARRAY[opcode], currentLineNumber);
        updateCurrentState(instruction);
    }

    public void visitIntInsn(final int opcodeUsed, final int operand) {
        final Opcode opcode = OPCODES_ARRAY[opcodeUsed];
        final Instruction instruction;
        if (opcode == Opcode.NEWARRAY) {
            instruction = new ReferenceOperandInstruction(instructionList.size(), opcode, currentLineNumber, NEWARRAY_TYPES[operand]);
        }
        else {
            instruction = new IntOperandInstruction(instructionList.size(), opcode, currentLineNumber, operand);
        }
        updateCurrentState(instruction);
    }

    public void visitVarInsn(final int opcodeUsed, final int var) {
        final Instruction instruction = new VarInstruction(instructionList.size(), OPCODES_ARRAY[opcodeUsed], currentLineNumber, var);
        updateCurrentState(instruction);
    }

    public void visitTypeInsn(final int opcodeUsed, final String type) {
        final Opcode opcode = OPCODES_ARRAY[opcodeUsed];
        final String operandType = "L" + type + ";";
        final Instruction instruction = new ReferenceOperandInstruction(instructionList.size(), opcode, currentLineNumber, operandType);
        updateCurrentState(instruction);
    }

    public void visitFieldInsn(
            final int opcode,
            final String owner,
            final String name,
            final String desc) {
        final Instruction instruction = new FieldInstruction(instructionList.size(), OPCODES_ARRAY[opcode], currentLineNumber, owner, name, desc);
        updateCurrentState(instruction);
    }

    public void visitMethodInsn(
            final int opcode,
            final String owner,
            final String name,
            final String desc) {
        final Matcher matcher = METHOD_DESC_PATTERN.matcher(desc);
        if (matcher.matches()) {
            final String argsAsString = matcher.group(1);
            final ArrayList<String> argsContainer = new ArrayList<String>();
            String returnType = matcher.group(2);
            parseArgs(argsAsString, argsContainer);
            String[] args = argsContainer.toArray(new String[argsContainer.size()]);
            final Instruction instruction = new MethodInvocationInstruction(instructionList.size(), OPCODES_ARRAY[opcode], currentLineNumber,
                                                                            "L" + owner + ";", name, args, returnType);
            updateCurrentState(instruction);
        }
    }

    public void visitJumpInsn(final int opcode, final org.objectweb.asm.Label asmLabel) {
        Label label = declareLabel(asmLabel);

//        System.out.println(name + " " + OPCODES_ARRAY[opcode] + " " + asmLabel + " " + label);


        final Instruction instruction = new JumpInstruction(instructionList.size(), OPCODES_ARRAY[opcode], currentLineNumber, label);
        updateCurrentState(instruction);
    }

    public void visitLabel(final org.objectweb.asm.Label asmLabel) {
        final Label label = declareLabel(asmLabel);
        label.declare(instructionList.size());
        final String handledType = label.getHandledType();
        if (handledType != null) {
            currentOperandStack = new StaticOperandStack(handledType, currentOperandStack, null);
        }
    }

    public void visitLdcInsn(final Object constant) {
        final Instruction instruction;
        if (constant instanceof Type) {
            instruction = new ReferenceOperandInstruction(instructionList.size(), Opcode.LDC, currentLineNumber, constant.toString());
        }
        else {
            instruction = new ConstInstruction(instructionList.size(), Opcode.LDC, currentLineNumber, constant);
        }
        updateCurrentState(instruction);
    }

    public void visitIincInsn(final int var, final int increment) {
        final Instruction instruction = new IntOperandInstruction(instructionList.size(), Opcode.IINC, currentLineNumber, increment);
        updateCurrentState(instruction);
    }

    public void visitTableSwitchInsn(
            final int min,
            final int max,
            final org.objectweb.asm.Label dflt,
            final org.objectweb.asm.Label[] labels) {
        for (int i = 0; i < labels.length; ++i) {
            declareLookupLabel(labels[i], min + i);
        }
        declareDefaultLookupLabel(dflt);
    }

    public void visitLookupSwitchInsn(
            final org.objectweb.asm.Label dflt,
            final int[] keys,
            final org.objectweb.asm.Label[] labels) {
        for (int i = 0; i < labels.length; ++i) {
            declareLookupLabel(labels[i], keys[i]);
        }
        declareDefaultLookupLabel(dflt);
    }

    public void visitTryCatchBlock(
            final org.objectweb.asm.Label start,
            final org.objectweb.asm.Label end,
            final org.objectweb.asm.Label handler,
            final String type) {
        declareHandlerLabel(handler, (type != null) ? "L" + type + ";" : "Ljava/lang/Throwable;");
        declareLabel(start);
        declareLabel(end);
    }

    public void visitFrame(
            final int type,
            final int nLocal,
            final Object[] local,
            final int nStack,
            final Object[] stack) {
        final FrameType frameType = FrameType.getFrameType(type);
        switch (frameType) {
            case F_SAME:
                currentOperandStack = AbstractOperandStack.EMPTY_STACK;
                break;
            case F_SAME1:
                currentOperandStack = new StaticOperandStack(getTypeFromFrame(stack[0]), AbstractOperandStack.EMPTY_STACK, null);
                break;
            case F_APPEND:
                currentOperandStack = AbstractOperandStack.EMPTY_STACK;
                for (int i = 0; i < nLocal; i++) {
                    currentLocals.add(getTypeFromFrame(local[i]));
                }
                break;
            case F_CHOP:
                currentOperandStack = AbstractOperandStack.EMPTY_STACK;
                currentLocals = currentLocals.subList(0, currentLocals.size() - nLocal);
                break;
            case F_FULL:
                currentLocals = new ArrayList<String>();
                for (int i = 0; i < nLocal; i++) {
                    currentLocals.add(getTypeFromFrame(local[i]));

                }
                currentOperandStack = AbstractOperandStack.EMPTY_STACK;
                for (int i = 0; i < nStack; i++) {
                    currentOperandStack = new StaticOperandStack(getTypeFromFrame(stack[i]), currentOperandStack, null);
                }
                break;
            case F_NEW:
                break;
        }
    }

    public void visitMultiANewArrayInsn(final String desc, final int dims) {
        final Instruction instruction = new ReferenceOperandInstruction(instructionList.size(), Opcode.MULTIANEWARRAY, currentLineNumber, desc, dims);
        updateCurrentState(instruction);
    }

    public void visitLocalVariable(
            final String name,
            final String desc,
            final String signature,
            final org.objectweb.asm.Label start,
            final org.objectweb.asm.Label end,
            final int index) {
        variableByNameMap.put(name, new LocalVariable(name, desc, signature, declareLabel(start), declareLabel(end)));
    }

    public void visitLineNumber(final int line, final org.objectweb.asm.Label start) {
        if (currentLineNumber < line) {
            currentLineNumber = line;
        }
        labelByAsmLabelMap.get(start).setLineNumber(line);
    }

    public void visitMaxs(final int maxStack, final int maxLocals) {
        // TODO
    }

    @Override
    public void visitEnd() {
        // no op
    }

    private String getTypeFromFrame(final Object item) {
        if (item instanceof String) {
            final String strItem = (String) item;
            return (strItem.indexOf('/') > -1) ? "L" + strItem + ";" : strItem;
        }
        else if (item instanceof org.objectweb.asm.Label) {
            return "Ljava/lang/Object;";
        }
        else if (item instanceof Integer) {
            final FrameValueType valueType = FrameValueType.getFrameValueType((Integer) item);
            switch (valueType) {
                case TOP:
                case NULL:
                case UNINITIALIZED_THIS:
                    return "Ljava/lang/Object;";
                case INTEGER:
                    return "I";
                case FLOAT:
                    return "F";
                case LONG:
                    return "J";
                case DOUBLE:
                    return "D";
                default:
                    throw new IllegalArgumentException("frame item " + item);
            }
        }
        else {
            throw new IllegalArgumentException("frame item " + item);
        }
    }

    private void initLocals(final String desc) {
        final Matcher matcher = METHOD_DESC_PATTERN.matcher(desc);
        if (matcher.matches()) {
            final String paramsAsString = matcher.group(1);
            parseArgs(paramsAsString, currentLocals);
            returnType = matcher.group(2);
        }
        else {
            throw new IllegalArgumentException("desc " + desc);
        }
    }

    @Override
    public String toString() {
        return "AsmMethod[" + name + "]";
    }

    ////////////////////////////////////////////////////////////////////////

    private void parseArgs(final String argsAsString, final List<String> args) {
        boolean start = true;
        final int len = argsAsString.length();
        int ptr = 0;
        for (int i = 0; i < len; i++) {
            final char c = argsAsString.charAt(i);
            switch (c) {
                case '[':
                case 'L':
                    start = false;
                    break;
                case ';':
                    args.add(argsAsString.substring(ptr, i + 1));
                    ptr = i + 1;
                    start = true;
                    break;
                case 'B':
                case 'C':
                case 'D':
                case 'F':
                case 'I':
                case 'J':
                case 'S':
                case 'Z':
                    if (start) {
                        args.add(String.valueOf(c));
                        ptr = i + 1;
                    }
                    break;
            }
        }
        if (ptr < len) {
            args.add(argsAsString.substring(ptr));
        }
    }

    private Label declareLabel(final org.objectweb.asm.Label asmLabel) {
        return storeLabel(asmLabel, Label.create(instructionList.size()));
    }

    private Label declareHandlerLabel(final org.objectweb.asm.Label asmLabel, final String type) {
        return storeLabel(asmLabel, Label.createHandler(instructionList.size(), type));
    }

    private Label declareLookupLabel(final org.objectweb.asm.Label asmLabel, final int key) {
        return storeLabel(asmLabel, Label.createLookupKey(instructionList.size(), key));
    }


    private Label declareDefaultLookupLabel(final org.objectweb.asm.Label asmLabel) {
        return storeLabel(asmLabel, Label.createDefaultLookupKey(instructionList.size()));
    }

    private Label storeLabel(final org.objectweb.asm.Label asmLabel, final Label label) {
        final Label oldLabel = labelByAsmLabelMap.get(asmLabel);
        if (oldLabel != null) {
            return oldLabel;
        }
        else {
            labelByAsmLabelMap.put(asmLabel, label);
            return label;
        }
    }

    private void updateCurrentState(final Instruction instruction) {
        instructionList.add(instruction);

//        System.out.println(name + " " + instruction + " "  + labelByAsmLabelMap);

        final Opcode opcode = instruction.getOpcode();
        ensureCurrentLocalsSize(instruction.getVarIndex());
        currentLocals = opcode.updateLocals(currentLocals, instruction);
        currentOperandStack = opcode.updateOperandStack(this, instruction, currentOperandStack);
        instruction.setOperandStack(currentOperandStack);
    }

    private void ensureCurrentLocalsSize(final int varIndex) {
        while (varIndex >= currentLocals.size()) {
            currentLocals.add("");
        }
    }

    private enum FrameValueType {
        TOP(Opcodes.TOP),
        INTEGER(Opcodes.INTEGER),
        FLOAT(Opcodes.FLOAT),
        LONG(Opcodes.LONG),
        DOUBLE(Opcodes.DOUBLE),
        NULL(Opcodes.NULL),
        UNINITIALIZED_THIS(Opcodes.UNINITIALIZED_THIS);

        private final int asmValue;

        FrameValueType(final int asmValue) {
            this.asmValue = asmValue;
        }

        public static FrameValueType getFrameValueType(final int asmValue) {
            for (FrameValueType valueType : FrameValueType.values()) {
                if (valueType.asmValue == asmValue) {
                    return valueType;
                }
            }
            throw new IllegalArgumentException("Unknown frame value type " + asmValue);
        }
    }

    private enum FrameType {
        F_NEW(Opcodes.F_NEW),
        F_FULL(Opcodes.F_FULL),
        F_APPEND(Opcodes.F_APPEND),
        F_CHOP(Opcodes.F_CHOP),
        F_SAME(Opcodes.F_SAME),
        F_SAME1(Opcodes.F_SAME1);

        private final int asmValue;

        FrameType(final int asmValue) {
            this.asmValue = asmValue;
        }

        public static FrameType getFrameType(final int asmValue) {
            for (FrameType type : FrameType.values()) {
                if (type.asmValue == asmValue) {
                    return type;
                }
            }
            throw new IllegalArgumentException("Unknown frame type " + asmValue);
        }

    }
}
TOP

Related Classes of org.freud.analysed.classbytecode.parser.asm.AsmMethod

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.