--- old/make/BuildNashorn.gmk 2015-08-12 21:19:20.653374800 +0530 +++ new/make/BuildNashorn.gmk 2015-08-12 21:19:20.299354600 +0530 @@ -53,7 +53,10 @@ SERVER_JVM := $(SJAVAC_SERVER_JAVA))) # Build nashorn into intermediate directory -$(eval $(call SetupJavaCompilation,BUILD_NASHORN, \ +# Name the compilation setup the same as the module, as is done in the global +# CompileJavaModules.gmk, to make dependency checking with other modules work +# seamlessly. +$(eval $(call SetupJavaCompilation,jdk.scripting.nashorn, \ SETUP := GENERATE_NEWBYTECODE_DEBUG, \ SRC := $(NASHORN_TOPDIR)/src/jdk.scripting.nashorn/share/classes, \ EXCLUDE_FILES := META-INF/MANIFEST.MF, \ @@ -71,7 +74,7 @@ ADD_JAVAC_FLAGS := -Xbootclasspath/p:"$(SUPPORT_OUTPUTDIR)/special_classes/jdk.scripting.nashorn/classes")) # Nasgen needs nashorn classes -$(BUILD_NASGEN): $(BUILD_NASHORN) +$(BUILD_NASGEN): $(jdk.scripting.nashorn) NASHORN_CLASSES_DIR := $(JDK_OUTPUTDIR)/modules/jdk.scripting.nashorn NASGEN_RUN_FILE := $(NASHORN_CLASSES_DIR)/_the.nasgen.run --- old/make/build.xml 2015-08-12 21:19:22.837499800 +0530 +++ new/make/build.xml 2015-08-12 21:19:22.498480400 +0530 @@ -147,16 +147,16 @@ - + - + - + - - + + ${line.separator} @@ -165,7 +165,14 @@ - + + + + @@ -230,13 +237,14 @@ - - + + --- old/make/project.properties 2015-08-12 21:19:25.043625900 +0530 +++ new/make/project.properties 2015-08-12 21:19:24.674604800 +0530 @@ -26,6 +26,9 @@ # location of JDK embedded ASM sources jdk.asm.src.dir=../jdk/src/java.base/share/classes/jdk/internal/org/objectweb/asm +# location of JDK embedded jline sources +jdk.jline.src.dir=../jdk/src/jdk.internal.le/share/classes + # source and target levels build.compiler=modern javac.source=1.8 @@ -112,7 +115,7 @@ ${build.test.classes.dir}${path.separator}\ ${file.reference.testng.jar} -meta.inf.dir=${src.dir}/META-INF +meta.inf.dir=${nashorn.module.src.dir}/META-INF run.classpath=\ ${build.classes.dir} @@ -266,7 +269,13 @@ ${nashorn.internal.tests.jar}${path.separator}\ ${nashorn.api.tests.jar} -src.dir=src/jdk.scripting.nashorn/share/classes +nashorn.module.src.dir=src/jdk.scripting.nashorn/share/classes +nashorn.shell.module.src.dir=src/jdk.scripting.nashorn.shell/share/classes + +src.dir=${nashorn.module.src.dir}${path.separator}\ + ${nashorn.shell.module.src.dir}${path.separator}\ + ${jdk.jline.src.dir} + test.src.dir=test/src # -Xmx is used for all tests, -Xms only for octane benchmark --- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java 2015-08-12 21:19:27.341757400 +0530 +++ new/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java 2015-08-12 21:19:26.992737400 +0530 @@ -68,7 +68,7 @@ /** * Shell message bundle. */ - private static final ResourceBundle bundle = ResourceBundle.getBundle(MESSAGE_RESOURCE, Locale.getDefault()); + protected static final ResourceBundle bundle = ResourceBundle.getBundle(MESSAGE_RESOURCE, Locale.getDefault()); /** * Exit code for command line tool - successful @@ -403,7 +403,7 @@ * @param global global scope object to use * @return return code */ - private static int readEvalPrint(final Context context, final Global global) { + protected int readEvalPrint(final Context context, final Global global) { final String prompt = bundle.getString("shell.prompt"); final BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); final PrintWriter err = context.getErr(); --- /dev/null 2015-08-12 21:19:29.000000000 +0530 +++ new/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Console.java 2015-08-12 21:19:29.158861300 +0530 @@ -0,0 +1,417 @@ +/* + * 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 java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import jdk.internal.jline.NoInterruptUnixTerminal; +import jdk.internal.jline.Terminal; +import jdk.internal.jline.TerminalFactory; +import jdk.internal.jline.TerminalFactory.Flavor; +import jdk.internal.jline.WindowsTerminal; +import jdk.internal.jline.console.ConsoleReader; +import jdk.internal.jline.console.KeyMap; +import jdk.internal.jline.console.UserInterruptException; +import jdk.internal.jline.console.completer.Completer; +import jdk.internal.jline.console.history.History; +import jdk.internal.jline.console.history.History.Entry; +import jdk.internal.jline.console.history.MemoryHistory; + +class Console implements AutoCloseable { + private final ConsoleReader in; + private final EditingHistory history; + + Console(InputStream cmdin, PrintStream cmdout, Preferences prefs) throws IOException { + in = new ConsoleReader(cmdin, cmdout); + in.setExpandEvents(false); + in.setHandleUserInterrupt(true); + in.setHistory(history = new EditingHistory(prefs)); + Runtime.getRuntime().addShutdownHook(new Thread(()->close())); + } + + String readLine(String prompt) throws IOException { + return in.readLine(prompt); + } + + + @Override + public void close() { + history.save(); + } + + public static class EditingHistory implements History { + + private final Preferences prefs; + private final History fullHistory; + private History currentDelegate; + + protected EditingHistory(Preferences prefs) { + this.prefs = prefs; + this.fullHistory = new MemoryHistory(); + this.currentDelegate = fullHistory; + load(); + } + + @Override + public int size() { + return currentDelegate.size(); + } + + @Override + public boolean isEmpty() { + return currentDelegate.isEmpty(); + } + + @Override + public int index() { + return currentDelegate.index(); + } + + @Override + public void clear() { + if (currentDelegate != fullHistory) + throw new IllegalStateException("narrowed"); + currentDelegate.clear(); + } + + @Override + public CharSequence get(int index) { + return currentDelegate.get(index); + } + + @Override + public void add(CharSequence line) { + NarrowingHistoryLine currentLine = null; + int origIndex = fullHistory.index(); + int fullSize; + try { + fullHistory.moveToEnd(); + fullSize = fullHistory.index(); + if (currentDelegate == fullHistory) { + if (origIndex < fullHistory.index()) { + for (Entry entry : fullHistory) { + if (!(entry.value() instanceof NarrowingHistoryLine)) + continue; + int[] cluster = ((NarrowingHistoryLine) entry.value()).span; + if (cluster[0] == origIndex && cluster[1] > cluster[0]) { + currentDelegate = new MemoryHistory(); + for (int i = cluster[0]; i <= cluster[1]; i++) { + currentDelegate.add(fullHistory.get(i)); + } + } + } + } + } + fullHistory.moveToEnd(); + while (fullHistory.previous()) { + CharSequence c = fullHistory.current(); + if (c instanceof NarrowingHistoryLine) { + currentLine = (NarrowingHistoryLine) c; + break; + } + } + } finally { + fullHistory.moveTo(origIndex); + } + if (currentLine == null || currentLine.span[1] != (-1)) { + line = currentLine = new NarrowingHistoryLine(line, fullSize); + } + StringBuilder complete = new StringBuilder(); + for (int i = currentLine.span[0]; i < fullSize; i++) { + complete.append(fullHistory.get(i)); + } + complete.append(line); + if (isComplete(complete.toString())) { + currentLine.span[1] = fullSize; //TODO: +1? + currentDelegate = fullHistory; + } + fullHistory.add(line); + } + + protected boolean isComplete(String input) { + return true; // FIXME + } + + @Override + public void set(int index, CharSequence item) { + if (currentDelegate != fullHistory) + throw new IllegalStateException("narrowed"); + currentDelegate.set(index, item); + } + + @Override + public CharSequence remove(int i) { + if (currentDelegate != fullHistory) + throw new IllegalStateException("narrowed"); + return currentDelegate.remove(i); + } + + @Override + public CharSequence removeFirst() { + if (currentDelegate != fullHistory) + throw new IllegalStateException("narrowed"); + return currentDelegate.removeFirst(); + } + + @Override + public CharSequence removeLast() { + if (currentDelegate != fullHistory) + throw new IllegalStateException("narrowed"); + return currentDelegate.removeLast(); + } + + @Override + public void replace(CharSequence item) { + if (currentDelegate != fullHistory) + throw new IllegalStateException("narrowed"); + currentDelegate.replace(item); + } + + @Override + public ListIterator entries(int index) { + return currentDelegate.entries(index); + } + + @Override + public ListIterator entries() { + return currentDelegate.entries(); + } + + @Override + public Iterator iterator() { + return currentDelegate.iterator(); + } + + @Override + public CharSequence current() { + return currentDelegate.current(); + } + + @Override + public boolean previous() { + return currentDelegate.previous(); + } + + @Override + public boolean next() { + return currentDelegate.next(); + } + + @Override + public boolean moveToFirst() { + return currentDelegate.moveToFirst(); + } + + @Override + public boolean moveToLast() { + return currentDelegate.moveToLast(); + } + + @Override + public boolean moveTo(int index) { + return currentDelegate.moveTo(index); + } + + @Override + public void moveToEnd() { + currentDelegate.moveToEnd(); + } + + public boolean previousSnippet() { + for (int i = index() - 1; i >= 0; i--) { + if (get(i) instanceof NarrowingHistoryLine) { + moveTo(i); + return true; + } + } + + return false; + } + + public boolean nextSnippet() { + for (int i = index() + 1; i < size(); i++) { + if (get(i) instanceof NarrowingHistoryLine) { + moveTo(i); + return true; + } + } + + if (index() < size()) { + moveToEnd(); + return true; + } + + return false; + } + + private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_"; + private static final String HISTORY_SNIPPET_START = "HISTORY_SNIPPET"; + + public final void load() { + try { + Set snippetsStart = new HashSet<>(); + for (String start : prefs.get(HISTORY_SNIPPET_START, "").split(";")) { + if (start.isEmpty()) + continue; + snippetsStart.add(Integer.parseInt(start)); + } + List keys = new ArrayList<>(Arrays.asList(prefs.keys())); + Collections.sort(keys); + NarrowingHistoryLine currentHistoryLine = null; + int currentLine = 0; + for (String key : keys) { + if (!key.startsWith(HISTORY_LINE_PREFIX)) + continue; + CharSequence line = prefs.get(key, ""); + if (snippetsStart.contains(currentLine)) { + class PersistentNarrowingHistoryLine extends NarrowingHistoryLine implements PersistentEntryMarker { + public PersistentNarrowingHistoryLine(CharSequence delegate, int start) { + super(delegate, start); + } + } + line = currentHistoryLine = new PersistentNarrowingHistoryLine(line, currentLine); + } else { + class PersistentLine implements CharSequence, PersistentEntryMarker { + private final CharSequence delegate; + public PersistentLine(CharSequence delegate) { + this.delegate = delegate; + } + @Override public int length() { + return delegate.length(); + } + @Override public char charAt(int index) { + return delegate.charAt(index); + } + @Override public CharSequence subSequence(int start, int end) { + return delegate.subSequence(start, end); + } + @Override public String toString() { + return delegate.toString(); + } + } + line = new PersistentLine(line); + } + if (currentHistoryLine != null) + currentHistoryLine.span[1] = currentLine; + currentLine++; + fullHistory.add(line); + } + currentLine = 0; + } catch (BackingStoreException ex) { + throw new IllegalStateException(ex); + } + } + + public void save() { + try { + for (String key : prefs.keys()) { + if (key.startsWith(HISTORY_LINE_PREFIX)) + prefs.remove(key); + } + Iterator entries = fullHistory.iterator(); + if (entries.hasNext()) { + int len = (int) Math.ceil(Math.log10(fullHistory.size()+1)); + String format = HISTORY_LINE_PREFIX + "%0" + len + "d"; + StringBuilder snippetStarts = new StringBuilder(); + String snippetStartDelimiter = ""; + while (entries.hasNext()) { + Entry entry = entries.next(); + prefs.put(String.format(format, entry.index()), entry.value().toString()); + if (entry.value() instanceof NarrowingHistoryLine) { + snippetStarts.append(snippetStartDelimiter); + snippetStarts.append(entry.index()); + snippetStartDelimiter = ";"; + } + } + prefs.put(HISTORY_SNIPPET_START, snippetStarts.toString()); + } + } catch (BackingStoreException ex) { + throw new IllegalStateException(ex); + } + } + + public List currentSessionEntries() { + List result = new ArrayList<>(); + + for (Entry e : fullHistory) { + if (!(e.value() instanceof PersistentEntryMarker)) { + result.add(e.value().toString()); + } + } + + return result; + } + + private class NarrowingHistoryLine implements CharSequence { + private final CharSequence delegate; + private final int[] span; + + public NarrowingHistoryLine(CharSequence delegate, int start) { + this.delegate = delegate; + this.span = new int[] {start, -1}; + } + + @Override + public int length() { + return delegate.length(); + } + + @Override + public char charAt(int index) { + return delegate.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return delegate.subSequence(start, end); + } + + @Override + public String toString() { + return delegate.toString(); + } + + } + + private interface PersistentEntryMarker {} + } +} --- /dev/null 2015-08-12 21:19:31.000000000 +0530 +++ new/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java 2015-08-12 21:19:30.833957100 +0530 @@ -0,0 +1,150 @@ +/* + * 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 java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.prefs.Preferences; +import jdk.nashorn.internal.objects.Global; +import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.ErrorManager; +import jdk.nashorn.internal.runtime.JSType; +import jdk.nashorn.internal.runtime.ScriptEnvironment; +import jdk.nashorn.internal.runtime.ScriptRuntime; +import jdk.nashorn.tools.Shell; +import jdk.internal.jline.console.UserInterruptException; + +/** + * Interactive command line Shell for Nashorn. + */ +public final class Main extends Shell { + private Main() {} + + static final Preferences PREFS = Preferences.userRoot().node("tool/jjs"); + + /** + * 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 Main().run(in, out, err, args); + } + + /** + * 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 PrintWriter err = context.getErr(); + final Global oldGlobal = Context.getGlobal(); + final boolean globalChanged = (oldGlobal != global); + + try (final Console in = new Console(System.in, System.out, PREFS)) { + if (globalChanged) { + Context.setGlobal(global); + } + + global.addShellBuiltins(); + + 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.isEmpty()) { + continue; + } + + try { + final Object res = context.eval(global, source, global, ""); + if (res != ScriptRuntime.UNDEFINED) { + err.println(JSType.toString(res)); + } + } catch (final Exception e) { + err.println(e); + if (env._dump_on_error) { + e.printStackTrace(err); + } + } + } + } catch (final Exception e) { + err.println(e); + if (env._dump_on_error) { + e.printStackTrace(err); + } + } finally { + if (globalChanged) { + Context.setGlobal(oldGlobal); + } + } + + return SUCCESS; + } +}