1 /*
  2  * Copyright (c) 1996, 2019, 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.CFVersion.DEFAULT_MAJOR_VERSION;
 26 import static org.openjdk.asmtools.jasm.CFVersion.DEFAULT_MINOR_VERSION;
 27 
 28 import org.openjdk.asmtools.common.Tool;
 29 import org.openjdk.asmtools.util.I18NResourceBundle;
 30 import org.openjdk.asmtools.util.ProductInfo;
 31 
 32 import java.io.*;
 33 import java.util.ArrayList;
 34 
 35 /**
 36  *
 37  *
 38  */
 39 public class Main extends Tool {
 40 
 41     public static final I18NResourceBundle i18n
 42             = I18NResourceBundle.getBundleForClass(Main.class);
 43 
 44     private File destDir = null;
 45     private boolean traceFlag = false;
 46     private long tm = System.currentTimeMillis();
 47     private ArrayList<String> v = new ArrayList<>();
 48     private boolean nowrite = false;
 49     private boolean nowarn = false;
 50     private boolean strict = false;
 51     private String props = null;
 52     private int nwarnings = 0;
 53     private CFVersion cfv = new CFVersion();
 54     private int bytelimit = 0;
 55     private boolean debugScanner = false;
 56     private boolean debugMembers = false;
 57     private boolean debugCP = false;
 58     private boolean debugAnnot = false;
 59     private boolean debugInstr = false;
 60 
 61 
 62     public Main(PrintWriter out, String programName) {
 63         super(out, programName);
 64         printCannotReadMsg = (fname) ->
 65             error( i18n.getString("jasm.error.cannot_read", fname));
 66     }
 67 
 68     public Main(PrintStream out, String program) {
 69         this(new PrintWriter(out), program);
 70     }
 71 
 72     @Override
 73     public void usage() {
 74         println(i18n.getString("jasm.usage"));
 75         println(i18n.getString("jasm.opt.d"));
 76         println(i18n.getString("jasm.opt.g"));
 77         println(i18n.getString("jasm.opt.v"));
 78         println(i18n.getString("jasm.opt.nowrite"));
 79         println(i18n.getString("jasm.opt.nowarn"));
 80         println(i18n.getString("jasm.opt.strict"));
 81         println(i18n.getString("jasm.opt.cv", DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION));
 82         println(i18n.getString("jasm.opt.version"));
 83     }
 84 
 85     /**
 86      * Run the compiler
 87      */
 88     private synchronized boolean parseArgs(String argv[]) {
 89         // Parse arguments
 90         boolean frozenCFV = false;
 91         for (int i = 0; i < argv.length; i++) {
 92             String arg = argv[i];
 93             switch (arg) {
 94                 case "-v":
 95                     traceFlag = true;
 96                     break;
 97                 case "-g":
 98                     super.DebugFlag = () -> true;
 99                     break;
100                 case "-nowrite":
101                     nowrite = true;
102                     break;
103                 case "-strict":
104                     strict = true;
105                     break;
106                 case "-nowarn":
107                     nowarn = true;
108                     break;
109                 case "-version":
110                     println(ProductInfo.FULL_VERSION);
111                     break;
112                 case "-d":
113                     if ((i + 1) >= argv.length) {
114                         error(i18n.getString("jasm.error.d_requires_argument"));
115                         usage();
116                         return false;
117                     }
118                     destDir = new File(argv[++i]);
119                     if (!destDir.exists()) {
120                         error(i18n.getString("jasm.error.does_not_exist", destDir.getPath()));
121                         return false;
122                     }
123                     break;
124                 // non-public options
125                 case "-XdScanner":
126                     debugScanner = true;
127                     break;
128                 case "-XdMember":
129                     debugMembers = true;
130                     break;
131                 case "-XdCP":
132                     debugCP = true;
133                     break;
134                 case "-XdInstr":
135                     debugInstr = true;
136                     break;
137                 case "-XdAnnot":
138                     debugAnnot = true;
139                     break;
140                 case "-XdAll":
141                     debugScanner = true;
142                     debugMembers = true;
143                     debugCP = true;
144                     debugInstr = true;
145                     debugAnnot = true;
146                     break;
147                 case "-Xdlimit":
148                     // parses file until the specified byte number
149                     if (i + 1 > argv.length) {
150                         println(" Error: Unspecified byte-limit");
151                         return false;
152                     } else {
153                         i++;
154                         String bytelimstr = argv[i];
155                         bytelimit = 0;
156                         try {
157                             bytelimit = Integer.parseInt(bytelimstr);
158                         } catch (NumberFormatException e) {
159                             println(" Error: Unspecified byte-limit");
160                             return false;
161                         }
162                     }
163                     break;
164                 case "-fixcv":
165                     // overrides cf version if it's defined in the source file.
166                     frozenCFV = true;
167                 // public options
168                 case "-cv":
169                     if ((i + 1) >= argv.length) {
170                         error(i18n.getString("jasm.error.cv_requires_arg"));
171                         usage();
172                         return false;
173                     }
174                     String[] versions = {"", ""};                      // workaround for String.split()
175                     int index = argv[++i].indexOf(".");                //
176                     if (index != -1) {                                 //
177                         versions[0] = argv[i].substring(0, index);     //
178                         versions[1] = argv[i].substring(index + 1);    //
179                     }                                                  //
180                     if (versions.length != 2) {
181                         error(i18n.getString("jasm.error.invalid_major_minor_param"));
182                         usage();
183                         return false;
184                     }
185                     try {
186                         cfv = new CFVersion(frozenCFV, Short.parseShort(versions[0]), Short.parseShort(versions[1]) );
187                     } catch (NumberFormatException e) {
188                         error(i18n.getString("jasm.error.invalid_major_minor_param"));
189                         usage();
190                         return false;
191                     }
192                     break;
193                 default:
194                     if (arg.startsWith("-")) {
195                         error(i18n.getString("jasm.error.invalid_option", arg));
196                         usage();
197                         return false;
198                     } else {
199                         v.add(argv[i]);
200                     }
201                     break;
202             }
203         }
204         if (v.size() == 0) {
205             usage();
206             return false;
207         }
208         if (strict) {
209             nowarn = false;
210         }
211         return true;
212     }
213 
214     private void reset() {
215         destDir = null;
216         traceFlag = false;
217         super.DebugFlag = () -> false;
218         System.currentTimeMillis();
219         v = new ArrayList<>();
220         nowrite = false;
221         nowarn = false;
222         strict = false;
223         props = null;
224         nwarnings = 0;
225         bytelimit = 0;
226     }
227 
228     /**
229      * Run the compiler
230      */
231     public synchronized boolean compile(String argv[]) {
232         // Reset the state of all objs
233         reset();
234 
235         boolean validArgs = parseArgs(argv);
236         if (!validArgs) {
237             return false;
238         }
239         // compile all input files
240         Environment sf = null;
241         try {
242             for (String inpname : v) {
243                 Parser p;
244 
245                 DataInputStream dataInputStream = getDataInputStream(inpname);
246                 if( dataInputStream == null ) {
247                     nerrors++;
248                     continue;
249                 }
250                 sf = new Environment(dataInputStream, inpname, out, nowarn);
251                 sf.traceFlag = traceFlag;
252                 sf.debugInfoFlag = DebugFlag.getAsBoolean();
253                 p = new Parser(sf, cfv.clone() );
254                 p.setDebugFlags(debugScanner, debugMembers, debugCP, debugAnnot, debugInstr);
255                 p.parseFile();
256 
257                 nerrors += sf.nerrors;
258                 nwarnings += sf.nwarnings;
259                 if (nowrite || (nerrors > 0)) {
260                     sf.flushErrors();
261                     continue;
262                 }
263                 try {
264                     ClassData[] clsData = p.getClassesData();
265                     for (int i = 0; i < clsData.length; i++) {
266                         ClassData cd = clsData[i];
267                         if (bytelimit > 0) {
268                             cd.setByteLimit(bytelimit);
269                         }
270                         cd.write(destDir);
271                     }
272                 } catch (IOException ex) {
273                     if (bytelimit > 0) {
274                         // IO Error thrown from user-specified byte count
275                         ex.printStackTrace();
276                         error("UserSpecified byte-limit at byte[" + bytelimit + "]: " +
277                                 ex.getMessage() + "\n" +
278                                 inpname + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
279                     } else {
280                         String er = i18n.getString("jasm.error.cannot_write", ex.getMessage());
281                         error(er + "\n" + inpname + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
282                     }
283                 }
284                 sf.flushErrors(); // possible errors from write()
285             }
286         } catch (Error ee) {
287             if (DebugFlag.getAsBoolean()) {
288                 ee.printStackTrace();
289             }
290             String er = ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_error");
291             error(er + "\n" + sf.getInputFileName() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
292         } catch (Exception ee) {
293             if (DebugFlag.getAsBoolean()) {
294                 ee.printStackTrace();
295             }
296             String er = ee.getMessage() + "\n" + ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_exception");
297             error(er + "\n" + sf.getInputFileName() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]");
298         }
299 
300         boolean errs = nerrors > 0;
301         boolean warns = (nwarnings > 0) && (!nowarn);
302         boolean errsOrWarns = errs || warns;
303         if (!errsOrWarns) {
304             return true;
305         }
306         if (errs) {
307             out.print(nerrors > 1 ? (nerrors + " errors") : "1 error");
308         }
309         if (errs && warns) {
310             out.print(", ");
311         }
312         if (warns) {
313             out.print(nwarnings > 1 ? (nwarnings + " warnings") : "1 warning");
314         }
315         println();
316         if (strict) {
317             return !errsOrWarns;
318         } else {
319             return !errs;
320         }
321     }
322 
323     /**
324      * main program
325      */
326     public static void main(String argv[]) {
327         Main compiler = new Main(new PrintWriter(System.out), "jasm");
328         System.exit(compiler.compile(argv) ? 0 : 1);
329     }
330 }