/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.nashorn.tools; import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.objects.NativeSymbol; import jdk.nashorn.internal.parser.Parser; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.ScriptingFunctions; import jdk.nashorn.internal.runtime.Symbol; import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; import jdk.nashorn.internal.runtime.options.Options; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StreamTokenizer; import java.io.StringReader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; import static jdk.nashorn.internal.runtime.Source.sourceFor; /** * Command line Shell for processing JavaScript files. */ public class Shell implements PartialParser { /** * Resource name for properties file */ private static final String MESSAGE_RESOURCE = "jdk.nashorn.tools.resources.Shell"; /** * Shell message bundle. */ protected static final ResourceBundle bundle = ResourceBundle.getBundle(MESSAGE_RESOURCE, Locale.getDefault()); /** * Exit code for command line tool - successful */ public static final int SUCCESS = 0; /** * Exit code for command line tool - error on command line */ public static final int COMMANDLINE_ERROR = 100; /** * Exit code for command line tool - error compiling script */ public static final int COMPILATION_ERROR = 101; /** * Exit code for command line tool - error during runtime */ public static final int RUNTIME_ERROR = 102; /** * Exit code for command line tool - i/o error */ public static final int IO_ERROR = 103; /** * Exit code for command line tool - internal error */ public static final int INTERNAL_ERROR = 104; /** * Constructor */ protected Shell() { } /** * Main entry point with the default input, output and error streams. * * @param args The command line arguments */ public static void main(final String[] args) { try { final int exitCode = main(System.in, System.out, System.err, args); if (exitCode != SUCCESS) { System.exit(exitCode); } } catch (final IOException e) { System.err.println(e); //bootstrapping, Context.err may not exist System.exit(IO_ERROR); } } /** * Starting point for executing a {@code Shell}. Starts a shell with the * given arguments and streams and lets it run until exit. * * @param in input stream for Shell * @param out output stream for Shell * @param err error stream for Shell * @param args arguments to Shell * * @return exit code * * @throws IOException if there's a problem setting up the streams */ public static int main(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException { return new Shell().run(in, out, err, args); } /** * Run method logic. * * @param in input stream for Shell * @param out output stream for Shell * @param err error stream for Shell * @param args arguments to Shell * * @return exit code * * @throws IOException if there's a problem setting up the streams */ protected final int run(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException { final Context context = makeContext(in, out, err, args); if (context == null) { return COMMANDLINE_ERROR; } final Global global = context.createGlobal(); final ScriptEnvironment env = context.getEnv(); final List files = env.getFiles(); if (files.isEmpty()) { return readEvalPrint(context, global); } if (env._compile_only) { return compileScripts(context, global, files); } if (env._fx) { return runFXScripts(context, global, files); } return runScripts(context, global, files); } /** * Make a new Nashorn Context to compile and/or run JavaScript files. * * @param in input stream for Shell * @param out output stream for Shell * @param err error stream for Shell * @param args arguments to Shell * * @return null if there are problems with option parsing. */ private static Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) { final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out); final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : new PrintStream(err); final PrintWriter wout = new PrintWriter(pout, true); final PrintWriter werr = new PrintWriter(perr, true); // Set up error handler. final ErrorManager errors = new ErrorManager(werr); // Set up options. final Options options = new Options("nashorn", werr); // parse options if (args != null) { try { final String[] prepArgs = preprocessArgs(args); options.process(prepArgs); } catch (final IllegalArgumentException e) { werr.println(bundle.getString("shell.usage")); options.displayHelp(e); return null; } } // detect scripting mode by any source's first character being '#' if (!options.getBoolean("scripting")) { for (final String fileName : options.getFiles()) { final File firstFile = new File(fileName); if (firstFile.isFile()) { try (final FileReader fr = new FileReader(firstFile)) { final int firstChar = fr.read(); // starts with '# if (firstChar == '#') { options.set("scripting", true); break; } } catch (final IOException e) { // ignore this. File IO errors will be reported later anyway } } } } return new Context(options, errors, wout, werr, Thread.currentThread().getContextClassLoader()); } /** * Preprocess the command line arguments passed in by the shell. This method checks, for the first non-option * argument, whether the file denoted by it begins with a shebang line. If so, it is assumed that execution in * shebang mode is intended. The consequence of this is that the identified script file will be treated as the * only script file, and all subsequent arguments will be regarded as arguments to the script. *

* This method canonicalizes the command line arguments to the form {@code