1 /* 2 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.openjdk.asmtools.jcoder; 24 25 import java.io.*; 26 import java.util.Hashtable; 27 28 import org.openjdk.asmtools.common.Tool; 29 import org.openjdk.asmtools.util.I18NResourceBundle; 30 31 /** 32 * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the 33 * end of a line, it always returns \n. It also parses UNICODE characters expressed as 34 * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It 35 * keeps track of the current position in the input stream. 36 * 37 * An position consists of: ((linenr << OFFSETBITS) | offset) this means that both 38 * the line number and the exact offset into the file are encoded in each postion 39 * value.<p> 40 */ 41 public class SourceFile implements org.openjdk.asmtools.jasm.Constants { 42 43 Tool tool; 44 45 boolean traceFlag = false; 46 boolean debugInfoFlag = false; 47 /** 48 * The increment for each character. 49 */ 50 static final int OFFSETINC = 1; 51 /** 52 * The increment for each line. 53 */ 54 static final int LINEINC = 1 << OFFSETBITS; 55 String inputFileName; 56 InputStream in; 57 PrintWriter out; 58 int pos; 59 private int chpos; 60 private int pushBack = -1; 61 62 public SourceFile(Tool tool, DataInputStream dataInputStream, String inputFileName, PrintWriter out) { 63 this.tool = tool; 64 this.inputFileName = inputFileName; 65 this.in = new BufferedInputStream(dataInputStream); 66 chpos = LINEINC; 67 this.out = out; 68 } 69 70 public String getInputFileName() { 71 return inputFileName; 72 } 73 74 public void closeInp() { 75 try { 76 in.close(); 77 } catch (IOException e) { 78 } 79 flushErrors(); 80 } 81 82 public int read() throws IOException { 83 pos = chpos; 84 chpos += OFFSETINC; 85 86 int c = pushBack; 87 if (c == -1) { 88 c = in.read(); 89 } else { 90 pushBack = -1; 91 } 92 93 // parse special characters 94 switch (c) { 95 case -2: 96 // -2 is a special code indicating a pushback of a backslash that 97 // definitely isn't the start of a unicode sequence. 98 return '\\'; 99 100 case '\\': 101 if ((c = in.read()) != 'u') { 102 pushBack = (c == '\\' ? -2 : c); 103 return '\\'; 104 } 105 // we have a unicode sequence 106 chpos += OFFSETINC; 107 while ((c = in.read()) == 'u') { 108 chpos += OFFSETINC; 109 } 110 111 // unicode escape sequence 112 int d = 0; 113 for (int i = 0; i < 4; i++, chpos += OFFSETINC, c = in.read()) { 114 switch (c) { 115 case '0': 116 case '1': 117 case '2': 118 case '3': 119 case '4': 120 case '5': 121 case '6': 122 case '7': 123 case '8': 124 case '9': 125 d = (d << 4) + c - '0'; 126 break; 127 128 case 'a': 129 case 'b': 130 case 'c': 131 case 'd': 132 case 'e': 133 case 'f': 134 d = (d << 4) + 10 + c - 'a'; 135 break; 136 137 case 'A': 138 case 'B': 139 case 'C': 140 case 'D': 141 case 'E': 142 case 'F': 143 d = (d << 4) + 10 + c - 'A'; 144 break; 145 146 default: 147 error(pos, "invalid.escape.char"); 148 pushBack = c; 149 return d; 150 } 151 } 152 pushBack = c; 153 return d; 154 155 case '\n': 156 chpos += LINEINC; 157 return '\n'; 158 159 case '\r': 160 if ((c = in.read()) != '\n') { 161 pushBack = c; 162 } else { 163 chpos += OFFSETINC; 164 } 165 chpos += LINEINC; 166 return '\n'; 167 168 default: 169 return c; 170 } 171 } 172 173 public int lineNumber(int pos) { 174 return pos >>> OFFSETBITS; 175 } 176 177 public int lineNumber() { 178 return pos >>> OFFSETBITS; 179 } 180 181 /*============================================================== Environment */ 182 /** 183 * The number of errors and warnings 184 */ 185 public int nerrors; 186 public int nwarnings; 187 188 public static final I18NResourceBundle i18n 189 = I18NResourceBundle.getBundleForClass(Main.class); 190 191 /* 192 * Until place for jasm.properties is defind, 193 * I have to keep them right here 194 * 195 static Hashtable properties = new Hashtable(40); 196 197 static { 198 // Scanner: 199 properties.put("err.eof.in.comment", "Comment not terminated at end of input."); 200 properties.put("err.invalid.number", "Invalid character \'%s\' in number."); 201 properties.put("err.invalid.octal.number", "Invalid character in octal number."); 202 properties.put("err.overflow", "Numeric overflow."); 203 properties.put("err.float.format", "Invalid floating point format."); 204 properties.put("err.eof.in.string", "String not terminated at end of input."); 205 properties.put("err.newline.in.string", "String not terminated at end of line."); 206 properties.put("err.funny.char", "Invalid character in input."); 207 properties.put("err.unbalanced.paren", "Unbalanced parentheses."); 208 // Parser: 209 properties.put("err.toplevel.expected", "Class or interface declaration expected."); 210 properties.put("err.token.expected", "'%s' expected."); 211 properties.put("err.identifier.expected", "Identifier expected."); 212 properties.put("err.name.expected", "Name expected."); 213 properties.put("err.io.exception", "I/O error in %s."); 214 properties.put("err.cannot.write", "Cannot write to %s."); 215 properties.put("warn.array.wronglength", "expected array length %s do not match real length %s; expected length written"); 216 properties.put("warn.attr.wronglength", "expected attribute length %s do not match real length %s; expected length written"); 217 properties.put("attrname.notfound", "Cannot find \"%s\" in constant pool"); 218 properties.put("err.attrname.expected", "Attribute's name or index expected."); 219 properties.put("err.element.expected", "Primary data item expected."); 220 properties.put("err.struct.expected", "Structured data item expected."); 221 properties.put("err.macro.undecl", "Macro %s undefined."); 222 } 223 static String getProperty(String nm) { 224 return (String) properties.get(nm); 225 } 226 */ 227 /** 228 * Error String 229 */ 230 String errorString(String err, Object arg1, Object arg2, Object arg3) { 231 String str = null; 232 233 if (!err.startsWith("warn.")) { 234 err = "err." + err; 235 } 236 //str = getProperty(err); 237 str = i18n.getString(err); 238 239 if (str == null) { 240 return "error message '" + err + "' not found"; 241 } 242 243 StringBuffer buf = new StringBuffer(); 244 for (int i = 0; i < str.length(); i++) { 245 char c = str.charAt(i); 246 if ((c == '%') && (i + 1 < str.length())) { 247 switch (str.charAt(++i)) { 248 case 's': 249 String arg = arg1.toString(); 250 for (int j = 0; j < arg.length(); j++) { 251 switch (c = arg.charAt(j)) { 252 case ' ': 253 case '\t': 254 case '\n': 255 case '\r': 256 buf.append((char) c); 257 break; 258 259 default: 260 if ((c > ' ') && (c <= 255)) { 261 buf.append((char) c); 262 } else { 263 buf.append('\\'); 264 buf.append('u'); 265 buf.append(Integer.toString(c, 16)); 266 } 267 } 268 } 269 arg1 = arg2; 270 arg2 = arg3; 271 break; 272 273 case '%': 274 buf.append('%'); 275 break; 276 277 default: 278 buf.append('?'); 279 break; 280 } 281 } else { 282 buf.append((char) c); 283 } 284 } 285 return buf.toString(); 286 } 287 288 /** 289 * List of outstanding error messages 290 */ 291 ErrorMessage errors; 292 293 /** 294 * Insert an error message in the list of outstanding error messages. The list is 295 * sorted on input position. 296 */ 297 void insertError(int where, String message) { 298 //output("ERR = " + message); 299 ErrorMessage msg = new ErrorMessage(where, message); 300 if (errors == null) { 301 errors = msg; 302 } else if (errors.where > where) { 303 msg.next = errors; 304 errors = msg; 305 } else { 306 ErrorMessage m = errors; 307 for (; (m.next != null) && (m.next.where <= where); m = m.next) { 308 ; 309 } 310 msg.next = m.next; 311 m.next = msg; 312 } 313 } 314 315 /** 316 * Flush outstanding errors 317 */ 318 public void flushErrors() { 319 if (errors == null) { 320 return; 321 } 322 323 try { 324 // Read the file 325 DataInputStream dataInputStream = tool.getDataInputStream(inputFileName); 326 if (dataInputStream == null) 327 return; 328 329 byte data[] = new byte[dataInputStream.available()]; 330 dataInputStream.read(data); 331 dataInputStream.close(); 332 333 // Report the errors 334 for (ErrorMessage msg = errors; msg != null; msg = msg.next) { 335 int ln = msg.where >>> OFFSETBITS; 336 int off = msg.where & ((1 << OFFSETBITS) - 1); 337 338 int i, j; 339 for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--) { 340 ; 341 } 342 for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++) { 343 ; 344 } 345 346 String prefix = inputFileName + ":" + ln + ":"; 347 outputln(prefix + " " + msg.message); 348 outputln(new String(data, i, j - i)); 349 350 char strdata[] = new char[(off - i) + 1]; 351 for (j = i; j < off; j++) { 352 strdata[j - i] = (data[j] == '\t') ? '\t' : ' '; 353 } 354 strdata[off - i] = '^'; 355 outputln(new String(strdata)); 356 } 357 } catch (IOException e) { 358 outputln("I/O exception"); 359 } 360 errors = null; 361 } 362 363 /** 364 * Output a string. This can either be an error message or something for debugging. 365 * This should be used instead of print. 366 */ 367 public void output(String msg) { 368 int len = msg.length(); 369 for (int i = 0; i < len; i++) { 370 out.write(msg.charAt(i)); 371 } 372 out.flush(); 373 } 374 375 /** 376 * Output a string. This can either be an error message or something for debugging. 377 * This should be used instead of println. 378 */ 379 public void outputln(String msg) { 380 output(msg); 381 out.write('\n'); 382 out.flush(); 383 } 384 385 /** 386 * Issue an error. 387 * @param where Offset in the source for the error 388 * @param err Error number (as defined in this interface) 389 * @param arg1 Optional argument to the error (null if not applicable) 390 * @param arg2 Optional argument to the error (null if not applicable) 391 * @param arg3 Optional argument to the error (null if not applicable) 392 */ 393 /** 394 * Issue an error 395 */ 396 public void error(int where, String err, Object arg1, Object arg2, Object arg3) { 397 String msg = errorString(err, arg1, arg2, arg3); 398 if (err.startsWith("warn.")) { 399 nwarnings++; 400 } else { 401 nerrors++; 402 } 403 traceln("error:" + msg); 404 insertError(where, msg); 405 } 406 407 public final void error(int where, String err, Object arg1, Object arg2) { 408 error(where, err, arg1, arg2, null); 409 } 410 411 public final void error(int where, String err, Object arg1) { 412 error(where, err, arg1, null, null); 413 } 414 415 public final void error(int where, String err) { 416 error(where, err, null, null, null); 417 } 418 419 public final void error(String err) { 420 error(pos, err, null, null, null); 421 } 422 423 public final void error(String err, Object arg1) { 424 error(pos, err, arg1, null, null); 425 } 426 427 /*============================================================== trace */ 428 public void trace(String message) { 429 if (traceFlag) { 430 output(message); 431 } 432 } 433 434 public void traceln(String message) { 435 if (traceFlag) { 436 outputln(message); 437 } 438 } 439 } // end SourceFile 440