1 /*
2  * Copyright (c) 2010, 2013, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.nashorn.internal.tools.nasgen;
27 
28 import java.io.File;
29 import java.io.FileInputStream;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import jdk.internal.org.objectweb.asm.ClassReader;
34 import jdk.internal.org.objectweb.asm.ClassWriter;
35 import jdk.internal.org.objectweb.asm.Opcodes;
36 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
37 
38 /**
39  * Main class for the "nasgen" tool.
40  *
41  */
42 public class Main {
43     /**
44      * ASM version to be used by nasgen tool.
45      */
46     public static final int ASM_VERSION = Opcodes.ASM5;
47 
48     private static final boolean DEBUG = Boolean.getBoolean("nasgen.debug");
49 
50     private interface ErrorReporter {
error(String msg)51         public void error(String msg);
52     }
53 
54     /**
55      * Public entry point for Nasgen if invoked from command line. Nasgen takes three arguments
56      * in order: input directory, package list, output directory
57      *
58      * @param args argument vector
59      */
main(final String[] args)60     public static void main(final String[] args) {
61         final ErrorReporter reporter = new ErrorReporter() {
62             @Override
63             public void error(final String msg) {
64                 Main.error(msg, 1);
65             }
66         };
67         if (args.length == 3) {
68             processAll(args[0], args[1], args[2], reporter);
69         } else {
70             error("Usage: nasgen <input-dir> <package-list> <output-dir>", 1);
71         }
72     }
73 
processAll(final String in, final String pkgList, final String out, final ErrorReporter reporter)74     private static void processAll(final String in, final String pkgList, final String out, final ErrorReporter reporter) {
75         final File inDir = new File(in);
76         if (!inDir.exists() || !inDir.isDirectory()) {
77             reporter.error(in + " does not exist or not a directory");
78             return;
79         }
80 
81         final File outDir = new File(out);
82         if (!outDir.exists() || !outDir.isDirectory()) {
83             reporter.error(out + " does not exist or not a directory");
84             return;
85         }
86 
87         final String[] packages = pkgList.split(":");
88         for (String pkg : packages) {
89             pkg = pkg.replace('.', File.separatorChar);
90             final File dir = new File(inDir, pkg);
91             final File[] classes = dir.listFiles();
92             for (final File clazz : classes) {
93                 if (clazz.isFile() && clazz.getName().endsWith(".class")) {
94                     if (! process(clazz, new File(outDir, pkg), reporter)) {
95                         return;
96                     }
97                 }
98             }
99         }
100     }
101 
process(final File inFile, final File outDir, final ErrorReporter reporter)102     private static boolean process(final File inFile, final File outDir, final ErrorReporter reporter) {
103         try {
104             byte[] buf = new byte[(int)inFile.length()];
105 
106             try (FileInputStream fin = new FileInputStream(inFile)) {
107                 fin.read(buf);
108             }
109 
110             final ScriptClassInfo sci = ClassGenerator.getScriptClassInfo(buf);
111 
112             if (sci != null) {
113                 try {
114                     sci.verify();
115                 } catch (final Exception e) {
116                     reporter.error(e.getMessage());
117                     return false;
118                 }
119 
120                 // create necessary output package dir
121                 outDir.mkdirs();
122 
123                 // instrument @ScriptClass
124                 final ClassWriter writer = ClassGenerator.makeClassWriter();
125                 final ClassReader reader = new ClassReader(buf);
126                 final ScriptClassInstrumentor inst = new ScriptClassInstrumentor(writer, sci);
127                 reader.accept(inst, 0);
128                 //noinspection UnusedAssignment
129 
130                 // write instrumented class
131                 try (FileOutputStream fos = new FileOutputStream(new File(outDir, inFile.getName()))) {
132                     buf = writer.toByteArray();
133                     if (DEBUG) {
134                         verify(buf);
135                     }
136                     fos.write(buf);
137                 }
138 
139                 // simple class name without package prefix
140                 String simpleName = inFile.getName();
141                 simpleName = simpleName.substring(0, simpleName.indexOf(".class"));
142 
143                 if (sci.isPrototypeNeeded()) {
144                     // generate prototype class
145                     final PrototypeGenerator protGen = new PrototypeGenerator(sci);
146                     buf = protGen.getClassBytes();
147                     if (DEBUG) {
148                         verify(buf);
149                     }
150                     try (FileOutputStream fos = new FileOutputStream(new File(outDir, simpleName + StringConstants.PROTOTYPE_SUFFIX + ".class"))) {
151                         fos.write(buf);
152                     }
153                 }
154 
155                 if (sci.isConstructorNeeded()) {
156                     // generate constructor class
157                     final ConstructorGenerator consGen = new ConstructorGenerator(sci);
158                     buf = consGen.getClassBytes();
159                     if (DEBUG) {
160                         verify(buf);
161                     }
162                     try (FileOutputStream fos = new FileOutputStream(new File(outDir, simpleName + StringConstants.CONSTRUCTOR_SUFFIX + ".class"))) {
163                         fos.write(buf);
164                     }
165                 }
166             }
167             return true;
168         } catch (final IOException | RuntimeException e) {
169             if (DEBUG) {
170                 e.printStackTrace(System.err);
171             }
172             reporter.error(e.getMessage());
173 
174             return false;
175         }
176     }
177 
verify(final byte[] buf)178     private static void verify(final byte[] buf) {
179         final ClassReader cr = new ClassReader(buf);
180         CheckClassAdapter.verify(cr, false, new PrintWriter(System.err));
181     }
182 
error(final String msg, final int exitCode)183     private static void error(final String msg, final int exitCode) {
184         System.err.println(msg);
185         System.exit(exitCode);
186     }
187 }
188