Tue, 07 May 2013 14:36:57 +0200
8013913: Removed Source field from all nodes except FunctionNode in order to save footprint
Reviewed-by: jlaskey, attila
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 */
26 package jdk.nashorn.tools;
28 import java.io.BufferedReader;
29 import java.io.File;
30 import java.io.FileReader;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.InputStreamReader;
34 import java.io.OutputStream;
35 import java.io.PrintStream;
36 import java.io.PrintWriter;
37 import java.security.AccessController;
38 import java.security.PrivilegedAction;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.ResourceBundle;
42 import jdk.nashorn.api.scripting.NashornException;
43 import jdk.nashorn.internal.codegen.Compiler;
44 import jdk.nashorn.internal.ir.FunctionNode;
45 import jdk.nashorn.internal.ir.debug.ASTWriter;
46 import jdk.nashorn.internal.ir.debug.PrintVisitor;
47 import jdk.nashorn.internal.parser.Parser;
48 import jdk.nashorn.internal.runtime.Context;
49 import jdk.nashorn.internal.runtime.ErrorManager;
50 import jdk.nashorn.internal.runtime.Property;
51 import jdk.nashorn.internal.runtime.ScriptEnvironment;
52 import jdk.nashorn.internal.runtime.ScriptFunction;
53 import jdk.nashorn.internal.runtime.ScriptObject;
54 import jdk.nashorn.internal.runtime.ScriptRuntime;
55 import jdk.nashorn.internal.runtime.Source;
56 import jdk.nashorn.internal.runtime.options.Options;
58 /**
59 * Command line Shell for processing JavaScript files.
60 */
61 public class Shell {
63 /**
64 * Resource name for properties file
65 */
66 private static final String MESSAGE_RESOURCE = "jdk.nashorn.tools.resources.Shell";
67 /**
68 * Shell message bundle.
69 */
70 private static ResourceBundle bundle;
72 static {
73 // Without do privileged, under security manager messages can not be
74 // loaded.
75 bundle = AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
76 @Override
77 public ResourceBundle run() {
78 return ResourceBundle.getBundle(MESSAGE_RESOURCE, Locale.getDefault());
79 }
80 });
81 }
83 /**
84 * Exit code for command line tool - successful
85 */
86 public static final int SUCCESS = 0;
87 /**
88 * Exit code for command line tool - error on command line
89 */
90 public static final int COMMANDLINE_ERROR = 100;
91 /**
92 * Exit code for command line tool - error compiling script
93 */
94 public static final int COMPILATION_ERROR = 101;
95 /**
96 * Exit code for command line tool - error during runtime
97 */
98 public static final int RUNTIME_ERROR = 102;
99 /**
100 * Exit code for command line tool - i/o error
101 */
102 public static final int IO_ERROR = 103;
103 /**
104 * Exit code for command line tool - internal error
105 */
106 public static final int INTERNAL_ERROR = 104;
108 /**
109 * Constructor
110 */
111 protected Shell() {
112 }
114 /**
115 * Main entry point with the default input, output and error streams.
116 *
117 * @param args The command line arguments
118 */
119 public static void main(final String[] args) {
120 try {
121 System.exit(main(System.in, System.out, System.err, args));
122 } catch (final IOException e) {
123 System.err.println(e); //bootstrapping, Context.err may not exist
124 System.exit(IO_ERROR);
125 }
126 }
128 /**
129 * Starting point for executing a {@code Shell}. Starts a shell with the
130 * given arguments and streams and lets it run until exit.
131 *
132 * @param in input stream for Shell
133 * @param out output stream for Shell
134 * @param err error stream for Shell
135 * @param args arguments to Shell
136 *
137 * @return exit code
138 *
139 * @throws IOException if there's a problem setting up the streams
140 */
141 public static int main(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException {
142 return new Shell().run(in, out, err, args);
143 }
145 /**
146 * Run method logic.
147 *
148 * @param in input stream for Shell
149 * @param out output stream for Shell
150 * @param err error stream for Shell
151 * @param args arguments to Shell
152 *
153 * @return exit code
154 *
155 * @throws IOException if there's a problem setting up the streams
156 */
157 protected final int run(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException {
158 final Context context = makeContext(in, out, err, args);
159 if (context == null) {
160 return COMMANDLINE_ERROR;
161 }
163 final ScriptObject global = context.createGlobal();
164 final ScriptEnvironment env = context.getEnv();
165 final List<String> files = env.getFiles();
166 if (files.isEmpty()) {
167 return readEvalPrint(context, global);
168 }
170 if (env._compile_only) {
171 return compileScripts(context, global, files);
172 }
174 if (env._fx) {
175 return runFXScripts(context, global, files);
176 }
178 return runScripts(context, global, files);
179 }
181 /**
182 * Make a new Nashorn Context to compile and/or run JavaScript files.
183 *
184 * @param in input stream for Shell
185 * @param out output stream for Shell
186 * @param err error stream for Shell
187 * @param args arguments to Shell
188 *
189 * @return null if there are problems with option parsing.
190 */
191 @SuppressWarnings("resource")
192 private static Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) {
193 final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
194 final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : new PrintStream(err);
195 final PrintWriter wout = new PrintWriter(pout, true);
196 final PrintWriter werr = new PrintWriter(perr, true);
198 // Set up error handler.
199 final ErrorManager errors = new ErrorManager(werr);
200 // Set up options.
201 final Options options = new Options("nashorn", werr);
203 // parse options
204 if (args != null) {
205 try {
206 options.process(args);
207 } catch (final IllegalArgumentException e) {
208 werr.println(bundle.getString("shell.usage"));
209 options.displayHelp(e);
210 return null;
211 }
212 }
214 // detect scripting mode by any source's first character being '#'
215 if (!options.getBoolean("scripting")) {
216 for (final String fileName : options.getFiles()) {
217 final File firstFile = new File(fileName);
218 if (firstFile.isFile()) {
219 try (final FileReader fr = new FileReader(firstFile)) {
220 final int firstChar = fr.read();
221 // starts with '#
222 if (firstChar == '#') {
223 options.set("scripting", true);
224 break;
225 }
226 } catch (final IOException e) {
227 // ignore this. File IO errors will be reported later anyway
228 }
229 }
230 }
231 }
233 return new Context(options, errors, wout, werr, Thread.currentThread().getContextClassLoader());
234 }
236 /**
237 * Compiles the given script files in the command line
238 *
239 * @param context the nashorn context
240 * @param global the global scope
241 * @param files the list of script files to compile
242 *
243 * @return error code
244 * @throws IOException when any script file read results in I/O error
245 */
246 private static int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
247 final ScriptObject oldGlobal = Context.getGlobal();
248 final boolean globalChanged = (oldGlobal != global);
249 final ScriptEnvironment env = context.getEnv();
250 try {
251 if (globalChanged) {
252 Context.setGlobal(global);
253 }
254 final ErrorManager errors = context.getErrorManager();
256 // For each file on the command line.
257 for (final String fileName : files) {
258 final FunctionNode functionNode = new Parser(env, new Source(fileName, new File(fileName)), errors).parse();
260 if (errors.getNumberOfErrors() != 0) {
261 return COMPILATION_ERROR;
262 }
264 if (env._print_ast) {
265 context.getErr().println(new ASTWriter(functionNode));
266 }
268 if (env._print_parse) {
269 context.getErr().println(new PrintVisitor(functionNode));
270 }
272 //null - pass no code installer - this is compile only
273 new Compiler(env).compile(functionNode);
274 }
275 } finally {
276 env.getOut().flush();
277 env.getErr().flush();
278 if (globalChanged) {
279 Context.setGlobal(oldGlobal);
280 }
281 }
283 return SUCCESS;
284 }
286 /**
287 * Runs the given JavaScript files in the command line
288 *
289 * @param context the nashorn context
290 * @param global the global scope
291 * @param files the list of script files to run
292 *
293 * @return error code
294 * @throws IOException when any script file read results in I/O error
295 */
296 private int runScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
297 final ScriptObject oldGlobal = Context.getGlobal();
298 final boolean globalChanged = (oldGlobal != global);
299 try {
300 if (globalChanged) {
301 Context.setGlobal(global);
302 }
303 final ErrorManager errors = context.getErrorManager();
305 // For each file on the command line.
306 for (final String fileName : files) {
307 final File file = new File(fileName);
308 final ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
309 if (script == null || errors.getNumberOfErrors() != 0) {
310 return COMPILATION_ERROR;
311 }
313 try {
314 apply(script, global);
315 } catch (final NashornException e) {
316 errors.error(e.toString());
317 if (context.getEnv()._dump_on_error) {
318 e.printStackTrace(context.getErr());
319 }
321 return RUNTIME_ERROR;
322 }
323 }
324 } finally {
325 context.getOut().flush();
326 context.getErr().flush();
327 if (globalChanged) {
328 Context.setGlobal(oldGlobal);
329 }
330 }
332 return SUCCESS;
333 }
335 /**
336 * Runs launches "fx:bootstrap.js" with the given JavaScript files provided
337 * as arguments.
338 *
339 * @param context the nashorn context
340 * @param global the global scope
341 * @param files the list of script files to provide
342 *
343 * @return error code
344 * @throws IOException when any script file read results in I/O error
345 */
346 private static int runFXScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
347 final ScriptObject oldGlobal = Context.getGlobal();
348 final boolean globalChanged = (oldGlobal != global);
349 try {
350 if (globalChanged) {
351 Context.setGlobal(global);
352 }
354 global.addOwnProperty("$GLOBAL", Property.NOT_ENUMERABLE, global);
355 global.addOwnProperty("$SCRIPTS", Property.NOT_ENUMERABLE, files);
356 context.load(global, "fx:bootstrap.js");
357 } catch (final NashornException e) {
358 context.getErrorManager().error(e.toString());
359 if (context.getEnv()._dump_on_error) {
360 e.printStackTrace(context.getErr());
361 }
363 return RUNTIME_ERROR;
364 } finally {
365 context.getOut().flush();
366 context.getErr().flush();
367 if (globalChanged) {
368 Context.setGlobal(oldGlobal);
369 }
370 }
372 return SUCCESS;
373 }
375 /**
376 * Hook to ScriptFunction "apply". A performance metering shell may
377 * introduce enter/exit timing here.
378 *
379 * @param target target function for apply
380 * @param self self reference for apply
381 *
382 * @return result of the function apply
383 */
384 protected Object apply(final ScriptFunction target, final Object self) {
385 return ScriptRuntime.apply(target, self);
386 }
388 /**
389 * read-eval-print loop for Nashorn shell.
390 *
391 * @param context the nashorn context
392 * @param global global scope object to use
393 * @return return code
394 */
395 private static int readEvalPrint(final Context context, final ScriptObject global) {
396 final String prompt = bundle.getString("shell.prompt");
397 final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
398 final PrintWriter err = context.getErr();
399 final ScriptObject oldGlobal = Context.getGlobal();
400 final boolean globalChanged = (oldGlobal != global);
401 final ScriptEnvironment env = context.getEnv();
403 try {
404 if (globalChanged) {
405 Context.setGlobal(global);
406 }
408 // initialize with "shell.js" script
409 try {
410 final Source source = new Source("<shell.js>", Shell.class.getResource("resources/shell.js"));
411 context.eval(global, source.getString(), global, "<shell.js>", false);
412 } catch (final Exception e) {
413 err.println(e);
414 if (env._dump_on_error) {
415 e.printStackTrace(err);
416 }
418 return INTERNAL_ERROR;
419 }
421 while (true) {
422 err.print(prompt);
423 err.flush();
425 String source = "";
426 try {
427 source = in.readLine();
428 } catch (final IOException ioe) {
429 err.println(ioe.toString());
430 }
432 if (source == null) {
433 break;
434 }
436 Object res;
437 try {
438 res = context.eval(global, source, global, "<shell>", env._strict);
439 } catch (final Exception e) {
440 err.println(e);
441 if (env._dump_on_error) {
442 e.printStackTrace(err);
443 }
444 continue;
445 }
447 if (res != null && res != ScriptRuntime.UNDEFINED) {
448 err.println(ScriptRuntime.safeToString(res));
449 }
450 }
451 } finally {
452 if (globalChanged) {
453 Context.setGlobal(global);
454 }
455 }
457 return SUCCESS;
458 }
459 }