Main.java revision 1326:a35490e96dc5
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
26package jdk.nashorn.internal.tools.nasgen;
27
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.io.PrintWriter;
33import jdk.internal.org.objectweb.asm.ClassReader;
34import jdk.internal.org.objectweb.asm.ClassWriter;
35import jdk.internal.org.objectweb.asm.Opcodes;
36import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
37
38/**
39 * Main class for the "nasgen" tool.
40 *
41 */
42public 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 {
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     */
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
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
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
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
183    private static void error(final String msg, final int exitCode) {
184        System.err.println(msg);
185        System.exit(exitCode);
186    }
187}
188