1 /* 2 * Copyright (c) 1996, 2020, 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.jasm; 24 25 import static org.openjdk.asmtools.jasm.Constants.EOF; 26 import static org.openjdk.asmtools.jasm.Constants.OFFSETBITS; 27 import org.openjdk.asmtools.util.I18NResourceBundle; 28 29 import java.io.*; 30 import java.nio.file.Paths; 31 import java.text.MessageFormat; 32 33 /** 34 * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the 35 * end of a line, it always returns \n. It also parses UNICODE characters expressed as 36 * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It 37 * keeps track of the current position in the input stream. 38 * 39 * An position consists of: ((linenr << OFFSETBITS) | offset) this means that both 40 * the line number and the exact offset into the file are encoded in each position 41 * value.<p> 42 */ 43 public class Environment { 44 45 /*-------------------------------------------------------- */ 46 /* Environment Inner Classes */ 47 /** 48 * A sorted list of error messages 49 */ 50 final class ErrorMessage { 51 52 int where; 53 String message; 54 ErrorMessage next; 55 56 /** 57 * Constructor 58 */ 59 ErrorMessage(int where, String message) { 60 this.where = where; 61 this.message = message; 62 } 63 } 64 65 /*-------------------------------------------------------- */ 66 /* Environment Fields */ 67 static boolean traceFlag = false; 68 boolean debugInfoFlag = false; 69 70 private String inputFileName; 71 private String simpleInputFileName; 72 public PrintWriter out; 73 private boolean nowarn; 74 private byte[] data; 75 private int bytepos; 76 private int linepos; 77 public int pos; 78 /*-------------------------------------------------------- */ 79 80 public Environment(DataInputStream dis, String inputFileName, PrintWriter out, boolean nowarn) throws IOException { 81 this.out = out; 82 this.inputFileName = inputFileName; 83 this.nowarn = nowarn; 84 // Read the file 85 data = new byte[dis.available()]; 86 dis.read(data); 87 dis.close(); 88 bytepos = 0; 89 linepos = 1; 90 } 91 92 public String getInputFileName() { 93 return inputFileName; 94 } 95 96 public String getSimpleInputFileName() { 97 if( simpleInputFileName == null ) { 98 simpleInputFileName = Paths.get(inputFileName).getFileName().toString(); 99 } 100 return simpleInputFileName; 101 } 102 103 int lookForward() { 104 try { 105 return data[bytepos]; 106 } catch (ArrayIndexOutOfBoundsException e) { 107 return EOF; 108 } 109 } 110 111 int convertUnicode() { 112 int c; 113 try { 114 while ((c = data[bytepos]) == 'u') { 115 bytepos++; 116 } 117 int d = 0; 118 for (int i = 0; i < 4; i++) { 119 switch (c) { 120 case '0': 121 case '1': 122 case '2': 123 case '3': 124 case '4': 125 case '5': 126 case '6': 127 case '7': 128 case '8': 129 case '9': 130 d = (d << 4) + c - '0'; 131 break; 132 case 'a': 133 case 'b': 134 case 'c': 135 case 'd': 136 case 'e': 137 case 'f': 138 d = (d << 4) + 10 + c - 'a'; 139 break; 140 case 'A': 141 case 'B': 142 case 'C': 143 case 'D': 144 case 'E': 145 case 'F': 146 d = (d << 4) + 10 + c - 'A'; 147 break; 148 default: 149 error(pos, "invalid.escape.char"); 150 return d; 151 } 152 ++bytepos; 153 c = data[bytepos]; 154 } 155 return d; 156 } catch (ArrayIndexOutOfBoundsException e) { 157 error(pos, "invalid.escape.char"); 158 return EOF; 159 } 160 } 161 162 public int read() { 163 int c; 164 pos = (linepos << OFFSETBITS) | bytepos; 165 try { 166 c = data[bytepos]; 167 } catch (ArrayIndexOutOfBoundsException e) { 168 return EOF; 169 } 170 bytepos++; 171 172 // parse special characters 173 switch (c) { 174 /* case '\\': 175 if (lookForward() != 'u') { 176 return '\\'; 177 } 178 // we have a unicode sequence 179 return convertUnicode();*/ 180 case '\n': 181 linepos++; 182 return '\n'; 183 184 case '\r': 185 if (lookForward() == '\n') { 186 bytepos++; 187 } 188 linepos++; 189 return '\n'; 190 191 default: 192 return c; 193 } 194 } 195 196 int lineNumber(int lcpos) { 197 return lcpos >>> OFFSETBITS; 198 } 199 200 int lineNumber() { 201 return lineNumber(pos); 202 } 203 204 int lineOffset(int lcpos) { 205 return lcpos & ((1 << OFFSETBITS) - 1); 206 } 207 208 int lineOffset() { 209 return lineOffset(pos); 210 } 211 212 /*============================================================== Environment */ 213 /** 214 * The number of errors and warnings 215 */ 216 public int nerrors; 217 public int nwarnings; 218 public static final I18NResourceBundle i18n 219 = I18NResourceBundle.getBundleForClass(Main.class); 220 221 /** 222 * Error String 223 */ 224 String errorString(String err, Object arg1, Object arg2, Object arg3) { 225 String str = null; 226 227 //str = getProperty(err); 228 str = i18n.getString(err); 229 if (str == null) { 230 return "error message '" + err + "' not found"; 231 } 232 233 StringBuffer buf = new StringBuffer(); 234 for (int i = 0; i < str.length(); i++) { 235 char c = str.charAt(i); 236 if ((c == '%') && (i + 1 < str.length())) { 237 switch (str.charAt(++i)) { 238 case 's': 239 String arg = arg1.toString(); 240 for (int j = 0; j < arg.length(); j++) { 241 switch (c = arg.charAt(j)) { 242 case ' ': 243 case '\t': 244 case '\n': 245 case '\r': 246 buf.append((char) c); 247 break; 248 249 default: 250 if ((c > ' ') && (c <= 255)) { 251 buf.append((char) c); 252 } else { 253 buf.append('\\'); 254 buf.append('u'); 255 buf.append(Integer.toString(c, 16)); 256 } 257 } 258 } 259 arg1 = arg2; 260 arg2 = arg3; 261 break; 262 263 case '%': 264 buf.append('%'); 265 break; 266 267 default: 268 buf.append('?'); 269 break; 270 } 271 } else { 272 buf.append((char) c); 273 } 274 } 275 // KTL 276 // Need to do message format to substitute args 277 String msg = buf.toString(); 278 MessageFormat form = new MessageFormat(msg); 279 Object args[] = {arg1, arg2, arg3}; 280 msg = form.format(args); 281 282 return msg; 283 284 } 285 286 /** 287 * List of outstanding error messages 288 */ 289 ErrorMessage errors; 290 291 /** 292 * Insert an error message in the list of outstanding error messages. The list is 293 * sorted on input position. 294 */ 295 void insertError(int where, String message) { 296 //output("ERR = " + message); 297 ErrorMessage msg = new ErrorMessage(where, message); 298 if (errors == null) { 299 errors = msg; 300 } else if (errors.where > where) { 301 msg.next = errors; 302 errors = msg; 303 } else { 304 ErrorMessage m = errors; 305 for (; (m.next != null) && (m.next.where <= where); m = m.next); 306 msg.next = m.next; 307 m.next = msg; 308 } 309 } 310 311 /** 312 * Flush outstanding errors 313 */ 314 public void flushErrors() { 315 if (errors == null) { 316 traceln("flushErrors: errors == null"); 317 return; 318 } 319 320 // Report the errors 321 for (ErrorMessage msg = errors; msg != null; msg = msg.next) { 322 int off = lineOffset(msg.where); 323 324 int i, j; 325 for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--); 326 for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++); 327 328 outputln( String.format( "%s (%d:%d) %s", getSimpleInputFileName(), lineNumber(msg.where), off - i, msg.message)); 329 outputln(new String(data, i, j - i)); 330 331 char strdata[] = new char[(off - i) + 1]; 332 for (j = i; j < off; j++) { 333 strdata[j - i] = (data[j] == '\t') ? '\t' : ' '; 334 } 335 strdata[off - i] = '^'; 336 outputln(new String(strdata)); 337 } 338 errors = null; 339 } 340 341 /** 342 * Output a string. This can either be an error message or something for debugging. 343 * This should be used instead of print. 344 */ 345 public void output(String msg) { 346 int len = msg.length(); 347 for (int i = 0; i < len; i++) { 348 out.write(msg.charAt(i)); 349 } 350 out.flush(); 351 } 352 353 /** 354 * Output a string. This can either be an error message or something for debugging. 355 * This should be used instead of println. 356 */ 357 public void outputln(String msg) { 358 output((msg == null ? "" : msg) + "\n"); 359 } 360 361 /** 362 * Issue an error. source - the input source, usually a file name string offset - the 363 * offset in the source of the error err - the error number (as defined in this 364 * interface) arg1 - an optional argument to the error (null if not applicable) arg2 - 365 * a second optional argument to the error (null if not applicable) arg3 - a third 366 * optional argument to the error (null if not applicable) 367 */ 368 /** 369 * Issue an error 370 */ 371 public void error(int where, String err, Object arg1, Object arg2, Object arg3) { 372 String msg; 373 if (err.startsWith("warn.")) { 374 if (nowarn) { 375 return; 376 } 377 nwarnings++; 378 msg = "Warning: "; 379 } else { 380 err = "err." + err; 381 nerrors++; 382 msg = "Error: "; 383 } 384 msg = msg + errorString(err, arg1, arg2, arg3); 385 traceln(msg); 386 insertError(where, msg); 387 } 388 389 public final void error(int where, String err, Object arg1, Object arg2) { 390 error(where, err, arg1, arg2, null); 391 } 392 393 public final void error(int where, String err, Object arg1) { 394 error(where, err, arg1, null, null); 395 } 396 397 public final void error(int where, String err) { 398 error(where, err, null, null, null); 399 } 400 401 public final void error(String err, Object arg1, Object arg2, Object arg3) { 402 error(pos, err, arg1, arg2, arg3); 403 } 404 405 public final void error(String err, Object arg1, Object arg2) { 406 error(pos, err, arg1, arg2, null); 407 } 408 409 public final void error(String err, Object arg1) { 410 error(pos, err, arg1, null, null); 411 } 412 413 public final void error(String err) { 414 error(pos, err, null, null, null); 415 } 416 417 public final String errorStr(String err, Object arg1, Object arg2, Object arg3) { 418 return errorString(err, arg1, arg2, arg3); 419 } 420 421 public final String errorStr(String err, Object arg1, Object arg2) { 422 return errorStr(err, arg1, arg2, null); 423 } 424 425 public final String errorStr(String err, Object arg1) { 426 return errorStr(err, arg1, null, null); 427 } 428 429 public final String errorStr(String err) { 430 return errorStr(err, null, null, null); 431 } 432 433 /*============================================================== trace */ 434 public boolean isTraceEnabled() { 435 return traceFlag; 436 } 437 438 public boolean isDebugEnabled() { 439 return debugInfoFlag; 440 } 441 442 void trace(String message) { 443 if (traceFlag) { 444 output(message); 445 } 446 } 447 448 void traceln(String message) { 449 if (traceFlag) { 450 outputln(message); 451 } 452 } 453 454 } // end Environment