Package com.strobel.assembler.metadata

Source Code of com.strobel.assembler.metadata.MethodReader

/*
* MethodReader.java
*
* Copyright (c) 2013 Mike Strobel
*
* This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain;
* and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/

package com.strobel.assembler.metadata;

import com.strobel.assembler.flowanalysis.ControlFlowGraph;
import com.strobel.assembler.flowanalysis.ControlFlowGraphBuilder;
import com.strobel.assembler.flowanalysis.ControlFlowNode;
import com.strobel.assembler.flowanalysis.ControlFlowNodeType;
import com.strobel.assembler.ir.ErrorOperand;
import com.strobel.assembler.ir.ExceptionBlock;
import com.strobel.assembler.ir.ExceptionHandler;
import com.strobel.assembler.ir.Instruction;
import com.strobel.assembler.ir.InstructionCollection;
import com.strobel.assembler.ir.OpCode;
import com.strobel.assembler.ir.OpCodeHelpers;
import com.strobel.assembler.ir.attributes.AttributeNames;
import com.strobel.assembler.ir.attributes.CodeAttribute;
import com.strobel.assembler.ir.attributes.ExceptionTableEntry;
import com.strobel.assembler.ir.attributes.LocalVariableTableAttribute;
import com.strobel.assembler.ir.attributes.LocalVariableTableEntry;
import com.strobel.assembler.ir.attributes.SourceAttribute;
import com.strobel.core.StringUtilities;
import com.strobel.core.VerifyArgument;
import com.strobel.decompiler.ast.Range;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class MethodReader {
    private final MethodDefinition _methodDefinition;
    private final CodeAttribute _code;
    private final IMetadataScope _scope;
    private final MethodBody _methodBody;
    private final TypeReference _declaringType;
    private final int _modifiers;

    public MethodReader(final MethodDefinition methodDefinition, final IMetadataScope scope) {
        _methodDefinition = VerifyArgument.notNull(methodDefinition, "methodDefinition");
        _scope = VerifyArgument.notNull(scope, "scope");
        _declaringType = methodDefinition.getDeclaringType();
        _modifiers = methodDefinition.getModifiers();
        _code = SourceAttribute.find(AttributeNames.Code, methodDefinition.getSourceAttributes());
        _methodBody = new MethodBody(methodDefinition);
        _methodBody.setCodeSize(_code.getCode().size());
        _methodBody.setMaxStackSize(_code.getMaxStack());
        _methodBody.setMaxLocals(_code.getMaxLocals());
    }

    @SuppressWarnings("ConstantConditions")
    public MethodBody readBody() {
        final Buffer b = _code.getCode();

        b.position(0);

        final InstructionCollection body = _methodBody.getInstructions();
        final VariableDefinitionCollection variables = _methodBody.getVariables();

        final LocalVariableTableAttribute localVariableTable = SourceAttribute.find(
            AttributeNames.LocalVariableTable,
            _code.getAttributes()
        );

        final LocalVariableTableAttribute localVariableTypeTable = SourceAttribute.find(
            AttributeNames.LocalVariableTypeTable,
            _code.getAttributes()
        );

        final boolean hasThis = !Modifier.isStatic(_modifiers);
        final List<ParameterDefinition> parameters = _methodDefinition.getParameters();

        if (hasThis) {
            final VariableDefinition thisVariable = new VariableDefinition(
                0,
                "this",
                _methodDefinition,
                _declaringType
            );

            thisVariable.setScopeStart(0);
            thisVariable.setScopeEnd(_code.getCodeSize());
            thisVariable.setFromMetadata(true);

            variables.add(thisVariable);

            _methodBody.setThisParameter(new ParameterDefinition(0, "this", _declaringType));
        }

        for (int i = 0; i < parameters.size(); i++) {
            final ParameterDefinition parameter = parameters.get(i);
            final int variableSlot = parameter.getSlot();
            final VariableDefinition variable = variables.ensure(variableSlot, OpCode.NOP, 0);

            variable.setScopeStart(0);
            variable.setScopeEnd(_code.getCodeSize());
            variable.setTypeKnown(true);
            variable.setName(parameter.getName());
            variable.setFromMetadata(true);
            variable.setVariableType(parameter.getParameterType());
        }

        if (localVariableTable != null) {
            processLocalVariableTable(variables, localVariableTable, parameters);
        }

        if (localVariableTypeTable != null) {
            processLocalVariableTable(variables, localVariableTypeTable, parameters);
        }

        variables.mergeVariables();

        @SuppressWarnings("unchecked")
        final Fixup[] fixups = new Fixup[b.size()];

        while (b.position() < b.size()) {
            final int offset = b.position();

            int code = b.readUnsignedByte();

            if (code == OpCode.WIDE) {
                code = code << 8 | b.readUnsignedByte();
            }

            final OpCode op = OpCode.get(code);
            final Instruction instruction;

            switch (op.getOperandType()) {
                case None: {
                    if (op.isLoad() || op.isStore()) {
                        variables.ensure(OpCodeHelpers.getLoadStoreMacroArgumentIndex(op), op, offset);
                    }
                    instruction = Instruction.create(op);
                    break;
                }

                case PrimitiveTypeCode: {
                    instruction = Instruction.create(op, BuiltinTypes.fromPrimitiveTypeCode(b.readUnsignedByte()));
                    break;
                }

                case TypeReference: {
                    final int typeToken = b.readUnsignedShort();
                    instruction = Instruction.create(op, _scope.lookupType(typeToken));
                    break;
                }

                case TypeReferenceU1: {
                    instruction = Instruction.create(op, _scope.lookupType(b.readUnsignedShort()), b.readUnsignedByte());
                    break;
                }

                case DynamicCallSite: {
                    instruction = Instruction.create(op, _scope.lookupDynamicCallSite(b.readUnsignedShort()));
                    b.readUnsignedByte();
                    b.readUnsignedByte();
                    break;
                }

                case MethodReference: {
                    instruction = Instruction.create(op, _scope.lookupMethod(b.readUnsignedShort()));

                    if (op == OpCode.INVOKEINTERFACE) {
                        b.readUnsignedByte();
                        b.readUnsignedByte();
                    }

                    break;
                }

                case FieldReference: {
                    instruction = Instruction.create(op, _scope.lookupField(b.readUnsignedShort()));
                    break;
                }

                case BranchTarget: {
                    final int targetOffset;

                    instruction = new Instruction(op);

                    if (op.isWide()) {
                        targetOffset = offset + _scope.<Integer>lookupConstant(b.readUnsignedShort());
                    }
                    else {
                        targetOffset = offset + (int) b.readShort();
                    }

                    if (targetOffset < offset) {
                        final Instruction target = body.atOffset(targetOffset);

                        if (!target.hasLabel()) {
                            target.setLabel(new Label(targetOffset));
                        }

                        instruction.setOperand(target);
                    }
                    else if (targetOffset == offset) {
                        instruction.setOperand(instruction);
                        instruction.setLabel(new Label(offset));
                    }
                    else if (targetOffset > b.size()) {
                        //
                        // Target is a label after the last instruction.  Insert a dummy NOP.
                        //
                        instruction.setOperand(new Instruction(targetOffset, OpCode.NOP));
                    }
                    else {
                        final Fixup oldFixup = fixups[targetOffset];
                        final Fixup newFixup = new Fixup() {
                            @Override
                            public void fix(final Instruction target) {
                                instruction.setOperand(target);
                            }
                        };

                        fixups[targetOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
                                                                : newFixup;
                    }

                    break;
                }

                case I1: {
                    instruction = Instruction.create(op, b.readByte());
                    break;
                }

                case I2: {
                    instruction = Instruction.create(op, b.readShort());
                    break;
                }

                case I8: {
                    instruction = Instruction.create(op, b.readLong());
                    break;
                }

                case Constant: {
                    instruction = new Instruction(op, _scope.lookupConstant(b.readUnsignedByte()));
                    break;
                }

                case WideConstant: {
                    final int constantToken = b.readUnsignedShort();
                    instruction = new Instruction(op, _scope.lookupConstant(constantToken));
                    break;
                }

                case Switch: {
                    while (b.position() % 4 != 0) {
                        b.readByte();
                    }

                    final SwitchInfo switchInfo = new SwitchInfo();
                    final int defaultOffset = offset + b.readInt();

                    instruction = Instruction.create(op, switchInfo);

                    if (defaultOffset < offset) {
                        switchInfo.setDefaultTarget(body.atOffset(defaultOffset));
                    }
                    else if (defaultOffset == offset) {
                        switchInfo.setDefaultTarget(instruction);
                    }
                    else {
                        switchInfo.setDefaultTarget(new Instruction(defaultOffset, OpCode.NOP));

                        final Fixup oldFixup = fixups[defaultOffset];
                        final Fixup newFixup = new Fixup() {
                            @Override
                            public void fix(final Instruction target) {
                                switchInfo.setDefaultTarget(target);
                            }
                        };

                        fixups[defaultOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
                                                                 : newFixup;
                    }

                    if (op == OpCode.TABLESWITCH) {
                        final int low = b.readInt();
                        final int high = b.readInt();
                        final Instruction[] targets = new Instruction[high - low + 1];

                        switchInfo.setLowValue(low);
                        switchInfo.setHighValue(high);

                        for (int i = 0; i < targets.length; i++) {
                            final int targetIndex = i;
                            final int targetOffset = offset + b.readInt();

                            if (targetOffset < offset) {
                                targets[targetIndex] = body.atOffset(targetOffset);
                            }
                            else if (targetOffset == offset) {
                                targets[targetIndex] = instruction;
                            }
                            else {
                                targets[targetIndex] = new Instruction(targetOffset, OpCode.NOP);

                                final Fixup oldFixup = fixups[targetOffset];
                                final Fixup newFixup = new Fixup() {
                                    @Override
                                    public void fix(final Instruction target) {
                                        targets[targetIndex] = target;
                                    }
                                };

                                fixups[targetOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
                                                                        : newFixup;
                            }
                        }

                        switchInfo.setTargets(targets);
                    }
                    else {
                        final int pairCount = b.readInt();
                        final int[] keys = new int[pairCount];
                        final Instruction[] targets = new Instruction[pairCount];

                        for (int i = 0; i < pairCount; i++) {
                            final int targetIndex = i;

                            keys[targetIndex] = b.readInt();

                            final int targetOffset = offset + b.readInt();

                            if (targetOffset < offset) {
                                targets[targetIndex] = body.atOffset(targetOffset);
                            }
                            else if (targetOffset == offset) {
                                targets[targetIndex] = instruction;
                            }
                            else {
                                targets[targetIndex] = new Instruction(targetOffset, OpCode.NOP);

                                final Fixup oldFixup = fixups[targetOffset];
                                final Fixup newFixup = new Fixup() {
                                    @Override
                                    public void fix(final Instruction target) {
                                        targets[targetIndex] = target;
                                    }
                                };

                                fixups[targetOffset] = oldFixup != null ? Fixup.combine(oldFixup, newFixup)
                                                                        : newFixup;
                            }
                        }

                        switchInfo.setKeys(keys);
                        switchInfo.setTargets(targets);
                    }

                    break;
                }

                case Local: {
                    final int variableSlot;

                    if (op.isWide()) {
                        variableSlot = b.readUnsignedShort();
                    }
                    else {
                        variableSlot = b.readUnsignedByte();
                    }

                    final VariableDefinition variable = variables.ensure(variableSlot, op, offset);

                    if (variableSlot < 0) {
                        instruction = new Instruction(op, new ErrorOperand("!!! BAD LOCAL: " + variableSlot + " !!!"));
                    }
                    else {
                        instruction = Instruction.create(op, variable);
                    }

                    break;
                }

                case LocalI1: {
                    final int variableSlot;
                    final int operand;

                    if (op.isWide()) {
                        variableSlot = b.readUnsignedShort();
                    }
                    else {
                        variableSlot = b.readUnsignedByte();
                    }

                    final VariableDefinition variable = variables.ensure(variableSlot, op, offset);

                    operand = b.readByte();

                    if (variableSlot < 0) {
                        instruction = new Instruction(
                            op,
                            new ErrorOperand("!!! BAD LOCAL: " + variableSlot + " !!!"),
                            operand
                        );
                    }
                    else {
                        instruction = Instruction.create(op, variable, operand);
                    }

                    break;
                }

                case LocalI2: {
                    final int variableSlot;
                    final int operand;

                    if (op.isWide()) {
                        variableSlot = b.readUnsignedShort();
                    }
                    else {
                        variableSlot = b.readUnsignedByte();
                    }

                    final VariableDefinition variable = variables.ensure(variableSlot, op, offset);

                    operand = b.readShort();

                    if (variableSlot < 0) {
                        instruction = new Instruction(
                            op,
                            new ErrorOperand("!!! BAD LOCAL: " + variableSlot + " !!!"),
                            operand
                        );
                    }
                    else {
                        instruction = Instruction.create(op, variable, operand);
                    }

                    break;
                }

                default: {
                    throw new IllegalStateException("Unrecognized opcode: " + code);
                }
            }

            instruction.setOffset(offset);
            body.add(instruction);

            final Fixup fixup = fixups[offset];

            if (fixup != null) {
                if (!instruction.hasLabel()) {
                    instruction.setLabel(new Label(offset));
                }
                fixup.fix(instruction);
            }
        }

        variables.updateScopes(_code.getCodeSize());
        variables.mergeVariables();
        variables.updateScopes(_code.getCodeSize());

        int labelCount = 0;

        for (int i = 0; i < body.size(); i++) {
            final Instruction instruction = body.get(i);
            final OpCode code = instruction.getOpCode();
            final Object operand = instruction.hasOperand() ? instruction.getOperand(0) : null;

            if (operand instanceof VariableDefinition) {
                final VariableDefinition currentVariable = (VariableDefinition) operand;
                final int effectiveOffset;

                if (code.isStore()) {
                    effectiveOffset = instruction.getOffset() + code.getSize() + code.getOperandType().getBaseSize();
                }
                else {
                    effectiveOffset = instruction.getOffset();
                }

                VariableDefinition actualVariable = variables.tryFind(currentVariable.getSlot(), effectiveOffset);

                if (actualVariable == null && code.isStore()) {
                    actualVariable = variables.find(
                        currentVariable.getSlot(),
                        effectiveOffset + code.getSize() + code.getOperandType().getBaseSize()
                    );
                }

                if (actualVariable != currentVariable) {
                    if (instruction.getOperandCount() > 1) {
                        final Object[] operands = new Object[instruction.getOperandCount()];

                        operands[0] = actualVariable;

                        for (int j = 1; j < operands.length; j++) {
                            operands[j] = instruction.getOperand(j);
                        }

                        instruction.setOperand(operands);
                    }
                    else {
                        instruction.setOperand(actualVariable);
                    }
                }
            }

            if (instruction.hasLabel()) {
                instruction.getLabel().setIndex(labelCount++);
            }
        }

        final List<ExceptionTableEntry> exceptionTable = _code.getExceptionTableEntries();

        if (!exceptionTable.isEmpty()) {
            populateExceptionHandlerInfo(body, exceptionTable);
        }

        return _methodBody;
    }

    private void processLocalVariableTable(
        final VariableDefinitionCollection variables,
        final LocalVariableTableAttribute table,
        final List<ParameterDefinition> parameters) {

        for (final LocalVariableTableEntry entry : table.getEntries()) {
            final int slot = entry.getIndex();
            final int scopeStart = entry.getScopeOffset();
            final int scopeEnd = scopeStart + entry.getScopeLength();

            VariableDefinition variable = variables.tryFind(slot, scopeStart);

            if (variable == null) {
                variable = new VariableDefinition(
                    slot,
                    entry.getName(),
                    _methodDefinition,
                    entry.getType()
                );

                variables.add(variable);
            }
            else if (!StringUtilities.isNullOrEmpty(entry.getName())) {
                variable.setName(entry.getName());
            }

            variable.setVariableType(entry.getType());
            variable.setTypeKnown(true);
            variable.setFromMetadata(true);
            variable.setScopeStart(scopeStart);
            variable.setScopeEnd(scopeEnd);

            if (entry.getScopeOffset() == 0) {
                ParameterDefinition parameter = null;

                for (int j = 0; j < parameters.size(); j++) {
                    if (parameters.get(j).getSlot() == entry.getIndex()) {
                        parameter = parameters.get(j);
                        break;
                    }
                }

                if (parameter != null && !parameter.hasName()) {
                    parameter.setName(entry.getName());
                }
            }
        }
    }

    // <editor-fold defaultstate="collapsed" desc="Exception Handler Info">

    @SuppressWarnings("ConstantConditions")
    private void populateExceptionHandlerInfo(final InstructionCollection body, final List<ExceptionTableEntry> exceptionTable) {
        if (body.isEmpty()) {
            return;
        }

        final Instruction bodyEndInstruction = body.get(body.size() - 1);

        final List<HandlerWithRange> entries = new ArrayList<>(exceptionTable.size());

        for (final ExceptionTableEntry entry : exceptionTable) {
            entries.add(
                new HandlerWithRange(
                    entry,
                    new Range(entry.getHandlerOffset(), Integer.MAX_VALUE)
                )
            );
        }

//        Collections.sort(entries);

        final ControlFlowGraph cfg = ControlFlowGraphBuilder.build(body, Collections.<ExceptionHandler>emptyList());

        cfg.computeDominance();
        cfg.computeDominanceFrontier();

        final List<ControlFlowNode> nodes = cfg.getNodes();
        final Map<Instruction, ControlFlowNode> nodeLookup = new IdentityHashMap<>();

        for (int j = 0; j < nodes.size(); j++) {
            final ControlFlowNode node = nodes.get(j);

            if (node.getNodeType() != ControlFlowNodeType.Normal) {
                continue;
            }

            for (Instruction i = node.getStart();
                 i != null && i.getOffset() < node.getEnd().getEndOffset();
                 i = i.getNext()) {

                nodeLookup.put(i, node);
            }
        }

        for (int i = 0; i < entries.size(); i++) {
            int minOffset = Integer.MAX_VALUE;

            final HandlerWithRange entry = entries.get(i);

            ControlFlowNode tryEnd = null;

            for (int j = 0; j < nodes.size(); j++) {
                final ControlFlowNode node = nodes.get(j);
                final Instruction end = node.getEnd();

                if (end != null && end.getOffset() == entry.entry.getEndOffset()) {
                    final Instruction previousInstruction = node.getStart().getPrevious();
                    final HandlerWithRange nearestHandler = findNearestHandler(entries, entry);
                    final Instruction firstHandlerInstruction = body.atOffset(nearestHandler.range.getStart());

                    if (end.getOpCode() == OpCode.GOTO && end.getNext() == firstHandlerInstruction) {
                        tryEnd = nodeLookup.get(end);
                    }
                    else if (previousInstruction != null) {
                        tryEnd = nodeLookup.get(previousInstruction);
                    }

                    break;
                }
            }

            for (int j = 0; j < nodes.size(); j++) {
                final ControlFlowNode node = nodes.get(j);

                if (node.getNodeType() != ControlFlowNodeType.Normal) {
                    continue;
                }

                if (node.getStart().getOffset() == entry.range.getStart()) {
                    final ControlFlowNode end = findHandlerEnd(node, tryEnd, new LinkedHashSet<ControlFlowNode>(), cfg.getRegularExit());

                    if (end != null && end.getNodeType() == ControlFlowNodeType.Normal) {
                        minOffset = end.getEnd().getEndOffset();
                    }
                    else {
                        minOffset = node.getEnd().getEndOffset();
                    }

                    for (int k = 0; k < entries.size(); k++) {
                        final HandlerWithRange other = entries.get(k);

                        if (k != i &&
                            entry.entry.getStartOffset() >= other.entry.getStartOffset() &&
                            entry.entry.getHandlerOffset() < other.entry.getHandlerOffset() &&
                            entry.entry.getEndOffset() <= other.entry.getEndOffset() &&
                            other.range.getStart() < minOffset) {

                            minOffset = other.range.getStart();
                        }
                    }

                    break;
                }
            }

            if (minOffset != Integer.MAX_VALUE) {
                entry.range.setEnd(minOffset);
            }
        }

//        Collections.sort(entries);

        final List<ExceptionHandler> exceptionHandlers = _methodBody.getExceptionHandlers();

        for (final HandlerWithRange entry : entries) {
            final int startOffset = entry.entry.getStartOffset();
            final int endOffset = entry.entry.getEndOffset();
            final int handlerStart = entry.range.getStart();
            final int handlerEnd = entry.range.getEnd();
            final TypeReference catchType = entry.entry.getCatchType();

            final Instruction firstInstruction = body.tryGetAtOffset(startOffset);
            final Instruction lastInstruction;
            final Instruction handlerFirstInstruction = body.tryGetAtOffset(handlerStart);
            final Instruction handlerLastInstruction;

            if (endOffset <= bodyEndInstruction.getOffset()) {
                lastInstruction = body.tryGetAtOffset(endOffset).getPrevious();
            }
            else if (endOffset == bodyEndInstruction.getEndOffset()) {
                lastInstruction = bodyEndInstruction;
            }
            else {
                lastInstruction = new Instruction(endOffset, OpCode.NOP);
            }

            if (handlerEnd <= bodyEndInstruction.getOffset()) {
                handlerLastInstruction = body.tryGetAtOffset(handlerEnd).getPrevious();
            }
            else if (handlerEnd == bodyEndInstruction.getEndOffset()) {
                handlerLastInstruction = bodyEndInstruction;
            }
            else {
                handlerLastInstruction = new Instruction(handlerEnd, OpCode.NOP);
            }

            final ExceptionHandler handler;

            if (catchType == null) {
                handler = ExceptionHandler.createFinally(
                    new ExceptionBlock(firstInstruction, lastInstruction),
                    new ExceptionBlock(handlerFirstInstruction, handlerLastInstruction)
                );
            }
            else {
                handler = ExceptionHandler.createCatch(
                    new ExceptionBlock(firstInstruction, lastInstruction),
                    new ExceptionBlock(handlerFirstInstruction, handlerLastInstruction),
                    catchType
                );
            }

            exceptionHandlers.add(handler);
        }
    }

    private HandlerWithRange findNearestHandler(final List<HandlerWithRange> entries, final HandlerWithRange entry) {
        HandlerWithRange nearestHandler = entry;
        int nearestHandlerStart = nearestHandler.range.getStart();

        for (final HandlerWithRange h : entries) {
            if (h.entry.getStartOffset() == entry.entry.getStartOffset() &&
                h.entry.getEndOffset() == entry.entry.getEndOffset() &&
                h.range.getStart() < nearestHandlerStart) {

                nearestHandler = h;
                nearestHandlerStart = nearestHandler.range.getStart();
            }
        }

        return nearestHandler;
    }

    private static ControlFlowNode findHandlerEnd(
        final ControlFlowNode node,
        final ControlFlowNode tryEnd, final Set<ControlFlowNode> visited,
        final ControlFlowNode regularExit) {

        if (!visited.add(node)) {
            return null;
        }

        for (final ControlFlowNode successor : node.getSuccessors()) {
            if (successor.getNodeType() != ControlFlowNodeType.Normal) {
                continue;
            }

            if (tryEnd != null && tryEnd.dominates(successor)) {
                continue;
            }

            if (successor.getDominatorTreeChildren().isEmpty()) {
                final ControlFlowNode result = findHandlerEnd(successor, tryEnd, visited, regularExit);

                if (result != null) {
                    return result;
                }

                if (!successor.getDominanceFrontier().contains(regularExit)) {
                    return successor;
                }
            }
        }

        return null;
    }

    private final static class HandlerWithRange implements Comparable<HandlerWithRange> {
        final ExceptionTableEntry entry;
        final Range range;

        HandlerWithRange(final ExceptionTableEntry entry, final Range range) {
            this.entry = entry;
            this.range = range;
        }

        @Override
        public final int compareTo(final HandlerWithRange o) {
            int compareResult = Integer.compare(this.entry.getEndOffset(), o.entry.getEndOffset());

            if (compareResult != 0) {
                return compareResult;
            }

            compareResult = Integer.compare(o.entry.getStartOffset(), this.entry.getStartOffset());

            if (compareResult != 0) {
                return compareResult;
            }

            compareResult = Integer.compare(this.range.getEnd(), o.range.getEnd());

            if (compareResult != 0) {
                return compareResult;
            }

            compareResult = Integer.compare(o.range.getStart(), this.range.getStart());

            return compareResult;
        }

        @Override
        public String toString() {
            final TypeReference catchType = entry.getCatchType();

            return "Entry{" +
                   "Try=" + entry.getStartOffset() + ":" + entry.getEndOffset() +
                   ", Handler=" + range.getStart() + ":" + range.getEnd() +
                   ", Type=" + (catchType != null ? catchType.getName() : "any") +
                   '}';
        }
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Fixup Class">

    private abstract static class Fixup {
        public abstract void fix(final Instruction target);

        public static Fixup combine(final Fixup first, final Fixup second) {
            final Fixup[] fixups;

            if (first instanceof MultiFixup) {
                final MultiFixup m1 = (MultiFixup) first;

                if (second instanceof MultiFixup) {
                    final MultiFixup m2 = (MultiFixup) second;

                    fixups = new Fixup[m1._fixups.length + m2._fixups.length];

                    System.arraycopy(
                        m2._fixups,
                        0,
                        fixups,
                        m1._fixups.length,
                        m2._fixups.length
                    );
                }
                else {
                    fixups = new Fixup[m1._fixups.length + 1];
                    fixups[m1._fixups.length] = second;
                }

                System.arraycopy(
                    m1._fixups,
                    0,
                    fixups,
                    0,
                    m1._fixups.length
                );
            }
            else {
                if (second instanceof MultiFixup) {
                    final MultiFixup m2 = (MultiFixup) second;

                    fixups = new Fixup[1 + m2._fixups.length];

                    System.arraycopy(
                        m2._fixups,
                        0,
                        fixups,
                        1,
                        m2._fixups.length
                    );
                }
                else {
                    fixups = new Fixup[] { first, second };
                }
            }

            return new MultiFixup(fixups);
        }

        private final static class MultiFixup extends Fixup {
            private final Fixup[] _fixups;

            private MultiFixup(final Fixup... fixups) {
                _fixups = VerifyArgument.noNullElements(fixups, "fixups");
            }

            @Override
            public void fix(final Instruction target) {
                for (final Fixup fixup : _fixups) {
                    fixup.fix(target);
                }
            }
        }
    }

    // </editor-fold>
}
TOP

Related Classes of com.strobel.assembler.metadata.MethodReader

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.