1 cananian 1.1.2.1 // Code.java, created Tue Jan 25 23:41:07 2000 by cananian 2 cananian 1.1.2.1 // Copyright (C) 2000 C. Scott Ananian <cananian@alumni.princeton.edu> 3 cananian 1.1.2.1 // Licensed under the terms of the GNU GPL; see COPYING for details. 4 cananian 1.1.2.1 package harpoon.IR.Assem; 5 cananian 1.1.2.1 6 kkz 1.1.2.6 import harpoon.Analysis.Maps.Derivation; 7 cananian 1.1.2.2 import harpoon.ClassFile.HClass; 8 cananian 1.1.2.2 import harpoon.ClassFile.HCode; 9 cananian 1.1.2.2 import harpoon.ClassFile.HCodeElement; 10 cananian 1.1.2.2 import harpoon.ClassFile.HMethod; 11 cananian 1.1.2.1 import harpoon.Backend.Generic.Frame; 12 cananian 1.1.2.2 import harpoon.Temp.Label; 13 cananian 1.1.2.2 import harpoon.Temp.Temp; 14 cananian 1.1.2.2 import harpoon.Temp.TempFactory; 15 cananian 1.1.2.2 import harpoon.Util.ArrayFactory; 16 cananian 1.1.2.2 import harpoon.Util.Util; 17 cananian 1.8 import net.cscott.jutil.GenericMultiMap; 18 cananian 1.7 import harpoon.Util.Collections.Graph; 19 cananian 1.8 import net.cscott.jutil.MultiMap; 20 cananian 1.8 import net.cscott.jutil.UnmodifiableIterator; 21 cananian 1.1.2.1 22 cananian 1.1.2.2 import java.io.BufferedReader; 23 cananian 1.1.2.2 import java.io.IOException; 24 cananian 1.1.2.2 import java.io.PrintWriter; 25 cananian 1.1.2.2 import java.io.StringReader; 26 cananian 1.7 import java.util.AbstractSet; 27 cananian 1.1.2.2 import java.util.Arrays; 28 cananian 1.6 import java.util.ConcurrentModificationException; 29 cananian 1.1.2.2 import java.util.HashSet; 30 cananian 1.1.2.2 import java.util.Iterator; 31 cananian 1.1.2.2 import java.util.List; 32 cananian 1.1.2.2 import java.util.NoSuchElementException; 33 cananian 1.1.2.2 import java.util.Set; 34 cananian 1.1.2.1 /** 35 cananian 1.1.2.1 * <code>IR.Assem.Code</code> is an abstract superclass of codeviews 36 cananian 1.1.2.1 * which use <code>Instr</code>s. 37 cananian 1.1.2.1 * 38 cananian 1.1.2.1 * @author C. Scott Ananian <cananian@alumni.princeton.edu> 39 cananian 1.8 * @version $Id: Code.java,v 1.8 2004/02/08 01:55:08 cananian Exp $ 40 cananian 1.1.2.1 */ 41 cananian 1.7 public abstract class Code extends HCode<Instr> 42 cananian 1.7 implements Graph<Instr,InstrEdge> { 43 cananian 1.1.2.1 private static boolean DEBUG = true; 44 cananian 1.1.2.1 45 cananian 1.1.2.1 /** The method that this code view represents. */ 46 cananian 1.1.2.1 protected HMethod parent; 47 cananian 1.1.2.1 /** The root Instr of the Instrs composing this code view. */ 48 cananian 1.1.2.1 protected Instr instrs; 49 cananian 1.1.2.1 /** Instruction factory. */ 50 cananian 1.1.2.1 protected final InstrFactory inf; 51 cananian 1.1.2.1 /** The Frame associated with this codeview. */ 52 cananian 1.1.2.1 protected final Frame frame; 53 cananian 1.6 /** Keep track of modifications to this <code>Code</code> so that the 54 cananian 1.6 * <code>getElementsI()</code> <code>Iterator</code> can fail-fast. */ 55 cananian 1.6 int modCount=0; 56 pnkfelix 1.1.2.4 57 pnkfelix 1.1.2.4 private InstrFactory newINF(final HMethod parent) { 58 pnkfelix 1.1.2.4 return newINF(parent, getName()); 59 pnkfelix 1.1.2.4 } 60 pnkfelix 1.1.2.4 61 cananian 1.1.2.1 /** Creates a new <code>InstrFactory</code> for this codeview. 62 cananian 1.1.2.1 * 63 cananian 1.1.2.1 * @param parent The method which this codeview corresponds to. 64 pnkfelix 1.1.2.4 * @param codeName The String that would be returned by a call 65 pnkfelix 1.1.2.4 * to <code>Code.this.getName()</code>. 66 cananian 1.1.2.1 * @return Returns a new instruction factory for the scope 67 cananian 1.1.2.1 * of the parent method and this codeview. 68 cananian 1.1.2.1 */ 69 pnkfelix 1.1.2.4 private InstrFactory newINF(final HMethod parent, String codeName) { 70 cananian 1.1.2.1 final String scope = parent.getDeclaringClass().getName() + "." + 71 pnkfelix 1.1.2.4 parent.getName() + parent.getDescriptor() + "/" + codeName; 72 cananian 1.1.2.1 return new InstrFactory() { 73 cananian 1.1.2.1 private final TempFactory tf = Temp.tempFactory(scope); 74 cananian 1.1.2.1 private int id = 0; 75 cananian 1.1.2.1 public TempFactory tempFactory() { return tf; } 76 cananian 1.3.2.2 public Code getParent() { return Code.this; } 77 cananian 1.1.2.1 public Frame getFrame() { return frame; } 78 cananian 1.1.2.1 public synchronized int getUniqueID() { return id++; } 79 cananian 1.1.2.1 }; 80 pnkfelix 1.1.2.4 } 81 pnkfelix 1.1.2.4 82 pnkfelix 1.1.2.4 /** constructor. */ 83 pnkfelix 1.1.2.4 protected Code(HMethod parent, Frame frame, String codeName) { 84 pnkfelix 1.1.2.4 this.parent = parent; 85 pnkfelix 1.1.2.4 this.instrs = null; 86 pnkfelix 1.1.2.4 this.inf = newINF(parent, codeName); 87 pnkfelix 1.1.2.4 this.frame = frame; 88 cananian 1.1.2.1 } 89 cananian 1.1.2.1 90 cananian 1.1.2.1 /** constructor. */ 91 cananian 1.1.2.1 protected Code(HMethod parent, Frame frame) { 92 cananian 1.1.2.1 this.parent = parent; 93 cananian 1.1.2.1 this.instrs = null; 94 cananian 1.1.2.1 this.inf = newINF(parent); 95 cananian 1.1.2.1 this.frame = frame; 96 cananian 1.1.2.1 } 97 cananian 1.1.2.14 98 cananian 1.1.2.1 public HMethod getMethod() { return parent; } 99 cananian 1.3.2.2 public Instr getRootElement() { return instrs; } 100 cananian 1.3.2.2 public Instr[] getLeafElements() { return null; } 101 cananian 1.1.2.1 102 cananian 1.1.2.1 /** Returns an <code>Iterator</code> over the instructions in this 103 cananian 1.1.2.1 codeview. 104 cananian 1.1.2.1 * 105 cananian 1.1.2.1 * @return An iterator over the <code>Instr</code>s 106 cananian 1.1.2.1 * making up this code view. The root Instr is 107 cananian 1.1.2.1 * the first element in the iteration. */ 108 cananian 1.3.2.2 public Iterator<Instr> getElementsI() { 109 cananian 1.3.2.2 return new UnmodifiableIterator<Instr>() { 110 cananian 1.6 // record # of modifications to enable fail-fast. 111 cananian 1.6 int modCount = Code.this.modCount; 112 cananian 1.6 // setup starting point. 113 cananian 1.3.2.2 Instr instr = getRootElement(); 114 cananian 1.1.2.1 public boolean hasNext() { return (instr != null); } 115 cananian 1.3.2.2 public Instr next() { 116 cananian 1.6 if (modCount != Code.this.modCount) 117 cananian 1.6 throw new ConcurrentModificationException(); 118 cananian 1.1.2.1 if (instr == null) throw new NoSuchElementException(); 119 cananian 1.1.2.1 Instr r = instr; 120 cananian 1.1.2.1 instr = r.getNext(); 121 cananian 1.1.2.1 return r; 122 cananian 1.1.2.1 } 123 cananian 1.1.2.1 }; 124 cananian 1.1.2.1 } 125 cananian 1.1.2.1 126 cananian 1.1.2.1 /** Returns an array factory to create the instruction elements 127 cananian 1.1.2.1 * of this codeview. 128 cananian 1.1.2.1 * 129 cananian 1.1.2.1 * @return An ArrayFactory which produces Instrs. 130 cananian 1.1.2.1 */ 131 cananian 1.3.2.2 public ArrayFactory<Instr> elementArrayFactory() { 132 cananian 1.1.2.1 return Instr.arrayFactory; 133 cananian 1.7 } 134 cananian 1.7 135 cananian 1.7 // Graph interface 136 cananian 1.7 public Set<Instr> nodes() { 137 cananian 1.7 final List<Instr> l = getElementsL(); 138 cananian 1.7 return new AbstractSet<Instr>() { 139 cananian 1.7 public Iterator<Instr> iterator() { return l.iterator(); } 140 cananian 1.7 public int size() { return l.size(); } 141 cananian 1.7 }; 142 cananian 1.1.2.1 } 143 cananian 1.1.2.1 144 cananian 1.1.2.1 /** Allows access to the InstrFactory used by this codeview. 145 cananian 1.1.2.1 * 146 cananian 1.1.2.1 * @return The InstrFactory used by this codeview. 147 cananian 1.1.2.1 */ 148 cananian 1.1.2.1 public InstrFactory getInstrFactory() { 149 cananian 1.1.2.1 return inf; 150 cananian 1.1.2.1 } 151 cananian 1.1.2.1 152 cananian 1.1.2.1 public Frame getFrame() { 153 cananian 1.1.2.1 return frame; 154 pnkfelix 1.1.2.10 } 155 pnkfelix 1.1.2.10 156 pnkfelix 1.1.2.10 /** Prints the assembly instructions of <code>this</code> to 157 pnkfelix 1.1.2.13 System.out. 158 pnkfelix 1.1.2.10 @see Code#print(java.io.PrintWriter) 159 pnkfelix 1.1.2.10 */ 160 pnkfelix 1.1.2.10 public void print() { 161 pnkfelix 1.1.2.10 print(new java.io.PrintWriter(System.out)); 162 cananian 1.1.2.1 } 163 pnkfelix 1.1.2.13 164 pnkfelix 1.1.2.13 /** Prints the assembly instructions of <code>this</code> to 165 pnkfelix 1.1.2.13 <code>pw</code>. Default implementation is just a wrapper 166 pnkfelix 1.1.2.13 call to <code>myPrint(pw, true, false)</code>, which turns 167 pnkfelix 1.1.2.13 each Instr into its architecture specific assembly format and 168 pnkfelix 1.1.2.13 omits Instr ID number information. 169 pnkfelix 1.1.2.13 */ 170 cananian 1.3.2.2 public void print(java.io.PrintWriter pw, PrintCallback<Instr> callback) { 171 cananian 1.1.2.15 myPrint(pw, true, false, callback); 172 pnkfelix 1.1.2.13 } 173 pnkfelix 1.1.2.13 174 pnkfelix 1.1.2.17 /** Simple wrapper around myPrint passing a nop PrintCallback. */ 175 pnkfelix 1.1.2.17 public final void myPrint(java.io.PrintWriter apw, boolean assem) { 176 cananian 1.3.2.2 myPrint(apw, assem, new PrintCallback<Instr>()); 177 pnkfelix 1.1.2.17 } 178 pnkfelix 1.1.2.16 179 cananian 1.1.2.1 /** Displays the assembly instructions of this codeview. Attempts 180 cananian 1.1.2.1 * to do so in a well-formatted, easy to read way. <BR> 181 cananian 1.1.2.1 * XXX - currently uses generic, not so easy to read, printer. 182 cananian 1.1.2.1 * 183 pnkfelix 1.1.2.16 * @deprecated Use Code#myPrint(PrintWriter,boolean,PrintCallback) 184 pnkfelix 1.1.2.16 * 185 pnkfelix 1.1.2.13 * @param pw The PrintWriter to send the formatted output to. 186 pnkfelix 1.1.2.13 * @param assem If true, uses <code>toAssem(Instr)</code> to 187 pnkfelix 1.1.2.13 * convert Instrs to assembly form. Else just calls 188 pnkfelix 1.1.2.13 * <code>Instr.toString()</code>. 189 pnkfelix 1.1.2.13 * @param annotateID If true, prints out the ID for each Instr 190 pnkfelix 1.1.2.13 * before printing the Instr itself. 191 pnkfelix 1.1.2.13 * @see Code#toAssem 192 cananian 1.1.2.1 */ 193 pnkfelix 1.1.2.16 protected final void myPrint(java.io.PrintWriter apw, 194 pnkfelix 1.1.2.16 boolean assem, 195 pnkfelix 1.1.2.16 final boolean annotateID, 196 cananian 1.3.2.2 final PrintCallback<Instr> callback) { 197 cananian 1.3.2.2 myPrint(apw, assem, new PrintCallback<Instr>() { 198 cananian 1.3.2.2 public void printBefore(java.io.PrintWriter pw, Instr hce ){ 199 pnkfelix 1.1.2.16 callback.printBefore(pw,hce); 200 pnkfelix 1.1.2.16 if (annotateID) { 201 cananian 1.3.2.2 pw.print( hce.getID() ); 202 pnkfelix 1.1.2.16 pw.print( '\t' ); 203 pnkfelix 1.1.2.16 } 204 pnkfelix 1.1.2.16 } 205 cananian 1.3.2.2 public void printAfter(java.io.PrintWriter pw, Instr hce ){ 206 pnkfelix 1.1.2.16 callback.printAfter(pw, hce); 207 pnkfelix 1.1.2.16 } 208 pnkfelix 1.1.2.16 }); 209 pnkfelix 1.1.2.16 } 210 pnkfelix 1.1.2.16 211 pnkfelix 1.1.2.16 /** Displays the assembly instructions of this codeview. Attempts 212 pnkfelix 1.1.2.16 * to do so in a well-formatted, easy to read way. <BR> 213 pnkfelix 1.1.2.16 * XXX - currently uses generic, not so easy to read, printer. 214 pnkfelix 1.1.2.16 * 215 pnkfelix 1.1.2.16 * @param pw The PrintWriter to send the formatted output to. 216 pnkfelix 1.1.2.16 * @param assem If true, uses <code>toAssem(Instr)</code> to 217 pnkfelix 1.1.2.16 * convert Instrs to assembly form. Else just calls 218 pnkfelix 1.1.2.16 * <code>Instr.toString()</code>. 219 pnkfelix 1.1.2.16 * @see Code#toAssem 220 pnkfelix 1.1.2.16 */ 221 pnkfelix 1.1.2.16 protected final void myPrint(java.io.PrintWriter pw, 222 pnkfelix 1.1.2.16 boolean assem, 223 cananian 1.3.2.2 PrintCallback<Instr> callback) { 224 cananian 1.1.2.1 final HashSet outputLabels = new HashSet(); 225 cananian 1.1.2.5 final MultiMap labelsNeeded = new GenericMultiMap(); 226 cananian 1.1.2.1 227 cananian 1.1.2.9 for (Instr instr=instrs; instr != null; instr=instr.getNext()) { 228 pnkfelix 1.1.2.12 StringBuffer str = new StringBuffer(); 229 pnkfelix 1.1.2.13 230 cananian 1.1.2.1 if (instr instanceof InstrLABEL || 231 cananian 1.1.2.1 instr instanceof InstrDIRECTIVE) { 232 cananian 1.1.2.1 233 pnkfelix 1.1.2.13 str.append(instr.toString()); 234 cananian 1.1.2.1 235 cananian 1.1.2.1 } else { 236 cananian 1.1.2.1 try { 237 pnkfelix 1.1.2.17 String asmstr = (assem?toAssem(instr):instr.toString()); 238 pnkfelix 1.1.2.17 // adding one because reader requires sz > 0 239 cananian 1.1.2.1 BufferedReader reader = 240 pnkfelix 1.1.2.11 new BufferedReader 241 pnkfelix 1.1.2.17 (new StringReader(asmstr),1+asmstr.length()); 242 cananian 1.1.2.1 String s = reader.readLine(); 243 cananian 1.1.2.1 while (s != null) { 244 pnkfelix 1.1.2.7 str.append("\t"+ s); 245 cananian 1.1.2.1 s = reader.readLine(); 246 pnkfelix 1.1.2.7 if (s!=null) str.append("\n"); 247 cananian 1.1.2.1 } 248 cananian 1.1.2.1 } catch (IOException e ) { 249 cananian 1.3.2.1 assert false : "IOException " + e.toString() + 250 cananian 1.1.2.1 " should not be thrown during assembly"+ 251 cananian 1.3.2.1 " code processing."; 252 cananian 1.1.2.1 } 253 cananian 1.1.2.1 } 254 cananian 1.1.2.1 255 cananian 1.1.2.15 callback.printBefore(pw, instr); 256 pnkfelix 1.1.2.16 pw.print(str.toString()); // FSK: was println! 257 cananian 1.1.2.15 callback.printAfter(pw, instr); 258 pnkfelix 1.1.2.16 pw.println(); // FSK: moved after callback 259 cananian 1.1.2.1 } 260 cananian 1.1.2.1 261 cananian 1.1.2.1 pw.flush(); 262 cananian 1.1.2.1 263 cananian 1.1.2.1 } 264 cananian 1.1.2.1 265 cananian 1.1.2.1 /** Produces an assembly code string for <code>instr</code>. 266 cananian 1.1.2.1 * Uses getRegisterName() to do register name string mapping. 267 cananian 1.1.2.1 */ 268 cananian 1.1.2.1 public String toAssem(Instr instr) { 269 cananian 1.1.2.1 String assem = instr.getAssem(); 270 pnkfelix 1.1.2.12 StringBuffer s = new StringBuffer(assem.length()); 271 cananian 1.1.2.1 272 cananian 1.1.2.1 int len = assem.length(); 273 cananian 1.1.2.1 for(int i=0; i<len; i++) { 274 cananian 1.1.2.1 char c = assem.charAt(i); 275 cananian 1.1.2.1 switch(c) { 276 cananian 1.1.2.1 case '`': 277 cananian 1.1.2.1 Temp temp = null; 278 cananian 1.1.2.1 Label label = null; 279 cananian 1.1.2.1 boolean getReg = false; 280 cananian 1.1.2.1 i++; c = assem.charAt(i); 281 cananian 1.1.2.1 switch(c) { 282 cananian 1.1.2.1 case 'd': { 283 cananian 1.1.2.1 i++; int n = Character.digit(assem.charAt(i), 10); 284 cananian 1.1.2.1 if (n < instr.def().length) { 285 cananian 1.1.2.1 temp = instr.def()[n]; 286 cananian 1.1.2.1 getReg = true; 287 cananian 1.1.2.1 } else { 288 cananian 1.3.2.1 assert false : "index mismatch in "+assem + 289 cananian 1.3.2.1 " " + Arrays.asList(instr.def()); 290 cananian 1.1.2.1 s.append("d?"); 291 cananian 1.1.2.1 } 292 cananian 1.1.2.1 break; 293 cananian 1.1.2.1 } 294 cananian 1.1.2.1 case 's': { 295 cananian 1.1.2.1 i++; int n = Character.digit(assem.charAt(i), 10); 296 cananian 1.1.2.1 if (n < instr.use().length) { 297 cananian 1.1.2.1 temp = instr.use()[n]; 298 cananian 1.1.2.1 getReg = true; 299 cananian 1.1.2.1 } else { 300 cananian 1.3.2.1 assert false : "index mismatch in "+assem + 301 cananian 1.3.2.1 " " + Arrays.asList(instr.use()); 302 cananian 1.1.2.1 s.append("s?"); 303 cananian 1.1.2.1 } 304 cananian 1.1.2.1 break; 305 cananian 1.1.2.1 } 306 cananian 1.1.2.1 case 'L': { 307 cananian 1.1.2.1 i++; int n = Character.digit(assem.charAt(i), 10); 308 cananian 1.1.2.1 if (n < instr.getTargets().size()) { 309 cananian 1.1.2.1 label = (Label) instr.getTargets().get(n); 310 cananian 1.1.2.1 s.append(label); 311 cananian 1.1.2.1 } else { 312 cananian 1.3.2.1 assert false : "index mismatch in "+assem + 313 cananian 1.3.2.1 " " + Arrays.asList(instr.use()); 314 cananian 1.1.2.1 s.append("L?"); 315 cananian 1.1.2.1 } 316 cananian 1.1.2.1 break; 317 cananian 1.1.2.1 } 318 cananian 1.1.2.1 case '`': 319 cananian 1.1.2.1 s.append("`"); 320 cananian 1.1.2.1 break; 321 cananian 1.1.2.1 default: 322 cananian 1.3.2.1 assert false : "error parsing "+assem; 323 cananian 1.1.2.1 } 324 cananian 1.1.2.1 325 cananian 1.1.2.1 if (getReg) { 326 cananian 1.1.2.1 char lastChar = ' '; 327 pnkfelix 1.1.2.12 StringBuffer var = new StringBuffer(assem.length()); 328 cananian 1.1.2.1 boolean more = true; 329 cananian 1.1.2.1 while(more && i<(assem.length()-1)) { 330 cananian 1.1.2.1 i++; c = assem.charAt(i); 331 cananian 1.1.2.1 if (!Character.isLetterOrDigit(c)) { 332 cananian 1.1.2.1 lastChar = c; 333 cananian 1.1.2.1 more = false; 334 cananian 1.1.2.1 } else { 335 cananian 1.1.2.1 var.append(c); 336 cananian 1.1.2.1 } 337 cananian 1.1.2.1 } 338 cananian 1.1.2.1 /* 339 cananian 1.3.2.1 assert ( ! mustGetRegs ) || 340 cananian 1.1.2.1 frame.getRegFileInfo().isRegister(temp) || 341 cananian 1.3.2.1 registerAssigned(instr, temp) : ("final assembly output for "+ 342 cananian 1.1.2.1 "Instr: "+instr+" must have "+ 343 cananian 1.1.2.1 "reg assigned to Temp: "+temp); 344 cananian 1.1.2.1 */ 345 cananian 1.1.2.1 s.append(getRegisterName(instr, temp, 346 cananian 1.1.2.1 var.toString())); 347 cananian 1.1.2.1 s.append(lastChar); 348 cananian 1.1.2.1 } 349 cananian 1.1.2.1 350 cananian 1.1.2.1 break; 351 cananian 1.1.2.1 352 cananian 1.1.2.1 default: 353 cananian 1.1.2.1 s.append(c); 354 cananian 1.1.2.1 } 355 cananian 1.1.2.1 } 356 cananian 1.1.2.1 357 pnkfelix 1.1.2.8 // return formatCommentedInstr(s.toString(),instr.toString()); 358 pnkfelix 1.1.2.8 return s.toString(); 359 cananian 1.1.2.1 } 360 kkz 1.1.2.6 361 kkz 1.1.2.6 /** Returns the <code>Derivation</code> associated with 362 kkz 1.1.2.6 <code>this</code>. Returns <code>null</code> if 363 kkz 1.1.2.6 <code>Derivation</code> information is not available. 364 kkz 1.1.2.6 */ 365 kkz 1.1.2.6 public Derivation getDerivation() { return null; } 366 cananian 1.1.2.1 367 cananian 1.1.2.1 /** Returns an assembly code identifier for the register that 368 cananian 1.1.2.1 <code>val</code> will be stored into. 369 cananian 1.1.2.1 */ 370 pnkfelix 1.1.2.3 public abstract String getRegisterName(Instr i, Temp val, 371 pnkfelix 1.1.2.3 String suffix); 372 pnkfelix 1.1.2.8 373 pnkfelix 1.1.2.8 /** Returns an assembly code String version of <code>exec</code> 374 pnkfelix 1.1.2.8 with <code>orig</code> in the comments for <code>exec</code>. 375 pnkfelix 1.1.2.8 <BR> <B>requires:</B> <code>exec</code> and <code>orig</code> 376 pnkfelix 1.1.2.8 have the same number of lines 377 pnkfelix 1.1.2.8 <BR> <B>effects:</B> 378 pnkfelix 1.1.2.8 let s be a new empty string 379 pnkfelix 1.1.2.8 in for each line:le in <code>exec</code>, 380 pnkfelix 1.1.2.8 let lo be the next line of <code>orig</code> ; 381 pnkfelix 1.1.2.12 s += (le + " @" + lo) 382 pnkfelix 1.1.2.8 return s 383 pnkfelix 1.1.2.8 */ 384 pnkfelix 1.1.2.8 public static String formatCommentedInstr(String exec, String orig) { 385 pnkfelix 1.1.2.12 StringBuffer sb = new StringBuffer(exec.length() + orig.length()); 386 pnkfelix 1.1.2.8 try { 387 pnkfelix 1.1.2.8 BufferedReader er = new BufferedReader(new StringReader(exec)); 388 pnkfelix 1.1.2.8 BufferedReader or = new BufferedReader(new StringReader(orig)); 389 pnkfelix 1.1.2.8 String e = er.readLine(); 390 pnkfelix 1.1.2.8 String o = or.readLine(); 391 pnkfelix 1.1.2.8 while(e != null) { 392 pnkfelix 1.1.2.8 sb.append(e + " @ " + o); 393 pnkfelix 1.1.2.8 e = er.readLine(); 394 pnkfelix 1.1.2.8 o = or.readLine(); 395 pnkfelix 1.1.2.8 } 396 pnkfelix 1.1.2.8 return sb.toString(); 397 pnkfelix 1.1.2.8 } catch (IOException e) { 398 cananian 1.3.2.1 assert false; 399 pnkfelix 1.1.2.8 return "died"; 400 pnkfelix 1.1.2.8 } 401 pnkfelix 1.1.2.8 } 402 cananian 1.1.2.1 } 403 cananian 1.2