Commit 35cdbff8 authored by andrei's avatar andrei

1. Fixed problem with annotation support 2. Fixed problem with missing...

1. Fixed problem with annotation support 2. Fixed problem with missing variable names on XSTORE instructions 3. Added missing props 4. Removed unused class 5. Added non-optimized libraries with full debug info
parent 13e652d8
BytecodeOutlineView.linkWithEditor_text=Link with editor
BytecodeOutlineView.linkWithEditorText_tooltip=Link With Editor
BytecodeOutlineView.showOnlySelection_text=Show current element only
BytecodeOutlineView.showOnlySelection_tooltip=Show bytecode for current field/method only
BytecodeOutlineView.enableRawMode_text=Show "raw" bytecode
BytecodeOutlineView.enableRawMode_tooltip=Show bytecode without any additional help
BytecodeOutlineView.enableVerifier_text=Show bytecode analyzer pane
BytecodeOutlineView.enableVerifier_tooltip=Shows the symbolic state of the execution stack frame at selected bytecode instruction
BytecodeOutline.Title=Bytecode Outline
BytecodeOutline.Error=Error (Bytecode Outline)
ToggleASMifierModeAction.toggleASMifierMode_text=Toggle ASMifier mode on/off
ToggleASMifierModeAction.toggleASMifierMode_tooltip=Switch between true bytecode and ASMifier java code view
\ No newline at end of file
......@@ -71,9 +71,16 @@ public class CommentedClassVisitor extends TraceClassVisitor {
}
buf1.append(getSimpleName(args[i]));
}
buf1.append(')');
buf1.append(") : ");
buf1.append(getSimpleName(res));
break;
case METHOD_SIGNATURE :
// TODO implement raw/not raw view of signature -
// remove package names like here:
// ()Ljava/util/List<Lde/loskutov/xml/impl/DummyForXml;>;
// to ()List<DummyForXml>
// (Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;)V
// to (Map<String,String>)void
default :
buf1.append(desc);
}
......@@ -113,19 +120,60 @@ public class CommentedClassVisitor extends TraceClassVisitor {
}
class CommentedMethodVisitor extends TraceMethodVisitor {
private Index getIndex(Label label){
Index index;
for (int i = 0; i < text.size(); i++) {
Object o = text.get(i);
if(o instanceof Index){
index = (Index)o;
if(index.label == label){
return index;
}
}
}
return null;
}
public void visitMethodInsn (
final int opcode,
final String owner,
final String name,
final String desc)
{
buf.setLength(0);
buf.append(tab2).append(OPCODES[opcode]).append(' ');
appendDescriptor(INTERNAL_NAME, owner);
buf.append(' ').append(name);
appendDescriptor(METHOD_DESCRIPTOR, desc);
buf.append('\n');
text.add(buf.toString());
}
public void visitVarInsn(final int opcode, final int var) {
text.add(tab2 + OPCODES[opcode] + " " + var);
if (!raw) {
text.add(new Integer(var));
text.add(Integer.valueOf(var));
}
text.add("\n");
}
public void visitLabel(Label label) {
buf.setLength(0);
buf.append(ltab);
appendLabel(label);
Index index = getIndex(label);
if(index != null){
buf.append(" (").append(index.insn).append(")");
}
buf.append('\n');
text.add(buf.toString());
}
public void visitIincInsn(final int var, final int increment) {
text.add(tab2 + "IINC " + var);
if (!raw) {
text.add(new Integer(var));
text.add(Integer.valueOf(var));
}
text.add(" " + increment + "\n");
}
......@@ -159,4 +207,6 @@ public class CommentedClassVisitor extends TraceClassVisitor {
CommentedClassVisitor.this.appendDescriptor(buf, type, desc, raw);
}
}
}
/*****************************************************************************************
* Copyright (c) 2004 Andrei Loskutov. All rights reserved. This program and the
* accompanying materials are made available under the terms of the BSD License which
* accompanies this distribution, and is available at
* http://www.opensource.org/licenses/bsd-license.php Contributor: Andrei Loskutov -
* initial API and implementation
****************************************************************************************/
package de.loskutov.bco.asm;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.loskutov.bco.BytecodeOutlinePlugin;
/**
* Container for decompiled bytecode
* @author Andrei
*/
public class DecompileResult {
/**
* start of source line marker
*/
public static final String SOURCE_LINE_PREFIX = " ("; //$NON-NLS-1$
/**
* stop of source line marker
*/
public static final String SOURCE_LINE_SUFFIX = ")\n"; //$NON-NLS-1$
/**
* " (345)\n" is a valid example for this pattern, 345 is stored as content of
* matching group #1
*/
private static final Pattern sourceLinePattern = Pattern
.compile(" \\((\\d+)\\)\\n"); //$NON-NLS-1$
private String decompiledCode;
/**
* @param decompiledCode full decompiled bytecode as string
*/
public DecompileResult(String decompiledCode) {
setDecompiledCode(decompiledCode);
}
/**
* Returns source line from corresponding source code.
* Based on text pattern search in decompiled bytecode - used pattern is
* currently (\d) just before line end
* @param byteCodeTextOffset text offset from decompiled code
* @return -1 if source line is not found
*/
public int getSourceLine(int byteCodeTextOffset) {
int startLine = -1;
// last character in the currrent line
int stopSearch = decompiledCode.indexOf("\n", byteCodeTextOffset) + 1; //$NON-NLS-1$
String subSequence = decompiledCode.substring(0, stopSearch);
int startSearch = subSequence.lastIndexOf("//") + 1; //$NON-NLS-1$
String lineNbrStr = getLastMatch(
startSearch, subSequence.length(), decompiledCode);
if (lineNbrStr == null) {
return startLine;
}
try {
startLine = Integer.parseInt(lineNbrStr);
} catch (NumberFormatException e) {
BytecodeOutlinePlugin.error(null, e);
}
return startLine;
}
private static final String getLastMatch(int startSearch, int stopSearch,
String code) {
Matcher matcher = sourceLinePattern.matcher(code.subSequence(
startSearch, stopSearch));
String lastGroup = null;
while (matcher.find()) {
lastGroup = matcher.group(1);
}
return lastGroup;
}
/**
* @param decompiledCode The decompiledCode to set.
*/
public void setDecompiledCode(String decompiledCode) {
this.decompiledCode = decompiledCode;
}
/**
* @return Returns the decompiledCode.
*/
public String getDecompiledCode() {
return decompiledCode;
}
}
\ No newline at end of file
......@@ -113,7 +113,7 @@ public class DecompiledClass {
DecompiledMethod m = (DecompiledMethod) o;
int l = m.getErrorLine();
if (l != -1) {
errors.add(new Integer(l + currentDecompiledLine));
errors.add(Integer.valueOf(l + currentDecompiledLine));
}
currentDecompiledLine += m.getLineCount();
} else {
......
......@@ -45,7 +45,7 @@ public class DecompiledMethod {
private int errorInsn;
public DecompiledMethod(final String owner, final List text,
public DecompiledMethod(final String owner, final List inputText,
final Map lineNumbers, final List localVariables,
final MethodNode meth, final ClassLoader cl) {
this.text = new ArrayList();
......@@ -54,46 +54,55 @@ public class DecompiledMethod {
this.decompiledLines = new HashMap();
this.insns = new HashMap();
this.insnLines = new HashMap();
this.meth = meth;
formatText(text, new HashMap(), "", this.text);
formatText(inputText, new HashMap(), new StringBuffer(), this.text);
computeMaps(lineNumbers);
if (meth != null) {
Analyzer a = new Analyzer(new SimpleVerifier() {
protected Class getClass(final Type t) {
try {
if (t.getSort() == Type.ARRAY) {
return cl.loadClass(t.getDescriptor().replace(
'/', '.'));
}
return cl.loadClass(t.getClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.toString(), e);
analyzeMethod(owner, cl);
}
}
/**
* @param owner
* @param meth
* @param cl
*/
private void analyzeMethod(final String owner, final ClassLoader cl) {
Analyzer a = new Analyzer(new SimpleVerifier() {
protected Class getClass(final Type t) {
try {
if (t.getSort() == Type.ARRAY) {
return cl.loadClass(t.getDescriptor().replace(
'/', '.'));
}
}
});
try {
a.analyze(owner, meth);
} catch (AnalyzerException e) {
error = e.getMessage();
if (error.startsWith("Error at instruction ")) {
error = error.substring("Error at instruction ".length());
errorInsn = Integer.parseInt(error.substring(0, error
.indexOf(':')));
error = error.substring(error.indexOf(':') + 2);
} else {
BytecodeOutlinePlugin.logError(e);
error = null;
return cl.loadClass(t.getClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.toString(), e);
}
}
frames = a.getFrames();
});
try {
a.analyze(owner, meth);
} catch (AnalyzerException e) {
error = e.getMessage();
if (error.startsWith("Error at instruction ")) {
error = error.substring("Error at instruction ".length());
errorInsn = Integer.parseInt(error.substring(0, error
.indexOf(':')));
error = error.substring(error.indexOf(':') + 2);
} else {
BytecodeOutlinePlugin.logError(e);
error = null;
}
}
frames = a.getFrames();
}
private void formatText(final List input, final Map locals, String line,
private void formatText(final List input, final Map locals, StringBuffer line,
final List result) {
for (int i = 0; i < input.size(); ++i) {
Object o = input.get(i);
......@@ -104,8 +113,15 @@ public class DecompiledMethod {
updateLocals((Index) o, locals);
} else if (o instanceof Integer) {
String localVariableName = (String) locals.get(o);
if (localVariableName != null) {
line = line + ": " + localVariableName;
if (localVariableName == null) {
Index index = getNextIndex(input, i);
if(index != null){
updateLocals(index, locals);
localVariableName = (String) locals.get(o);
}
}
if(localVariableName != null) {
line.append(": ").append(localVariableName);
}
} else {
String s = o.toString();
......@@ -113,24 +129,39 @@ public class DecompiledMethod {
do {
p = s.indexOf('\n');
if (p == -1) {
line = line + s;
line.append(s);
} else {
result.add(line + s.substring(0, p + 1));
result.add(line.toString() + s.substring(0, p + 1));
s = s.substring(p + 1);
line = "";
line.setLength(0);
}
} while (p != -1);
}
}
}
/**
* @param i
* @param input
* @return
*/
private Index getNextIndex(List input, int startOffset) {
for (int i = startOffset + 1; i < input.size(); i++) {
Object object = input.get(i);
if(object instanceof Index){
return (Index)object;
}
}
return null;
}
private void updateLocals(final Index index, final Map locals) {
for (int i = 0; i < localVariables.size(); ++i) {
LocalVariableNode lvn = (LocalVariableNode) localVariables.get(i);
if (lvn.start == index.label) {
locals.put(new Integer(lvn.index), lvn.name);
} else if (lvn.end == index.label) {
locals.remove(new Integer(lvn.index));
LocalVariableNode lvNode = (LocalVariableNode) localVariables.get(i);
if (lvNode.start == index.label) {
locals.put(Integer.valueOf(lvNode.index), lvNode.name);
} else if (lvNode.end == index.label) {
locals.remove(Integer.valueOf(lvNode.index));
}
}
}
......@@ -151,9 +182,9 @@ public class DecompiledMethod {
} else {
++currentDecompiledLine;
}
Integer csl = new Integer(currentSourceLine);
Integer cdl = new Integer(currentDecompiledLine);
Integer ci = new Integer(currentInsn);
Integer csl = Integer.valueOf(currentSourceLine);
Integer cdl = Integer.valueOf(currentDecompiledLine);
Integer ci = Integer.valueOf(currentInsn);
sourceLines.put(cdl, csl);
if (decompiledLines.get(csl) == null) {
decompiledLines.put(csl, cdl);
......@@ -215,7 +246,7 @@ public class DecompiledMethod {
if (error == null) {
return -1;
}
Integer i = (Integer) insnLines.get(new Integer(errorInsn));
Integer i = (Integer) insnLines.get(Integer.valueOf(errorInsn));
return i == null
? -1
: i.intValue();
......@@ -244,14 +275,14 @@ public class DecompiledMethod {
}
public int getSourceLine(final int decompiledLine) {
Integer i = (Integer) sourceLines.get(new Integer(decompiledLine));
Integer i = (Integer) sourceLines.get(Integer.valueOf(decompiledLine));
return i == null
? -1
: i.intValue();
}
public String getFrame(final int decompiledLine) {
Integer insn = (Integer) insns.get(new Integer(decompiledLine));
Integer insn = (Integer) insns.get(Integer.valueOf(decompiledLine));
if (error != null && insn != null && insn.intValue() == errorInsn) {
return error;
}
......@@ -280,7 +311,7 @@ public class DecompiledMethod {
}
public int getDecompiledLine(final int sourceLine) {
Integer i = (Integer) decompiledLines.get(new Integer(sourceLine));
Integer i = (Integer) decompiledLines.get(Integer.valueOf(sourceLine));
return i == null
? -1
: i.intValue();
......
......@@ -32,6 +32,8 @@ public class DecompilerClassVisitor extends ClassAdapter {
private List methods;
private AnnotationVisitor dummyAnnVisitor;
public DecompilerClassVisitor(final ClassVisitor cv, final String field,
final String method, final boolean verify) {
super(cv);
......@@ -60,22 +62,22 @@ public class DecompilerClassVisitor extends ClassAdapter {
} else {
cv = new CommentedClassVisitor(raw);
}
DecompilerClassVisitor dcv;
dcv = new DecompilerClassVisitor(cv, field, method, verify);
DecompilerClassVisitor dcv = new DecompilerClassVisitor(
cv, field, method, verify);
cr.accept(dcv, false);
return dcv.getResult(cl);
}
public DecompiledClass getResult(final ClassLoader cl) {
List text = new ArrayList();
formatText(((AbstractVisitor) cv).getText(), "", text, cl);
formatText(((AbstractVisitor) cv).getText(), new StringBuffer(), text, cl);
while (text.size() > 0 && text.get(0).equals("\n")) {
text.remove(0);
}
return new DecompiledClass(text);
}
private void formatText(final List input, String line, final List result,
private void formatText(final List input, StringBuffer line, final List result,
final ClassLoader cl) {
for (int i = 0; i < input.size(); ++i) {
Object o = input.get(i);
......@@ -89,11 +91,11 @@ public class DecompilerClassVisitor extends ClassAdapter {
do {
p = s.indexOf('\n');
if (p == -1) {
line = line + s;
line.append(s);
} else {
result.add(line + s.substring(0, p + 1));
result.add(line.toString() + s.substring(0, p + 1));
s = s.substring(p + 1);
line = "";
line.setLength(0);
}
} while (p != -1);
}
......@@ -128,7 +130,7 @@ public class DecompilerClassVisitor extends ClassAdapter {
if (methodFilter == null && fieldFilter == null) {
return super.visitAnnotation(desc, visible);
}
return null;
return getDummyAnnotationVisitor();
}
public void visitAttribute(final Attribute attr) {
......@@ -154,7 +156,7 @@ public class DecompilerClassVisitor extends ClassAdapter {
}
return super.visitField(access, name1, desc, signature, value);
}
public MethodVisitor visitMethod(final int access, final String name1,
final String desc, final String signature, final String[] exceptions) {
if (fieldFilter != null) {
......@@ -186,4 +188,27 @@ public class DecompilerClassVisitor extends ClassAdapter {
super.visitEnd();
}
}
private AnnotationVisitor getDummyAnnotationVisitor(){
if (dummyAnnVisitor == null) {
dummyAnnVisitor = new AnnotationVisitor() {
public void visit(String n, Object value) {
/* empty */
}
public void visitEnum(String n, String desc, String value) {
/* empty */
}
public AnnotationVisitor visitAnnotation(String n, String desc) {
return this;
}
public AnnotationVisitor visitArray(String n) {
return this;
}
public void visitEnd() {
/* empty*/
}
};
}
return dummyAnnVisitor;
}
}
......@@ -12,6 +12,9 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
......@@ -57,7 +60,7 @@ import de.loskutov.bco.BytecodeOutlinePlugin;
public class JdtUtils {
/**
*
*
*/
private JdtUtils() {
// don't call
......@@ -80,13 +83,13 @@ public class JdtUtils {
}
sb.append('(');
IType declaringType = iMethod.getDeclaringType();
String[] parameterTypes = iMethod.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
sb.append(getResolvedType(parameterTypes[i], declaringType));
}
sb.append(')');
// continue here with adding resolved return type
String returnType = iMethod.getReturnType();
sb.append(getResolvedType(returnType, declaringType));
......@@ -112,7 +115,7 @@ public class JdtUtils {
} else {
// we need resolved types
String resolved = getResolvedTypeName(typeToResolve, declaringType);
while (arrayCount > 0) {
sb.append(Signature.C_ARRAY);
arrayCount--;
......@@ -136,21 +139,29 @@ public class JdtUtils {
*/
private static String getResolvedTypeName(String refTypeSig,
IType declaringType) throws JavaModelException {
/* the whole method is copied from JavaModelUtil.getResolvedTypeName(...).
* The problem is, that JavaModelUtil uses '.' to separate package
* names, but we need '/' -> see JavaModelUtil.concatenateName() vs
* JdtUtils.concatenateName()
*/
int arrayCount = Signature.getArrayCount(refTypeSig);
char type = refTypeSig.charAt(arrayCount);
char type= refTypeSig.charAt(arrayCount);
if (type == Signature.C_UNRESOLVED) {
int semi = refTypeSig
.indexOf(Signature.C_SEMICOLON, arrayCount + 1);
if (semi == -1) {
throw new IllegalArgumentException();
String name= ""; //$NON-NLS-1$
int bracket= refTypeSig.indexOf(Signature.C_GENERIC_START, arrayCount + 1);
if (bracket > 0) {
name= refTypeSig.substring(arrayCount + 1, bracket);
} else {
int semi= refTypeSig.indexOf(Signature.C_SEMICOLON, arrayCount + 1);
if (semi == -1) {
throw new IllegalArgumentException();
}
name= refTypeSig.substring(arrayCount + 1, semi);
}
String name = refTypeSig.substring(arrayCount + 1, semi);
String[][] resolvedNames = declaringType.resolveType(name);
String[][] resolvedNames= declaringType.resolveType(name);
if (resolvedNames != null && resolvedNames.length > 0) {
char innerPrefix = '$';// JdtUtils.getInnerPrefix(declaringType);
return concatenateName(
resolvedNames[0][0], resolvedNames[0][1], innerPrefix);
return concatenateName(resolvedNames[0][0], resolvedNames[0][1]);
}
return null;
}
......@@ -160,8 +171,7 @@ public class JdtUtils {
/**
* Concatenates package and Class name Both strings can be empty or <code>null</code>.
*/
private static String concatenateName(String packageName, String className,
char innerPrefix) {
private static String concatenateName(String packageName, String className) {
StringBuffer buf = new StringBuffer();
if (packageName != null && packageName.length() > 0) {
packageName = packageName.replace(Signature.C_DOT, '/');
......@@ -171,7 +181,7 @@ public class JdtUtils {
if (buf.length() > 0) {
buf.append('/');
}
className = className.replace(Signature.C_DOT, innerPrefix);
className = className.replace(Signature.C_DOT, '$');
buf.append(className);
}
return buf.toString();
......@@ -797,6 +807,68 @@ public class JdtUtils {
return 6; // from inner from main type
}
/**
* @param type
* @return
*/
public static ClassLoader getClassLoader(IJavaElement type) {
ClassLoader cl;
IJavaProject javaProject = type.getJavaProject();
IPath projectPath = javaProject.getProject().getLocation();
IClasspathEntry[] paths = null;
IPath defaultOutputLocation = null;
try {
paths = javaProject.getResolvedClasspath(true);
defaultOutputLocation = javaProject.getOutputLocation();
} catch (JavaModelException e) {
// don't show message to user
BytecodeOutlinePlugin.logError(e);
}
if (paths == null) {
return JdtUtils.class.getClassLoader();
}
List urls = new ArrayList();
for (int i = 0; i < paths.length; ++i) {
IClasspathEntry cpEntry = paths[i];
IPath p = null;
if (cpEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
// filter out source container - there are unused for class search -
// add bytecode output location instead
p = cpEntry.getOutputLocation();
if (p == null) {
// default output used:
p = defaultOutputLocation;
}
} else {
p = cpEntry.getPath();
}
if (p == null) {
continue;
}
if (!p.toFile().exists()) {
// removeFirstSegments: remove project from relative path
p = projectPath.append(p.removeFirstSegments(1));
if (!p.toFile().exists()) {
continue;
}
}
try {
urls.add(p.toFile().toURL());
} catch (MalformedURLException e) {
// don't show message to user
BytecodeOutlinePlugin.logError(e);
}
}
if (urls.isEmpty()) {
cl = JdtUtils.class.getClassLoader();
} else {
cl = new URLClassLoader((URL[]) urls.toArray(new URL[urls.size()]));
}
return cl;
}
static class SourceOffsetComparator implements Comparator {
/**
......
......@@ -9,19 +9,12 @@ package de.loskutov.bco.views;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IInitializer;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;