1 package de.loskutov.bco.asm; 2 3 import java.util.ArrayList ; 4 import java.util.BitSet ; 5 import java.util.HashMap ; 6 import java.util.Iterator ; 7 import java.util.List ; 8 import java.util.Map ; 9 10 import org.eclipse.core.runtime.IStatus; 11 import org.objectweb.asm.Opcodes; 12 import org.objectweb.asm.Type; 13 import org.objectweb.asm.tree.LocalVariableNode; 14 import org.objectweb.asm.tree.MethodNode; 15 import org.objectweb.asm.tree.analysis.Analyzer; 16 import org.objectweb.asm.tree.analysis.AnalyzerException; 17 import org.objectweb.asm.tree.analysis.BasicValue; 18 import org.objectweb.asm.tree.analysis.Frame; 19 import org.objectweb.asm.tree.analysis.SimpleVerifier; 20 import org.objectweb.asm.tree.analysis.Value; 21 22 import de.loskutov.bco.BytecodeOutlinePlugin; 23 import de.loskutov.bco.preferences.BCOConstants; 24 25 28 29 public class DecompiledMethod { 30 31 private List text; 32 33 private List localVariables; 34 35 private Map sourceLines; 37 private Map decompiledLines; 39 private Map insns; 41 private Map opcodes; 43 private Map insnLines; 45 private int lineCount; 46 47 private int firstSourceLine; 49 private MethodNode meth; 50 51 private Frame[] frames; 52 53 private String error; 54 55 private int errorInsn; 56 57 public DecompiledMethod(final String owner, final List inputText, 58 final Map lineNumbers, final MethodNode meth, final ClassLoader cl, BitSet modes) { 59 this.text = new ArrayList (); 60 this.localVariables = meth.localVariables; 61 this.sourceLines = new HashMap (); 62 this.decompiledLines = new HashMap (); 63 this.insns = new HashMap (); 64 this.opcodes = new HashMap (); 65 this.insnLines = new HashMap (); 66 67 this.meth = meth; 68 formatText(inputText, new HashMap (), new StringBuffer (), this.text); 69 computeMaps(lineNumbers); 70 71 if (modes.get(BCOConstants.F_SHOW_ANALYZER) 72 && (meth.access & Opcodes.ACC_ABSTRACT) == 0) { 73 analyzeMethod(owner, cl); 74 } 75 } 76 77 public boolean hasSourceLinesInfo(){ 78 return ! sourceLines.isEmpty(); 79 } 80 81 public boolean hasLocalVariablesInfo(){ 82 return ! localVariables.isEmpty(); 83 } 84 85 public String getSignature(){ 86 return meth.name + meth.desc; 87 } 88 89 94 private void analyzeMethod(final String owner, final ClassLoader cl) { 95 Analyzer a = new Analyzer(new SimpleVerifier() { 96 97 protected Class getClass(final Type t) { 98 try { 99 if (t.getSort() == Type.ARRAY) { 100 return Class.forName(t.getDescriptor().replace( 101 '/', '.'), true, cl); 102 } 103 return cl.loadClass(t.getClassName()); 104 } catch (ClassNotFoundException e) { 105 throw new RuntimeException (e.toString()+" " +cl, e); 106 } 107 } 108 }); 109 try { 110 a.analyze(owner, meth); 111 } catch (AnalyzerException e) { 112 error = e.getMessage(); 113 if (error.startsWith("Error at instruction ")) { 114 error = error.substring("Error at instruction ".length()); 115 errorInsn = Integer.parseInt(error.substring(0, error 116 .indexOf(':'))); 117 error = error.substring(error.indexOf(':') + 2); 118 } else { 119 BytecodeOutlinePlugin.log(e, IStatus.ERROR); 120 error = null; 121 } 122 } 123 frames = a.getFrames(); 124 } 125 126 private void formatText(final List input, final Map locals, StringBuffer line, 127 final List result) { 128 for (int i = 0; i < input.size(); ++i) { 129 Object o = input.get(i); 130 if (o instanceof List ) { 131 formatText((List ) o, locals, line, result); 132 } else if (o instanceof Index) { 133 result.add(o); 134 updateLocals((Index) o, locals); 135 } else if (o instanceof Integer ) { 136 String localVariableName = (String ) locals.get(o); 137 if (localVariableName == null) { 138 Index index = getNextIndex(input, i); 139 if(index != null){ 140 updateLocals(index, locals); 141 localVariableName = (String ) locals.get(o); 142 } 143 } 144 if(localVariableName != null) { 145 line.append(": ").append(localVariableName); 146 } 147 } else { 148 String s = o.toString(); 149 int p; 150 do { 151 p = s.indexOf('\n'); 152 if (p == -1) { 153 line.append(s); 154 } else { 155 result.add(line.toString() + s.substring(0, p + 1)); 156 s = s.substring(p + 1); 157 line.setLength(0); 158 } 159 } while (p != -1); 160 } 161 } 162 } 163 164 169 private Index getNextIndex(List input, int startOffset) { 170 for (int i = startOffset + 1; i < input.size(); i++) { 171 Object object = input.get(i); 172 if(object instanceof Index){ 173 return (Index)object; 174 } 175 } 176 return null; 177 } 178 179 private void updateLocals(final Index index, final Map locals) { 180 for (int i = 0; i < localVariables.size(); ++i) { 181 LocalVariableNode lvNode = (LocalVariableNode) localVariables.get(i); 182 if (lvNode.start == index.labelNode) { 183 locals.put(new Integer (lvNode.index), lvNode.name); 184 } else if (lvNode.end == index.labelNode) { 185 locals.remove(new Integer (lvNode.index)); 186 } 187 } 188 } 189 190 private void computeMaps(final Map lineNumbers) { 191 int currentSourceLine = -1; 192 int currentDecompiledLine = 0; 193 int currentInsn = -1; 194 int currentOpcode = -1; 195 int firstLine = -1; 196 for (int i = 0; i < text.size(); ++i) { 197 Object o = text.get(i); 198 if (o instanceof Index) { 199 Index index = (Index) o; 200 Integer sourceLine = null; 201 if(index.labelNode != null) { 202 sourceLine = (Integer ) lineNumbers.get(index.labelNode.getLabel()); 203 } 204 if (sourceLine != null) { 205 currentSourceLine = sourceLine.intValue(); 206 if(firstLine == -1 || currentSourceLine < firstLine){ 207 firstLine = currentSourceLine; 208 } 209 } 210 currentInsn = index.insn; 211 currentOpcode = index.opcode; 212 } else { 213 ++currentDecompiledLine; 214 } 215 Integer csl = new Integer (currentSourceLine); 216 Integer cdl = new Integer (currentDecompiledLine); 217 Integer ci = new Integer (currentInsn); 218 Integer co = new Integer (currentOpcode); 219 sourceLines.put(cdl, csl); 220 if (decompiledLines.get(csl) == null) { 221 decompiledLines.put(csl, cdl); 222 } 223 insns.put(cdl, ci); 224 opcodes.put(cdl, co); 225 if (insnLines.get(ci) == null) { 226 insnLines.put(ci, cdl); 227 } 228 } 229 lineCount = currentDecompiledLine; 230 firstSourceLine = firstLine; 231 } 232 233 public String getText() { 234 StringBuffer buf = new StringBuffer (); 235 for (int i = 0; i < text.size(); ++i) { 236 Object o = text.get(i); 237 if (!(o instanceof Index)) { 238 buf.append((String ) o); 239 } 240 } 241 return buf.toString(); 242 } 243 244 public String [][] getTextTable() { 245 Frame frame = null; 246 String error1 = ""; 247 List lines = new ArrayList (); 248 for (int i = 0; i < text.size(); ++i) { 249 Object o = text.get(i); 250 if (o instanceof Index) { 251 if (frames != null) { 252 frame = frames[((Index) o).insn]; 253 if (this.error != null && ((Index) o).insn == this.errorInsn) { 254 error1 = this.error; 255 } 256 } 257 } else { 258 String locals = " "; 259 String stack = " "; 260 if (frame != null) { 261 StringBuffer buf = new StringBuffer (); 262 appendFrame(buf, frame); 263 int p = buf.indexOf(" "); 264 locals = buf.substring(0, p); 265 if("".equals(locals)){ 266 locals = " "; 267 } 268 stack = buf.substring(p + 1); 269 if("".equals(stack)){ 270 stack = " "; 271 } 272 } 273 lines.add(new String []{locals, stack, o.toString(), error1}); 274 frame = null; 275 error1 = ""; 276 } 277 } 278 return (String [][]) lines.toArray(new String [lines.size()][]); 279 } 280 281 public int getLineCount() { 282 return lineCount; 283 } 284 285 public String getError() { 286 return error; 287 } 288 289 public int getErrorLine() { 290 if (error == null) { 291 return -1; 292 } 293 Integer i = (Integer ) insnLines.get(new Integer (errorInsn)); 294 return i == null 295 ? -1 296 : i.intValue(); 297 } 298 299 private void appendFrame(final StringBuffer buf, final Frame f) { 300 try { 301 for (int i = 0; i < f.getLocals(); ++i) { 302 appendValue(buf, f.getLocal(i)); 303 } 304 buf.append(' '); 305 for (int i = 0; i < f.getStackSize(); ++i) { 306 appendValue(buf, f.getStack(i)); 307 } 308 } catch (IndexOutOfBoundsException e) { 309 BytecodeOutlinePlugin.log(e, IStatus.WARNING); 311 } 312 } 313 314 private void appendValue(final StringBuffer buf, final Value v) { 315 if (((BasicValue) v).isReference()) { 316 buf.append("R"); 317 } else { 318 buf.append(v.toString()); 319 } 320 } 321 322 public int getFirstSourceLine(){ 323 return firstSourceLine; 324 } 325 326 public int getSourceLine(final int decompiledLine) { 327 Integer i = (Integer ) sourceLines.get(new Integer (decompiledLine)); 328 return i == null 329 ? -1 330 : i.intValue(); 331 } 332 333 339 public String [] getFrame(final int decompiledLine, final boolean useQualifiedNames) { 340 Integer insn = getBytecodeOffset(decompiledLine); 341 if (error != null && insn != null && insn.intValue() == errorInsn) { 342 return new String [] {error,error}; 343 } 344 if (frames != null && insn != null) { 345 Frame f = frames[insn.intValue()]; 346 if (f == null) { 347 return null; 348 } 349 350 try { 351 StringBuffer localsBuf = new StringBuffer (); 352 353 for (int i = 0; i < f.getLocals(); ++i) { 354 String s = f.getLocal(i).toString(); 355 appendTypeName(i, useQualifiedNames, localsBuf, s); 356 357 for (Iterator it = localVariables.iterator(); it.hasNext();) { 358 LocalVariableNode lvnode = (LocalVariableNode) it.next(); 359 int n = lvnode.index; 360 if( n==i) { 361 localsBuf.append( " : ").append( lvnode.name); 362 } 363 } 364 365 localsBuf.append('\n'); 366 } 367 StringBuffer stackBuf = new StringBuffer (); 368 for (int i = 0; i < f.getStackSize(); ++i) { 369 String s = f.getStack(i).toString(); 370 appendTypeName(i, useQualifiedNames, stackBuf, s); 371 stackBuf.append('\n'); 372 } 373 return new String [] {localsBuf.toString(), stackBuf.toString()}; 374 } catch (IndexOutOfBoundsException e) { 375 BytecodeOutlinePlugin.log(e, IStatus.WARNING); 377 } 378 } 379 return null; 380 } 381 382 386 public Integer getBytecodeOffset(final int decompiledLine) { 387 Integer insn = (Integer ) insns.get(new Integer (decompiledLine)); 388 return insn; 389 } 390 391 395 public Integer getBytecodeInsn(final int decompiledLine) { 396 Integer insn = (Integer ) opcodes.get(new Integer (decompiledLine)); 397 return insn; 398 } 399 400 public String [][][] getFrameTables(final int decompiledLine, boolean useQualifiedNames) { 401 Integer insn = getBytecodeOffset(decompiledLine); 402 if (error != null && insn != null && insn.intValue() == errorInsn) { 403 return null; 404 } 405 if (frames != null && insn != null && insn.intValue() >= 0 406 && insn.intValue() < frames.length) { 407 Frame f = frames[insn.intValue()]; 408 if (f == null) { 409 return null; 410 } 411 412 try { 413 ArrayList locals = new ArrayList (); 414 for (int i = 0; i < f.getLocals(); ++i) { 415 String varName = ""; 416 for (Iterator it = localVariables.iterator(); it.hasNext();) { 417 LocalVariableNode lvnode = (LocalVariableNode) it.next(); 418 int n = lvnode.index; 419 if( n==i) { 420 varName = lvnode.name; 421 break; 423 } 424 } 425 426 locals.add( new String [] { 427 ""+i, 428 getTypeName( useQualifiedNames, f.getLocal(i).toString()), 429 varName}); 430 } 431 432 ArrayList stack = new ArrayList (); 433 for (int i = 0; i < f.getStackSize(); ++i) { 434 stack.add( new String [] { 435 ""+i, 436 getTypeName( useQualifiedNames, f.getStack(i).toString())}); 437 } 438 return new String [][][] { 439 (String [][]) locals.toArray( new String [ 3][]), 440 (String [][]) stack.toArray( new String [ 2][])}; 441 } catch (IndexOutOfBoundsException e) { 442 BytecodeOutlinePlugin.log(e, IStatus.ERROR); 444 } 445 } 446 return null; 447 } 448 449 450 458 private void appendTypeName(int n, final boolean useQualifiedNames, StringBuffer buf, String s) { 459 buf.append(n).append( " "); 460 if(!useQualifiedNames) { 461 int idx = s.lastIndexOf('/'); 462 if(idx > 0){ 463 buf.append(s.substring(idx + 1, s.length() - 1)); 465 return; 466 } 467 } 468 if("Lnull;".equals(s)){ 469 buf.append("null"); 470 } else { 471 buf.append(s); 472 } 473 } 474 475 private String getTypeName(final boolean useQualifiedNames, String s) { 476 if (!useQualifiedNames) { 477 String arraySymbols = ""; 479 while (s.startsWith("[")){ 480 arraySymbols += "["; 481 s = s.substring(1); 482 } 483 484 int idx = s.lastIndexOf('/'); 485 if (idx > 0) { 486 return arraySymbols + s.substring(idx + 1, s.length() - 1); 488 } 489 if("." == s){ 491 return arraySymbols + s; 492 } 493 return arraySymbols + 495 CommentedClassVisitor.getSimpleName(Type.getType(s)); 496 } 497 return "Lnull;".equals(s) ? "null" : s; 498 } 499 500 public int getDecompiledLine(final int sourceLine) { 501 Integer i = (Integer ) decompiledLines.get(new Integer (sourceLine)); 502 return i == null 503 ? -1 504 : i.intValue(); 505 } 506 } 507 | Popular Tags |