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.
| Metric | Description | |
|---|---|---|
/**
* 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.
| Metric | Value | Description | |
|---|---|---|---|



