MethodRewriter.java

Index Score
eclipseme.preverifier.internal
EclipseME

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
/** * Copyright (c) 2003-2004 Craig Setera * All Rights Reserved. * Licensed under the Eclipse Public License - v 1.0 * For more information see http://www.eclipse.org/legal/epl-v10.html */ package eclipseme.preverifier.internal; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.attrs.StackMapAttribute; import org.objectweb.asm.attrs.StackMapFrame; import org.objectweb.asm.attrs.StackMapType; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LineNumberNode; import org.objectweb.asm.tree.LocalVariableNode; import org.objectweb.asm.tree.LookupSwitchInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.TableSwitchInsnNode; import org.objectweb.asm.tree.TryCatchBlockNode; import org.objectweb.asm.tree.VarInsnNode; import org.objectweb.asm.tree.analysis.Analyzer; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.BasicValue; import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.Interpreter; import org.objectweb.asm.tree.analysis.Value; /** * Handler for a single method in the class. Capable of * inlining subroutines and creating the Stack map attribute. * <p /> * Copyright (c) 2003-2005 Craig Setera<br> * All Rights Reserved.<br> * Licensed under the Eclipse Public License - v 1.0<p/> * <br> * $Revision: 1.7 $ * <br> * $Date: 2005/11/15 00:34:26 $ * <br> * @author Craig Setera */ public class MethodRewriter { /** A Label instance mapped to the new region and code offset after inlining */ public class MappedLabel extends Label { private Label originalLabel; /** * Construct a new instance */ public MappedLabel(Label label) { super(); originalLabel = label; } public Label getOriginalLabel() { return originalLabel; } /** * @see java.lang.Object#toString() */ public String toString() { return originalLabel.toString() + " -> " + super.toString(); } } /** A region of instructions to be handled. */ class Region { protected Region parentRegion; protected int startIndex; protected int endIndex; protected Map labelMap; protected Set labels; protected List tryCatchBlocks; /** * Construct a new instance. */ Region() { this(0, 0); } /** * Construct a new instance. * * @param startIndex * @param endIndex */ Region(int startIndex, int endIndex) { this.startIndex = startIndex; this.endIndex = endIndex; labelMap = new HashMap(); labels = new HashSet(); tryCatchBlocks = new ArrayList(); } /** * Add a new label to this region's list. * * @param label */ void addLabel(Label label) { labels.add(label); } /** * Add a new try catch block that is contained within * this region. * * @param tryCatchBlock */ void addTryCatchBlock(TryCatchBlockNode tryCatchBlock) { tryCatchBlocks.add(tryCatchBlock); } /** * Return a boolean indicating whether the specified * TryCatchBlockNode is enclosed within this instruction * region. * * @param tryCatchBlock * @return */ boolean encloses(TryCatchBlockNode tryCatchBlock) { return labels.contains(tryCatchBlock.start) && labels.contains(tryCatchBlock.end); } /** * Enter the region. Do any setup for this region. * @param method */ void enter(MethodNode method) { labelMap.clear(); copyTryCatchBlocks(method, this, tryCatchBlocks); } /** * Exit the region. Do any cleanup for this region. * @param method */ void exit(MethodNode method) { } /** * Return the length (in AbstractInsnNode instances) of * this region. * * @return */ int getLength() { return endIndex - startIndex; } /** * Get the mapped label for the specified label based on the * current region. * * @param originalLabel * @return */ Label getMappedLabel(Label originalLabel) { Label mappedLabel = findMappedLabel(originalLabel); if (mappedLabel == null) { mappedLabel = new MappedLabel(originalLabel); Map map = findLabelMap(originalLabel); map.put(originalLabel, mappedLabel); } return mappedLabel; } /** * Returnn a boolean indicating whether the specified opcode * at the specified index in the method is a subroutine store * opcode. * * @param methodNode * @param index * @return */ boolean isSubroutineReturnStore(MethodNode methodNode, int index) { return false; } /** * Store the parent region. * * @param parentRegion */ void setParentRegion(Region parentRegion) { this.parentRegion = parentRegion; } /** * Find the label map recursively as necessary. * * @param originalLabel * @return */ protected Map findLabelMap(Label originalLabel) { Map map = findLabelMapRecursive(originalLabel); return (map != null) ? map : labelMap; } /** * Find the label map recursively. * * @param originalLabel * @return */ protected Map findLabelMapRecursive(Label originalLabel) { Map map = null; if (labels.contains(originalLabel)) { map = labelMap; } else if (parentRegion != null) { map = parentRegion.findLabelMapRecursive(originalLabel); } return map; } /** * Find and return the specified label mapped appropriately for the * region or <code>null</code> if not found. * * @param originalLabel * @return */ protected Label findMappedLabel(Label originalLabel) { Label mappedLabel = (Label) labelMap.get(originalLabel); if ((mappedLabel == null) && (parentRegion != null)) { mappedLabel = parentRegion.findMappedLabel(originalLabel); } return mappedLabel; } /** * Return the try catch blocks in this region. * * @return */ List getTryCatchBlocks() { return tryCatchBlocks; } } /** Holder for information about a subroutine in a method */ class Subroutine extends Region { Label label; int returnVariable; /** * Construct a new subroutine instance. * * @param label */ Subroutine(Label label) { super(); this.label = label; } /** * @return Returns the returnVariable. */ public int getReturnVariable() { return returnVariable; } /** * @see eclipseme.preverifier.internal.MethodRewriter.Region#isSubroutineReturnStore(org.objectweb.asm.tree.MethodNode, int) */ boolean isSubroutineReturnStore(MethodNode methodNode, int index) { boolean isReturnStore = false; AbstractInsnNode insnNode = getInstruction(methodNode, index); if (insnNode.getOpcode() == Opcodes.ASTORE) { VarInsnNode varInsnNode = (VarInsnNode) insnNode; isReturnStore = (varInsnNode.var == returnVariable); } return isReturnStore; } /** * @param returnVariable The returnVariable to set. */ public void setReturnVariable(int returnVariable) { this.returnVariable = returnVariable; } } private PreverificationClassNode classNode; private PreverifierMethodNode srcMethod; private MethodNode updatedMethod; private Map subroutineMap; private Map lineNumberMap; private Map localVariableByStartLabelMap; private Map localVariableByEndLabelMap; /** * Construct a new rewriter instance. * * @param classNode * @param srcMethod */ public MethodRewriter(PreverificationClassNode classNode, PreverifierMethodNode srcMethod) { super(); this.classNode = classNode; this.srcMethod = srcMethod; localVariableByEndLabelMap = new HashMap(); } /** * Return the method with subroutines inlined and * an associated StackMapAttribute. * * @return * @throws AnalyzerException */ public MethodNode getUpdatedMethod() throws AnalyzerException { boolean inliningRequired = (srcMethod.getJsrInstructionIndices().size() > 0); if (inliningRequired) { inlineSubroutines(); } else { updatedMethod = srcMethod; } createStackMapAttribute(); return updatedMethod; } /** * Create a new StackMapAttribute for the method. * This method also removes any dead code that would * cause the stack map attribute to be incorrect. * * @throws AnalyzerException */ private void createStackMapAttribute() throws AnalyzerException { StackMapAttribute stackMapAttribute = new StackMapAttribute(); Set targetLabels = findTargetLabels(); // We need to have the verifier operating with the classpath // of the class being rewritten Interpreter interpreter = new SimpleVerifierPlusClassloader(classNode.getClassLoader()); Analyzer analyzer = new Analyzer(interpreter); Frame[] frames = analyzer.analyze(classNode.name, updatedMethod); int deadCodeFrameCount = 0; int[] deadCodeIndices = new int[frames.length]; for (int i = 0; i < updatedMethod.instructions.size(); i++) { Frame frame = frames[i]; // We only need to add stack map attributes for labels // that are the target of certain instructions AbstractInsnNode insnNode = getInstruction(updatedMethod, i); if (insnNode.getType() == AbstractInsnNode.LABEL) { LabelNode labelNode = (LabelNode) insnNode; if (targetLabels.contains(labelNode.label) && (frame != null)) { stackMapAttribute.frames.add(newStackMapFrame(labelNode.label, frame)); } } else if (frame == null) { // Track the indices of dead code for removal after // building the stack map deadCodeIndices[deadCodeFrameCount++] = i; } } // Write out the newly created stack map attribute // and remove any unnecessary dead code that would // have an adverse effect on the stack map attribute if (stackMapAttribute.frames.size() != 0) { updatedMethod.visitAttribute(stackMapAttribute); // Remove the dead code in reverse order so that // the indices remain correct after each removal for (int i = deadCodeFrameCount - 1; i >= 0; i--) { updatedMethod.instructions.remove(deadCodeIndices[i]); } } } /** * Inline all subroutines. * * @throws AnalyzerException */ private void inlineSubroutines() throws AnalyzerException { // Set up the first instruction region to be // processed as the entire method code... Region methodRegion = new Region(0, srcMethod.instructions.size()); // Scan through the instructions in the method and // get organized createSubroutineMap(); sortTryCatchBlocks(methodRegion); // Copy the non-code information updatedMethod = copyMethodMetadata(srcMethod); // Set up some mapping information lineNumberMap = createLineNumberMap(); localVariableByStartLabelMap = createLocalVariableMap(); // Copy the instructions while inlining subroutines copyRegion(updatedMethod, methodRegion); // The WTK reduces the visibility of the local variables if (shouldReduceVariableVisibility(updatedMethod)) { List instructions = updatedMethod.instructions; int instructionCount = instructions.size(); AbstractInsnNode lastLabel = (AbstractInsnNode) instructions.remove(instructionCount - 1); instructions.add(instructionCount - 2, lastLabel); } updatedMethod.visitMaxs(srcMethod.maxStack, srcMethod.maxLocals); } /** * Add a new local variabled to the variables by end label map. * * @param newLocalVariable */ private void addNewLocalVariableByEnd(LocalVariableNode newLocalVariable) { ArrayList endLocalVariables = (ArrayList) localVariableByEndLabelMap.get(newLocalVariable.end); if (endLocalVariables == null) { endLocalVariables = new ArrayList(); localVariableByEndLabelMap.put(newLocalVariable.end, endLocalVariables); } endLocalVariables.add(newLocalVariable); } /** * Add a new stack map type. * * @param localOrStack * @param region * @param label * @param value */ private void addStackMapType(List localOrStack, Label label, Value value) { BasicValue basicValue = (BasicValue) value; if (this == BasicValue.UNINITIALIZED_VALUE) { localOrStack.add(newStackMapType(label, StackMapType.ITEM_Uninitialized)); } else { Type valueType = basicValue.getType(); if (valueType == null) { localOrStack.add(newStackMapType(label, StackMapType.ITEM_Top)); } else { switch (valueType.getSort()) { case Type.BOOLEAN: case Type.BYTE: case Type.CHAR: case Type.INT: case Type.SHORT: localOrStack.add(newStackMapType(label, StackMapType.ITEM_Integer)); break; case Type.DOUBLE: localOrStack.add(newStackMapType(label, StackMapType.ITEM_Double)); localOrStack.add(newStackMapType(label, StackMapType.ITEM_Top)); break; case Type.FLOAT: localOrStack.add(newStackMapType(label, StackMapType.ITEM_Float)); break; case Type.LONG: localOrStack.add(newStackMapType(label, StackMapType.ITEM_Long)); localOrStack.add(newStackMapType(label, StackMapType.ITEM_Top)); break; case Type.OBJECT: { StackMapType mapType = null; String typeName = valueType.getInternalName(); if (typeName.equals("null")) { mapType = newStackMapType(label, StackMapType.ITEM_Null); } else { mapType = newStackMapType(label, StackMapType.ITEM_Object); mapType.setObject(typeName); } localOrStack.add(mapType); break; } case Type.ARRAY: { StackMapType mapType = newStackMapType(label, StackMapType.ITEM_Object); mapType.setObject(valueType.toString()); localOrStack.add(mapType); break; } } } } } /** * Copy all of the try/catch blocks in the method, realigning based on * the rewritten code. * * @param methodNode * @param methodRegion */ private void copyTryCatchBlocks(MethodNode methodNode, Region region, List blocks) { Iterator tryCatchBlocks = blocks.iterator(); while (tryCatchBlocks.hasNext()) { TryCatchBlockNode tryCatch = (TryCatchBlockNode) tryCatchBlocks.next(); if (shouldCopy(tryCatch)) { methodNode.visitTryCatchBlock( region.getMappedLabel(tryCatch.start), region.getMappedLabel(tryCatch.end), region.getMappedLabel(tryCatch.handler), tryCatch.type); } } } /** * Create a mapping from line number location (Label) to the instance * of the line number nodes. * * @return */ private Map createLineNumberMap() { Map map = new HashMap(); Iterator lineNumbers = srcMethod.lineNumbers.iterator(); while (lineNumbers.hasNext()) { LineNumberNode lineNumber = (LineNumberNode) lineNumbers.next(); map.put(lineNumber.start, lineNumber); } return map; } /** * Create a mapping from local variable location (Label) to the instance * of the local variable nodes. * * @return */ private Map createLocalVariableMap() { Map map = new HashMap(); Iterator localVariables = srcMethod.localVariables.iterator(); while (localVariables.hasNext()) { LocalVariableNode localVariable = (LocalVariableNode) localVariables.next(); ArrayList startList = (ArrayList) map.get(localVariable.start); if (startList == null) { startList = new ArrayList(); map.put(localVariable.start, startList); } startList.add(localVariable); } return map; } /** * Copy the method node's metadata that is not changing. * * @param method * @return */ private MethodNode copyMethodMetadata(MethodNode method) { // Start a new method node String[] exceptions = (String[]) method.exceptions.toArray(new String[method.exceptions.size()]); return new MethodNode( method.access, method.name, method.desc, method.signature, exceptions); } /** * Copy the specified region of code potentially recursively. * * @param method * @param region * @throws AnalyzerException */ private void copyRegion(MethodNode method, Region region) throws AnalyzerException { // Do any region-specific setup region.enter(method); // Walk the instructions.. inlining as we go for (int index = region.startIndex; index < region.endIndex; ++index) { AbstractInsnNode insnNode = getInstruction(srcMethod, index); // Special case for labels, as they may indicate the start of // a subroutine that can be skipped. if (insnNode.getType() == AbstractInsnNode.LABEL) { Label label = ((LabelNode) insnNode).label; visitLabel(method, region, label); if (isSubroutineStart(label)) { // Skip over this subroutine implementation Subroutine subroutine = (Subroutine) subroutineMap.get(label); index = subroutine.endIndex; } } else { if (!region.isSubroutineReturnStore(srcMethod, index)) { visitInstruction(method, region, insnNode); } } } // Do any region-specific cleanup region.exit(method); } /** * Create a new stack map frame. * * @param region * @param label * @param frame * @return * @throws AnalyzerException */ private StackMapFrame newStackMapFrame(Label label, Frame frame) throws AnalyzerException { // Handle the locals ArrayList locals = new ArrayList(); int localsCount = frame.getLocals(); for (int i = 0; i < localsCount; i++) { addStackMapType(locals, label, frame.getLocal(i)); } removeTrailingTops(locals); // Handle the stack ArrayList stack = new ArrayList(); int stackCount = frame.getStackSize(); for (int i = 0; i < stackCount; i++) { addStackMapType(stack, label, frame.getStack(i)); } removeTrailingTops(stack); return new StackMapFrame(label, locals, stack); } /** * Create a new StackMapType instance for the specified label. * * @param label * @param typeCode * @return */ private StackMapType newStackMapType(Label label, int typeCode) { StackMapType type = StackMapType.getTypeInfo(typeCode); type.setLabel(label); return type; } /** * Remove the trailing TOP types from the specified locals or stack. * * @param locals */ private void removeTrailingTops(ArrayList localsOrStack) { while (removeTrailingTop(localsOrStack)) { } } /** * Remove the trailing TOP value from the list. * * @param localsOrStack * @return */ private boolean removeTrailingTop(ArrayList localsOrStack) { boolean removed = false; if (localsOrStack.size() > 0) { int lastIndex = localsOrStack.size() - 1; StackMapType type = (StackMapType) localsOrStack.get(lastIndex); if (type.getType() == StackMapType.ITEM_Top) { localsOrStack.remove(lastIndex); removed = true; } } return removed; } /** * Scan the instructions in the source method and find * the subroutines and locations of any target labels. * * @param method * @throws AnalyzerException */ private void createSubroutineMap() throws AnalyzerException { subroutineMap = new HashMap(); // Look through the JSR instructions and collect the // target labels. Those target labels are the starting // points for the subroutines. Set subroutineStartLabels = new HashSet(); Iterator jsrInstructionIndices = srcMethod.getJsrInstructionIndices().iterator(); while (jsrInstructionIndices.hasNext()) { Integer instructionIndex = (Integer) jsrInstructionIndices.next(); JumpInsnNode jumpNode = (JumpInsnNode) getInstruction(srcMethod, instructionIndex.intValue()); subroutineStartLabels.add(jumpNode.label); } // Now, start searching for the subroutine starts. This // must be done to account for nested subroutine implementations if (subroutineStartLabels.size() > 0) { for (int i = 0; i < srcMethod.instructions.size(); i++) { AbstractInsnNode insnNode = getInstruction(srcMethod, i); if (insnNode.getType() == AbstractInsnNode.LABEL) { LabelNode labelNode = (LabelNode) insnNode; if (subroutineStartLabels.contains(labelNode.label)) { // This is the start of a subroutine i = captureSubroutine( subroutineStartLabels, subroutineMap, i, srcMethod, (LabelNode) insnNode); } } } } } /** * Capture the specified subroutine, potentially recursively * capturing nested subroutines. Captured subroutines are * added to the map of subroutines. * * @param subroutineStartLabels * @param subroutineMap * @param index * @param method * @param labelNode * @return */ private int captureSubroutine( Set subroutineStartLabels, Map subroutineMap, int index, MethodNode method, LabelNode labelNode) { Subroutine subroutine = new Subroutine(labelNode.label); subroutine.startIndex = index + 1; for (subroutine.endIndex = subroutine.startIndex; true; subroutine.endIndex++) { AbstractInsnNode insn = getInstruction(method, subroutine.endIndex); if (insn.getType() == AbstractInsnNode.LABEL) { Label label = ((LabelNode) insn).label; if (subroutineStartLabels.contains(label)) { subroutine.endIndex = captureSubroutine( subroutineStartLabels, subroutineMap, subroutine.endIndex, srcMethod, (LabelNode) insn); } else { subroutine.addLabel(label); } } else { if (insn.getOpcode() == Opcodes.RET) { // Figure out the variable that is being used // for the return instruction VarInsnNode varInsnNode = (VarInsnNode) insn; int variableNumber = varInsnNode.var; subroutine.setReturnVariable(variableNumber); break; } } } // Add the newly found subroutine to the map Label label = labelNode.label; if (!subroutineMap.containsKey(label)) { subroutineMap.put(label, subroutine); } return subroutine.endIndex + 1; } /** * Return a boolean indicating whether or not this * try-catch block should be copied to the new class. * * @param tryCatch * @return */ private boolean shouldCopy(TryCatchBlockNode tryCatch) { boolean shouldCopy = true; int startIndex = getLabelIndex(srcMethod, tryCatch.start); ArrayList insns = new ArrayList(); for (int i = startIndex; i < srcMethod.instructions.size(); i++) { AbstractInsnNode insn = getInstruction(srcMethod, i); if (insn.getType() == AbstractInsnNode.LABEL) { LabelNode l = (LabelNode) insn; if (l.label.equals(tryCatch.end)) { break; } } else { insns.add(insn); } } if (insns.size() == 1) { AbstractInsnNode insn = (AbstractInsnNode) insns.get(0); shouldCopy = (insn.getOpcode() != Opcodes.JSR); } return shouldCopy; } /** * Return a boolean indicating whether the variable visibility should * be reduced. * * @param method * @return */ private boolean shouldReduceVariableVisibility(MethodNode method) { boolean shouldReduce = false; if (getLastInstruction(method) instanceof LabelNode) { List instructions = updatedMethod.instructions; int instructionCount = instructions.size(); AbstractInsnNode node = getInstruction(method, instructionCount - 2); if (node instanceof InsnNode) { InsnNode insnNode = (InsnNode) node; switch (insnNode.getOpcode()) { case Opcodes.IRETURN: case Opcodes.LRETURN: case Opcodes.FRETURN: case Opcodes.DRETURN: case Opcodes.ARETURN: case Opcodes.RETURN: case Opcodes.ATHROW: shouldReduce = true; break; } } } return shouldReduce; } /** * Sort the try/catch blocks such that they are associated with * the smallest region that surrounds that block. * * @param methodRegion */ private void sortTryCatchBlocks(Region methodRegion) { Iterator blocks = srcMethod.tryCatchBlocks.iterator(); while (blocks.hasNext()) { Region enclosingRegion = methodRegion; TryCatchBlockNode block = (TryCatchBlockNode) blocks.next(); Iterator regions = subroutineMap.values().iterator(); while (regions.hasNext()) { Region region = (Region) regions.next(); if (region.encloses(block)) { if (enclosingRegion == null) { enclosingRegion = region; } else { // Pick the smallest region that encloses the block if (region.getLength() < enclosingRegion.getLength()) { enclosingRegion = region; } } } } enclosingRegion.addTryCatchBlock(block); } } /** * Return the target labels used in generation of the stack map attribute. * * @return */ private Set findTargetLabels() { Set targetLabels = new HashSet(); Iterator insns = updatedMethod.instructions.iterator(); while (insns.hasNext()) { AbstractInsnNode insnNode = (AbstractInsnNode) insns.next(); switch (insnNode.getType()) { case AbstractInsnNode.JUMP_INSN: JumpInsnNode jumpInsnNode = (JumpInsnNode) insnNode; targetLabels.add(jumpInsnNode.label); break; case AbstractInsnNode.LOOKUPSWITCH_INSN: { LookupSwitchInsnNode lookupSwitchNode = (LookupSwitchInsnNode) insnNode; Iterator labels = lookupSwitchNode.labels.iterator(); while (labels.hasNext()) { Label label = (Label) labels.next(); targetLabels.add(label); } targetLabels.add(lookupSwitchNode.dflt); } break; case AbstractInsnNode.TABLESWITCH_INSN: { TableSwitchInsnNode tableSwitchNode = (TableSwitchInsnNode) insnNode; Iterator labels = tableSwitchNode.labels.iterator(); while (labels.hasNext()) { Label label = (Label) labels.next(); targetLabels.add(label); } targetLabels.add(tableSwitchNode.dflt); } break; } } Iterator blocks = updatedMethod.tryCatchBlocks.iterator(); while (blocks.hasNext()) { TryCatchBlockNode tryCatchBlock = (TryCatchBlockNode) blocks.next(); targetLabels.add(tryCatchBlock.handler); } return targetLabels; } /** * Return the instruction at the specified index. * * @param method * @param index * @return */ private AbstractInsnNode getInstruction(MethodNode method, int index) { List instructions = method.instructions; return (AbstractInsnNode) ((index < instructions.size()) ? instructions.get(index) : null); } /** * Return the index of the specified label within the specified method node. * * @param methodNode * @param label * @return */ private int getLabelIndex(PreverifierMethodNode methodNode, Label label) { Integer i = (Integer) methodNode.getLabelIndices().get(label); return i.intValue(); } /** * Return the last instruction in the specified method. * * @param method * @return */ private AbstractInsnNode getLastInstruction(MethodNode method) { return getInstruction(method, method.instructions.size() - 1); } /** * Map the specified list of labels into an array of labels * mapped into the target region. * * @param region * @param labels * @return */ private Label[] getMappedLabelArray(Region region, List labels) { Label[] mappedLabels = new Label[labels.size()]; for (int i = 0; i < mappedLabels.length; i++) { mappedLabels[i] = region.getMappedLabel((Label) labels.get(i)); } return mappedLabels; } /** * Return a method indicating whether the specified label is the start * of a referenced subroutine. * * @param label * @return */ private boolean isSubroutineStart(Label label) { return subroutineMap.containsKey(label); } /** * Visit the specified instruction and do the right thing. * * @param method * @param region * @param insnNode * @throws AnalyzerException */ private void visitInstruction( MethodNode method, Region region, AbstractInsnNode insnNode) throws AnalyzerException { int opcode = insnNode.getOpcode(); switch (opcode) { case Opcodes.JSR: visitJumpToSubroutine(method, region, (JumpInsnNode) insnNode); break; case Opcodes.IFEQ: case Opcodes.IFNE: case Opcodes.IFLT: case Opcodes.IFGE: case Opcodes.IFGT: case Opcodes.IFLE: case Opcodes.IF_ICMPEQ: case Opcodes.IF_ICMPNE: case Opcodes.IF_ICMPLT: case Opcodes.IF_ICMPGE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLE: case Opcodes.IF_ACMPEQ: case Opcodes.IF_ACMPNE: case Opcodes.GOTO: case Opcodes.IFNULL: case Opcodes.IFNONNULL: visitJump(method, region, (JumpInsnNode) insnNode); break; case Opcodes.LOOKUPSWITCH: visitLookupSwitch(method, region, (LookupSwitchInsnNode) insnNode); break; case Opcodes.TABLESWITCH: visitTableSwitch(method, region, (TableSwitchInsnNode) insnNode); break; default: insnNode.accept(method); } } /** * Visit the specified jump instructions, mapping the labels into the target * method. * * @param method * @param region * @param jumpNode */ private void visitJump(MethodNode method, Region region, JumpInsnNode jumpNode) { Label mappedLabel = region.getMappedLabel(jumpNode.label); JumpInsnNode newJumpNode = new JumpInsnNode(jumpNode.getOpcode(), mappedLabel); newJumpNode.accept(method); } /** * Visit a JSR instruction... Inlining the subroutine. * * @param method * @param region * @param jumpNode * @throws AnalyzerException */ private void visitJumpToSubroutine(MethodNode method, Region region, JumpInsnNode jumpNode) throws AnalyzerException { // Back up and see if we need to remap a local variable label // The WTK preverifier extends the scope of a local variable // one instruction further if the last instruction of the block // is a variable store instruction AbstractInsnNode insnNode = getLastInstruction(updatedMethod); if (insnNode instanceof VarInsnNode) { List instructions = updatedMethod.instructions; AbstractInsnNode insnNode2 = (AbstractInsnNode) instructions.get(instructions.size() - 2); if (insnNode2 instanceof LabelNode) { // Looks like we have the correct situation here. // Introduce a new label as the last instruction // and add it to the label map to be used when // adding the local variables LabelNode labelNode = (LabelNode) insnNode2; ArrayList localVariables = (ArrayList) localVariableByEndLabelMap.get(labelNode.label); if (localVariables != null) { Iterator vars = localVariables.iterator(); while (vars.hasNext()) { LocalVariableNode var = (LocalVariableNode) vars.next(); var.end = new Label(); method.visitLabel(var.end); } } } } // Inline the subroutine Subroutine subroutine = (Subroutine) subroutineMap.get(jumpNode.label); subroutine.setParentRegion(region); copyRegion(method, subroutine); } /** * Visit the specified label in the context of the region. Handle * all things related to that label. * * @param method * @param region * @param label */ private void visitLabel(MethodNode method, Region region, Label label) { Label mappedLabel = region.getMappedLabel(label); method.visitLabel(mappedLabel); // Check for any related attributes LineNumberNode lineNumber = (LineNumberNode) lineNumberMap.get(label); if (lineNumber != null) { method.visitLineNumber(lineNumber.line, mappedLabel); } ArrayList localVariables = (ArrayList) localVariableByStartLabelMap.get(label); if (localVariables != null) { Iterator vars = localVariables.iterator(); while (vars.hasNext()) { int localVariableCount = method.localVariables.size(); LocalVariableNode localVariable = (LocalVariableNode) vars.next(); method.visitLocalVariable( localVariable.name, localVariable.desc, localVariable.signature, region.getMappedLabel(localVariable.start), region.getMappedLabel(localVariable.end), localVariable.index); LocalVariableNode newLocalVariable = (LocalVariableNode) method.localVariables.get(localVariableCount); addNewLocalVariableByEnd(newLocalVariable); } } } /** * Visit the specified instruction, mapping the labels into the target * method. * * @param codeVisitor * @param region * @param node */ private void visitLookupSwitch(MethodNode codeVisitor, Region region, LookupSwitchInsnNode node) { Label dflt = region.getMappedLabel(node.dflt); int[] keys = new int[node.keys.size()]; for (int i = 0; i < keys.length; i++) { keys[i] = ((Integer) node.keys.get(i)).intValue(); } Label[] labels = getMappedLabelArray(region, node.labels); LookupSwitchInsnNode newSwitch = new LookupSwitchInsnNode(dflt, keys, labels); newSwitch.accept(codeVisitor); } /** * Visit the specified instructions, mapping the labels into the target * method. * * @param codeVisitor * @param region * @param node */ private void visitTableSwitch(MethodNode codeVisitor, Region region, TableSwitchInsnNode node) { Label dflt = region.getMappedLabel(node.dflt); Label[] labels = getMappedLabelArray(region, node.labels); TableSwitchInsnNode newSwitch = new TableSwitchInsnNode(node.min, node.max, dflt, labels); newSwitch.accept(codeVisitor); } }

The table below shows all metrics for MethodRewriter.java.

MetricValueDescription