Java tutorial
/** * Copyright (c) 2008-2012, Dr. Garbage Community * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.drgarbage.asm.render.impl; import java.io.IOException; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.Platform; import org.eclipse.draw2d.AbstractLocator; import org.eclipse.jface.preference.IPreferenceStore; import org.osgi.framework.Bundle; import com.drgarbage.asm.AnnotationVisitor; import com.drgarbage.asm.Attribute; import com.drgarbage.asm.ClassVisitor; import com.drgarbage.asm.FieldVisitor; import com.drgarbage.asm.Label; import com.drgarbage.asm.MethodVisitor; import com.drgarbage.asm.Type; import com.drgarbage.asm.render.impl.ClassFileDocument.ExceptionTableEntryComparator; import com.drgarbage.asm.render.impl.ClassFileDocument.ListMap; import com.drgarbage.asm.render.intf.IClassFileDocument; import com.drgarbage.asm.render.intf.IFieldSection; import com.drgarbage.asm.render.intf.IInstructionLine; import com.drgarbage.asm.render.intf.ILocalVariableTable; import com.drgarbage.asm.render.intf.IMethodSection; import com.drgarbage.asm.render.intf.ITryBlock; import com.drgarbage.asm.signature.SignatureReader; import com.drgarbage.asm.signature.SignatureVisitor; import com.drgarbage.asm_ext.ICodeVisitor; import com.drgarbage.asm_ext.IConstantPoolVisitor; import com.drgarbage.asm_ext.ILocalVariableTableVisitor; import com.drgarbage.bytecode.ByteCodeConstants; import com.drgarbage.bytecode.BytecodeUtils; import com.drgarbage.bytecode.ConstantPoolParser; import com.drgarbage.bytecode.ExceptionTableEntry; import com.drgarbage.bytecode.InstructionParser; import com.drgarbage.bytecode.LineNumberTableEntry; import com.drgarbage.bytecode.LocalVariableTableEntry; import com.drgarbage.bytecode.LocalVariableTypeTableEntry; import com.drgarbage.bytecode.ByteCodeConstants.Align; import com.drgarbage.bytecode.constant_pool.AbstractConstantPoolEntry; import com.drgarbage.bytecode.constant_pool.ConstantClassInfo; import com.drgarbage.bytecode.constant_pool.ConstantDoubleInfo; import com.drgarbage.bytecode.constant_pool.ConstantFieldrefInfo; import com.drgarbage.bytecode.constant_pool.ConstantFloatInfo; import com.drgarbage.bytecode.constant_pool.ConstantIntegerInfo; import com.drgarbage.bytecode.constant_pool.ConstantInterfaceMethodrefInfo; import com.drgarbage.bytecode.constant_pool.ConstantInvokeDynamicInfo; import com.drgarbage.bytecode.constant_pool.ConstantLongInfo; import com.drgarbage.bytecode.constant_pool.ConstantMethodHandleInfo; import com.drgarbage.bytecode.constant_pool.ConstantMethodTypeInfo; import com.drgarbage.bytecode.constant_pool.ConstantMethodrefInfo; import com.drgarbage.bytecode.constant_pool.ConstantNameAndTypeInfo; import com.drgarbage.bytecode.constant_pool.ConstantStringInfo; import com.drgarbage.bytecode.constant_pool.ConstantUtf8Info; import com.drgarbage.bytecode.instructions.AbstractInstruction; import com.drgarbage.bytecode.instructions.BranchInstruction; import com.drgarbage.bytecode.instructions.IConstantPoolIndexProvider; import com.drgarbage.bytecode.instructions.ILocalVariableIndexProvider; import com.drgarbage.bytecode.instructions.ImmediateByteInstruction; import com.drgarbage.bytecode.instructions.ImmediateIntInstruction; import com.drgarbage.bytecode.instructions.ImmediateShortInstruction; import com.drgarbage.bytecode.instructions.IncrementInstruction; import com.drgarbage.bytecode.instructions.InvokeInterfaceInstruction; import com.drgarbage.bytecode.instructions.LookupSwitchInstruction; import com.drgarbage.bytecode.instructions.MultianewarrayInstruction; import com.drgarbage.bytecode.instructions.NewArrayInstruction; import com.drgarbage.bytecode.instructions.SimpleInstruction; import com.drgarbage.bytecode.instructions.TableSwitchInstruction; import com.drgarbage.bytecode.instructions.LookupSwitchInstruction.MatchOffsetEntry; import com.drgarbage.bytecodevisualizer.BytecodeVisualizerPlugin; import com.drgarbage.bytecodevisualizer.preferences.BytecodeVisualizerPreferenceConstats; import com.drgarbage.core.CoreConstants; import com.drgarbage.javasrc.JavaAnnotations; import com.drgarbage.javasrc.JavaKeywords; import com.drgarbage.javasrc.JavaLexicalConstants; import com.drgarbage.javasrc.JavaSourceUtils; public abstract class AbstractClassFileDocument extends ClassVisitor implements com.drgarbage.asm.Opcodes, IClassFileDocument, IConstantPoolVisitor, BytecodeVisualizerPreferenceConstats { protected abstract class AbstractClassFileElement extends MethodVisitor { // ------------------------------------------------------------------------ // Utility methods // ------------------------------------------------------------------------ public AbstractClassFileElement(int arg0) { super(arg0); } /** * Appends an internal name, a type descriptor or a type signature to * {@link #buf buf}. * * @param type indicates if desc is an internal name, a field descriptor, a * method descriptor, a class signature, ... * @param desc an internal name, type descriptor, or type signature. May be * <tt>null</tt>. */ protected void appendDescriptor(final int type, final String desc) { //TODO: test this sb.append(desc); } } protected abstract class AbstractMethodRenderer extends AbstractClassFileElement implements com.drgarbage.asm.Opcodes, ICodeVisitor, ILocalVariableTableVisitor, IMethodSection { protected class Block { protected TryBlock enclosingTryBlock; protected int endLine = ByteCodeConstants.INVALID_OFFSET; protected int endOffset = ByteCodeConstants.INVALID_OFFSET; protected String startKeyWord; protected int startLine = ByteCodeConstants.INVALID_OFFSET; protected int startOffset = ByteCodeConstants.INVALID_OFFSET; public Block(TryBlock parent, String startKeyWord, int start) { super(); this.enclosingTryBlock = parent; this.startKeyWord = startKeyWord; this.startOffset = start; } public Block(TryBlock parent, String startKeyWord, int startOffset, int endOffset) { super(); this.enclosingTryBlock = parent; this.startKeyWord = startKeyWord; this.startOffset = startOffset; this.endOffset = endOffset; } public void appendEnd() { endLine = AbstractClassFileDocument.this.lineCount; if (renderTryCatchFinallyBlocks) { decrementIndent(); appendRightBrace(); //append(" /* endOffset of try */"); appendNewline(); } } public void appendStart() { startLine = AbstractClassFileDocument.this.lineCount; if (renderTryCatchFinallyBlocks) { append(startKeyWord); appendSpace(); appendLeftBrace(); appendNewline(); incrementIndent(); } } public boolean encloses(ExceptionTableEntry en) { if (en == null) { return false; } else { return en.getStartPc() >= startOffset && en.getEndPc() <= endOffset; } } public int getEndLine() { return endLine; } public int getStartLine() { return startLine; } } protected class CatchBlock extends FinallyBlock { protected String catchType; protected String variableName; public CatchBlock(TryBlock parent, ExceptionTableEntry en) { super(parent, en.getHandlerPc()); startKeyWord = JavaKeywords.CATCH; if (constantPool == null) { catchType = String.valueOf(ByteCodeConstants.UNKNOWN_INFORMATION); } else { AbstractConstantPoolEntry cpInfo = constantPool[en.getCatchType()]; if (cpInfo instanceof ConstantClassInfo) { ConstantClassInfo constantClassInfo = (ConstantClassInfo) cpInfo; String name = ((ConstantUtf8Info) constantPool[constantClassInfo.getNameIndex()]) .getString(); catchType = name.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); } } } @Override public void appendStart() { startLine = AbstractClassFileDocument.this.lineCount; if (renderTryCatchFinallyBlocks) { append(JavaKeywords.CATCH); appendSpace(); appendLeftParenthesis(); if (catchType != null) { append(catchType); } appendRightParenthesis(); appendSpace(); appendLeftBrace(); appendStartComment(); appendNewline(); incrementIndent(); } } } protected class FinallyBlock extends Block { public FinallyBlock(TryBlock parent, int start) { super(parent, JavaKeywords.FINALLY, start); } public FinallyBlock(TryBlock parent, int start, int end) { super(parent, JavaKeywords.FINALLY, start, end); } @Override public void appendStart() { startLine = AbstractClassFileDocument.this.lineCount; if (renderTryCatchFinallyBlocks) { append(startKeyWord); appendSpace(); appendLeftBrace(); appendStartComment(); appendNewline(); incrementIndent(); } } protected void appendStartComment() { appendPaddedCommentBegin(); appendSpace(); append(formatCoversBytesXToY .format(new Object[] { enclosingTryBlock.startOffset, enclosingTryBlock.endOffset })); appendSpace(); appendCommentEnd(); } } protected class TryBlock extends Block implements ITryBlock { protected List<CatchBlock> catchBlocks; protected int enclosedGotoTarget = ByteCodeConstants.INVALID_OFFSET; protected List<TryBlock> enclosedTryBlocks; private FinallyBlock finallyBlock; public TryBlock(ExceptionTableEntry en) { this(null, en.getStartPc(), en.getEndPc()); // if (tryBlocks == null) { // tryBlocks = new ArrayList<ITryBlock>(); // } // tryBlocks.add(this); adoptHandler(en); } public TryBlock(TryBlock parent, int start, int end) { super(parent, JavaKeywords.TRY, start, end); // if (tryBlocks == null) { // tryBlocks = new ArrayList<ITryBlock>(); // } // tryBlocks.add(this); } private void adoptHandler(ExceptionTableEntry en) { if (en.getCatchType() == 0) { /* finally handler */ if (finallyBlock != null) { /* this should not happen */ throw new IllegalStateException(); } finallyBlock = new FinallyBlock(this, en.getHandlerPc()); } else { /* the catch block */ if (catchBlocks == null) { catchBlocks = new ArrayList<CatchBlock>(); } catchBlocks.add(new CatchBlock(this, en)); } } public void computeHandlersEnds(Map<Integer, AbstractInstruction> offsetInstructionMap) { if (enclosedTryBlocks != null) { for (TryBlock t : enclosedTryBlocks) { t.computeHandlersEnds(offsetInstructionMap); } } if (catchBlocks != null) { CatchBlock lastCatchBlock = null; for (CatchBlock c : catchBlocks) { if (lastCatchBlock != null && lastCatchBlock.endOffset == ByteCodeConstants.INVALID_OFFSET) { lastCatchBlock.endOffset = c.startOffset; blockStarts.putToList(lastCatchBlock.startOffset, lastCatchBlock); blockEnds.putToList(lastCatchBlock.endOffset, lastCatchBlock); } lastCatchBlock = c; } if (lastCatchBlock != null && lastCatchBlock.endOffset == ByteCodeConstants.INVALID_OFFSET) { AbstractInstruction inst = offsetInstructionMap.get(endOffset); int gotoTarget = Integer.MAX_VALUE; if (inst instanceof BranchInstruction) { BranchInstruction bi = (BranchInstruction) inst; gotoTarget = bi.getOffset() + bi.getBranchOffset(); if (enclosingTryBlock.enclosedGotoTarget == ByteCodeConstants.INVALID_OFFSET) { enclosingTryBlock.enclosedGotoTarget = gotoTarget; } } lastCatchBlock.endOffset = Math.min(enclosingTryBlock.endOffset, gotoTarget); blockStarts.putToList(lastCatchBlock.startOffset, lastCatchBlock); blockEnds.putToList(lastCatchBlock.endOffset, lastCatchBlock); } } if (finallyBlock != null) { AbstractInstruction inst = offsetInstructionMap.get(endOffset); int gotoTarget = Integer.MAX_VALUE; if (inst instanceof BranchInstruction) { if (inst instanceof BranchInstruction) { BranchInstruction bi = (BranchInstruction) inst; gotoTarget = bi.getOffset() + bi.getBranchOffset(); } } if (enclosingTryBlock.endOffset >= gotoTarget) { finallyBlock.endOffset = gotoTarget; } else { /* the above did not function * we have to find some better way */ if (this.enclosedGotoTarget != ByteCodeConstants.INVALID_OFFSET) { finallyBlock.endOffset = Math.min(enclosingTryBlock.endOffset, this.enclosedGotoTarget); } else { finallyBlock.endOffset = enclosingTryBlock.endOffset; } } blockStarts.putToList(finallyBlock.startOffset, finallyBlock); blockEnds.putToList(finallyBlock.endOffset, finallyBlock); } blockStarts.putToList(startOffset, this); blockEnds.putToList(endOffset, this); } public TryBlock createEnclosedTryBlock(ExceptionTableEntry en) { if (en.getStartPc() < startOffset || en.getEndPc() > endOffset) { throw new IllegalArgumentException(); } TryBlock result = null; if (en.getStartPc() == startOffset && en.getEndPc() == endOffset && (en.getCatchType() != 0 || this.finallyBlock == null)) { /* this covers the same area * and the handler to add is either not finally * or finallyBlock is not set yet * only add handlers */ result = this; } else { /* enclosed try block */ result = new TryBlock(this, en.getStartPc(), en.getEndPc()); if (enclosedTryBlocks == null) { enclosedTryBlocks = new ArrayList<TryBlock>(); } enclosedTryBlocks.add(result); } result.adoptHandler(en); return result; } public List<Integer> getExceptionHandlerLines() { int cnt = 0; if (catchBlocks != null) { cnt += catchBlocks.size(); } if (finallyBlock != null) { cnt++; } if (cnt > 0) { ArrayList<Integer> result = new ArrayList<Integer>(cnt); if (catchBlocks != null) { for (CatchBlock cb : catchBlocks) { result.add(Integer.valueOf(cb.getStartLine())); } } if (finallyBlock != null) { result.add(Integer.valueOf(finallyBlock.getStartLine())); } return result; } else { return null; } } } protected int access; private ListMap<Integer, Block> blockEnds = new ListMap<Integer, Block>(); private ListMap<Integer, Block> blockStarts = new ListMap<Integer, Block>(); protected int currentLineNumberTableIndex = 0; protected String descriptor; protected String[] exceptions; protected int firstLine = ByteCodeConstants.INVALID_LINE; private ArrayList<IInstructionLine> instructionLines = new ArrayList<IInstructionLine>(); private List<AbstractInstruction> instructions; protected boolean isConstructor = false; protected int lastLine = ByteCodeConstants.INVALID_LINE; private LineNumberTableEntry[] lineNumberTable; private ExceptionTableEntry[] exceptionTable; protected ILocalVariableTable localVariableTable; /** * The {@link MethodVisitor} to which this visitor delegates calls. May be * <tt>null</tt>. */ protected MethodVisitor mv; protected String name; private Map<Integer, AbstractInstruction> offsetInstructionMap; private TryBlock openedTryBlock; private List<TryBlock> rootTryBlocks; protected String signature; private int max_stack = ByteCodeConstants.INVALID_OFFSET; private int max_locals = ByteCodeConstants.INVALID_OFFSET; private List<ITryBlock> tryBlocks; /** * Constructs a new {@link AbstractMethodRenderer}. */ public AbstractMethodRenderer(int access, String name, String descriptor, String signature, String[] exceptions) { this(access, name, descriptor, signature, exceptions, null); } /** * Constructs a new {@link AbstractMethodRenderer}. * * @param mv the {@link MethodVisitor} to which this visitor delegates * calls. May be <tt>null</tt>. */ public AbstractMethodRenderer(int access, String name, String descriptor, String signature, String[] exceptions, final MethodVisitor mv) { super(com.drgarbage.asm.Opcodes.ASM4); this.access = access; this.name = name; this.descriptor = descriptor; this.exceptions = exceptions; this.mv = mv; } protected abstract ILocalVariableTable createLocalVariableTable(boolean available); private TryBlock findEnclosingTryBlock(ExceptionTableEntry en) { TryBlock b = openedTryBlock; while (b != null) { if (b.encloses(en)) { return b; } b = b.enclosingTryBlock; } return null; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#getLocalVariableTable() */ public ILocalVariableTable getLocalVariableTable() { return localVariableTable; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#isLocalVariableTableAvailable() */ public boolean isLocalVariableTableAvailable() { if (localVariableTable == null) return false; return localVariableTable.isAvailable(); } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#getExceptionTable() */ public ExceptionTableEntry[] getExceptionTable() { return exceptionTable; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#isExceptionTableAvailable() */ public boolean isExceptionTableAvailable() { if (exceptionTable == null || exceptionTable.length == 0) { return false; } return true; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#getMaxStack() */ public int getMaxStack() { return max_stack; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#getMaxLocals() */ public int getMaxLocals() { return max_locals; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#findOffsetLine(int) */ public int findOffsetLine(int offset) { List<IInstructionLine> ils = getInstructionLines(); if (ils != null && ils.size() > 0) { for (IInstructionLine il : ils) { if (il.getInstruction().getOffset() == offset) { return il.getLine(); } } } return IInstructionLine.INVALID_LINE; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#getDocLine(int) */ public int getBytecodeLine(int sourceCodeLine) { if (lineNumberTable == null || lineNumberTable.length == 0 || sourceCodeLine == ByteCodeConstants.INVALID_OFFSET /* if there are no instructions we will find nothing either */ || instructionLines.size() == 0) { return ByteCodeConstants.INVALID_OFFSET; } int offset = lookUpLineNumberTableForStartPC(sourceCodeLine); int byteCodeDocLine = ByteCodeConstants.INVALID_OFFSET; int n = instructionLines.size(); for (int i = 0; i < n; i++) { IInstructionLine il = instructionLines.get(i); if (offset == il.getInstruction().getOffset()) { byteCodeDocLine = il.getLine(); break; } } /* not found */ return byteCodeDocLine; } /** * @return the descriptor */ public String getDescriptor() { return descriptor; } // ------------------------------------------------------------------------ // Implementation of the MethodVisitor interface // ------------------------------------------------------------------------ /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IMethodSection#getFirstLine() */ public int getFirstLine() { return firstLine; } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IMethodSection#getInstructionLines() */ public List<IInstructionLine> getInstructionLines() { return instructionLines; } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IMethodSection#getLastLine() */ public int getLastLine() { return lastLine; } /** * @return the name */ public String getName() { return name; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#getSourceCodeLine(int) */ public int getSourceCodeLine(int byteCodeDocLine) { if (lineNumberTable == null || lineNumberTable.length == 0 || byteCodeDocLine < firstLine || byteCodeDocLine > lastLine /* if there are no instructions we will find nothing either */ || instructionLines.size() == 0) { return ByteCodeConstants.INVALID_OFFSET; } int offset = ByteCodeConstants.INVALID_OFFSET; int n = instructionLines.size(); for (int i = 0; i < n; i++) { IInstructionLine il = instructionLines.get(i); if (byteCodeDocLine <= il.getLine()) { offset = il.getInstruction().getOffset(); break; } } if (offset == ByteCodeConstants.INVALID_OFFSET) { /* if we have not found it yet * we are after the last instruction * let us use the offset of the last instruction */ offset = instructionLines.get(n - 1).getInstruction().getOffset(); } return lookUpLineNumberTable(offset); } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IMethodSection#getTryBlocks() */ public List<ITryBlock> getTryBlocks() { return tryBlocks; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#hasCode() */ public boolean hasCode() { return !isNative() && !isAbstract(); } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#hasLineNumberTable() */ public boolean hasLineNumberTable() { return lineNumberTable != null; } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IMethodSection#isAbstract() */ public boolean isAbstract() { return (access & ACC_ABSTRACT) != 0; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#isConstructor() */ public boolean isConstructor() { return isConstructor; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IMethodSection#isNative() */ public boolean isNative() { return (access & ACC_NATIVE) != 0; } private int lookUpLineNumberTable(int offset) { if (offset != ByteCodeConstants.INVALID_OFFSET) { /* * Added by Sergej on 08.05.2008 * FIX: if the lineNumberTable includes only one entry, * then this entry is valid for all offsets. */ if (lineNumberTable.length == 1) { LineNumberTableEntry en = lineNumberTable[0]; return en.getLineNumber(); } /* * Added by Sergej on 09.05.2008 * FIX: Check if the current offset > the startPC of the last entry. * The last entry is not checked in the loop. */ LineNumberTableEntry en = lineNumberTable[lineNumberTable.length - 1]; if (offset > en.getStartPc()) { return en.getLineNumber(); } /* find offset */ LineNumberTableEntry lastEntry = null; for (int i = 0; i < lineNumberTable.length; i++) { en = lineNumberTable[i]; if (offset == en.getStartPc()) { return en.getLineNumber(); } else if (lastEntry != null && offset < en.getStartPc()) { return lastEntry.getLineNumber(); } lastEntry = en; } } /* not found */ return ByteCodeConstants.INVALID_OFFSET; } /** * Returns an offset of the startPC for the given sourceCodeLine. * @param sourceCodeLine 1-based source code line * @return the offset of the startPC */ private int lookUpLineNumberTableForStartPC(int oneBasedsourceCodeLine) { if (oneBasedsourceCodeLine != ByteCodeConstants.INVALID_OFFSET) { /* find startPointCode */ LineNumberTableEntry lastEntry = null; for (int i = 0; i < lineNumberTable.length; i++) { LineNumberTableEntry en = lineNumberTable[i]; if (oneBasedsourceCodeLine == en.getLineNumber()) { return en.getStartPc(); } else if (lastEntry != null && oneBasedsourceCodeLine < en.getLineNumber()) { return lastEntry.getStartPc(); } lastEntry = en; } } /* not found */ return ByteCodeConstants.INVALID_OFFSET; } private void renderLineNumberTable(LineNumberTableEntry[] lineNumberTable) { if (lineNumberTable == null) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.LINE_NUMBER_TABLE_NOT_AVAILABLE); appendSpace(); appendCommentEnd(); appendNewline(); } else { appendNewline(); TextTable tbl = new TextTable(new ByteCodeConstants.Align[] { ByteCodeConstants.Align.RIGHT, ByteCodeConstants.Align.RIGHT }); tbl.addRow(new String[] { ByteCodeConstants.START_PC, ByteCodeConstants.LINE_NUMBER }); for (int i = 0; i < lineNumberTable.length; i++) { LineNumberTableEntry en = lineNumberTable[i]; tbl.addRow( new String[] { String.valueOf(en.getStartPc()), String.valueOf(en.getLineNumber()) }); } tbl.recomputeColumnWidths(); tbl.beginRow(); tbl.appendValue(ByteCodeConstants.LINE_NUMBER_TABLE, ByteCodeConstants.Align.CENTER, tbl.computeWidth()); tbl.endRow(); tbl.render(); } } private void renderLocalVariableTable() { if (localVariableTable == null || !localVariableTable.isAvailable()) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.LOCAL_VARIABLE_TABLE_NOT_AVAILABLE); appendSpace(); appendCommentEnd(); appendNewline(); } else if (localVariableTable.getLength() == 0) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.LOCAL_VARIABLE_TABLE_EMPTY); appendSpace(); appendCommentEnd(); appendNewline(); } else { appendNewline(); String[] header = localVariableTable.getHeader(); ByteCodeConstants.Align[] alignments = new ByteCodeConstants.Align[header.length]; for (int i = 0; i < alignments.length; i++) { alignments[i] = localVariableTable.getAlignment(i); } TextTable tbl = new TextTable(alignments); tbl.addRow(header); int len = localVariableTable.getLength(); if (len > 0) { for (int i = 0; i < len; i++) { tbl.addRow(localVariableTable.getRow(i)); } } tbl.recomputeColumnWidths(); tbl.beginRow(); tbl.appendValue(ByteCodeConstants.LOCAL_VARIABLE_TABLE, ByteCodeConstants.Align.CENTER, tbl.computeWidth()); tbl.endRow(); tbl.render(); } } private void renderExceptionTable() { if (exceptionTable == null) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.EXCEPTION_TABLE_NOT_AVAILABLE); appendSpace(); appendCommentEnd(); appendNewline(); } else if (exceptionTable.length == 0) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.EXCEPTION_TABLE_EMPTY); appendSpace(); appendCommentEnd(); appendNewline(); } else { appendNewline(); TextTable tbl = new TextTable(new ByteCodeConstants.Align[] { ByteCodeConstants.Align.RIGHT, ByteCodeConstants.Align.RIGHT, ByteCodeConstants.Align.RIGHT, ByteCodeConstants.Align.RIGHT }); /* * u2 exception_table_length; * { u2 start_pc; * u2 end_pc; * u2 handler_pc; * u2 catch_type; * } exception_table[exception_table_length]; */ tbl.addRow(new String[] { ByteCodeConstants.START_PC, ByteCodeConstants.END_PC, ByteCodeConstants.HANDLER_PC, ByteCodeConstants.CATCH_TYPE }); for (int i = 0; i < exceptionTable.length; i++) { ExceptionTableEntry en = exceptionTable[i]; String catch_type_class = String.valueOf(ByteCodeConstants.UNKNOWN_INFORMATION); int catch_type = en.getCatchType(); if (catch_type == 0) { catch_type_class = ByteCodeConstants.ANY_EXCEPTION; } else { AbstractConstantPoolEntry cpInfo = constantPool[catch_type]; if (cpInfo instanceof ConstantClassInfo) { ConstantClassInfo constantClassInfo = (ConstantClassInfo) cpInfo; String name = ((ConstantUtf8Info) constantPool[constantClassInfo.getNameIndex()]) .getString(); catch_type_class = name.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); } } tbl.addRow(new String[] { String.valueOf(en.getStartPc()), String.valueOf(en.getEndPc()), String.valueOf(en.getHandlerPc()), catch_type_class }); } tbl.recomputeColumnWidths(); tbl.beginRow(); tbl.appendValue(ByteCodeConstants.EXCEPTION_TABLE, ByteCodeConstants.Align.CENTER, tbl.computeWidth()); tbl.endRow(); tbl.render(); } } private void renderMaxs() { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.MAX_STACK); appendColon(); appendSpace(); sb.append(max_stack); appendSpace(); sb.append(ByteCodeConstants.MAX_LOCALS); appendColon(); appendSpace(); sb.append(max_locals); appendSpace(); appendCommentEnd(); appendNewline(); } private void renderSignature() { appendNewline(); if ((access & ACC_BRIDGE) != 0) { appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.BRIDGE_METHOD_GENERATED_BY_THE_COMPILER); appendSpace(); appendCommentEnd(); appendNewline(); } appendDeprecated(access); /* changed to 0-based */ this.firstLine = lineCount; appendAccess(access); if ((access & ACC_NATIVE) != 0) { sb.append(JavaKeywords.NATIVE); appendSpace(); } if ((access & ACC_VARARGS) != 0) { appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.VARARGS); appendSpace(); appendCommentEnd(); appendSpace(); } if (signature != null) { //TODO test generic signature SignatureRenderer v = new SignatureRenderer(0); SignatureReader r = new SignatureReader(signature); r.accept(v); String genericDecl = v.getDeclaration(); String genericReturn = v.getReturnType(); String genericExceptions = v.getExceptions(); sb.append(genericReturn); appendSpace(); sb.append(name); sb.append(genericDecl); //TODO will this generic exceptions get doubled with the normal ones below? if (genericExceptions != null) { appendSpace(); sb.append(genericExceptions); } } else { /* null signature */ isConstructor = true; try { if (ByteCodeConstants.INIT.equals(name)) { /* constructor */ BytecodeUtils.appendMethodDescriptor(classSimpleName, true, (access & ACC_STATIC) != 0, descriptor, 0, localVariableTable, constantPool, sb); } else if (ByteCodeConstants.CLINIT.equals(name)) { /* static block */ /* remove the final space, that has been appended by appendAccessFlags() */ sb.setLength(sb.length() - 1); } else { BytecodeUtils.appendMethodDescriptor(name, false, (access & ACC_STATIC) != 0, descriptor, 0, localVariableTable, constantPool, sb); } } catch (IOException e) { throw new RuntimeException(e); } } if (exceptions != null && exceptions.length > 0) { appendSpace(); sb.append(JavaKeywords.THROWS); appendSpace(); for (int i = 0; i < exceptions.length; ++i) { if (i > 0) { appendComma(); appendSpace(); } sb.append(exceptions[i].replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT)); } } if (!hasCode()) { /* no instructions for interface and abstract methods */ appendSemicolon(); appendNewline(); } else { appendSpace(); appendLeftBrace(); appendNewline(); incrementIndent(); } } public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { if (mv != null) { return mv.visitAnnotation(desc, visible); } return null; } public AnnotationVisitor visitAnnotationDefault() { if (mv != null) { return mv.visitAnnotationDefault(); } return null; } public void visitAttribute(final Attribute attr) { if (mv != null) { mv.visitAttribute(attr); } } public void visitCode() { if (mv != null) { mv.visitCode(); } } public boolean visitCode(byte[] bytes) { return visitCode(bytes, 0, bytes.length); } public boolean visitCode(byte[] bytes, int offset, int length) { IndexedInstructionParser parser = new IndexedInstructionParser(bytes, offset, length); instructions = parser.parse(); offsetInstructionMap = parser.offsetInstructionMap; /* load try blocks */ exceptionTable = parser.parseExceptionTable(); HashSet<String> attributeNames = new HashSet<String>(); attributeNames.add(ByteCodeConstants.LINE_NUMBER_TABLE); attributeNames.add(ByteCodeConstants.LOCAL_VARIABLE_TABLE); attributeNames.add(ByteCodeConstants.LOCAL_VARIABLE_TYPE_TABLE); Map<String, Object> attrs = parser.parseAttributes(attributeNames, constantPool); lineNumberTable = (LineNumberTableEntry[]) attrs.get(ByteCodeConstants.LINE_NUMBER_TABLE); LocalVariableTableEntry[] varTableEntries = (LocalVariableTableEntry[]) attrs .get(ByteCodeConstants.LOCAL_VARIABLE_TABLE); LocalVariableTypeTableEntry[] varTypeTable = (LocalVariableTypeTableEntry[]) attrs .get(ByteCodeConstants.LOCAL_VARIABLE_TYPE_TABLE); localVariableTable = createLocalVariableTable(varTableEntries != null); if (varTableEntries != null && varTableEntries.length > 0) { for (LocalVariableTableEntry entry : varTableEntries) { localVariableTable.addLocalVariableTableEntry(entry); } } if (varTypeTable != null && varTypeTable.length > 0) { for (LocalVariableTypeTableEntry entry : varTypeTable) { localVariableTable.addLocalVariableTypeTableEntry(entry); } } renderSignature(); if (exceptionTable != null && exceptionTable.length > 0) { rootTryBlocks = new ArrayList<TryBlock>(); ExceptionTableEntry[] sortedExceptionTable = new ExceptionTableEntry[exceptionTable.length]; System.arraycopy(exceptionTable, 0, sortedExceptionTable, 0, exceptionTable.length); Arrays.sort(sortedExceptionTable, new ExceptionTableEntryComparator()); for (ExceptionTableEntry en : sortedExceptionTable) { TryBlock b = findEnclosingTryBlock(en); TryBlock newTryBlock = null; if (b == null) { /* this is a new root block */ newTryBlock = new TryBlock(en); rootTryBlocks.add(newTryBlock); } else { newTryBlock = b.createEnclosedTryBlock(en); } openedTryBlock = newTryBlock; } TryBlock dummyParentTryBlock = new TryBlock(null, 0, length); for (TryBlock tb : rootTryBlocks) { tb.enclosingTryBlock = dummyParentTryBlock; tb.computeHandlersEnds(offsetInstructionMap); } } for (AbstractInstruction instruction : instructions) { /* starting try blocks */ List<Block> blocks = blockStarts.get(instruction.getOffset()); if (blocks != null) { for (Block block : blocks) { block.appendStart(); } } InstructionRenderer r = null; if (instruction instanceof SimpleInstruction) { r = new InstructionRenderer((SimpleInstruction) instruction); } else if (instruction instanceof AbstractInstruction) { r = new InstructionRenderer((AbstractInstruction) instruction); } else { throw new RuntimeException( "Could not find renderer for '" + instruction.getClass().getName() + "'"); } try { r.render(this); } catch (IOException e) { throw new RuntimeException(e); } if (r instanceof IInstructionLine) { instructionLines.add((IInstructionLine) r); } /* ending try blocks */ blocks = blockEnds.get(instruction.getOffset() + instruction.getLength()); if (blocks != null) { for (Block block : blocks) { block.appendEnd(); } } } if (showLineNumberTable) { renderLineNumberTable(lineNumberTable); } if (showLocalVariableTable) { renderLocalVariableTable(); } if (showExceptionTable) { renderExceptionTable(); } return true; } public void visitEnd() { if (!hasCode()) { /* signature of an abstract or native method * we must call it here as visitCode(...) will not be called for abstract methods */ renderSignature(); } else { /* end of non-abstract method */ decrementIndent(); appendRightBrace(); appendNewline(); } /* changed to 0-based */ lastLine = lineCount - 1; if (mv != null) { mv.visitEnd(); } /* add every method, no matter if it is abstract */ methodSections.add(this); methodBorderLinesList.add(firstLine); methodBorderLinesList.add(lastLine); } public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { if (mv != null) { mv.visitFieldInsn(opcode, owner, name, desc); } } public void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { if (mv != null) { mv.visitFrame(type, nLocal, local, nStack, stack); } } public void visitIincInsn(final int var, final int increment) { if (mv != null) { mv.visitIincInsn(var, increment); } } public void visitInsn(final int opcode) { if (mv != null) { mv.visitInsn(opcode); } } public void visitIntInsn(final int opcode, final int operand) { if (mv != null) { mv.visitIntInsn(opcode, operand); } } public void visitJumpInsn(final int opcode, final Label label) { if (mv != null) { mv.visitJumpInsn(opcode, label); } } public void visitLabel(final Label label) { if (mv != null) { mv.visitLabel(label); } } public void visitLdcInsn(final Object cst) { if (mv != null) { mv.visitLdcInsn(cst); } } public void visitLineNumber(final int line, final Label start) { if (mv != null) { mv.visitLineNumber(line, start); } } public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { if (mv != null) { mv.visitLocalVariable(name, desc, signature, start, end, index); } } public void visitLocalVariableTable(byte[] bytes, int offset, int entryCount) { // LocalVariableTableParser p = new LocalVariableTableParser(bytes, offset, entryCount); // LocalVariableTableEntry[] tbl = p.parse(); // for (LocalVariableTableEntry entry : tbl) { // System.out.println(); // //addLocalVariableTableEntry(entry); // } } // public void addLocalVariableTableEntry(LocalVariableTableEntry entry) { // indexedLocalVariableTable.put(entry.getIndex(), entry); // } public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { if (mv != null) { mv.visitLookupSwitchInsn(dflt, keys, labels); } } public void visitMaxs(final int maxStack, final int maxLocals) { if (mv != null) { mv.visitMaxs(maxStack, maxLocals); } max_stack = maxStack; max_locals = maxLocals; if (showMaxs) { renderMaxs(); } } public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { if (mv != null) { mv.visitMethodInsn(opcode, owner, name, desc); } } public void visitMultiANewArrayInsn(final String desc, final int dims) { if (mv != null) { mv.visitMultiANewArrayInsn(desc, dims); } } public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { if (mv != null) { return mv.visitParameterAnnotation(parameter, desc, visible); } return null; } public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label[] labels) { if (mv != null) { mv.visitTableSwitchInsn(min, max, dflt, labels); } } public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { if (mv != null) { mv.visitTryCatchBlock(start, end, handler, type); } } public void visitTypeInsn(final int opcode, final String type) { if (mv != null) { mv.visitTypeInsn(opcode, type); } } public void visitVarInsn(final int opcode, final int var) { if (mv != null) { mv.visitVarInsn(opcode, var); } } public IInstructionLine findInstructionLine(int sourceCodeLine) { if (lineNumberTable == null || lineNumberTable.length == 0 || sourceCodeLine == ByteCodeConstants.INVALID_OFFSET /* if there are no instructions we will find nothing either */ || instructionLines.size() == 0) { return null; } int offset = lookUpLineNumberTableForStartPC(sourceCodeLine + 1); if (offset != ByteCodeConstants.INVALID_OFFSET) { List<IInstructionLine> lines = getInstructionLines(); for (IInstructionLine line : lines) { if (offset == line.getInstruction().getOffset()) { return line; } } } return null; } } protected class AnnotationRenderer extends AnnotationVisitor { /** * The {@link AnnotationVisitor} to which this visitor delegates calls. May * be <tt>null</tt>. */ protected AnnotationVisitor av; private int valueNumber = 0; /** * Constructs a new {@link AnnotationRenderer}. */ public AnnotationRenderer() { super(com.drgarbage.asm.Opcodes.ASM4); } // ------------------------------------------------------------------------ // Implementation of the AnnotationVisitor interface // ------------------------------------------------------------------------ private void appendComa(final int i) { if (i != 0) { sb.append(", "); } } public void visit(final String name, final Object value) { //sb.setLength(0); appendComa(valueNumber++); if (name != null) { sb.append(name).append('='); } if (value instanceof String) { visitString((String) value); } else if (value instanceof Type) { visitType((Type) value); } else if (value instanceof Byte) { visitByte(((Byte) value).byteValue()); } else if (value instanceof Boolean) { visitBoolean(((Boolean) value).booleanValue()); } else if (value instanceof Short) { visitShort(((Short) value).shortValue()); } else if (value instanceof Character) { visitChar(((Character) value).charValue()); } else if (value instanceof Integer) { visitInt(((Integer) value).intValue()); } else if (value instanceof Float) { visitFloat(((Float) value).floatValue()); } else if (value instanceof Long) { visitLong(((Long) value).longValue()); } else if (value instanceof Double) { visitDouble(((Double) value).doubleValue()); } else if (value.getClass().isArray()) { sb.append('{'); if (value instanceof byte[]) { byte[] v = (byte[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitByte(v[i]); } } else if (value instanceof boolean[]) { boolean[] v = (boolean[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitBoolean(v[i]); } } else if (value instanceof short[]) { short[] v = (short[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitShort(v[i]); } } else if (value instanceof char[]) { char[] v = (char[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitChar(v[i]); } } else if (value instanceof int[]) { int[] v = (int[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitInt(v[i]); } } else if (value instanceof long[]) { long[] v = (long[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitLong(v[i]); } } else if (value instanceof float[]) { float[] v = (float[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitFloat(v[i]); } } else if (value instanceof double[]) { double[] v = (double[]) value; for (int i = 0; i < v.length; i++) { appendComa(i); visitDouble(v[i]); } } sb.append('}'); } //text.add(sb.toString()); if (av != null) { av.visit(name, value); } } //FIXME Annotations rendering public AnnotationVisitor visitAnnotation(final String name, final String desc) { //sb.setLength(0); appendComa(valueNumber++); if (name != null) { sb.append(name).append('='); } sb.append('@'); sb.append(desc); sb.append('('); //text.add(sb.toString()); AnnotationRenderer tav = createTraceAnnotationVisitor(); //sb.append(tav.getText()); sb.append(")"); if (av != null) { tav.av = av.visitAnnotation(name, desc); } return tav; } //FIXME Annotations rendering public AnnotationVisitor visitArray(final String name) { //sb.setLength(0); appendComa(valueNumber++); if (name != null) { sb.append(name).append('='); } sb.append('{'); //text.add(sb.toString()); AnnotationRenderer tav = createTraceAnnotationVisitor(); //sb.append(tav.getText()); sb.append("}"); if (av != null) { tav.av = av.visitArray(name); } return tav; } private void visitBoolean(final boolean value) { sb.append(value); } private void visitByte(final byte value) { sb.append("(byte)").append(value); } private void visitChar(final char value) { sb.append("(char)").append((int) value); } private void visitDouble(final double value) { sb.append(value).append('D'); } public void visitEnd() { if (av != null) { av.visitEnd(); } } public void visitEnum(final String name, final String desc, final String value) { //FIXME annotation rendering appendComa(valueNumber++); if (name != null) { sb.append(name).append('='); } sb.append(desc); sb.append('.').append(value); //text.add(sb.toString()); if (av != null) { av.visitEnum(name, desc, value); } } private void visitFloat(final float value) { sb.append(value).append('F'); } private void visitInt(final int value) { sb.append(value); } private void visitLong(final long value) { sb.append(value).append('L'); } private void visitShort(final short value) { sb.append("(short)").append(value); } private void visitString(final String value) { BytecodeUtils.appendString(sb, value); } // ------------------------------------------------------------------------ // Utility methods // ------------------------------------------------------------------------ private void visitType(final Type value) { sb.append(value.getClassName()).append(".class"); } } protected class FieldRenderer extends FieldVisitor implements IFieldSection { /** * Type signature. */ private String descriptor; protected int documentLine = ByteCodeConstants.INVALID_OFFSET; /** * The {@link FieldVisitor} to which this visitor delegates calls. May be * <tt>null</tt>. */ protected FieldVisitor fv; protected String name; public FieldRenderer(String name, String descriptor, int documentLine) { super(com.drgarbage.asm.Opcodes.ASM4); this.name = name; this.descriptor = descriptor; this.documentLine = documentLine; } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IOutlineElement#getBytecodeDocumentLine() */ public int getBytecodeDocumentLine() { return documentLine; } /* (non-Javadoc) * @see org.eclipse.jdt.core.IField#getTypeSignature() */ public String getDescriptor() { return descriptor; } public String getName() { return name; } public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { AnnotationVisitor av = visitAnnotationImpl(desc, visible); if (fv != null) { ((AnnotationRenderer) av).av = fv.visitAnnotation(desc, visible); } return av; } public void visitAttribute(final Attribute attr) { visitAttributeImpl(attr); if (fv != null) { fv.visitAttribute(attr); } } public void visitEnd() { if (fv != null) { fv.visitEnd(); } } } public static class IndexedInstructionParser extends InstructionParser { private HashMap<Integer, AbstractInstruction> offsetInstructionMap = new HashMap<Integer, AbstractInstruction>(); public IndexedInstructionParser(byte[] code, int offset, int length) { super(code, offset, length); } @Override protected AbstractInstruction parseNextInstruction(boolean wide) throws IOException { AbstractInstruction instruction = super.parseNextInstruction(wide); offsetInstructionMap.put(instruction.getOffset(), instruction); return instruction; } } protected class InstructionRenderer implements IInstructionLine { protected boolean commentOpened = false; protected AbstractInstruction instruction; private int line = INVALID_LINE; private AbstractMethodRenderer methodRenderer; public InstructionRenderer(AbstractInstruction instruction) { this.instruction = instruction; } protected void appendComment() { try { if (instruction instanceof ILocalVariableIndexProvider) { if (methodRenderer != null) { String name = methodRenderer.localVariableTable.findArgName( ((ILocalVariableIndexProvider) instruction).getLocalVariableIndex(), instruction.getOffset(), methodRenderer.isConstructor(), methodRenderer.isAbstract()); if (name != null) { openCommentIfNeeded(); if (instruction instanceof IncrementInstruction) { appendSpace(); append(name); int incr = ((IncrementInstruction) instruction).getIncrementConst(); char sign = incr >= 0 ? JavaLexicalConstants.PLUS : JavaLexicalConstants.MINUS; if (Math.abs(incr) == 1) { append(sign); append(sign); } else { appendSpace(); append(sign); append(JavaLexicalConstants.EQUALS); appendSpace(); append(Math.abs(incr)); } } else { appendSpace(); append(name); } appendSpace(); } } } if (instruction instanceof IConstantPoolIndexProvider) { if (methodRenderer != null && constantPool != null) { AbstractConstantPoolEntry cpInfo = constantPool[((IConstantPoolIndexProvider) instruction) .getConstantPoolIndex()]; String const_ = null; if (cpInfo instanceof ConstantFloatInfo) { const_ = String.valueOf(((ConstantFloatInfo) cpInfo).getFloat()); } else if (cpInfo instanceof ConstantIntegerInfo) { const_ = String.valueOf(((ConstantIntegerInfo) cpInfo).getInt()); } else if (cpInfo instanceof ConstantDoubleInfo) { const_ = String.valueOf(((ConstantDoubleInfo) cpInfo).getDouble()); } else if (cpInfo instanceof ConstantLongInfo) { const_ = String.valueOf(((ConstantLongInfo) cpInfo).getLong()); } else if (cpInfo instanceof ConstantStringInfo) { int i = ((ConstantStringInfo) cpInfo).getStringIndex(); StringBuffer buf = new StringBuffer(); BytecodeUtils.appendString(buf, ((ConstantUtf8Info) constantPool[i]).getString()); const_ = buf.toString(); } else if (cpInfo instanceof ConstantFieldrefInfo) { ConstantFieldrefInfo constantFieldrefInfo = (ConstantFieldrefInfo) cpInfo; if (instruction.getOpcode() == Opcodes.GETSTATIC || instruction.getOpcode() == Opcodes.PUTSTATIC) { ConstantClassInfo constantClassInfo = (ConstantClassInfo) constantPool[constantFieldrefInfo .getClassIndex()]; String name = ((ConstantUtf8Info) constantPool[constantClassInfo.getNameIndex()]) .getString(); const_ = name.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); } else { const_ = ""; } ConstantNameAndTypeInfo constantNameAndTypeInfo = (ConstantNameAndTypeInfo) constantPool[constantFieldrefInfo .getNameAndTypeIndex()]; String fieldName = ((ConstantUtf8Info) constantPool[constantNameAndTypeInfo .getNameIndex()]).getString(); const_ += JavaLexicalConstants.DOT + fieldName; } else if (cpInfo instanceof ConstantMethodrefInfo) { ConstantMethodrefInfo constantMethodrefInfo = (ConstantMethodrefInfo) cpInfo; ConstantNameAndTypeInfo constantNameAndTypeInfo = (ConstantNameAndTypeInfo) constantPool[constantMethodrefInfo .getNameAndTypeIndex()]; String methodName = ((ConstantUtf8Info) constantPool[constantNameAndTypeInfo .getNameIndex()]).getString(); if (ByteCodeConstants.INIT.equals(methodName)) { ConstantClassInfo constantClassInfo = (ConstantClassInfo) constantPool[constantMethodrefInfo .getClassIndex()]; String name = ((ConstantUtf8Info) constantPool[constantClassInfo.getNameIndex()]) .getString(); methodName = name.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); } else if (instruction.getOpcode() == Opcodes.INVOKESTATIC) { ConstantClassInfo constantClassInfo = (ConstantClassInfo) constantPool[constantMethodrefInfo .getClassIndex()]; String name = ((ConstantUtf8Info) constantPool[constantClassInfo.getNameIndex()]) .getString(); methodName = name.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT) + JavaLexicalConstants.DOT + methodName; } StringBuilder sb = new StringBuilder(); boolean isConstructor = instruction.getOpcode() == Opcodes.INVOKESPECIAL; boolean isStatic = false; String descriptor = ((ConstantUtf8Info) constantPool[constantNameAndTypeInfo .getDescriptorIndex()]).getString(); BytecodeUtils.appendMethodDescriptor(methodName, isConstructor, isStatic, descriptor, 0, methodRenderer.localVariableTable, constantPool, sb); //ClassFileDocument.appendMethodDescriptor(methodName, isConstructor, isStatic, constantMethodrefInfo.getNameAndTypeInfo().getDescriptor(), methodRenderer.getIndexedLocalVariableTable(), methodRenderer.methodInfo.getClassFile(), 0, sb); const_ = sb.toString(); } else if (cpInfo instanceof ConstantInterfaceMethodrefInfo) { ConstantInterfaceMethodrefInfo constantInterfaceMethodrefInfo = (ConstantInterfaceMethodrefInfo) cpInfo; ConstantNameAndTypeInfo constantNameAndTypeInfo = (ConstantNameAndTypeInfo) constantPool[constantInterfaceMethodrefInfo .getNameAndTypeIndex()]; String methodName = ((ConstantUtf8Info) constantPool[constantNameAndTypeInfo .getNameIndex()]).getString(); StringBuilder sb = new StringBuilder(); boolean isStatic = false; String descriptor = ((ConstantUtf8Info) constantPool[constantNameAndTypeInfo .getDescriptorIndex()]).getString(); BytecodeUtils.appendMethodDescriptor(methodName, false, isStatic, descriptor, 0, methodRenderer.localVariableTable, constantPool, sb); const_ = sb.toString(); } else if (cpInfo instanceof ConstantClassInfo) { /* the constructor info should be clear from the subsequent invokespecial */ //ConstantClassInfo constantClassInfo = (ConstantClassInfo) cpInfo; //String methodName = constantClassInfo.getName().replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); //const_ = JavaKeywords.NEW + JavaLexicalConstants.SPACE + methodName; ConstantClassInfo constantClassInfo = (ConstantClassInfo) cpInfo; if (instruction.getOpcode() == Opcodes.ANEWARRAY) { String className = BytecodeUtils.resolveConstantPoolTypeName(constantClassInfo, constantPool); // String className = ((ConstantUtf8Info)constantPool[constantClassInfo.getNameIndex()]).getString(); className = className.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); StringBuilder sb = new StringBuilder(); sb.append(JavaKeywords.NEW); sb.append(JavaLexicalConstants.SPACE); sb.append(className); sb.append(JavaLexicalConstants.LEFT_SQUARE_BRACKET); sb.append(JavaLexicalConstants.RIGHT_SQUARE_BRACKET); const_ = sb.toString(); } else if (instruction.getOpcode() == Opcodes.MULTIANEWARRAY) { String className = ((ConstantUtf8Info) constantPool[constantClassInfo .getNameIndex()]).getString(); StringBuilder sb = new StringBuilder(); sb.append(JavaKeywords.NEW); sb.append(JavaLexicalConstants.SPACE); BytecodeUtils.appendFieldDescriptor(className, 0, sb); const_ = sb.toString(); } else if (instruction.getOpcode() == Opcodes.CHECKCAST) { String className = ((ConstantUtf8Info) constantPool[constantClassInfo .getNameIndex()]).getString(); const_ = className.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); } else if (instruction.getOpcode() == Opcodes.INSTANCEOF) { String className = ((ConstantUtf8Info) constantPool[constantClassInfo .getNameIndex()]).getString(); if (className.charAt(0) == ByteCodeConstants.ARRAY_BEGINNING_BRACKET) { StringBuilder sbb = new StringBuilder(); BytecodeUtils.appendArrayDescriptor(className, 0, sbb); className = sbb.toString(); } else { className = className.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT); } const_ = JavaKeywords.INSTANCEOF + JavaLexicalConstants.SPACE + className; } } else if (cpInfo instanceof ConstantMethodHandleInfo) { // ConstantMethodHandleInfo constantMethodHandleInfo = (ConstantMethodHandleInfo) cpInfo; // int index = constantMethodHandleInfo.getReferenceIndex(); } else if (cpInfo instanceof ConstantMethodTypeInfo) { ConstantMethodTypeInfo constantMethodTypeInfo = (ConstantMethodTypeInfo) cpInfo; String descriptor = ((ConstantUtf8Info) constantPool[constantMethodTypeInfo .getDescriptorIndex()]).getString(); const_ = descriptor; } else if (cpInfo instanceof ConstantInvokeDynamicInfo) { ConstantInvokeDynamicInfo constantInvokeDynamicInfo = (ConstantInvokeDynamicInfo) cpInfo; ConstantNameAndTypeInfo nameAndTypeInfo = (ConstantNameAndTypeInfo) constantPool[constantInvokeDynamicInfo .getNameAndTypeIndex()]; String nameAndType_name = ((ConstantUtf8Info) constantPool[nameAndTypeInfo .getNameIndex()]).getString(); String nameAndType_descr = ((ConstantUtf8Info) constantPool[nameAndTypeInfo .getDescriptorIndex()]).getString(); StringBuilder sb = new StringBuilder(); boolean isConstructor = false; boolean isStatic = false; BytecodeUtils.appendMethodDescriptor(nameAndType_name, isConstructor, isStatic, nameAndType_descr, 0, methodRenderer.localVariableTable, constantPool, sb); const_ = sb.toString(); } else { throw new RuntimeException("invalid type " + cpInfo.getClass()); } if (const_ != null) { openCommentIfNeeded(); appendSpace(); append(const_); appendSpace(); } } } //FIX: bug #5 // if (instruction instanceof BranchInstruction) { // if (methodRenderer != null) { // BranchInstruction bi = (BranchInstruction) instruction; // openCommentIfNeeded(); // // appendSpace(); // append(bi.getOffset()); // appendSpace(); // append(JavaLexicalConstants.PLUS); // appendSpace(); // append(bi.getBranchOffset()); // appendSpace(); // append(JavaLexicalConstants.EQUALS); // appendSpace(); // append(bi.getBranchOffset() + bi.getOffset()); // appendSpace(); // } // } if (instruction instanceof NewArrayInstruction) { if (methodRenderer != null) { NewArrayInstruction newArrayInstruction = (NewArrayInstruction) instruction; openCommentIfNeeded(); appendSpace(); append(JavaKeywords.NEW); appendSpace(); append(NewArrayInstruction.resolveType(newArrayInstruction.getImmediateByte())); append(JavaLexicalConstants.LEFT_SQUARE_BRACKET); append(JavaLexicalConstants.RIGHT_SQUARE_BRACKET); appendSpace(); } } closeCommentIfNeeded(); } catch (IOException e) { throw new RuntimeException(e); } } private void appendInt(int i) { appendSpace(); append(String.valueOf(i)); } protected void appendOperands() { if (instruction instanceof MultianewarrayInstruction) { appendInt(((MultianewarrayInstruction) instruction).getImmediateShort()); appendInt(((MultianewarrayInstruction) instruction).getDimensions()); } else if (instruction instanceof IncrementInstruction) { appendInt(((IncrementInstruction) instruction).getImmediateByte()); appendInt(((IncrementInstruction) instruction).getIncrementConst()); } else if (instruction instanceof InvokeInterfaceInstruction) { appendInt(((InvokeInterfaceInstruction) instruction).getImmediateShort()); appendInt(((InvokeInterfaceInstruction) instruction).getCount()); } else if (instruction instanceof ImmediateByteInstruction) { appendInt(((ImmediateByteInstruction) instruction).getImmediateByte()); } else if (instruction instanceof ImmediateShortInstruction) { appendInt(((ImmediateShortInstruction) instruction).getImmediateShort()); } else if (instruction instanceof ImmediateIntInstruction) { appendInt(((ImmediateIntInstruction) instruction).getImmediateInt()); } else if (instruction instanceof BranchInstruction) { BranchInstruction bi = (BranchInstruction) instruction; int val = bi.getBranchOffset(); if (!showRelativeBranchTargetOffsets) { /* compute absolute offset */ val += bi.getOffset(); } appendInt(val); } else if (instruction instanceof TableSwitchInstruction) { TableSwitchInstruction tsi = (TableSwitchInstruction) instruction; appendInt(tsi.getDefaultOffset()); appendInt(tsi.getLow()); appendInt(tsi.getHigh()); appendSpace(); append(JavaLexicalConstants.LEFT_SQUARE_BRACKET); int[] offs = tsi.getJumpOffsets(); for (int i = 0; i < offs.length; i++) { if (i != 0) { appendComma(); appendSpace(); } int val = offs[i]; if (!showRelativeBranchTargetOffsets) { /* compute absolute offset */ val += tsi.getOffset(); } append(String.valueOf(val)); } append(JavaLexicalConstants.RIGHT_SQUARE_BRACKET); } else if (instruction instanceof LookupSwitchInstruction) { LookupSwitchInstruction lsi = (LookupSwitchInstruction) instruction; appendInt(lsi.getDefaultOffset()); List<MatchOffsetEntry> ens = lsi.getMatchOffsetPairs(); appendInt(ens.size()); appendSpace(); append(JavaLexicalConstants.LEFT_SQUARE_BRACKET); for (int i = 0; i < ens.size(); i++) { if (i != 0) { appendComma(); appendSpace(); } MatchOffsetEntry en = ens.get(i); append(en.getMatch()); append(" => "); int val = en.getOffset(); if (!showRelativeBranchTargetOffsets) { /* compute absolute offset */ val += lsi.getOffset(); } append(String.valueOf(val)); } append(JavaLexicalConstants.RIGHT_SQUARE_BRACKET); } } protected void closeCommentIfNeeded() { if (commentOpened) { appendCommentEnd(); commentOpened = false; } } public AbstractInstruction getInstruction() { return instruction; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IInstructionLine#getLine() */ public int getLine() { return line; } protected void openCommentIfNeeded() { if (!commentOpened) { appendPaddedCommentBegin(); commentOpened = true; } } public void render(AbstractMethodRenderer methodRenderer) throws IOException { this.methodRenderer = methodRenderer; if (showSourceLineNumbers && methodRenderer.lineNumberTable != null) { int currentStartPc = ByteCodeConstants.INVALID_OFFSET; if (methodRenderer.currentLineNumberTableIndex >= 0 && methodRenderer.currentLineNumberTableIndex < methodRenderer.lineNumberTable.length) { currentStartPc = methodRenderer.lineNumberTable[methodRenderer.currentLineNumberTableIndex] .getStartPc(); } if (currentStartPc == instruction.getOffset()) { /* yes, this is the begining of a line */ appendCommentBegin(); appendSpace(); append(ByteCodeConstants.L_LINE); append(methodRenderer.lineNumberTable[methodRenderer.currentLineNumberTableIndex] .getLineNumber()); appendSpace(); appendCommentEnd(); appendNewline(); methodRenderer.currentLineNumberTableIndex++; } } line = AbstractClassFileDocument.this.lineCount; /* leading spaces to padd the offsets * commented out as this padding does not * look nice in combination with try and * catch blocks for (int i = String.valueOf(instruction.getOffset()).length(); i < methodRenderer.getLengthsOrderMagnitude(); i++) { appendSpace(); } */ append(String.valueOf(instruction.getOffset())); appendSpace(); append(ByteCodeConstants.OPCODE_MNEMONICS[instruction.getOpcode()]); appendOperands(); appendSemicolon(); appendComment(); appendNewline(); this.methodRenderer = null; } } protected class SignatureRenderer extends SignatureVisitor { /** * Stack used to keep track of class types that have arguments. Each element * of this stack is a boolean encoded in one bit. The top of the stack is * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping = * /2. */ private int argumentStack; /** * Stack used to keep track of array class types. Each element of this stack * is a boolean encoded in one bit. The top of the stack is the lowest order * bit. Pushing false = *2, pushing true = *2+1, popping = /2. */ private int arrayStack; private final StringBuffer declaration; private StringBuffer exceptions; private boolean isInterface; private StringBuffer returnType; private boolean seenFormalParameter; private boolean seenInterface; private boolean seenInterfaceBound; private boolean seenParameter; private String separator = ""; public SignatureRenderer(final int access) { super(com.drgarbage.asm.Opcodes.ASM4); isInterface = (access & ACC_INTERFACE) != 0; this.declaration = new StringBuffer(); } private SignatureRenderer(final StringBuffer buf) { super(com.drgarbage.asm.Opcodes.ASM4); this.declaration = buf; } private void endFormals() { if (seenFormalParameter) { declaration.append(JavaLexicalConstants.GT); seenFormalParameter = false; } } private void endType() { if (arrayStack % 2 == 0) { arrayStack /= 2; } else { while (arrayStack % 2 != 0) { arrayStack /= 2; declaration.append(JavaLexicalConstants.LEFT_SQUARE_BRACKET) .append(JavaLexicalConstants.RIGHT_SQUARE_BRACKET); } } } public String getDeclaration() { return declaration.toString(); } public String getExceptions() { return exceptions == null ? null : exceptions.toString(); } public String getReturnType() { return returnType == null ? null : returnType.toString(); } private void startType() { arrayStack *= 2; } public SignatureVisitor visitArrayType() { startType(); arrayStack |= 1; return this; } public void visitBaseType(final char descriptor) { switch (descriptor) { case ByteCodeConstants.V_VOID: declaration.append(JavaKeywords.VOID); break; case ByteCodeConstants.B_BYTE: declaration.append(JavaKeywords.BYTE); break; case ByteCodeConstants.J_LONG: declaration.append(JavaKeywords.LONG); break; case ByteCodeConstants.Z_BOOLEAN: declaration.append(JavaKeywords.BOOLEAN); break; case ByteCodeConstants.I_INT: declaration.append(JavaKeywords.INT); break; case ByteCodeConstants.S_SHORT: declaration.append(JavaKeywords.SHORT); break; case ByteCodeConstants.C_CHAR: declaration.append(JavaKeywords.CHAR); break; case ByteCodeConstants.F_FLOAT: declaration.append(JavaKeywords.FLOAT); break; case ByteCodeConstants.D_DOUBLE: declaration.append(JavaKeywords.DOUBLE); break; default: throw new IllegalArgumentException( "Unexpected base type character '" + descriptor + "' in descriptor."); } endType(); } public SignatureVisitor visitClassBound() { separator = _EXTENDS_; startType(); return this; } public void visitClassType(final String name) { if (ByteCodeConstants.JAVA_LANG_OBJECT.equals(name)) { // Map<java.lang.Object,java.util.List> // or // abstract public V get(Object key); (seen in Dictionary.class) // should have Object // but java.lang.String extends java.lang.Object is unnecessary boolean needObjectClass = argumentStack % 2 != 0 || seenParameter; if (needObjectClass) { declaration.append(separator) .append(name.replace(JavaLexicalConstants.SLASH, JavaLexicalConstants.DOT)); } } else { declaration.append(separator) .append(name.replace(JavaLexicalConstants.SLASH, JavaLexicalConstants.DOT)); } separator = ""; argumentStack *= 2; } public void visitEnd() { if (argumentStack % 2 != 0) { declaration.append(JavaLexicalConstants.GT); } argumentStack /= 2; endType(); } public SignatureVisitor visitExceptionType() { if (exceptions == null) { exceptions = new StringBuffer(); } else { exceptions.append(COMMA_); } // startType(); return new SignatureRenderer(exceptions); } public void visitFormalTypeParameter(final String name) { if (seenFormalParameter) { declaration.append(JavaLexicalConstants.COMMA).append(JavaLexicalConstants.SPACE); } else { declaration.append(JavaLexicalConstants.LT); seenFormalParameter = true; } declaration.append(name); seenInterfaceBound = false; } public void visitInnerClassType(final String name) { if (argumentStack % 2 != 0) { declaration.append(JavaLexicalConstants.GT); } argumentStack /= 2; declaration.append(JavaLexicalConstants.DOT); declaration.append(separator) .append(name.replace(JavaLexicalConstants.SLASH, JavaLexicalConstants.DOT)); separator = ""; argumentStack *= 2; } public SignatureVisitor visitInterface() { separator = seenInterface ? COMMA_ : isInterface ? _EXTENDS_ : _IMPLEMENTS_; seenInterface = true; startType(); return this; } public SignatureVisitor visitInterfaceBound() { if (seenInterfaceBound) { separator = COMMA_; } else { separator = _EXTENDS_; } startType(); return this; } public SignatureVisitor visitParameterType() { endFormals(); if (seenParameter) { declaration.append(COMMA_); } else { seenParameter = true; declaration.append(JavaLexicalConstants.LEFT_PARENTHESIS); } startType(); return this; } public SignatureVisitor visitReturnType() { endFormals(); if (seenParameter) { seenParameter = false; } else { declaration.append(JavaLexicalConstants.LEFT_PARENTHESIS); } declaration.append(JavaLexicalConstants.RIGHT_PARENTHESIS); returnType = new StringBuffer(); return new SignatureRenderer(returnType); } public SignatureVisitor visitSuperclass() { endFormals(); separator = _EXTENDS_; startType(); return this; } // ----------------------------------------------- public void visitTypeArgument() { if (argumentStack % 2 == 0) { ++argumentStack; declaration.append(JavaLexicalConstants.LT); } else { declaration.append(JavaLexicalConstants.COMMA).append(JavaLexicalConstants.SPACE); } declaration.append(JavaLexicalConstants.QUESTION_MARK); } public SignatureVisitor visitTypeArgument(final char tag) { if (argumentStack % 2 == 0) { ++argumentStack; declaration.append(JavaLexicalConstants.LT); } else { declaration.append(JavaLexicalConstants.COMMA).append(JavaLexicalConstants.SPACE); } if (tag == EXTENDS) { declaration.append(JavaLexicalConstants.QUESTION_MARK).append(_EXTENDS_); } else if (tag == SUPER) { declaration.append(JavaLexicalConstants.QUESTION_MARK).append(JavaLexicalConstants.SPACE) .append(JavaKeywords.SUPER).append(JavaLexicalConstants.SPACE); } startType(); return this; } public void visitTypeVariable(final String name) { declaration.append(name); endType(); } } protected class TextTable { private ByteCodeConstants.Align[] columnAlignments; private int[] columnWidths; private List<String[]> rows; public TextTable(Align[] columnAlignments) { super(); this.columnAlignments = columnAlignments; } public void addRow(String[] values) { if (rows == null) { rows = new ArrayList<String[]>(4); } rows.add(values); } public void appendLine() { beginRow(); for (int i = 0; i < columnWidths.length; i++) { if (i > 0) { sb.append(JavaLexicalConstants.PLUS).append(JavaLexicalConstants.MINUS); } else { /* first column */ sb.append(JavaLexicalConstants.MINUS); } for (int j = 0; j < columnWidths[i]; j++) { sb.append(JavaLexicalConstants.MINUS); } sb.append(JavaLexicalConstants.MINUS); } endRow(); } public void appendRow(String[] values) { beginRow(); for (int i = 0; i < values.length; i++) { if (i > 0) { sb.append(JavaLexicalConstants.PIPE).append(JavaLexicalConstants.SPACE); } else { /* first column */ sb.append(JavaLexicalConstants.SPACE); } appendValue(values[i], columnAlignments[i], columnWidths[i]); sb.append(JavaLexicalConstants.SPACE); } endRow(); } public void appendValue(String value, ByteCodeConstants.Align align, int width) { if (value == null) { value = ""; } int diff = width - value.length(); if (diff < 0) { diff = 0; } switch (align) { case LEFT: sb.append(value); for (int i = 0; i < diff; i++) { sb.append(JavaLexicalConstants.SPACE); } break; case CENTER: int halfDiff = diff / 2; for (int i = 0; i < halfDiff; i++) { sb.append(JavaLexicalConstants.SPACE); } sb.append(value); if (diff % 2 != 0) { /* diff was not even */ sb.append(JavaLexicalConstants.SPACE); } for (int i = 0; i < halfDiff; i++) { sb.append(JavaLexicalConstants.SPACE); } break; case RIGHT: for (int i = 0; i < diff; i++) { sb.append(JavaLexicalConstants.SPACE); } sb.append(value); break; default: throw new IllegalStateException("Unexpected alignment type '" + align + "'"); } } public void beginRow() { appendCommentBegin(); sb.append(JavaLexicalConstants.SPACE); } public int computeWidth() { int result = 0; for (int i = 0; i < columnWidths.length; i++) { if (i > 0) { result += 2; } else { /* first column */ result++; } result += columnWidths[i]; result++; } return result; } public void endRow() { sb.append(JavaLexicalConstants.SPACE); appendCommentEnd(); appendNewline(); } protected void recomputeColumnWidths() { this.columnWidths = new int[columnAlignments.length]; /* compute column widths */ for (int col = 0; col < columnAlignments.length; col++) { int w = 0; if (rows != null) { for (String[] row : rows) { String val = row[col]; if (val != null && val.length() > w) { w = val.length(); } } } columnWidths[col] = w; } } public void render() { if (rows != null) { if (this.columnWidths == null) { recomputeColumnWidths(); } int row = 0; appendLine(); appendRow(rows.get(row)); appendLine(); for (int i = 1; i < rows.size(); i++) { appendRow(rows.get(i)); } appendLine(); } } } public static final String _EXTENDS_ = new StringBuilder().append(JavaLexicalConstants.SPACE) .append(JavaKeywords.EXTENDS).append(JavaLexicalConstants.SPACE).toString(); public static final String _IMPLEMENTS_ = new StringBuilder().append(JavaLexicalConstants.SPACE) .append(JavaKeywords.IMPLEMENTS).append(JavaLexicalConstants.SPACE).toString(); protected static final String COMMA_ = new StringBuilder().append(JavaLexicalConstants.COMMA) .append(JavaLexicalConstants.SPACE).toString(); public static final Attribute[] DEFAULT_ATTRIBUTES = new Attribute[0]; public static final int DEFAULT_COMMENT_OFFSET = 24; public static final String L_JAVA_LANG_DEPRECATED_SEMICOLON = new StringBuilder() .append(ByteCodeConstants.L_REFERENCE).append(ByteCodeConstants.JAVA_LANG_DEPRECATED) .append(JavaLexicalConstants.SEMICOLON).toString(); protected int classSignatureDocumentLine; protected String classSimpleName; protected int commentOffset = DEFAULT_COMMENT_OFFSET; protected AbstractConstantPoolEntry[] constantPool; protected ArrayList<IFieldSection> fieldSections = new ArrayList<IFieldSection>(); protected MessageFormat formatCoversBytesXToY = new MessageFormat(ByteCodeConstants.COVERS_BYTES_X_TO_Y); protected ArrayList<String> headerLines; protected int indent = 0; protected String indentationString = " "; protected int lineCount = 0; protected int[] methodBorderLines; protected ArrayList<Integer> methodBorderLinesList = new ArrayList<Integer>(); protected ArrayList<IMethodSection> methodSections = new ArrayList<IMethodSection>(); protected String name; protected boolean renderTryCatchFinallyBlocks = false; protected StringBuffer sb = new StringBuffer(); protected boolean showConstantPool = false; protected boolean showLineNumberTable = false; protected boolean showLocalVariableTable = false; protected boolean showExceptionTable = false; protected boolean showRelativeBranchTargetOffsets = true; protected boolean showSourceLineNumbers = false; protected boolean showMaxs = false; public AbstractClassFileDocument() { super(com.drgarbage.asm.Opcodes.ASM4); /* initialize preferences */ if (BytecodeVisualizerPlugin.getDefault() != null) { /* jUnit tests work only if we test for null here */ IPreferenceStore store = BytecodeVisualizerPlugin.getDefault().getPreferenceStore(); showConstantPool = store.getBoolean(CLASS_FILE_ATTR_SHOW_CONSTANT_POOL); showLineNumberTable = store.getBoolean(CLASS_FILE_ATTR_SHOW_LINE_NUMBER_TABLE); showSourceLineNumbers = store.getBoolean(CLASS_FILE_ATTR_SHOW_SOURCE_LINE_NUMBERS); showLocalVariableTable = store.getBoolean(CLASS_FILE_ATTR_SHOW_VARIABLE_TABLE); showExceptionTable = store.getBoolean(CLASS_FILE_ATTR_SHOW_EXCEPTION_TABLE); showMaxs = store.getBoolean(CLASS_FILE_ATTR_SHOW_MAXS); renderTryCatchFinallyBlocks = store.getBoolean(CLASS_FILE_ATTR_RENDER_TRYCATCH_BLOCKS); if (BRANCH_TARGET_ADDRESS_ABSOLUTE.equals(store.getString(BRANCH_TARGET_ADDRESS_RENDERING))) { showRelativeBranchTargetOffsets = false; } } } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IClassFileDocument#addFieldSection(com.drgarbage.asm.render.intf.IFieldSection) */ public void addFieldSection(IFieldSection f) { fieldSections.add(f); } /* (non-Javadoc) * @see java.lang.Appendable#append(char) */ public Appendable append(char c) { if (c == '\n') { appendNewline(); } else { appendIndentationIfNeeded(); sb.append(c); } return null; } /* (non-Javadoc) * @see java.lang.Appendable#append(java.lang.CharSequence) */ public Appendable append(CharSequence str) { for (int i = 0; i < str.length(); i++) { append(str.charAt(i)); } return null; } /* (non-Javadoc) * @see java.lang.Appendable#append(java.lang.CharSequence, int, int) */ public Appendable append(CharSequence str, int start, int end) throws IOException { for (int i = start; i < end; i++) { append(str.charAt(i)); } return null; } /** * Append an indentation string if needed an the * <code>String.valueOf(obj)</code>. * * @param obj */ public void append(Object obj) { appendIndentationIfNeeded(); String s = String.valueOf(obj); append(s); } /** * Appends a string representation of the given access modifiers to {@link * #buf buf}. * * @param access some access modifiers. */ protected void appendAccess(final int access) { appendIndentationIfNeeded(); if ((access & ACC_PUBLIC) != 0) { append(JavaKeywords.PUBLIC); appendSpace(); } if ((access & ACC_PRIVATE) != 0) { append(JavaKeywords.PRIVATE); appendSpace(); } if ((access & ACC_PROTECTED) != 0) { append(JavaKeywords.PROTECTED); appendSpace(); } if ((access & ACC_FINAL) != 0) { append(JavaKeywords.FINAL); appendSpace(); } if ((access & ACC_STATIC) != 0) { append(JavaKeywords.STATIC); appendSpace(); } if ((access & ACC_SYNCHRONIZED) != 0) { append(JavaKeywords.SYNCHRONIZED); appendSpace(); } if ((access & ACC_VOLATILE) != 0) { append(JavaKeywords.VOLATILE); appendSpace(); } if ((access & ACC_TRANSIENT) != 0) { append(JavaKeywords.TRANSIENT); appendSpace(); } if ((access & ACC_ABSTRACT) != 0) { append(JavaKeywords.ABSTRACT); appendSpace(); } if ((access & ACC_STRICT) != 0) { append(JavaKeywords.STRICTFP); appendSpace(); } if ((access & ACC_ENUM) != 0) { append(JavaKeywords.ENUM); appendSpace(); } } /** * Appends a {@link JavaLexicalConstants#AT} and <code>annotation</code>. * * @param annotation */ public void appendAnnotation(String annotation) { appendIndentationIfNeeded(); append(JavaLexicalConstants.AT); append(annotation); } protected void appendClassFileFormatVersion(int version) { int major = BytecodeUtils.getMajor(version); int minor = BytecodeUtils.getMinor(version); appendClassFileFormatVersion(major, minor); } protected void appendClassFileFormatVersion(int major, int minor) { appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.CLASS_FILE_FORMAT_VERSION); appendSpace(); if (major == ByteCodeConstants.INVALID_OFFSET || minor == ByteCodeConstants.INVALID_OFFSET) { sb.append(ByteCodeConstants.UNKNOWN_INFORMATION); } else { sb.append(major); appendDot(); sb.append(minor); appendSpace(); appendLeftParenthesis(); sb.append(ByteCodeConstants.JAVA); appendSpace(); sb.append(BytecodeUtils.getLowestJavaPlatformVersion(major, minor)); appendRightParenthesis(); appendSpace(); appendCommentEnd(); appendNewline(); } } /** * Append an indentation string if needed * and {@link JavaLexicalConstants#COLON}. * */ public void appendColon() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.COLON); } /** * Append an indentation string if needed * and {@link JavaLexicalConstants#COMMA}. * */ public void appendComma() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.COMMA); } /** * Append an indentation string if needed * and {@link JavaLexicalConstants#COMMENT_BEGIN}. * */ public void appendCommentBegin() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.COMMENT_BEGIN); } /** * Append an indentation string if needed * and {@link JavaLexicalConstants#COMMENT_END}. * */ public void appendCommentEnd() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.COMMENT_END); } protected void appendConstantPool() { if (showConstantPool) { if (constantPool == null) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.CONSTANT_POOL_NOT_AVAILABLE); appendSpace(); appendCommentEnd(); appendNewline(); } else if (constantPool.length == 0) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.CONSTANT_POOL_EMPTY); appendSpace(); appendCommentEnd(); appendNewline(); } else { /* available and not empty */ appendNewline(); TextTable tbl = new TextTable(new ByteCodeConstants.Align[] { ByteCodeConstants.Align.RIGHT, ByteCodeConstants.Align.LEFT, ByteCodeConstants.Align.LEFT }); tbl.addRow(new String[] { ByteCodeConstants.INDEX, ByteCodeConstants.TAG, ByteCodeConstants.INFO }); for (int i = 0; i < constantPool.length; i++) { AbstractConstantPoolEntry en = constantPool[i]; if (en != null) { tbl.addRow(new String[] { String.valueOf(i), en.getTagMnemonics(), en.getInfo() }); } } tbl.recomputeColumnWidths(); tbl.beginRow(); tbl.appendValue(ByteCodeConstants.CONSTANT_POOL, ByteCodeConstants.Align.CENTER, tbl.computeWidth()); tbl.endRow(); tbl.render(); } } } protected void appendDebugInfoComment(String debugInfo) { appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.DEBUG_INFO); appendSpace(); sb.append(debugInfo); appendSpace(); appendCommentEnd(); appendNewline(); } protected void appendDeprecated(int access) { if ((access & ACC_DEPRECATED) != 0) { appendAnnotation(JavaAnnotations.DEPRECATED); appendNewline(); } } /** * Append an indentation string if needed * and {@link JavaLexicalConstants#DOT}. * */ public void appendDot() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.DOT); } /** * Append an indentation string if needed * and {@link JavaLexicalConstants#GT}. * */ public void appendGt() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.GT); } protected void appendHeaderComment(String classLoadedFrom, String debugTargetName) { Bundle bundle = BytecodeVisualizerPlugin.getDefault().getBundle(); bundle = Platform.getBundle(CoreConstants.BYTECODE_VISUALIZER_PLUGIN_ID); if (bundle == null) { /* this should not happen */ throw new RuntimeException("" + CoreConstants.BYTECODE_VISUALIZER_PLUGIN_ID + " not installed."); } String providerWww = Platform.getResourceString(bundle, "%" + CoreConstants.providerWww); String provider = Platform.getResourceString(bundle, "%" + CoreConstants.providerNameLabel); String pluginName = Platform.getResourceString(bundle, "%" + CoreConstants.pluginName); String msg = MessageFormat.format(ByteCodeConstants.Generated_by_x, new Object[] { provider + " " + pluginName }); appendHeaderLine(msg); appendHeaderLine(providerWww); if (BytecodeVisualizerPlugin.getDefault() != null) { /* jUnit tests work only if we test for null here */ appendHeaderLine(ByteCodeConstants.Version, BytecodeVisualizerPlugin.PLUGIN_VERSION); } if (classLoadedFrom != null) { appendHeaderLine(ByteCodeConstants.Class_retrieved_from, classLoadedFrom); } if (debugTargetName != null) { appendHeaderLine(ByteCodeConstants.Debug_target, debugTargetName); } SimpleDateFormat sdf = new SimpleDateFormat(CoreConstants.ISO_DATE_TIME_FORMAT_FULL); appendHeaderLine(ByteCodeConstants.Retrieved_on, sdf.format(new Date())); flushHeader(); } protected void appendHeaderLine(String line) { if (headerLines == null) { headerLines = new ArrayList<String>(8); } headerLines.add(line); } protected void appendHeaderLine(String key, String value) { appendHeaderLine(key + JavaLexicalConstants.COLON + JavaLexicalConstants.SPACE + value); } /** * Appends the indentation string {@link #indent}-times. */ protected void appendIndentation() { for (int i = 0; i < indent; i++) { sb.append(indentationString); } } /** * Appends the indentation string {@link #indent}-times * if we are just at the beginning of a line. */ protected void appendIndentationIfNeeded() { if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\n') { appendIndentation(); } } public void appendJavaSourcePath(String byteCodePath) { appendIndentationIfNeeded(); sb.append(byteCodePath.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT)); } public void appendLeftBrace() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.LEFT_BRACE); } public void appendLeftParenthesis() { sb.append(JavaLexicalConstants.LEFT_PARENTHESIS); } /** * Append an indentation string if needed * and {@link JavaLexicalConstants#LT}. * */ public void appendLt() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.LT); } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IClassFileDocument#appendNewline() */ public void appendNewline() { sb.append(JavaLexicalConstants.NEWLINE); lineCount++; } protected void appendPackage(String thisClassName) { String packageName = getPackageName(thisClassName); if (packageName != null) { append(JavaKeywords.PACKAGE); appendSpace(); append(packageName); appendSemicolon(); appendNewline(); } } public void appendPaddedCommentBegin() { /* count the chars fom the endOffset to * the last \n or beginning of the document */ int i = sb.length() - 1; int lastNonWsIndex = i; for (; i >= 0 && sb.charAt(i) != '\n'; i--) { if (!Character.isWhitespace(sb.charAt(i))) { lastNonWsIndex = i; } } if (sb.charAt(i) == '\n') { i++; } int targetLenghth = lastNonWsIndex + getCommentOffset() + 1; if (targetLenghth <= sb.length()) { targetLenghth = sb.length() + 1; } for (int j = sb.length() - 1; j < targetLenghth; j++) { sb.append(JavaLexicalConstants.SPACE); } appendCommentBegin(); } public void appendRightBrace() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.RIGHT_BRACE); } public void appendRightParenthesis() { sb.append(JavaLexicalConstants.RIGHT_PARENTHESIS); } public void appendSemicolon() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.SEMICOLON); } protected void appendSourcePathComment(String path) { appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.COMPILED_FROM); appendSpace(); sb.append(path); appendSpace(); appendCommentEnd(); appendNewline(); } public void appendSpace() { appendIndentationIfNeeded(); sb.append(JavaLexicalConstants.SPACE); } protected AnnotationRenderer createTraceAnnotationVisitor() { return new AnnotationRenderer(); } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IClassFileDocument#decrementIndent() */ public void decrementIndent() { indent--; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { return sb.equals(obj); } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IClassFileDocument#findField(int) */ public IFieldSection findFieldSection(int line) { for (IFieldSection f : getFieldSections()) { if (f.getBytecodeDocumentLine() == line) { return f; } } return null; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IClassFileDocument#findFieldSection(java.lang.String) */ public IFieldSection findFieldSection(String fieldName) { for (IFieldSection f : getFieldSections()) { if (f.getName().equals(fieldName)) { return f; } } return null; } public int findIndentationAt(int index) { int result = 0; int i = index; while ((sb.charAt(i)) != '\n') { i--; } while (i + indentationString.length() < index && matchPosition(i, indentationString)) { result++; i += indentationString.length(); } return result; } public IInstructionLine findInstructionLine(int sourceCodeLine) { List<IMethodSection> methodSections = getMethodSections(); if (methodSections != null) { for (IMethodSection methodSection : methodSections) { IInstructionLine result = methodSection.findInstructionLine(sourceCodeLine); if (result != null) { return result; } } } return null; } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IClassFileDocument#findMethod(int) */ public IMethodSection findMethodSection(int line) { for (IMethodSection ms : methodSections) { if (ms.getFirstLine() != ByteCodeConstants.INVALID_OFFSET && ms.getFirstLine() <= line && ms.getLastLine() != ByteCodeConstants.INVALID_OFFSET && ms.getLastLine() >= line) { return ms; } } return null; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IClassFileDocument#findMethodSection(java.lang.String, java.lang.String) */ public IMethodSection findMethodSection(String methodName, String methodSignature) { for (IMethodSection m : getMethodSections()) { if (m.getDescriptor().equals(methodSignature) && m.getName().equals(methodName)) { return m; } } return null; } private void flushHeader() { if (headerLines != null && headerLines.size() > 0) { int maxLength = 0; for (String line : headerLines) { if (line.length() > maxLength) { maxLength = line.length(); } } appendCommentBegin(); appendSpace(); for (int i = 0; i < maxLength; i++) { sb.append(JavaLexicalConstants.ASTERISK); } appendSpace(); appendCommentEnd(); appendNewline(); for (String line : headerLines) { appendCommentBegin(); appendSpace(); sb.append(line); for (int i = 0; i < maxLength - line.length(); i++) { appendSpace(); } appendSpace(); appendCommentEnd(); appendNewline(); } appendCommentBegin(); appendSpace(); for (int i = 0; i < maxLength; i++) { sb.append(JavaLexicalConstants.ASTERISK); } appendSpace(); appendCommentEnd(); appendNewline(); } /* free the list */ headerLines = null; } public String getClassName() { return name; } public int getClassSignatureDocumentLine() { return classSignatureDocumentLine; } public String getClassSimpleName() { return classSimpleName; } public int getCommentOffset() { return commentOffset; } public AbstractConstantPoolEntry[] getConstantPool() { return constantPool; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IClassFileDocument#getFieldSections() */ public List<IFieldSection> getFieldSections() { return fieldSections; } public int getIndent() { return indent; } public String getIndentationString() { return indentationString; } public int getLineCount() { return lineCount; } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IClassFileDocument#getMethodSections() */ public List<IMethodSection> getMethodSections() { return methodSections; } protected String getPackageName(String thisClassName) { return JavaSourceUtils.getPackage(name); } public int hashCode() { return sb.hashCode(); } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IClassFileDocument#incrementIndent() */ public void incrementIndent() { indent++; } /* (non-Javadoc) * @see com.drgarbage.asm.render.intf.IClassFileDocument#isFieldSelected(int) */ public boolean isLineInField(int line) { for (IFieldSection fs : fieldSections) { if (fs.getBytecodeDocumentLine() == line) { return true; } } return false; } /* (non-Javadoc) * @see com.drgarbage.classfile.render.intf.IClassFileDocument#isLineInMethod(int) */ public boolean isLineInMethod(int line) { int found = Arrays.binarySearch(methodBorderLines, line); if (found >= 0) { /* we found a border line * which is in a method */ return true; } else { /* the match is somewhere in between the border line numbers */ /* the theory here is very simple: * test, if the * found = (-(<i>insertion point</i>) - 1) * is even or odd * even means between start and end of a method so return true * or false otherwise */ return found % 2 == 0; } } /** * <code>true</code> if the <code>try</code>, <code>catch</code> and <code>finally</code> * blocks are rendered by this; <code>false</code> otherwise. * * @return the renderTryCatchFinallyBlocks */ public boolean isRenderTryCatchFinallyBlocks() { return renderTryCatchFinallyBlocks; } /** * Returns the number of characters in the internal {@link StringBuilder}. * * @return <code>true</code> or <code>false</code> */ public int length() { return sb.length(); } protected boolean matchPosition(int position, String str) { for (int i = 0; i < str.length(); i++) { if (sb.charAt(i + position) != str.charAt(i)) { return false; } } return true; } public void setCommentOffset(int commentOffset) { this.commentOffset = commentOffset; } public void setIndent(int indent) { this.indent = indent; } public void setIndentationString(String indentationString) { this.indentationString = indentationString; } /** * Sets the boolean flag telling if the * the <code>try</code>, <code>catch</code> and <code>finally</code> * blocks will be rendered (<code>true</code>) by this or not (<code>false</code>). * * @param renderTryCatchFinallyBlocks the renderTryCatchFinallyBlocks to set */ public void setRenderTryCatchFinallyBlocks(boolean renderTryCatchFinallyBlocks) { this.renderTryCatchFinallyBlocks = renderTryCatchFinallyBlocks; } /** * Returns the textual representation of this {@link ClassFileDocument}. * * @return the textual representation of this {@link ClassFileDocument} */ public String toString() { return sb.toString(); } /** * Prints a disassembled view of the given annotation. * * @param desc the class descriptor of the annotation class. * @param visible <tt>true</tt> if the annotation is visible at runtime. * @return a visitor to visit the annotation values. */ protected AnnotationVisitor visitAnnotationImpl(final String desc, final boolean visible) { if (!L_JAVA_LANG_DEPRECATED_SEMICOLON.equals(desc)) { //TODO test the annotation appendNewline(); appendCommentBegin(); appendSpace(); if (visible) { sb.append(ByteCodeConstants.VISIBLE); } else { sb.append(ByteCodeConstants.INVISIBLE); } appendSpace(); sb.append(ByteCodeConstants.ANNOTATION); appendSpace(); appendCommentEnd(); appendNewline(); append(JavaLexicalConstants.AT); //FIXME render the descriptor sb.append(desc); sb.append(JavaLexicalConstants.LEFT_PARENTHESIS); //FIXME Annotations rendering AnnotationRenderer tav = createTraceAnnotationVisitor(); sb.append(JavaLexicalConstants.RIGHT_PARENTHESIS); appendNewline(); return tav; } else { return null; } } /** * Prints a disassembled view of the given attribute. * * @param attr an attribute. */ public void visitAttributeImpl(final Attribute attr) { //FIXME attribute rendering appendNewline(); appendCommentBegin(); appendSpace(); sb.append(ByteCodeConstants.ATTRIBUTE); appendSpace(); appendCommentEnd(); appendNewline(); appendCommentBegin(); appendSpace(); //TODO class name rendering sb.append(attr.type); appendSpace(); appendCommentEnd(); appendNewline(); // if (attr instanceof Traceable) { // ((Traceable) attr).trace(sb, null); // } } public void visitConstantPool(final byte[] bytes, int offset, int entryCount) { if (bytes == null || entryCount == 0) { constantPool = null; } else { ConstantPoolParser cpp = new ConstantPoolParser(bytes, offset, entryCount); constantPool = cpp.parse(); } } protected FieldVisitor visitField(final int access, final String name, final String desc, final String signature // final Object value ) { appendNewline(); appendDeprecated(access); int fieldLine = lineCount; appendAccess(access); if (signature != null) { SignatureRenderer sv = new SignatureRenderer(0); SignatureReader r = new SignatureReader(signature); r.acceptType(sv); sb.append(sv.getDeclaration()); appendSpace(); append(name); appendSemicolon(); appendNewline(); } else { try { BytecodeUtils.appendFieldDescriptor(desc, 0, sb); } catch (IOException e) { throw new RuntimeException(e); } appendSpace(); append(name); appendSemicolon(); appendNewline(); } FieldRenderer fieldRenderer = new FieldRenderer(name, desc, fieldLine); fieldSections.add(fieldRenderer); return fieldRenderer; } }