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