/*
 * Copyright (c) 2015, 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.jjs;

import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;

import java.awt.Desktop;
import java.awt.GraphicsEnvironment;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;
import jdk.internal.jline.console.completer.Completer;
import jdk.internal.jline.console.UserInterruptException;
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.objects.NativeJava;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.tools.Shell;

Interactive command line Shell for Nashorn.
/** * Interactive command line Shell for Nashorn. */
public final class Main extends Shell { private Main() {} private static final String DOC_PROPERTY_NAME = "__doc__"; static final boolean DEBUG = Boolean.getBoolean("nashorn.jjs.debug"); static final boolean HEADLESS = GraphicsEnvironment.isHeadless(); // file where history is persisted. private static final File HIST_FILE = new File(new File(System.getProperty("user.home")), ".jjs.history");
Main entry point with the default input, output and error streams.
Params:
  • args – The command line arguments
/** * 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 Shell. Starts a shell with the given arguments and streams and lets it run until exit.
Params:
  • in – input stream for Shell
  • out – output stream for Shell
  • err – error stream for Shell
  • args – arguments to Shell
Throws:
  • IOException – if there's a problem setting up the streams
Returns:exit code
/** * 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 Main().run(in, out, err, args); }
read-eval-print loop for Nashorn shell.
Params:
  • context – the nashorn context
  • global – global scope object to use
Returns:return code
/** * read-eval-print loop for Nashorn shell. * * @param context the nashorn context * @param global global scope object to use * @return return code */
protected int readEvalPrint(final Context context, final Global global) { final ScriptEnvironment env = context.getEnv(); final String prompt = bundle.getString("shell.prompt"); final String prompt2 = bundle.getString("shell.prompt2"); final PrintWriter err = context.getErr(); final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); final PropertiesHelper propsHelper = new PropertiesHelper(context); final NashornCompleter completer = new NashornCompleter(context, global, this, propsHelper); try (final Console in = new Console(System.in, System.out, HIST_FILE, completer, str -> { try { final Object res = context.eval(global, str, global, "<shell>"); if (res != null && res != UNDEFINED) { // Special case Java types: show the javadoc for the class. if (NativeJava.isType(UNDEFINED, res)) { final String typeName = NativeJava.typeName(UNDEFINED, res).toString(); final String url = typeName.replace('.', '/').replace('$', '.') + ".html"; openBrowserForJavadoc(url); } else if (res instanceof NativeJavaPackage) { final String pkgName = ((NativeJavaPackage)res).getName(); final String url = pkgName.replace('.', '/') + "/package-summary.html"; openBrowserForJavadoc(url); } else if (res instanceof ScriptObject) { final ScriptObject sobj = (ScriptObject)res; if (sobj.has(DOC_PROPERTY_NAME)) { return toString(sobj.get(DOC_PROPERTY_NAME), global); } else if (sobj instanceof ScriptFunction) { return ((ScriptFunction)sobj).getDocumentation(); } } // FIXME: better than toString for other cases? return toString(res, global); } } catch (Exception ignored) { } return null; })) { if (globalChanged) { Context.setGlobal(global); } global.addShellBuiltins(); // redefine readLine to use jline Console's readLine! ScriptingFunctions.setReadLineHelper(str-> { try { return in.readLine(str); } catch (final IOException ioExp) { throw new UncheckedIOException(ioExp); } }); if (System.getSecurityManager() == null) { final Consumer<String> evaluator = str -> { // could be called from different thread (GUI), we need to handle Context set/reset final Global _oldGlobal = Context.getGlobal(); final boolean _globalChanged = (_oldGlobal != global); if (_globalChanged) { Context.setGlobal(global); } try { evalImpl(context, global, str, err, env._dump_on_error); } finally { if (_globalChanged) { Context.setGlobal(_oldGlobal); } } }; // expose history object for reflecting on command line history global.addOwnProperty("history", Property.NOT_ENUMERABLE, new HistoryObject(in.getHistory(), err, evaluator)); // 'edit' command global.addOwnProperty("edit", Property.NOT_ENUMERABLE, new EditObject(in, err::println, evaluator)); } while (true) { String source = ""; try { source = in.readLine(prompt); } catch (final IOException ioe) { err.println(ioe.toString()); if (env._dump_on_error) { ioe.printStackTrace(err); } return IO_ERROR; } catch (final UserInterruptException ex) { break; } if (source == null) { break; } if (source.isEmpty()) { continue; } try { final Object res = context.eval(global, source, global, "<shell>"); if (res != UNDEFINED) { err.println(toString(res, global)); } } catch (final Exception exp) { // Is this a ECMAScript SyntaxError at last column (of the single line)? // If so, it is because parser expected more input but got EOF. Try to // to more lines from the user (multiline edit support). if (completer.isSyntaxErrorAt(exp, 1, source.length())) { final String fullSrc = completer.readMoreLines(source, exp, in, prompt2, err); // check if we succeeded in getting complete code. if (fullSrc != null && !fullSrc.isEmpty()) { evalImpl(context, global, fullSrc, err, env._dump_on_error); } // else ignore, error reported already by 'completer.readMoreLines' } else { // can't read more lines to have parseable/complete code. err.println(exp); if (env._dump_on_error) { exp.printStackTrace(err); } } } } } catch (final Exception e) { err.println(e); if (env._dump_on_error) { e.printStackTrace(err); } } finally { if (globalChanged) { Context.setGlobal(oldGlobal); } try { propsHelper.close(); } catch (final Exception exp) { if (DEBUG) { exp.printStackTrace(); } } } return SUCCESS; } static String getMessage(final String id) { return bundle.getString(id); } private void evalImpl(final Context context, final Global global, final String source, final PrintWriter err, final boolean doe) { try { final Object res = context.eval(global, source, global, "<shell>"); if (res != UNDEFINED) { err.println(toString(res, global)); } } catch (final Exception e) { err.println(e); if (doe) { e.printStackTrace(err); } } } private static String JAVADOC_BASE = "https://docs.oracle.com/javase/9/docs/api/"; private static void openBrowserForJavadoc(String relativeUrl) { try { final URI uri = new URI(JAVADOC_BASE + relativeUrl); Desktop.getDesktop().browse(uri); } catch (Exception ignored) { } } }