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