Commit d93f7883 authored by andrei's avatar andrei

Eric Bruneton have completely rewritten the bco.asm

package, which now works with ASM 2.0: the
decompiler visitor inserts hidden indexes into the text produced by
another visitor to compute the mappings between source and bytecode
lines, and no longer uses the tree package to find local variable
names (but uses it for the verifier). So the "link with editor" now
works in ASMifier mode, as well as the new "verify" mode.
I have fixed Eric's implementation of some Eclipse specific issues, build.xml and added settings for eclipse project. Included asm jar's are with debug info and not optimized - for test/debug purposes.
The overall state is "alpha". Code cleanup/fixinf/docu still needed.
parent 43128f49
......@@ -3,9 +3,10 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="externals/asm-1.5.1.jar"/>
<classpathentry kind="lib" path="externals/asm-attrs-1.5.1.jar"/>
<classpathentry kind="lib" path="externals/asm-tree-1.5.1.jar"/>
<classpathentry kind="lib" path="externals/asm-util-1.5.1.jar"/>
<classpathentry sourcepath="/asm 2.0/src" kind="lib" path="externals/asm-2.0_alpha.jar"/>
<classpathentry sourcepath="/asm 2.0/src" kind="lib" path="externals/asm-analysis-2.0_alpha.jar"/>
<classpathentry sourcepath="/asm 2.0/src" kind="lib" path="externals/asm-attrs-2.0_alpha.jar"/>
<classpathentry sourcepath="/asm 2.0/src" kind="lib" path="externals/asm-tree-2.0_alpha.jar"/>
<classpathentry sourcepath="/asm 2.0/src" kind="lib" path="externals/asm-util-2.0_alpha.jar"/>
<classpathentry kind="output" path="output/eclipse"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>bytecode</name>
<name>BytecodeOutline 2.0</name>
<comment></comment>
<projects>
</projects>
......
This diff is collapsed.
#Fri Jan 07 19:52:43 CET 2005
comment_format_source_code=true
org.eclipse.jdt.ui.exception.name=e
comment_new_line_for_parameter=false
comment_format_html=true
comment_line_length=90
org.eclipse.jdt.ui.gettersetter.use.is=true
comment_format_header=true
eclipse.preferences.version=1
comment_format_comments=true
comment_indent_parameter_description=false
org.eclipse.jdt.ui.keywordthis=false
comment_separate_root_tags=false
comment_clear_blank_lines=true
comment_indent_root_tags=false
Manifest-Version: 1.0
Bundle-Name: Bytecode Outline Plug-in
Bundle-SymbolicName: de.loskutov.BytecodeOutline;singleton=true
Bundle-Version: 1.3.1
Bundle-ClassPath: BytecodeOutline.jar,externals/asm-1.5.1.jar,externals/asm-attrs-1.5.1.jar,externals/asm-tree-1.5.1.jar,externals/asm-util-1.5.1.jar
Bundle-Activator: de.loskutov.bco.BytecodeOutlinePlugin
Bundle-Vendor: Andrei Loskutov
Bundle-Localization: plugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.jface.text,
org.eclipse.core.filebuffers,
org.eclipse.jdt.core,
org.eclipse.ui.editors,
org.eclipse.ui.workbench.texteditor,
org.eclipse.jdt.ui,
org.eclipse.core.resources,
org.eclipse.ui.ide,
org.eclipse.jdt,
org.eclipse.compare
Eclipse-AutoStart: true
Manifest-Version: 1.0
Bundle-Name: Bytecode Outline Plug-in
Bundle-SymbolicName: de.loskutov.BytecodeOutline;singleton=true
Bundle-Version: 2.0.0
Bundle-ClassPath: BytecodeOutline.jar,
externals/asm-2.0_alpha.jar,
externals/asm-analysis-2.0_alpha.jar,
externals/asm-attrs-2.0_alpha.jar,
externals/asm-tree-2.0_alpha.jar,
externals/asm-util-2.0_alpha.jar
Bundle-Activator: de.loskutov.bco.BytecodeOutlinePlugin
Bundle-Vendor: Andrei Loskutov
Bundle-Localization: plugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.jface.text,
org.eclipse.core.filebuffers,
org.eclipse.jdt.core,
org.eclipse.ui.editors,
org.eclipse.ui.workbench.texteditor,
org.eclipse.jdt.ui,
org.eclipse.core.resources,
org.eclipse.ui.ide,
org.eclipse.jdt,
org.eclipse.compare
Eclipse-AutoStart: true
plugin.id de.loskutov.ByteCodeOutline
plugin.version 1.3.1
plugin.version 2.0.0
#Home directory of Eclipse (3.x)
eclipse /usr/local/bin/eclipse3.1M2
\ No newline at end of file
#Home directory of Eclipse (3.x) like /usr/local/bin/eclipse3.1M2
eclipse F:/java/e31M4
\ No newline at end of file
......@@ -47,7 +47,7 @@
<fileset dir=".">
<include name="externals/**/*"/>
<include name="icons/**/*"/>
<include name="METAINF/**/*"/>
<include name="META-INF/**/*"/>
<include name="plugin.xml"/>
<include name="*.txt"/>
</fileset>
......
This diff is collapsed.
package de.loskutov.bco.asm;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.TraceAnnotationVisitor;
import org.objectweb.asm.util.TraceClassVisitor;
import org.objectweb.asm.util.TraceFieldVisitor;
import org.objectweb.asm.util.TraceMethodVisitor;
/**
* @author Eric Bruneton
*/
public class CommentedClassVisitor extends TraceClassVisitor {
protected boolean raw;
public CommentedClassVisitor(final boolean raw) {
super(null);
this.raw = raw;
}
public void visitEnd() {
text.add("}\n");
}
protected TraceAnnotationVisitor createTraceAnnotationVisitor() {
return new CommentedAnnotationVisitor();
}
protected TraceFieldVisitor createTraceFieldVisitor() {
return new CommentedFieldVisitor();
}
protected TraceMethodVisitor createTraceMethodVisitor() {
return new CommentedMethodVisitor();
}
protected void appendDescriptor(final int type, final String desc) {
appendDescriptor(buf, type, desc, raw);
}
protected void appendDescriptor(final StringBuffer buf1, final int type,
final String desc, final boolean raw1) {
if (desc == null) {
buf1.append("null");
return;
}
if (raw1) {
buf1.append(desc);
} else {
switch (type) {
case INTERNAL_NAME :
int p = desc.lastIndexOf('/');
if (p == -1) {
buf1.append(desc);
} else {
buf1.append(desc.substring(p + 1));
}
break;
case FIELD_DESCRIPTOR :
buf1.append(getSimpleName(Type.getType(desc)));
break;
case METHOD_DESCRIPTOR :
Type[] args = Type.getArgumentTypes(desc);
Type res = Type.getReturnType(desc);
buf1.append('(');
for (int i = 0; i < args.length; ++i) {
if (i > 0) {
buf1.append(',');
}
buf1.append(getSimpleName(args[i]));
}
buf1.append(')');
buf1.append(getSimpleName(res));
break;
default :
buf1.append(desc);
}
}
}
private String getSimpleName(Type t) {
String name = t.getClassName();
int p = name.lastIndexOf('.');
if (p == -1) {
return name;
}
return name.substring(p + 1);
}
class CommentedAnnotationVisitor extends TraceAnnotationVisitor {
protected void appendDescriptor(final String desc) {
CommentedClassVisitor.this.appendDescriptor(
buf, FIELD_DESCRIPTOR, desc, raw);
}
protected TraceAnnotationVisitor createTraceAnnotationVisitor() {
return new CommentedAnnotationVisitor();
}
}
class CommentedFieldVisitor extends TraceFieldVisitor {
protected void appendDescriptor(final int type, final String desc) {
CommentedClassVisitor.this.appendDescriptor(buf, type, desc, raw);
}
protected TraceAnnotationVisitor createTraceAnnotationVisitor() {
return new CommentedAnnotationVisitor();
}
}
class CommentedMethodVisitor extends TraceMethodVisitor {
public void visitVarInsn(final int opcode, final int var) {
text.add(tab2 + OPCODES[opcode] + " " + var);
if (!raw) {
text.add(new Integer(var));
}
text.add("\n");
}
public void visitIincInsn(final int var, final int increment) {
text.add(tab2 + "IINC " + var);
if (!raw) {
text.add(new Integer(var));
}
text.add(" " + increment + "\n");
}
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
final int index) {
if (raw) {
super.visitLocalVariable(
name, desc, signature, start, end, index);
}
}
public void visitLineNumber(final int line, final Label start) {
if (raw) {
super.visitLineNumber(line, start);
}
}
public void visitMaxs(final int maxStack, final int maxLocals) {
if (raw) {
super.visitMaxs(maxStack, maxLocals);
}
}
protected TraceAnnotationVisitor createTraceAnnotationVisitor() {
return new CommentedAnnotationVisitor();
}
protected void appendDescriptor(final int type, final String desc) {
CommentedClassVisitor.this.appendDescriptor(buf, type, desc, raw);
}
}
}
This diff is collapsed.
package de.loskutov.bco.asm;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eric Bruneton
*/
public class DecompiledClass {
private List text;
private String value;
public DecompiledClass(final List text) {
this.text = text;
}
public String getText() {
if (value == null) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof DecompiledMethod) {
buf.append(((DecompiledMethod) o).getText());
} else {
buf.append(o);
}
}
value = buf.toString();
}
return value;
}
public String[][] getTextTable() {
List lines = new ArrayList();
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof DecompiledMethod) {
String[][] mlines = ((DecompiledMethod) o).getTextTable();
for (int j = 0; j < mlines.length; ++j) {
lines.add(mlines[j]);
}
} else {
lines.add(new String[]{"", "", o.toString()});
}
}
return (String[][]) lines.toArray(new String[lines.size()][]);
}
public int getSourceLine(final int decompiledLine) {
int currentDecompiledLine = 0;
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof DecompiledMethod) {
DecompiledMethod m = (DecompiledMethod) o;
int l = m.getSourceLine(decompiledLine - currentDecompiledLine);
if (l != -1) {
return l;
}
currentDecompiledLine += m.getLineCount();
} else {
currentDecompiledLine++;
}
}
return -1;
}
public String getFrame(final int decompiledLine) {
int currentDecompiledLine = 0;
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof DecompiledMethod) {
DecompiledMethod m = (DecompiledMethod) o;
String frame = m.getFrame(decompiledLine
- currentDecompiledLine);
if (frame != null) {
return frame;
}
currentDecompiledLine += m.getLineCount();
} else {
currentDecompiledLine++;
}
}
return null;
}
public int getDecompiledLine(final int sourceLine) {
int currentDecompiledLine = 0;
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof DecompiledMethod) {
DecompiledMethod m = (DecompiledMethod) o;
int l = m.getDecompiledLine(sourceLine);
if (l != -1) {
return l + currentDecompiledLine;
}
currentDecompiledLine += m.getLineCount();
} else {
currentDecompiledLine++;
}
}
return -1;
}
public List getErrorLines() {
List errors = new ArrayList();
int currentDecompiledLine = 0;
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof DecompiledMethod) {
DecompiledMethod m = (DecompiledMethod) o;
int l = m.getErrorLine();
if (l != -1) {
errors.add(new Integer(l + currentDecompiledLine));
}
currentDecompiledLine += m.getLineCount();
} else {
currentDecompiledLine++;
}
}
return errors;
}
}
package de.loskutov.bco.asm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.SimpleVerifier;
import org.objectweb.asm.tree.analysis.Value;
import de.loskutov.bco.BytecodeOutlinePlugin;
/**
* @author Eric Bruneton
*/
public class DecompiledMethod {
private List text;
private List localVariables;
private Map sourceLines; // decompiled line -> source line
private Map decompiledLines; // source line -> decompiled line
private Map insns; // decompiled line -> insn
private Map insnLines; // insn -> decompile line
private int lineCount;
private MethodNode meth;
private Frame[] frames;
private String error;
private int errorInsn;
public DecompiledMethod(final String owner, final List text,
final Map lineNumbers, final List localVariables,
final MethodNode meth, final ClassLoader cl) {
this.text = new ArrayList();
this.localVariables = localVariables;
this.sourceLines = new HashMap();
this.decompiledLines = new HashMap();
this.insns = new HashMap();
this.insnLines = new HashMap();
this.meth = meth;
formatText(text, new HashMap(), "", 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);
}
}
});
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,
final List result) {
for (int i = 0; i < input.size(); ++i) {
Object o = input.get(i);
if (o instanceof List) {
formatText((List) o, locals, line, result);
} else if (o instanceof Index) {
result.add(o);
updateLocals((Index) o, locals);
} else if (o instanceof Integer) {
String localVariableName = (String) locals.get(o);
if (localVariableName != null) {
line = line + ": " + localVariableName;
}
} else {
String s = o.toString();
int p;
do {
p = s.indexOf('\n');
if (p == -1) {
line = line + s;
} else {
result.add(line + s.substring(0, p + 1));
s = s.substring(p + 1);
line = "";
}
} while (p != -1);
}
}
}
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));
}
}
}
private void computeMaps(final Map lineNumbers) {
int currentSourceLine = -1;
int currentDecompiledLine = 0;
int currentInsn = -1;
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof Index) {
Index index = (Index) o;
Integer sourceLine = (Integer) lineNumbers.get(index.label);
if (sourceLine != null) {
currentSourceLine = sourceLine.intValue();
}
currentInsn = index.insn;
} else {
++currentDecompiledLine;
}
Integer csl = new Integer(currentSourceLine);
Integer cdl = new Integer(currentDecompiledLine);
Integer ci = new Integer(currentInsn);
sourceLines.put(cdl, csl);
if (decompiledLines.get(csl) == null) {
decompiledLines.put(csl, cdl);
}
insns.put(cdl, ci);
if (insnLines.get(ci) == null) {
insnLines.put(ci, cdl);
}
}
lineCount = currentDecompiledLine;
}
public String getText() {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (!(o instanceof Index)) {
buf.append((String) o);
}
}
return buf.toString();
}
public String[][] getTextTable() {
Frame frame = null;
List lines = new ArrayList();
for (int i = 0; i < text.size(); ++i) {
Object o = text.get(i);
if (o instanceof Index) {
if (frames != null) {
frame = frames[((Index) o).insn];
}
} else {
String locals = "";
String stack = "";
if (frame != null) {
StringBuffer buf = new StringBuffer();
appendFrame(buf, frame);
int p = buf.indexOf(" ");
locals = buf.substring(0, p);
stack = buf.substring(p + 1);
}
lines.add(new String[]{locals, stack, o.toString()});
frame = null;
}
}
return (String[][]) lines.toArray(new String[lines.size()][]);
}
public int getLineCount() {
return lineCount;
}
public String getError() {
return error;
}
public int getErrorLine() {
if (error == null) {
return -1;
}
Integer i = (Integer) insnLines.get(new Integer(errorInsn));
return i == null
? -1
: i.intValue();
}
private void appendFrame(final StringBuffer buf, final Frame f) {
try {
for (int i = 0; i < f.getLocals(); ++i) {
appendValue(buf, f.getLocal(i));
}
buf.append(' ');
for (int i = 0; i < f.getStackSize(); ++i) {
appendValue(buf, f.getStack(i));
}
} catch (AnalyzerException e) {
e.printStackTrace();
}
}
private void appendValue(final StringBuffer buf, final Value v) {
if (((BasicValue) v).isReference()) {
buf.append("R");
} else {
buf.append(v.toString());
}
}
public int getSourceLine(final int decompiledLine) {
Integer i = (Integer) sourceLines.get(new Integer(decompiledLine));
return i == null
? -1
: i.intValue();
}
public String getFrame(final int decompiledLine) {
Integer insn = (Integer) insns.get(new Integer(decompiledLine));
if (error != null && insn != null && insn.intValue() == errorInsn) {
return error;
}
if (frames != null && insn != null) {
Frame f = frames[insn.intValue()];
if (f == null) {
return null;
}
try {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < f.getLocals(); ++i) {
buf.append(f.getLocal(i)).append('\n');
}
buf.append('\n');
for (int i = 0; i < f.getStackSize(); ++i) {
buf.append(f.getStack(i)).append('\n');
}
return buf.toString();
} catch (AnalyzerException e) {
e.printStackTrace();
}
}
return null;
}