
com.strobel.decompiler.ast.AstBuilder Maven / Gradle / Ivy
/*
* AstBuilder.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.decompiler.ast;
import com.strobel.annotations.NotNull;
import com.strobel.assembler.flowanalysis.ControlFlowEdge;
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.flowanalysis.JumpType;
import com.strobel.assembler.ir.*;
import com.strobel.assembler.metadata.*;
import com.strobel.core.*;
import com.strobel.decompiler.DecompilerContext;
import com.strobel.decompiler.ITextOutput;
import com.strobel.decompiler.InstructionHelper;
import com.strobel.decompiler.PlainTextOutput;
import com.strobel.functions.Function;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.strobel.core.CollectionUtilities.*;
import static com.strobel.decompiler.ast.PatternMatching.*;
import static java.lang.String.format;
public final class AstBuilder {
private final static Logger LOG = Logger.getLogger(AstBuilder.class.getSimpleName());
private final static AstCode[] CODES = AstCode.values();
private final static StackSlot[] EMPTY_STACK = new StackSlot[0];
private final static ByteCode[] EMPTY_DEFINITIONS = new ByteCode[0];
private final Map _loadExceptions = new LinkedHashMap<>();
private final Set _removed = new LinkedHashSet<>();
private Map _originalInstructionMap;
private ControlFlowGraph _cfg;
private InstructionCollection _instructions;
private List _exceptionHandlers;
private MethodBody _body;
private boolean _optimize;
private DecompilerContext _context;
private CoreMetadataFactory _factory;
public static List build(final MethodBody body, final boolean optimize, final DecompilerContext context) {
final AstBuilder builder = new AstBuilder();
builder._body = VerifyArgument.notNull(body, "body");
builder._optimize = optimize;
builder._context = VerifyArgument.notNull(context, "context");
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(
format(
"Beginning bytecode AST construction for %s:%s...",
body.getMethod().getFullName(),
body.getMethod().getSignature()
)
);
}
if (body.getInstructions().isEmpty()) {
return Collections.emptyList();
}
builder._instructions = copyInstructions(body.getInstructions());
final InstructionCollection oldInstructions = body.getInstructions();
final InstructionCollection newInstructions = builder._instructions;
builder._originalInstructionMap = new IdentityHashMap<>();
for (int i = 0; i < newInstructions.size(); i++) {
builder._originalInstructionMap.put(newInstructions.get(i), oldInstructions.get(i));
}
builder._exceptionHandlers = remapHandlers(body.getExceptionHandlers(), builder._instructions);
Collections.sort(builder._exceptionHandlers);
builder.removeGetClassCallsForInvokeDynamic();
builder.pruneExceptionHandlers();
builder.inlineSubroutines();
FinallyInlining.run(builder._body, builder._instructions, builder._exceptionHandlers, builder._removed);
builder._cfg = ControlFlowGraphBuilder.build(builder._instructions, builder._exceptionHandlers);
builder._cfg.computeDominance();
builder._cfg.computeDominanceFrontier();
LOG.fine("Performing stack analysis...");
final List byteCode = builder.performStackAnalysis();
LOG.fine("Creating bytecode AST...");
@SuppressWarnings("UnnecessaryLocalVariable")
final List ast = builder.convertToAst(
byteCode,
new LinkedHashSet<>(builder._exceptionHandlers),
0,
new MutableInteger(byteCode.size())
);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(
format(
"Finished bytecode AST construction for %s:%s.",
body.getMethod().getFullName(),
body.getMethod().getSignature()
)
);
}
return ast;
}
private static boolean isGetClassInvocation(final Instruction p) {
return p != null &&
p.getOpCode() == OpCode.INVOKEVIRTUAL &&
p.getOperand(0).getParameters().isEmpty() &&
StringUtilities.equals(p.getOperand(0).getName(), "getClass");
}
private void removeGetClassCallsForInvokeDynamic() {
for (final Instruction i : _instructions) {
if (i.getOpCode() != OpCode.INVOKEDYNAMIC) {
continue;
}
final Instruction p1 = i.getPrevious();
if (p1 == null || p1.getOpCode() != OpCode.POP) {
continue;
}
final Instruction p2 = p1.getPrevious();
if (p2 == null || !isGetClassInvocation(p2)) {
continue;
}
final Instruction p3 = p2.getPrevious();
if (p3 == null || p3.getOpCode() != OpCode.DUP) {
continue;
}
p1.setOpCode(OpCode.NOP);
p1.setOperand(null);
p2.setOpCode(OpCode.NOP);
p2.setOperand(null);
p3.setOpCode(OpCode.NOP);
p3.setOperand(null);
}
}
@SuppressWarnings("ConstantConditions")
private void inlineSubroutines() {
LOG.fine("Inlining subroutines...");
final List subroutines = findSubroutines();
if (subroutines.isEmpty()) {
return;
}
final List handlers = _exceptionHandlers;
final Set originalHandlers = new HashSet<>(handlers);
final List inlinedSubroutines = new ArrayList<>();
final Set instructionsToKeep = new HashSet<>();
for (final SubroutineInfo subroutine : subroutines) {
if (callsOtherSubroutine(subroutine, subroutines)) {
continue;
}
boolean fullyInlined = true;
for (final Instruction reference : subroutine.liveReferences) {
fullyInlined &= inlineSubroutine(subroutine, reference);
}
for (final Instruction p : subroutine.deadReferences) {
p.setOpCode(OpCode.NOP);
p.setOperand(null);
_removed.add(p);
}
if (fullyInlined) {
inlinedSubroutines.add(subroutine);
}
else {
for (final ControlFlowNode node : subroutine.contents) {
for (Instruction p = node.getStart();
p != null && p.getOffset() < node.getStart().getEndOffset();
p = p.getNext()) {
instructionsToKeep.add(p);
}
}
}
}
//
// NOP-out the original subroutine instructions only after all subroutines have been processed.
// Note that there might be overlapping subroutines, and it's possible that some ranges may still
// be live code if not all subroutines were successfully inlined at all jump sites.
//
for (final SubroutineInfo subroutine : inlinedSubroutines) {
for (Instruction p = subroutine.start;
p != null && p.getOffset() < subroutine.end.getEndOffset();
p = p.getNext()) {
if (instructionsToKeep.contains(p)) {
continue;
}
p.setOpCode(OpCode.NOP);
p.setOperand(null);
_removed.add(p);
}
for (final ExceptionHandler handler : subroutine.containedHandlers) {
if (originalHandlers.contains(handler)) {
handlers.remove(handler);
}
}
}
}
private boolean inlineSubroutine(final SubroutineInfo subroutine, final Instruction reference) {
if (!subroutine.start.getOpCode().isStore() && subroutine.start.getOpCode() != OpCode.POP) {
return false;
}
final InstructionCollection instructions = _instructions;
final Map originalInstructionMap = _originalInstructionMap;
final boolean nonEmpty = subroutine.start != subroutine.end && subroutine.start.getNext() != subroutine.end;
if (nonEmpty) {
final int jumpIndex = instructions.indexOf(reference);
final List originalContents = new ArrayList<>();
for (final ControlFlowNode node : subroutine.contents) {
for (Instruction p = node.getStart();
p != null && p.getOffset() < node.getEnd().getEndOffset();
p = p.getNext()) {
originalContents.add(p);
}
}
final Map remappedJumps = new IdentityHashMap<>();
final List contents = copyInstructions(originalContents);
for (int i = 0, n = originalContents.size(); i < n; i++) {
remappedJumps.put(originalContents.get(i), contents.get(i));
originalInstructionMap.put(contents.get(i), mappedInstruction(originalInstructionMap, originalContents.get(i)));
}
final Instruction newStart = mappedInstruction(remappedJumps, subroutine.start);
final Instruction newEnd = reference.getNext() != null ? reference.getNext()
: mappedInstruction(remappedJumps, subroutine.end).getPrevious();
for (final ControlFlowNode exitNode : subroutine.exitNodes) {
final Instruction newExit = mappedInstruction(remappedJumps, exitNode.getEnd());
if (newExit != null) {
newExit.setOpCode(OpCode.GOTO);
newExit.setOperand(newEnd);
remappedJumps.put(newExit, newEnd);
}
}
newStart.setOpCode(OpCode.NOP);
newStart.setOperand(null);
instructions.addAll(jumpIndex, toList(contents));
if (newStart != first(contents)) {
instructions.add(jumpIndex, new Instruction(OpCode.GOTO, newStart));
}
instructions.remove(reference);
instructions.recomputeOffsets();
remappedJumps.put(reference, first(contents));
remappedJumps.put(subroutine.end, newEnd);
remappedJumps.put(subroutine.start, newStart);
remapJumps(Collections.singletonMap(reference, newStart));
remapHandlersForInlinedSubroutine(reference, first(contents), last(contents));
duplicateHandlersForInlinedSubroutine(subroutine, remappedJumps);
}
else {
reference.setOpCode(OpCode.NOP);
reference.setOperand(OpCode.NOP);
}
return true;
}
@SuppressWarnings("ConstantConditions")
private void remapHandlersForInlinedSubroutine(
final Instruction jump,
final Instruction start,
final Instruction end) {
final List handlers = _exceptionHandlers;
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final InstructionBlock oldTry = handler.getTryBlock();
final InstructionBlock oldHandler = handler.getHandlerBlock();
final InstructionBlock newTryBlock;
final InstructionBlock newHandlerBlock;
if (oldTry.getFirstInstruction() == jump || oldTry.getLastInstruction() == jump) {
newTryBlock = new InstructionBlock(
oldTry.getFirstInstruction() == jump ? start : oldTry.getFirstInstruction(),
oldTry.getLastInstruction() == jump ? end : oldTry.getLastInstruction()
);
}
else {
newTryBlock = oldTry;
}
if (oldHandler.getFirstInstruction() == jump || oldHandler.getLastInstruction() == jump) {
newHandlerBlock = new InstructionBlock(
oldHandler.getFirstInstruction() == jump ? start : oldHandler.getFirstInstruction(),
oldHandler.getLastInstruction() == jump ? end : oldHandler.getLastInstruction()
);
}
else {
newHandlerBlock = oldHandler;
}
if (newTryBlock != oldTry || newHandlerBlock != oldHandler) {
if (handler.isCatch()) {
handlers.set(
i,
ExceptionHandler.createCatch(newTryBlock, newHandlerBlock, handler.getCatchType())
);
}
else {
handlers.set(
i,
ExceptionHandler.createFinally(newTryBlock, newHandlerBlock)
);
}
}
}
}
@SuppressWarnings("ConstantConditions")
private void duplicateHandlersForInlinedSubroutine(final SubroutineInfo subroutine, final Map oldToNew) {
final List handlers = _exceptionHandlers;
for (final ExceptionHandler handler : subroutine.containedHandlers) {
final InstructionBlock oldTry = handler.getTryBlock();
final InstructionBlock oldHandler = handler.getHandlerBlock();
final InstructionBlock newTryBlock;
final InstructionBlock newHandlerBlock;
final Instruction newTryStart = mappedInstruction(oldToNew, oldTry.getFirstInstruction());
final Instruction newTryEnd = mappedInstruction(oldToNew, oldTry.getLastInstruction());
final Instruction newHandlerStart = mappedInstruction(oldToNew, oldHandler.getFirstInstruction());
final Instruction newHandlerEnd = mappedInstruction(oldToNew, oldHandler.getLastInstruction());
if (newTryStart != null || newTryEnd != null) {
newTryBlock = new InstructionBlock(
newTryStart != null ? newTryStart : oldTry.getFirstInstruction(),
newTryEnd != null ? newTryEnd : oldTry.getLastInstruction()
);
}
else {
newTryBlock = oldTry;
}
if (newHandlerStart != null || newHandlerEnd != null) {
newHandlerBlock = new InstructionBlock(
newHandlerStart != null ? newHandlerStart : oldHandler.getFirstInstruction(),
newHandlerEnd != null ? newHandlerEnd : oldHandler.getLastInstruction()
);
}
else {
newHandlerBlock = oldHandler;
}
if (newTryBlock != oldTry || newHandlerBlock != oldHandler) {
handlers.add(
handler.isCatch() ? ExceptionHandler.createCatch(newTryBlock, newHandlerBlock, handler.getCatchType())
: ExceptionHandler.createFinally(newTryBlock, newHandlerBlock)
);
}
}
}
@SuppressWarnings("ConstantConditions")
private void remapJumps(final Map remappedJumps) {
for (final Instruction instruction : _instructions) {
if (instruction.hasLabel()) {
instruction.getLabel().setIndex(instruction.getOffset());
}
if (instruction.getOperandCount() == 0) {
continue;
}
final Object operand = instruction.getOperand(0);
if (operand instanceof Instruction) {
final Instruction oldTarget = (Instruction) operand;
final Instruction newTarget = mappedInstruction(remappedJumps, oldTarget);
if (newTarget != null) {
if (newTarget == instruction) {
instruction.setOpCode(OpCode.NOP);
instruction.setOperand(null);
}
else {
instruction.setOperand(newTarget);
if (!newTarget.hasLabel()) {
newTarget.setLabel(new com.strobel.assembler.metadata.Label(newTarget.getOffset()));
}
}
}
}
else if (operand instanceof SwitchInfo) {
final SwitchInfo oldOperand = (SwitchInfo) operand;
final Instruction oldDefault = oldOperand.getDefaultTarget();
final Instruction newDefault = mappedInstruction(remappedJumps, oldDefault);
if (newDefault != null && !newDefault.hasLabel()) {
newDefault.setLabel(new com.strobel.assembler.metadata.Label(newDefault.getOffset()));
}
final Instruction[] oldTargets = oldOperand.getTargets();
Instruction[] newTargets = null;
for (int i = 0; i < oldTargets.length; i++) {
final Instruction newTarget = mappedInstruction(remappedJumps, oldTargets[i]);
if (newTarget != null) {
if (newTargets == null) {
newTargets = Arrays.copyOf(oldTargets, oldTargets.length);
}
newTargets[i] = newTarget;
if (!newTarget.hasLabel()) {
newTarget.setLabel(new com.strobel.assembler.metadata.Label(newTarget.getOffset()));
}
}
}
if (newDefault != null || newTargets != null) {
final SwitchInfo newOperand = new SwitchInfo(
oldOperand.getKeys(),
newDefault != null ? newDefault : oldDefault,
newTargets != null ? newTargets : oldTargets
);
instruction.setOperand(newOperand);
}
}
}
}
private boolean callsOtherSubroutine(final SubroutineInfo subroutine, final List subroutines) {
return any(
subroutines,
new Predicate() {
@Override
public boolean test(final SubroutineInfo info) {
return info != subroutine &&
any(
info.liveReferences,
new Predicate() {
@Override
public boolean test(final Instruction p) {
return p.getOffset() >= subroutine.start.getOffset() &&
p.getOffset() < subroutine.end.getEndOffset();
}
}
) &&
!subroutine.contents.containsAll(info.contents);
}
}
);
}
private List findSubroutines() {
final InstructionCollection instructions = _instructions;
if (instructions.isEmpty()) {
return Collections.emptyList();
}
Map, Set>> handlerContents = null;
Map subroutineMap = null;
ControlFlowGraph cfg = null;
for (Instruction p = first(instructions);
p != null;
p = p.getNext()) {
if (!p.getOpCode().isJumpToSubroutine()) {
continue;
}
final boolean isLive = !_removed.contains(p);
if (cfg == null) {
cfg = ControlFlowGraphBuilder.build(instructions, _exceptionHandlers);
cfg.computeDominance();
cfg.computeDominanceFrontier();
subroutineMap = new IdentityHashMap<>();
handlerContents = new IdentityHashMap<>();
for (final ExceptionHandler handler : _exceptionHandlers) {
final InstructionBlock tryBlock = handler.getTryBlock();
final InstructionBlock handlerBlock = handler.getHandlerBlock();
final Set tryNodes = findDominatedNodes(
cfg,
findNode(cfg, tryBlock.getFirstInstruction()),
true,
Collections.emptySet()
);
final Set handlerNodes = findDominatedNodes(
cfg,
findNode(cfg, handlerBlock.getFirstInstruction()),
true,
Collections.emptySet()
);
handlerContents.put(handler, Pair.create(tryNodes, handlerNodes));
}
}
final Instruction target = p.getOperand(0);
if (_removed.contains(target)) {
continue;
}
SubroutineInfo info = subroutineMap.get(target);
if (info == null) {
final ControlFlowNode start = findNode(cfg, target);
final List contents = toList(
findDominatedNodes(
cfg,
start,
true,
Collections.emptySet()
)
);
Collections.sort(contents);
subroutineMap.put(target, info = new SubroutineInfo(start, contents, cfg));
for (final ExceptionHandler handler : _exceptionHandlers) {
final Pair, Set> pair = handlerContents.get(handler);
if (contents.containsAll(pair.getFirst()) && contents.containsAll(pair.getSecond())) {
info.containedHandlers.add(handler);
}
}
}
if (isLive) {
info.liveReferences.add(p);
}
else {
info.deadReferences.add(p);
}
}
if (subroutineMap == null) {
return Collections.emptyList();
}
final List subroutines = toList(subroutineMap.values());
Collections.sort(
subroutines,
new Comparator() {
@Override
public int compare(@NotNull final SubroutineInfo o1, @NotNull final SubroutineInfo o2) {
if (o1.contents.containsAll(o2.contents)) {
return 1;
}
if (o2.contents.containsAll(o1.contents)) {
return -1;
}
return Integer.compare(o2.start.getOffset(), o1.start.getOffset());
}
}
);
return subroutines;
}
private final static class SubroutineInfo {
final Instruction start;
final Instruction end;
final List liveReferences = new ArrayList<>();
final List deadReferences = new ArrayList<>();
final List contents;
final ControlFlowNode entryNode;
final List exitNodes = new ArrayList<>();
final List containedHandlers = new ArrayList<>();
final ControlFlowGraph cfg;
public SubroutineInfo(final ControlFlowNode entryNode, final List contents, final ControlFlowGraph cfg) {
this.start = entryNode.getStart();
this.end = last(contents).getEnd();
this.entryNode = entryNode;
this.contents = contents;
this.cfg = cfg;
for (final ControlFlowNode node : contents) {
if (node.getNodeType() == ControlFlowNodeType.Normal &&
node.getEnd().getOpCode().isReturnFromSubroutine()) {
this.exitNodes.add(node);
}
}
}
}
private final static class HandlerInfo {
final ExceptionHandler handler;
final ControlFlowNode handlerNode;
final ControlFlowNode head;
final ControlFlowNode tail;
final List tryNodes;
final List handlerNodes;
HandlerInfo(
final ExceptionHandler handler,
final ControlFlowNode handlerNode,
final ControlFlowNode head,
final ControlFlowNode tail,
final List tryNodes,
final List handlerNodes) {
this.handler = handler;
this.handlerNode = handlerNode;
this.head = head;
this.tail = tail;
this.tryNodes = tryNodes;
this.handlerNodes = handlerNodes;
}
}
private static ControlFlowNode findNode(final ControlFlowGraph cfg, final Instruction instruction) {
final int offset = instruction.getOffset();
for (final ControlFlowNode node : cfg.getNodes()) {
if (node.getNodeType() != ControlFlowNodeType.Normal) {
continue;
}
if (offset >= node.getStart().getOffset() &&
offset < node.getEnd().getEndOffset()) {
return node;
}
}
return null;
}
private static Set findDominatedNodes(
final ControlFlowGraph cfg,
final ControlFlowNode head,
final boolean diveIntoHandlers,
final Set terminals) {
final Set visited = new LinkedHashSet<>();
final ArrayDeque agenda = new ArrayDeque<>();
final Set result = new LinkedHashSet<>();
agenda.add(head);
visited.add(head);
while (!agenda.isEmpty()) {
ControlFlowNode addNode = agenda.removeFirst();
if (terminals.contains(addNode)) {
continue;
}
if (diveIntoHandlers && addNode.getExceptionHandler() != null) {
addNode = findNode(cfg, addNode.getExceptionHandler().getHandlerBlock().getFirstInstruction());
}
else if (diveIntoHandlers && addNode.getNodeType() == ControlFlowNodeType.EndFinally) {
agenda.addAll(addNode.getDominatorTreeChildren());
continue;
}
if (addNode == null || addNode.getNodeType() != ControlFlowNodeType.Normal) {
continue;
}
if (!head.dominates(addNode) &&
!shouldIncludeExceptionalExit(cfg, head, addNode)) {
continue;
}
if (!result.add(addNode)) {
continue;
}
for (final ControlFlowNode successor : addNode.getSuccessors()) {
if (visited.add(successor)) {
agenda.add(successor);
}
}
}
return result;
}
private static boolean shouldIncludeExceptionalExit(
final ControlFlowGraph cfg,
final ControlFlowNode head,
final ControlFlowNode node) {
if (node.getNodeType() != ControlFlowNodeType.Normal) {
return false;
}
if (!node.getDominanceFrontier().contains(cfg.getExceptionalExit()) &&
!node.dominates(cfg.getExceptionalExit())) {
final ControlFlowNode innermostHandlerNode = findInnermostExceptionHandlerNode(cfg, node.getEnd().getOffset(), false);
if (innermostHandlerNode == null || !node.getDominanceFrontier().contains(innermostHandlerNode)) {
return false;
}
}
return head.getNodeType() == ControlFlowNodeType.Normal &&
node.getNodeType() == ControlFlowNodeType.Normal &&
node.getStart().getNext() == node.getEnd() &&
head.getStart().getOpCode().isStore() &&
node.getStart().getOpCode().isLoad() &&
node.getEnd().getOpCode() == OpCode.ATHROW &&
InstructionHelper.getLoadOrStoreSlot(head.getStart()) == InstructionHelper.getLoadOrStoreSlot(node.getStart());
}
private static ControlFlowNode findInnermostExceptionHandlerNode(
final ControlFlowGraph cfg,
final int offsetInTryBlock,
final boolean finallyOnly) {
ExceptionHandler result = null;
ControlFlowNode resultNode = null;
final List nodes = cfg.getNodes();
for (int i = nodes.size() - 1; i >= 0; i--) {
final ControlFlowNode node = nodes.get(i);
final ExceptionHandler handler = node.getExceptionHandler();
if (handler == null) {
break;
}
if (finallyOnly && handler.isCatch()) {
continue;
}
final InstructionBlock tryBlock = handler.getTryBlock();
if (tryBlock.getFirstInstruction().getOffset() <= offsetInTryBlock &&
offsetInTryBlock < tryBlock.getLastInstruction().getEndOffset() &&
(result == null ||
tryBlock.getFirstInstruction().getOffset() > result.getTryBlock().getFirstInstruction().getOffset())) {
result = handler;
resultNode = node;
}
}
return resultNode != null ? resultNode
: cfg.getExceptionalExit();
}
private static boolean opCodesMatch(
final Instruction tail1,
final Instruction tail2,
final int count,
final Function previous) {
int i = 0;
if (tail1 == null || tail2 == null) {
return false;
}
for (Instruction p1 = tail1, p2 = tail2;
p1 != null && p2 != null && i < count;
p1 = previous.apply(p1), p2 = previous.apply(p2), i++) {
final OpCode c1 = p1.getOpCode();
final OpCode c2 = p2.getOpCode();
if (c1.isLoad()) {
if (!c2.isLoad() || c2.getStackBehaviorPush() != c1.getStackBehaviorPush()) {
return false;
}
}
else if (c1.isStore()) {
if (!c2.isStore() || c2.getStackBehaviorPop() != c1.getStackBehaviorPop()) {
return false;
}
}
else if (c1 != p2.getOpCode()) {
return false;
}
switch (c1.getOperandType()) {
case TypeReferenceU1:
if (!Objects.equals(p1.getOperand(1), p2.getOperand(1))) {
return false;
}
// fall through
case PrimitiveTypeCode:
case TypeReference: {
if (!Objects.equals(p1.getOperand(0), p2.getOperand(0))) {
return false;
}
break;
}
case MethodReference:
case FieldReference: {
final MemberReference m1 = p1.getOperand(0);
final MemberReference m2 = p2.getOperand(0);
if (!StringUtilities.equals(m1.getFullName(), m2.getFullName()) ||
!StringUtilities.equals(m1.getErasedSignature(), m2.getErasedSignature())) {
return false;
}
break;
}
case I1:
case I2:
case I8:
case Constant:
case WideConstant: {
if (!Objects.equals(p1.getOperand(0), p2.getOperand(0))) {
return false;
}
break;
}
case LocalI1:
case LocalI2: {
if (!Objects.equals(p1.getOperand(1), p2.getOperand(1))) {
return false;
}
break;
}
}
}
return i == count;
}
private static Map createNodeMap(final ControlFlowGraph cfg) {
final Map nodeMap = new IdentityHashMap<>();
for (final ControlFlowNode node : cfg.getNodes()) {
if (node.getNodeType() != ControlFlowNodeType.Normal) {
continue;
}
for (Instruction p = node.getStart();
p != null && p.getOffset() < node.getEnd().getEndOffset();
p = p.getNext()) {
nodeMap.put(p, node);
}
}
return nodeMap;
}
private static List remapHandlers(final List handlers, final InstructionCollection instructions) {
final List newHandlers = new ArrayList<>();
for (final ExceptionHandler handler : handlers) {
final InstructionBlock oldTry = handler.getTryBlock();
final InstructionBlock oldHandler = handler.getHandlerBlock();
final InstructionBlock newTry = new InstructionBlock(
instructions.atOffset(oldTry.getFirstInstruction().getOffset()),
instructions.atOffset(oldTry.getLastInstruction().getOffset())
);
final InstructionBlock newHandler = new InstructionBlock(
instructions.atOffset(oldHandler.getFirstInstruction().getOffset()),
instructions.atOffset(oldHandler.getLastInstruction().getOffset())
);
if (handler.isCatch()) {
newHandlers.add(
ExceptionHandler.createCatch(
newTry,
newHandler,
handler.getCatchType()
)
);
}
else {
newHandlers.add(
ExceptionHandler.createFinally(
newTry,
newHandler
)
);
}
}
return newHandlers;
}
private static InstructionCollection copyInstructions(final List instructions) {
final InstructionCollection instructionsCopy = new InstructionCollection();
final Map oldToNew = new IdentityHashMap<>();
for (final Instruction instruction : instructions) {
final Instruction copy = new Instruction(instruction.getOffset(), instruction.getOpCode());
if (instruction.getOperandCount() > 1) {
final Object[] operands = new Object[instruction.getOperandCount()];
for (int i = 0; i < operands.length; i++) {
operands[i] = instruction.getOperand(i);
}
copy.setOperand(operands);
}
else {
copy.setOperand(instruction.getOperand(0));
}
copy.setLabel(instruction.getLabel());
instructionsCopy.add(copy);
oldToNew.put(instruction, copy);
}
for (final Instruction instruction : instructionsCopy) {
if (!instruction.hasOperand()) {
continue;
}
final Object operand = instruction.getOperand(0);
if (operand instanceof Instruction) {
instruction.setOperand(mappedInstruction(oldToNew, (Instruction) operand));
}
else if (operand instanceof SwitchInfo) {
final SwitchInfo oldOperand = (SwitchInfo) operand;
final Instruction oldDefault = oldOperand.getDefaultTarget();
final Instruction newDefault = mappedInstruction(oldToNew, oldDefault);
final Instruction[] oldTargets = oldOperand.getTargets();
final Instruction[] newTargets = new Instruction[oldTargets.length];
for (int i = 0; i < newTargets.length; i++) {
newTargets[i] = mappedInstruction(oldToNew, oldTargets[i]);
}
final SwitchInfo newOperand = new SwitchInfo(oldOperand.getKeys(), newDefault, newTargets);
newOperand.setLowValue(oldOperand.getLowValue());
newOperand.setHighValue(oldOperand.getHighValue());
instruction.setOperand(newOperand);
}
}
instructionsCopy.recomputeOffsets();
return instructionsCopy;
}
@SuppressWarnings("ConstantConditions")
private void pruneExceptionHandlers() {
LOG.fine("Pruning exception handlers...");
final List handlers = _exceptionHandlers;
if (handlers.isEmpty()) {
return;
}
removeSelfHandlingFinallyHandlers();
removeEmptyCatchBlockBodies();
trimAggressiveFinallyBlocks();
trimAggressiveCatchBlocks();
closeTryHandlerGaps();
// extendHandlers();
mergeSharedHandlers();
alignFinallyBlocksWithSiblingCatchBlocks();
ensureDesiredProtectedRanges();
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
if (!handler.isFinally()) {
continue;
}
final InstructionBlock tryBlock = handler.getTryBlock();
final List siblings = findHandlers(tryBlock, handlers);
for (int j = 0; j < siblings.size(); j++) {
final ExceptionHandler sibling = siblings.get(j);
if (sibling.isCatch() && j < siblings.size() - 1) {
final ExceptionHandler nextSibling = siblings.get(j + 1);
if (sibling.getHandlerBlock().getLastInstruction() !=
nextSibling.getHandlerBlock().getFirstInstruction().getPrevious()) {
final int index = handlers.indexOf(sibling);
handlers.set(
index,
ExceptionHandler.createCatch(
sibling.getTryBlock(),
new InstructionBlock(
sibling.getHandlerBlock().getFirstInstruction(),
nextSibling.getHandlerBlock().getFirstInstruction().getPrevious()
),
sibling.getCatchType()
)
);
siblings.set(j, handlers.get(j));
}
}
}
}
outer:
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
if (!handler.isFinally()) {
continue;
}
final InstructionBlock tryBlock = handler.getTryBlock();
final List siblings = findHandlers(tryBlock, handlers);
for (final ExceptionHandler sibling : siblings) {
if (sibling == handler || sibling.isFinally()) {
continue;
}
for (int j = 0; j < handlers.size(); j++) {
final ExceptionHandler e = handlers.get(j);
if (e == handler || e == sibling || !e.isFinally()) {
continue;
}
if (e.getTryBlock().getFirstInstruction() == sibling.getHandlerBlock().getFirstInstruction() &&
e.getHandlerBlock().equals(handler.getHandlerBlock())) {
handlers.remove(j);
final int removeIndex = j--;
if (removeIndex < i) {
--i;
continue outer;
}
}
}
}
}
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
if (!handler.isFinally()) {
continue;
}
final InstructionBlock tryBlock = handler.getTryBlock();
final InstructionBlock handlerBlock = handler.getHandlerBlock();
for (int j = 0; j < handlers.size(); j++) {
final ExceptionHandler other = handlers.get(j);
if (other != handler &&
other.isFinally() &&
other.getHandlerBlock().equals(handlerBlock) &&
tryBlock.contains(other.getTryBlock()) &&
tryBlock.getLastInstruction() == other.getTryBlock().getLastInstruction()) {
handlers.remove(j);
if (j < i) {
--i;
break;
}
--j;
}
}
}
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final InstructionBlock tryBlock = handler.getTryBlock();
final ExceptionHandler firstHandler = findFirstHandler(tryBlock, handlers);
final InstructionBlock firstHandlerBlock = firstHandler.getHandlerBlock();
final Instruction firstAfterTry = tryBlock.getLastInstruction().getNext();
final Instruction firstInHandler = firstHandlerBlock.getFirstInstruction();
final Instruction lastBeforeHandler = firstInHandler.getPrevious();
if (firstAfterTry != firstInHandler &&
firstAfterTry != null &&
lastBeforeHandler != null) {
InstructionBlock newTryBlock = null;
final FlowControl flowControl = lastBeforeHandler.getOpCode().getFlowControl();
if (flowControl == FlowControl.Branch ||
flowControl == FlowControl.Return && lastBeforeHandler.getOpCode() == OpCode.RETURN) {
if (lastBeforeHandler == firstAfterTry) {
newTryBlock = new InstructionBlock(tryBlock.getFirstInstruction(), lastBeforeHandler);
}
}
else if (flowControl == FlowControl.Throw ||
flowControl == FlowControl.Return && lastBeforeHandler.getOpCode() != OpCode.RETURN) {
if (lastBeforeHandler.getPrevious() == firstAfterTry) {
newTryBlock = new InstructionBlock(tryBlock.getFirstInstruction(), lastBeforeHandler);
}
}
if (newTryBlock != null) {
final List siblings = findHandlers(tryBlock, handlers);
for (int j = 0; j < siblings.size(); j++) {
final ExceptionHandler sibling = siblings.get(j);
final int index = handlers.indexOf(sibling);
if (sibling.isCatch()) {
handlers.set(
index,
ExceptionHandler.createCatch(
newTryBlock,
sibling.getHandlerBlock(),
sibling.getCatchType()
)
);
}
else {
handlers.set(
index,
ExceptionHandler.createFinally(
newTryBlock,
sibling.getHandlerBlock()
)
);
}
}
}
}
}
//
// Look for finally blocks which duplicate an outer catch.
//
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final InstructionBlock tryBlock = handler.getTryBlock();
final InstructionBlock handlerBlock = handler.getHandlerBlock();
if (!handler.isFinally()) {
continue;
}
final ExceptionHandler innermostHandler = findInnermostExceptionHandler(
tryBlock.getFirstInstruction().getOffset(),
handler
);
if (innermostHandler == null ||
innermostHandler == handler ||
innermostHandler.isFinally()) {
continue;
}
for (int j = 0; j < handlers.size(); j++) {
final ExceptionHandler sibling = handlers.get(j);
if (sibling != handler &&
sibling != innermostHandler &&
sibling.getTryBlock().equals(handlerBlock) &&
sibling.getHandlerBlock().equals(innermostHandler.getHandlerBlock())) {
handlers.remove(j);
if (j < i) {
--i;
break;
}
--j;
}
}
}
}
private void removeEmptyCatchBlockBodies() {
final List handlers = _exceptionHandlers;
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
if (!handler.isCatch()) {
continue;
}
final InstructionBlock catchBlock = handler.getHandlerBlock();
final Instruction start = catchBlock.getFirstInstruction();
final Instruction end = catchBlock.getLastInstruction();
if (start != end || !start.getOpCode().isStore()) {
continue;
}
// final InstructionBlock tryBlock = handler.getTryBlock();
//
// for (int j = 0; j < handlers.size(); j++) {
// if (i == j) {
// continue;
// }
//
// final ExceptionHandler other = handlers.get(j);
// final InstructionBlock finallyBlock = other.getHandlerBlock();
//
// if (other.isFinally() &&
// finallyBlock.contains(tryBlock) &&
// finallyBlock.contains(catchBlock)) {
//
// final Instruction endFinally = finallyBlock.getLastInstruction();
//
// if (endFinally != null &&
// endFinally.getOpCode().isThrow() &&
// endFinally.getPrevious() != null &&
// endFinally.getPrevious().getOpCode().isLoad() &&
// endFinally.getPrevious().getPrevious() == end &&
// (InstructionHelper.getLoadOrStoreSlot(endFinally.getPrevious()) !=
// InstructionHelper.getLoadOrStoreSlot(end))) {
//
// end.setOpCode(OpCode.POP);
// end.setOperand(null);
// _removed.add(end);
// break;
// }
// }
// }
end.setOpCode(OpCode.POP);
end.setOperand(null);
_removed.add(end);
}
}
private void ensureDesiredProtectedRanges() {
final List handlers = _exceptionHandlers;
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final InstructionBlock tryBlock = handler.getTryBlock();
final List siblings = findHandlers(tryBlock, handlers);
final ExceptionHandler firstSibling = first(siblings);
final InstructionBlock firstHandler = firstSibling.getHandlerBlock();
final Instruction desiredEndTry = firstHandler.getFirstInstruction().getPrevious();
for (int j = 0; j < siblings.size(); j++) {
ExceptionHandler sibling = siblings.get(j);
if (handler.getTryBlock().getLastInstruction() != desiredEndTry) {
final int index = handlers.indexOf(sibling);
if (sibling.isCatch()) {
handlers.set(
index,
ExceptionHandler.createCatch(
new InstructionBlock(
tryBlock.getFirstInstruction(),
desiredEndTry
),
sibling.getHandlerBlock(),
sibling.getCatchType()
)
);
}
else {
handlers.set(
index,
ExceptionHandler.createFinally(
new InstructionBlock(
tryBlock.getFirstInstruction(),
desiredEndTry
),
sibling.getHandlerBlock()
)
);
}
sibling = handlers.get(index);
siblings.set(j, sibling);
}
}
}
}
private void alignFinallyBlocksWithSiblingCatchBlocks() {
final List handlers = _exceptionHandlers;
outer:
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
if (handler.isCatch()) {
continue;
}
final InstructionBlock tryBlock = handler.getTryBlock();
final InstructionBlock handlerBlock = handler.getHandlerBlock();
for (int j = 0; j < handlers.size(); j++) {
if (i == j) {
continue;
}
final ExceptionHandler other = handlers.get(j);
final InstructionBlock otherTry = other.getTryBlock();
final InstructionBlock otherHandler = other.getHandlerBlock();
if (other.isCatch() &&
otherHandler.getLastInstruction().getNext() == handlerBlock.getFirstInstruction() &&
otherTry.getFirstInstruction() == tryBlock.getFirstInstruction() &&
otherTry.getLastInstruction().getOffset() < tryBlock.getLastInstruction().getOffset() &&
tryBlock.getLastInstruction().getEndOffset() > otherHandler.getFirstInstruction().getOffset()) {
handlers.set(
i,
ExceptionHandler.createFinally(
new InstructionBlock(
tryBlock.getFirstInstruction(),
otherHandler.getFirstInstruction().getPrevious()
),
handlerBlock
)
);
--i;
continue outer;
}
}
}
}
private void mergeSharedHandlers() {
final List handlers = _exceptionHandlers;
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final List duplicates = findDuplicateHandlers(handler, handlers);
for (int j = 0; j < duplicates.size() - 1; j++) {
final ExceptionHandler h1 = duplicates.get(j);
final ExceptionHandler h2 = duplicates.get(1 + j);
final InstructionBlock try1 = h1.getTryBlock();
final InstructionBlock try2 = h2.getTryBlock();
final Instruction head = try1.getLastInstruction().getNext();
final Instruction tail = try2.getFirstInstruction().getPrevious();
final int i1 = handlers.indexOf(h1);
final int i2 = handlers.indexOf(h2);
if (head != tail) {
// if (tail.getOpCode().isUnconditionalBranch()) {
// switch (tail.getOpCode()) {
// case GOTO:
// case GOTO_W:
// case RETURN:
// tail = tail.getPrevious();
// break;
//
// case IRETURN:
// case LRETURN:
// case FRETURN:
// case DRETURN:
// case ARETURN:
// tail = tail.getPrevious().getPrevious();
// break;
// }
// }
//
// if (!areAllRemoved(head, tail)) {
// continue;
// }
if (h1.isCatch()) {
handlers.set(
i1,
ExceptionHandler.createCatch(
new InstructionBlock(try1.getFirstInstruction(), try2.getLastInstruction()),
h1.getHandlerBlock(),
h1.getCatchType()
)
);
}
else {
handlers.set(
i1,
ExceptionHandler.createFinally(
new InstructionBlock(try1.getFirstInstruction(), try2.getLastInstruction()),
h1.getHandlerBlock()
)
);
}
duplicates.set(j, handlers.get(i1));
duplicates.remove(j + 1);
handlers.remove(i2);
if (i2 <= i) {
--i;
}
--j;
}
}
}
}
private void trimAggressiveCatchBlocks() {
final List handlers = _exceptionHandlers;
outer:
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final InstructionBlock tryBlock = handler.getTryBlock();
final InstructionBlock handlerBlock = handler.getHandlerBlock();
if (!handler.isCatch()) {
continue;
}
for (int j = 0; j < handlers.size(); j++) {
if (i == j) {
continue;
}
final ExceptionHandler other = handlers.get(j);
if (!other.isFinally()) {
continue;
}
final InstructionBlock otherTry = other.getTryBlock();
final InstructionBlock otherHandler = other.getHandlerBlock();
if (handlerBlock.getFirstInstruction().getOffset() < otherHandler.getFirstInstruction().getOffset() &&
handlerBlock.intersects(otherHandler) &&
!(handlerBlock.contains(otherTry) && handlerBlock.contains(otherHandler)) &&
!otherTry.contains(tryBlock)) {
handlers.set(
i--,
ExceptionHandler.createCatch(
tryBlock,
new InstructionBlock(
handlerBlock.getFirstInstruction(),
otherHandler.getFirstInstruction().getPrevious()
),
handler.getCatchType()
)
);
continue outer;
}
}
}
}
private void removeSelfHandlingFinallyHandlers() {
final List handlers = _exceptionHandlers;
//
// Remove self-handling finally blocks.
//
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final InstructionBlock tryBlock = handler.getTryBlock();
final InstructionBlock handlerBlock = handler.getHandlerBlock();
if (handler.isFinally() &&
handlerBlock.getFirstInstruction() == tryBlock.getFirstInstruction() &&
tryBlock.getLastInstruction().getOffset() < handlerBlock.getLastInstruction().getEndOffset()) {
handlers.remove(i--);
}
}
}
private void trimAggressiveFinallyBlocks() {
final List handlers = _exceptionHandlers;
outer:
for (int i = 0; i < handlers.size(); i++) {
final ExceptionHandler handler = handlers.get(i);
final InstructionBlock tryBlock = handler.getTryBlock();
final InstructionBlock handlerBlock = handler.getHandlerBlock();
if (!handler.isFinally()) {
continue;
}
for (int j = 0; j < handlers.size(); j++) {
if (i == j) {
continue;
}
final ExceptionHandler other = handlers.get(j);
if (!other.isCatch()) {
continue;
}
final InstructionBlock otherTry = other.getTryBlock();
final InstructionBlock otherHandler = other.getHandlerBlock();
if (tryBlock.getFirstInstruction() == otherTry.getFirstInstruction() &&
tryBlock.getLastInstruction() == otherHandler.getFirstInstruction()) {
handlers.set(
i--,
ExceptionHandler.createFinally(
new InstructionBlock(
tryBlock.getFirstInstruction(),
otherHandler.getFirstInstruction().getPrevious()
),
handlerBlock
)
);
continue outer;
}
}
}
}
private static ControlFlowNode findHandlerNode(final ControlFlowGraph cfg, final ExceptionHandler handler) {
final List nodes = cfg.getNodes();
for (int i = nodes.size() - 1; i >= 0; i--) {
final ControlFlowNode node = nodes.get(i);
if (node.getExceptionHandler() == handler) {
return node;
}
}
return null;
}
private ExceptionHandler findInnermostExceptionHandler(final int offsetInTryBlock, final ExceptionHandler exclude) {
ExceptionHandler result = null;
for (final ExceptionHandler handler : _exceptionHandlers) {
if (handler == exclude) {
continue;
}
final InstructionBlock tryBlock = handler.getTryBlock();
if (tryBlock.getFirstInstruction().getOffset() <= offsetInTryBlock &&
offsetInTryBlock < tryBlock.getLastInstruction().getEndOffset() &&
(result == null ||
tryBlock.getFirstInstruction().getOffset() > result.getTryBlock().getFirstInstruction().getOffset())) {
result = handler;
}
}
return result;
}
private void closeTryHandlerGaps() {
//
// Java does this retarded thing where a try block gets split along exit branches,
// but with the split parts sharing the same handler. We can't represent this in
// out AST, so just merge the parts back together.
//
final List handlers = _exceptionHandlers;
for (int i = 0; i < handlers.size() - 1; i++) {
final ExceptionHandler current = handlers.get(i);
final ExceptionHandler next = handlers.get(i + 1);
if (current.getHandlerBlock().equals(next.getHandlerBlock())) {
final Instruction lastInCurrent = current.getTryBlock().getLastInstruction();
final Instruction firstInNext = next.getTryBlock().getFirstInstruction();
final Instruction branchInBetween = firstInNext.getPrevious();
final Instruction beforeBranch;
if (branchInBetween != null) {
beforeBranch = branchInBetween.getPrevious();
}
else {
beforeBranch = null;
}
if (branchInBetween != null &&
branchInBetween.getOpCode().isBranch() &&
(lastInCurrent == beforeBranch || lastInCurrent == branchInBetween)) {
final ExceptionHandler newHandler;
if (current.isFinally()) {
newHandler = ExceptionHandler.createFinally(
new InstructionBlock(
current.getTryBlock().getFirstInstruction(),
next.getTryBlock().getLastInstruction()
),
new InstructionBlock(
current.getHandlerBlock().getFirstInstruction(),
current.getHandlerBlock().getLastInstruction()
)
);
}
else {
newHandler = ExceptionHandler.createCatch(
new InstructionBlock(
current.getTryBlock().getFirstInstruction(),
next.getTryBlock().getLastInstruction()
),
new InstructionBlock(
current.getHandlerBlock().getFirstInstruction(),
current.getHandlerBlock().getLastInstruction()
),
current.getCatchType()
);
}
handlers.set(i, newHandler);
handlers.remove(i + 1);
--i;
}
}
}
}
// private void extendHandlers() {
// final List handlers = _exceptionHandlers;
//
// outer:
// for (int i = 0; i < handlers.size(); i++) {
// final ExceptionHandler handler = handlers.get(i);
// final InstructionBlock tryBlock = handler.getTryBlock();
// final InstructionBlock handlerBlock = handler.getHandlerBlock();
//
// for (int j = 0; j < handlers.size(); j++) {
// if (i == j) {
// continue;
// }
//
// final ExceptionHandler other = handlers.get(j);
// final InstructionBlock otherHandler = other.getHandlerBlock();
//
// if (handlerBlock.intersects(otherHandler) &&
// !handlerBlock.contains(otherHandler) &&
// handlerBlock.getFirstInstruction().getOffset() <= otherHandler.getFirstInstruction().getOffset()) {
//
// if (handler.isCatch()) {
// handlers.set(
// i--,
// ExceptionHandler.createCatch(
// tryBlock,
// new InstructionBlock(
// handlerBlock.getFirstInstruction(),
// otherHandler.getLastInstruction()
// ),
// handler.getCatchType()
// )
// );
// }
// else {
// handlers.set(
// i--,
// ExceptionHandler.createFinally(
// tryBlock,
// new InstructionBlock(
// handlerBlock.getFirstInstruction(),
// otherHandler.getLastInstruction()
// )
// )
// );
// }
//
// continue outer;
// }
// }
// }
// }
private static ExceptionHandler findFirstHandler(final InstructionBlock tryBlock, final Collection handlers) {
ExceptionHandler result = null;
for (final ExceptionHandler handler : handlers) {
if (handler.getTryBlock().equals(tryBlock) &&
(result == null ||
handler.getHandlerBlock().getFirstInstruction().getOffset() < result.getHandlerBlock().getFirstInstruction().getOffset())) {
result = handler;
}
}
return result;
}
private static List findHandlers(final InstructionBlock tryBlock, final Collection handlers) {
List result = null;
for (final ExceptionHandler handler : handlers) {
if (handler.getTryBlock().equals(tryBlock)) {
if (result == null) {
result = new ArrayList<>();
}
result.add(handler);
}
}
if (result == null) {
return Collections.emptyList();
}
Collections.sort(
result,
new Comparator() {
@Override
public int compare(@NotNull final ExceptionHandler o1, @NotNull final ExceptionHandler o2) {
return Integer.compare(
o1.getHandlerBlock().getFirstInstruction().getOffset(),
o2.getHandlerBlock().getFirstInstruction().getOffset()
);
}
}
);
return result;
}
private static List findDuplicateHandlers(final ExceptionHandler handler, final Collection handlers) {
final List result = new ArrayList<>();
for (final ExceptionHandler other : handlers) {
if (other.getHandlerBlock().equals(handler.getHandlerBlock())) {
if (handler.isFinally()) {
if (other.isFinally()) {
result.add(other);
}
}
else if (other.isCatch() &&
MetadataHelper.isSameType(other.getCatchType(), handler.getCatchType())) {
result.add(other);
}
}
}
Collections.sort(
result,
new Comparator() {
@Override
public int compare(@NotNull final ExceptionHandler o1, @NotNull final ExceptionHandler o2) {
return Integer.compare(
o1.getTryBlock().getFirstInstruction().getOffset(),
o2.getTryBlock().getFirstInstruction().getOffset()
);
}
}
);
return result;
}
@SuppressWarnings("ConstantConditions")
private List performStackAnalysis() {
final Set handlerStarts = new HashSet<>();
final Map byteCodeMap = new LinkedHashMap<>();
final Map nodeMap = new IdentityHashMap<>();
final InstructionCollection instructions = _instructions;
final List exceptionHandlers = new ArrayList<>();
final List successors = new ArrayList<>();
for (final ControlFlowNode node : _cfg.getNodes()) {
if (node.getExceptionHandler() != null) {
exceptionHandlers.add(node.getExceptionHandler());
}
if (node.getNodeType() != ControlFlowNodeType.Normal) {
continue;
}
for (Instruction p = node.getStart();
p != null && p.getOffset() < node.getEnd().getEndOffset();
p = p.getNext()) {
nodeMap.put(p, node);
}
}
_exceptionHandlers.retainAll(exceptionHandlers);
final List body = new ArrayList<>(instructions.size());
final StackMappingVisitor stackMapper = new StackMappingVisitor();
final InstructionVisitor instructionVisitor = stackMapper.visitBody(_body);
final StrongBox codeBox = new StrongBox<>();
final StrongBox
© 2015 - 2025 Weber Informatics LLC | Privacy Policy