8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,202 @@
/*
* 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.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Writer;
import java.nio.file.Files;
import java.util.function.Function;
import java.util.stream.Collectors;
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.extra.EditingHistory;
import jdk.internal.misc.Signal;
import jdk.internal.misc.Signal.Handler;
class Console implements AutoCloseable {
private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB
private final ConsoleReader in;
private final File historyFile;
Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile,
final NashornCompleter completer, final Function<String, String> docHelper) throws IOException {
this.historyFile = historyFile;
TerminalFactory.registerFlavor(Flavor.WINDOWS, isCygwin()? JJSUnixTerminal::new : JJSWindowsTerminal::new);
TerminalFactory.registerFlavor(Flavor.UNIX, JJSUnixTerminal::new);
in = new ConsoleReader(cmdin, cmdout);
in.setExpandEvents(false);
in.setHandleUserInterrupt(true);
in.setBellEnabled(true);
in.setCopyPasteDetection(true);
final Iterable<String> existingHistory = historyFile.exists() ? Files.readAllLines(historyFile.toPath()) : null;
in.setHistory(new EditingHistory(in, existingHistory) {
@Override protected boolean isComplete(CharSequence input) {
return completer.isComplete(input.toString());
}
});
in.addCompleter(completer);
Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory));
bind(DOCUMENTATION_SHORTCUT, (ActionListener)evt -> showDocumentation(docHelper));
try {
Signal.handle(new Signal("CONT"), new Handler() {
@Override public void handle(Signal sig) {
try {
in.getTerminal().reset();
in.redrawLine();
in.flush();
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
} catch (IllegalArgumentException ignored) {
//the CONT signal does not exist on this platform
}
}
String readLine(final String prompt) throws IOException {
return in.readLine(prompt);
}
@Override
public void close() {
saveHistory();
}
private void saveHistory() {
try (Writer out = Files.newBufferedWriter(historyFile.toPath())) {
String lineSeparator = System.getProperty("line.separator");
out.write(getHistory().save()
.stream()
.collect(Collectors.joining(lineSeparator)));
} catch (final IOException exp) {}
}
EditingHistory getHistory() {
return (EditingHistory) in.getHistory();
}
boolean terminalEditorRunning() {
Terminal terminal = in.getTerminal();
if (terminal instanceof JJSUnixTerminal) {
return ((JJSUnixTerminal) terminal).isRaw();
}
return false;
}
void suspend() {
try {
in.getTerminal().restore();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
void resume() {
try {
in.getTerminal().init();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
static final class JJSUnixTerminal extends NoInterruptUnixTerminal {
JJSUnixTerminal() throws Exception {
}
boolean isRaw() {
try {
return getSettings().get("-a").contains("-icanon");
} catch (IOException | InterruptedException ex) {
return false;
}
}
@Override
public void disableInterruptCharacter() {
}
@Override
public void enableInterruptCharacter() {
}
}
static final class JJSWindowsTerminal extends WindowsTerminal {
public JJSWindowsTerminal() throws Exception {
}
@Override
public void init() throws Exception {
super.init();
setAnsiSupported(false);
}
}
private static boolean isCygwin() {
return System.getenv("SHELL") != null;
}
private void bind(String shortcut, Object action) {
KeyMap km = in.getKeys();
for (int i = 0; i < shortcut.length(); i++) {
final Object value = km.getBound(Character.toString(shortcut.charAt(i)));
if (value instanceof KeyMap) {
km = (KeyMap) value;
} else {
km.bind(shortcut.substring(i), action);
}
}
}
private void showDocumentation(final Function<String, String> docHelper) {
final String buffer = in.getCursorBuffer().buffer.toString();
final int cursor = in.getCursorBuffer().cursor;
final String doc = docHelper.apply(buffer.substring(0, cursor));
try {
if (doc != null) {
in.println();
in.println(doc);
in.redrawLine();
in.flush();
} else {
in.beep();
}
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}

View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2015, 2017, 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.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.ServiceLoader;
import jdk.nashorn.api.scripting.AbstractJSObject;
import jdk.internal.editor.spi.BuildInEditorProvider;
import jdk.nashorn.internal.runtime.JSType;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
/*
* "edit" top level script function which shows an external Window
* for editing and evaluating scripts from it.
*/
final class EditObject extends AbstractJSObject {
private static final Set<String> props;
static {
final HashSet<String> s = new HashSet<>();
s.add("editor");
props = Collections.unmodifiableSet(s);
}
private final Console console;
private final Consumer<String> errorHandler;
private final Consumer<String> evaluator;
private String editor;
EditObject(final Console console, final Consumer<String> errorHandler,
final Consumer<String> evaluator) {
this.console = console;
this.errorHandler = errorHandler;
this.evaluator = evaluator;
}
@Override
public Object getDefaultValue(final Class<?> hint) {
if (hint == String.class) {
return toString();
}
return UNDEFINED;
}
@Override
public String toString() {
return "function edit() { [native code] }";
}
@Override
public Set<String> keySet() {
return props;
}
@Override
public Object getMember(final String name) {
if (name.equals("editor")) {
return editor;
}
return UNDEFINED;
}
@Override
public void setMember(final String name, final Object value) {
if (name.equals("editor")) {
this.editor = value != null && value != UNDEFINED? JSType.toString(value) : "";
}
}
// called whenever user 'saves' script in editor
class SaveHandler implements Consumer<String> {
private String lastStr; // last seen code
SaveHandler(final String str) {
this.lastStr = str;
}
@Override
public void accept(final String str) {
// ignore repeated save of the same code!
if (! str.equals(lastStr)) {
this.lastStr = str;
// evaluate the new code
evaluator.accept(str);
}
}
}
@Override
public Object call(final Object thiz, final Object... args) {
final String initText = args.length > 0? JSType.toString(args[0]) : "";
final SaveHandler saveHandler = new SaveHandler(initText);
if (editor != null && !editor.isEmpty()) {
ExternalEditor.edit(editor, errorHandler, initText, saveHandler, console);
} else if (! Main.HEADLESS) {
try {
ServiceLoader<BuildInEditorProvider> sl
= ServiceLoader.load(BuildInEditorProvider.class);
//find the highest ranking provider
BuildInEditorProvider provider = null;
for (BuildInEditorProvider p : sl){
if (provider == null || p.rank() > provider.rank()) {
provider = p;
}
}
if (provider != null) {
provider.edit(null, initText, saveHandler, errorHandler);
} else {
errorHandler.accept(Main.getMessage("jjs.err.no.builtin.editor"));
}
} catch (RuntimeException ex) {
errorHandler.accept(Main.getMessage("jjs.err.cant.launch.editor"));
}
} else {
errorHandler.accept(Main.getMessage("no.editor"));
}
return UNDEFINED;
}
@Override
public boolean isFunction() {
return true;
}
}

View file

@ -0,0 +1,152 @@
/*
* 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.nio.charset.Charset;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import java.util.function.Consumer;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
final class ExternalEditor {
private final Consumer<String> errorHandler;
private final Consumer<String> saveHandler;
private final Console input;
private WatchService watcher;
private Thread watchedThread;
private Path dir;
private Path tmpfile;
ExternalEditor(final Consumer<String> errorHandler, final Consumer<String> saveHandler, final Console input) {
this.errorHandler = errorHandler;
this.saveHandler = saveHandler;
this.input = input;
}
private void edit(final String cmd, final String initialText) {
try {
setupWatch(initialText);
launch(cmd);
} catch (IOException ex) {
errorHandler.accept(ex.getMessage());
}
}
/**
* Creates a WatchService and registers the given directory
*/
private void setupWatch(final String initialText) throws IOException {
this.watcher = FileSystems.getDefault().newWatchService();
this.dir = Files.createTempDirectory("REPL");
this.tmpfile = Files.createTempFile(dir, null, ".js");
Files.write(tmpfile, initialText.getBytes(Charset.forName("UTF-8")));
dir.register(watcher,
ENTRY_CREATE,
ENTRY_DELETE,
ENTRY_MODIFY);
watchedThread = new Thread(() -> {
for (;;) {
WatchKey key;
try {
key = watcher.take();
} catch (final ClosedWatchServiceException ex) {
break;
} catch (final InterruptedException ex) {
continue; // tolerate an intrupt
}
if (!key.pollEvents().isEmpty()) {
if (!input.terminalEditorRunning()) {
saveFile();
}
}
boolean valid = key.reset();
if (!valid) {
errorHandler.accept("Invalid key");
break;
}
}
});
watchedThread.start();
}
private void launch(final String cmd) throws IOException {
ProcessBuilder pb = new ProcessBuilder(cmd, tmpfile.toString());
pb = pb.inheritIO();
try {
input.suspend();
Process process = pb.start();
process.waitFor();
} catch (final IOException ex) {
errorHandler.accept("process IO failure: " + ex.getMessage());
} catch (final InterruptedException ex) {
errorHandler.accept("process interrupt: " + ex.getMessage());
} finally {
try {
watcher.close();
watchedThread.join(); //so that saveFile() is finished.
saveFile();
} catch (InterruptedException ex) {
errorHandler.accept("process interrupt: " + ex.getMessage());
} finally {
input.resume();
}
}
}
private void saveFile() {
List<String> lines;
try {
lines = Files.readAllLines(tmpfile);
} catch (final IOException ex) {
errorHandler.accept("Failure read edit file: " + ex.getMessage());
return ;
}
StringBuilder sb = new StringBuilder();
for (String ln : lines) {
sb.append(ln);
sb.append('\n');
}
saveHandler.accept(sb.toString());
}
static void edit(final String cmd, final Consumer<String> errorHandler, final String initialText,
final Consumer<String> saveHandler, final Console input) {
ExternalEditor ed = new ExternalEditor(errorHandler, saveHandler, input);
ed.edit(cmd, initialText);
}
}

View file

@ -0,0 +1,195 @@
/*
* 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.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import jdk.internal.jline.console.history.History;
import jdk.nashorn.api.scripting.AbstractJSObject;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.runtime.JSType;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
/*
* A script friendly object that exposes history of commands to scripts.
*/
final class HistoryObject extends AbstractJSObject {
private static final Set<String> props;
static {
final HashSet<String> s = new HashSet<>();
s.add("clear");
s.add("forEach");
s.add("load");
s.add("print");
s.add("save");
s.add("size");
s.add("toString");
props = Collections.unmodifiableSet(s);
}
private final History hist;
private final PrintWriter err;
private final Consumer<String> evaluator;
HistoryObject(final History hist, final PrintWriter err,
final Consumer<String> evaluator) {
this.hist = hist;
this.err = err;
this.evaluator = evaluator;
}
@Override
public boolean isFunction() {
return true;
}
@Override
public Object call(final Object thiz, final Object... args) {
if (args.length > 0) {
int index = JSType.toInteger(args[0]);
if (index < 0) {
index += (hist.size() - 1);
} else {
index--;
}
if (index >= 0 && index < (hist.size() - 1)) {
final CharSequence src = hist.get(index);
hist.replace(src);
err.println(src);
evaluator.accept(src.toString());
} else {
hist.removeLast();
err.println("no history entry @ " + (index + 1));
}
}
return UNDEFINED;
}
@Override
public Object getMember(final String name) {
switch (name) {
case "clear":
return (Runnable)hist::clear;
case "forEach":
return (Function<JSObject, Object>)this::iterate;
case "load":
return (Consumer<Object>)this::load;
case "print":
return (Runnable)this::print;
case "save":
return (Consumer<Object>)this::save;
case "size":
return hist.size();
case "toString":
return (Supplier<String>)this::toString;
}
return UNDEFINED;
}
@Override
public Object getDefaultValue(final Class<?> hint) {
if (hint == String.class) {
return toString();
}
return UNDEFINED;
}
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
for (History.Entry e : hist) {
buf.append(e.value()).append('\n');
}
return buf.toString();
}
@Override
public Set<String> keySet() {
return props;
}
private void save(final Object obj) {
final File file = getFile(obj);
try (final PrintWriter pw = new PrintWriter(file)) {
for (History.Entry e : hist) {
pw.println(e.value());
}
} catch (final IOException exp) {
throw new RuntimeException(exp);
}
}
private void load(final Object obj) {
final File file = getFile(obj);
String item = null;
try (final BufferedReader r = new BufferedReader(new FileReader(file))) {
while ((item = r.readLine()) != null) {
hist.add(item);
}
} catch (final IOException exp) {
throw new RuntimeException(exp);
}
}
private void print() {
for (History.Entry e : hist) {
System.out.printf("%3d %s\n", e.index() + 1, e.value());
}
}
private Object iterate(final JSObject func) {
for (History.Entry e : hist) {
if (JSType.toBoolean(func.call(this, e.value().toString()))) {
break; // return true from callback to skip iteration
}
}
return UNDEFINED;
}
private static File getFile(final Object obj) {
File file = null;
if (obj instanceof String) {
file = new File((String)obj);
} else if (obj instanceof File) {
file = (File)obj;
} else {
throw typeError("not.a.file", JSType.toString(obj));
}
return file;
}
}

View file

@ -0,0 +1,293 @@
/*
* 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.
*/
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.
*
* @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 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) {
}
}
}

View file

@ -0,0 +1,448 @@
/*
* 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.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.regex.Pattern;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.SwingUtilities;
import jdk.internal.jline.console.completer.Completer;
import jdk.internal.jline.console.UserInterruptException;
import jdk.nashorn.api.tree.AssignmentTree;
import jdk.nashorn.api.tree.BinaryTree;
import jdk.nashorn.api.tree.CompilationUnitTree;
import jdk.nashorn.api.tree.CompoundAssignmentTree;
import jdk.nashorn.api.tree.ConditionalExpressionTree;
import jdk.nashorn.api.tree.ExpressionTree;
import jdk.nashorn.api.tree.ExpressionStatementTree;
import jdk.nashorn.api.tree.FunctionCallTree;
import jdk.nashorn.api.tree.IdentifierTree;
import jdk.nashorn.api.tree.InstanceOfTree;
import jdk.nashorn.api.tree.MemberSelectTree;
import jdk.nashorn.api.tree.NewTree;
import jdk.nashorn.api.tree.SimpleTreeVisitorES5_1;
import jdk.nashorn.api.tree.Tree;
import jdk.nashorn.api.tree.UnaryTree;
import jdk.nashorn.api.tree.Parser;
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.tools.PartialParser;
import jdk.nashorn.internal.objects.NativeSyntaxError;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* A simple source completer for nashorn. Handles code completion for
* expressions as well as handles incomplete single line code.
*/
final class NashornCompleter implements Completer {
private final Context context;
private final Global global;
private final ScriptEnvironment env;
private final PartialParser partialParser;
private final PropertiesHelper propsHelper;
private final Parser parser;
private static final boolean BACKSLASH_FILE_SEPARATOR = File.separatorChar == '\\';
NashornCompleter(final Context context, final Global global,
final PartialParser partialParser, final PropertiesHelper propsHelper) {
this.context = context;
this.global = global;
this.env = context.getEnv();
this.partialParser = partialParser;
this.propsHelper = propsHelper;
this.parser = createParser(env);
}
/**
* Is this a ECMAScript SyntaxError thrown for parse issue at the given line and column?
*
* @param exp Throwable to check
* @param line line number to check
* @param column column number to check
*
* @return true if the given Throwable is a ECMAScript SyntaxError at given line, column
*/
boolean isSyntaxErrorAt(final Throwable exp, final int line, final int column) {
if (exp instanceof ECMAException) {
final ECMAException eexp = (ECMAException)exp;
if (eexp.getThrown() instanceof NativeSyntaxError) {
return isParseErrorAt(eexp.getCause(), line, column);
}
}
return false;
}
/**
* Is this a parse error at the given line and column?
*
* @param exp Throwable to check
* @param line line number to check
* @param column column number to check
*
* @return true if the given Throwable is a parser error at given line, column
*/
boolean isParseErrorAt(final Throwable exp, final int line, final int column) {
if (exp instanceof NashornException) {
final NashornException nexp = (NashornException)exp;
return nexp.getLineNumber() == line && nexp.getColumnNumber() == column;
}
return false;
}
/**
* Read more lines of code if we got SyntaxError at EOF and we can it fine by
* by reading more lines of code from the user. This is used for multiline editing.
*
* @param firstLine First line of code from the user
* @param exp Exception thrown by evaluting first line code
* @param in Console to get read more lines from the user
* @param prompt Prompt to be printed to read more lines from the user
* @param err PrintWriter to print any errors in the proecess of reading
*
* @return Complete code read from the user including the first line. This is null
* if any error or the user discarded multiline editing by Ctrl-C.
*/
String readMoreLines(final String firstLine, final Exception exp, final Console in,
final String prompt, final PrintWriter err) {
int line = 1;
final StringBuilder buf = new StringBuilder(firstLine);
while (true) {
buf.append('\n');
String curLine = null;
try {
curLine = in.readLine(prompt);
buf.append(curLine);
line++;
} catch (final Throwable th) {
if (th instanceof UserInterruptException) {
// Ctrl-C from user - discard the whole thing silently!
return null;
} else {
// print anything else -- but still discard the code
err.println(th);
if (env._dump_on_error) {
th.printStackTrace(err);
}
return null;
}
}
final String allLines = buf.toString();
try {
parser.parse("<shell>", allLines, null);
} catch (final Exception pexp) {
// Do we have a parse error at the end of current line?
// If so, read more lines from the console.
if (isParseErrorAt(pexp, line, curLine.length())) {
continue;
} else {
// print anything else and bail out!
err.println(pexp);
if (env._dump_on_error) {
pexp.printStackTrace(err);
}
return null;
}
}
// We have complete parseable code!
return buf.toString();
}
}
public boolean isComplete(String input) {
try {
parser.parse("<shell>", input, null);
} catch (final Exception pexp) {
// Do we have a parse error at the end of current line?
// If so, read more lines from the console.
int line = input.split("\n").length;
int lastLineLen = input.length() - (input.lastIndexOf("\n") + 1);
if (isParseErrorAt(pexp, line, lastLineLen)) {
return false;
}
}
return true;
}
// Pattern to match a unfinished member selection expression. object part and "."
// but property name missing pattern.
private static final Pattern SELECT_PROP_MISSING = Pattern.compile(".*\\.\\s*");
// Pattern to match load call
private static final Pattern LOAD_CALL = Pattern.compile("\\s*load\\s*\\(\\s*");
@Override
public int complete(final String test, final int cursor, final List<CharSequence> result) {
// check that cursor is at the end of test string. Do not complete in the middle!
if (cursor != test.length()) {
return cursor;
}
// get the start of the last expression embedded in the given code
// using the partial parsing support - so that we can complete expressions
// inside statements, function call argument lists, array index etc.
final int exprStart = partialParser.getLastExpressionStart(context, test);
if (exprStart == -1) {
return cursor;
}
// extract the last expression string
final String exprStr = test.substring(exprStart);
// do we have an incomplete member selection expression that misses property name?
final boolean endsWithDot = SELECT_PROP_MISSING.matcher(exprStr).matches();
// If this is an incomplete member selection, then it is not legal code.
// Make it legal by adding a random property name "x" to it.
final String completeExpr = endsWithDot? exprStr + "x" : exprStr;
final ExpressionTree topExpr = getTopLevelExpression(parser, completeExpr);
if (topExpr == null) {
// special case for load call that looks like "load(" with optional whitespaces
if (LOAD_CALL.matcher(test).matches()) {
String name = readFileName(context.getErr());
if (name != null) {
// handle '\' file separator
if (BACKSLASH_FILE_SEPARATOR) {
name = name.replace("\\", "\\\\");
}
result.add("\"" + name + "\")");
return cursor + name.length() + 3;
}
}
// did not parse to be a top level expression, no suggestions!
return cursor;
}
// Find 'right most' expression of the top level expression
final Tree rightMostExpr = getRightMostExpression(topExpr);
if (rightMostExpr instanceof MemberSelectTree) {
return completeMemberSelect(exprStr, cursor, result, (MemberSelectTree)rightMostExpr, endsWithDot);
} else if (rightMostExpr instanceof IdentifierTree) {
return completeIdentifier(exprStr, cursor, result, (IdentifierTree)rightMostExpr);
} else {
// expression that we cannot handle for completion
return cursor;
}
}
// Internals only below this point
// read file name from the user using by showing a swing file chooser diablog
private static String readFileName(final PrintWriter err) {
// if running on AWT Headless mode, don't attempt swing dialog box!
if (Main.HEADLESS) {
return null;
}
final FutureTask<String> fileChooserTask = new FutureTask<String>(() -> {
// show a file chooser dialog box
final JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(new FileNameExtensionFilter("JavaScript Files", "js"));
final int retVal = chooser.showOpenDialog(null);
return retVal == JFileChooser.APPROVE_OPTION ?
chooser.getSelectedFile().getAbsolutePath() : null;
});
SwingUtilities.invokeLater(fileChooserTask);
try {
return fileChooserTask.get();
} catch (final ExecutionException | InterruptedException e) {
err.println(e);
if (Main.DEBUG) {
e.printStackTrace();
}
}
return null;
}
// fill properties of the incomplete member expression
private int completeMemberSelect(final String exprStr, final int cursor, final List<CharSequence> result,
final MemberSelectTree select, final boolean endsWithDot) {
final ExpressionTree objExpr = select.getExpression();
final String objExprCode = exprStr.substring((int)objExpr.getStartPosition(), (int)objExpr.getEndPosition());
// try to evaluate the object expression part as a script
Object obj = null;
try {
obj = context.eval(global, objExprCode, global, "<suggestions>");
} catch (Exception exp) {
// throw away the exception - this is during tab-completion
if (Main.DEBUG) {
exp.printStackTrace();
}
}
if (obj != null && obj != ScriptRuntime.UNDEFINED) {
if (endsWithDot) {
// no user specified "prefix". List all properties of the object
result.addAll(propsHelper.getProperties(obj));
return cursor;
} else {
// list of properties matching the user specified prefix
final String prefix = select.getIdentifier();
result.addAll(propsHelper.getProperties(obj, prefix));
return cursor - prefix.length();
}
}
return cursor;
}
// fill properties for the given (partial) identifer
private int completeIdentifier(final String test, final int cursor, final List<CharSequence> result,
final IdentifierTree ident) {
final String name = ident.getName();
result.addAll(propsHelper.getProperties(global, name));
return cursor - name.length();
}
// returns ExpressionTree if the given code parses to a top level expression.
// Or else returns null.
private ExpressionTree getTopLevelExpression(final Parser parser, final String code) {
try {
final CompilationUnitTree cut = parser.parse("<code>", code, null);
final List<? extends Tree> stats = cut.getSourceElements();
if (stats.size() == 1) {
final Tree stat = stats.get(0);
if (stat instanceof ExpressionStatementTree) {
return ((ExpressionStatementTree)stat).getExpression();
}
}
} catch (final NashornException ignored) {
// ignore any parser error. This is for completion anyway!
// And user will get that error later when the expression is evaluated.
}
return null;
}
// get the right most expreesion of the given expression
private Tree getRightMostExpression(final ExpressionTree expr) {
return expr.accept(new SimpleTreeVisitorES5_1<Tree, Void>() {
@Override
public Tree visitAssignment(final AssignmentTree at, final Void v) {
return getRightMostExpression(at.getExpression());
}
@Override
public Tree visitCompoundAssignment(final CompoundAssignmentTree cat, final Void v) {
return getRightMostExpression(cat.getExpression());
}
@Override
public Tree visitConditionalExpression(final ConditionalExpressionTree cet, final Void v) {
return getRightMostExpression(cet.getFalseExpression());
}
@Override
public Tree visitBinary(final BinaryTree bt, final Void v) {
return getRightMostExpression(bt.getRightOperand());
}
@Override
public Tree visitIdentifier(final IdentifierTree ident, final Void v) {
return ident;
}
@Override
public Tree visitInstanceOf(final InstanceOfTree it, final Void v) {
return it.getType();
}
@Override
public Tree visitMemberSelect(final MemberSelectTree select, final Void v) {
return select;
}
@Override
public Tree visitNew(final NewTree nt, final Void v) {
final ExpressionTree call = nt.getConstructorExpression();
if (call instanceof FunctionCallTree) {
final ExpressionTree func = ((FunctionCallTree)call).getFunctionSelect();
// Is this "new Foo" or "new obj.Foo" with no user arguments?
// If so, we may be able to do completion of constructor name.
if (func.getEndPosition() == nt.getEndPosition()) {
return func;
}
}
return null;
}
@Override
public Tree visitUnary(final UnaryTree ut, final Void v) {
return getRightMostExpression(ut.getExpression());
}
}, null);
}
// create a Parser instance that uses compatible command line options of the
// current ScriptEnvironment being used for REPL.
private static Parser createParser(final ScriptEnvironment env) {
final List<String> args = new ArrayList<>();
if (env._const_as_var) {
args.add("--const-as-var");
}
if (env._no_syntax_extensions) {
args.add("-nse");
}
if (env._scripting) {
args.add("-scripting");
}
if (env._strict) {
args.add("-strict");
}
if (env._es6) {
args.add("--language=es6");
}
return Parser.create(args.toArray(new String[0]));
}
}

View file

@ -0,0 +1,260 @@
/*
* Copyright (c) 2015, 2016, 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.lang.reflect.Modifier;
import java.io.IOException;
import java.io.File;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager.Location;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import jdk.nashorn.internal.runtime.Context;
/**
* A helper class to compute properties of a Java package object. Properties of
* package object are (simple) top level class names in that java package and
* immediate subpackages of that package.
*/
final class PackagesHelper {
// JavaCompiler may be null on certain platforms (eg. JRE)
private static final JavaCompiler compiler;
static {
// Use javac only if security manager is not around!
compiler = System.getSecurityManager() == null? ToolProvider.getSystemJavaCompiler() : null;
}
/**
* Is javac available?
*
* @return true if javac is available
*/
private static boolean isJavacAvailable() {
return compiler != null;
}
private final Context context;
private final boolean modulePathSet;
private final StandardJavaFileManager fm;
private final Set<JavaFileObject.Kind> fileKinds;
private final FileSystem jrtfs;
/**
* Construct a new PackagesHelper.
*
* @param context the current Nashorn Context
*/
PackagesHelper(final Context context) throws IOException {
this.context = context;
final String modulePath = context.getEnv()._module_path;
this.modulePathSet = modulePath != null && !modulePath.isEmpty();
if (isJavacAvailable()) {
final String classPath = context.getEnv()._classpath;
fm = compiler.getStandardFileManager(null, null, null);
fileKinds = EnumSet.of(JavaFileObject.Kind.CLASS);
if (this.modulePathSet) {
fm.setLocation(StandardLocation.MODULE_PATH, getFiles(modulePath));
}
if (classPath != null && !classPath.isEmpty()) {
fm.setLocation(StandardLocation.CLASS_PATH, getFiles(classPath));
} else {
// no classpath set. Make sure that it is empty and not any default like "."
fm.setLocation(StandardLocation.CLASS_PATH, Collections.<File>emptyList());
}
jrtfs = null;
} else {
// javac is not available - directly use jrt fs
// to support at least platform classes.
fm = null;
fileKinds = null;
jrtfs = FileSystems.getFileSystem(URI.create("jrt:/"));
}
}
// LRU cache for java package properties lists
private final LinkedHashMap<String, List<String>> propsCache =
new LinkedHashMap<String, List<String>>(32, 0.75f, true) {
private static final int CACHE_SIZE = 100;
private static final long serialVersionUID = 1;
@Override
protected boolean removeEldestEntry(final Map.Entry<String, List<String>> eldest) {
return size() > CACHE_SIZE;
}
};
/**
* Return the list of properties of the given Java package or package prefix
*
* @param pkg Java package name or package prefix name
* @return the list of properties of the given Java package or package prefix
*/
List<String> getPackageProperties(final String pkg) {
// check the cache first
if (propsCache.containsKey(pkg)) {
return propsCache.get(pkg);
}
try {
// make sorted list of properties
final List<String> props = new ArrayList<>(listPackage(pkg));
Collections.sort(props);
propsCache.put(pkg, props);
return props;
} catch (final IOException exp) {
if (Main.DEBUG) {
exp.printStackTrace();
}
return Collections.<String>emptyList();
}
}
public void close() throws IOException {
if (fm != null) {
fm.close();
}
}
private Set<String> listPackage(final String pkg) throws IOException {
final Set<String> props = new HashSet<>();
if (fm != null) {
listPackage(StandardLocation.PLATFORM_CLASS_PATH, pkg, props);
if (this.modulePathSet) {
for (Set<Location> locs : fm.listLocationsForModules(StandardLocation.MODULE_PATH)) {
for (Location loc : locs) {
listPackage(loc, pkg, props);
}
}
}
listPackage(StandardLocation.CLASS_PATH, pkg, props);
} else if (jrtfs != null) {
// look for the /packages/<package_name> directory
Path pkgDir = jrtfs.getPath("/packages/" + pkg);
if (Files.exists(pkgDir)) {
String pkgSlashName = pkg.replace('.', '/');
try (DirectoryStream<Path> ds = Files.newDirectoryStream(pkgDir)) {
// it has module links under which this package occurs
for (Path mod : ds) {
// get the package directory under /modules
Path pkgUnderMod = jrtfs.getPath(mod.toString() + "/" + pkgSlashName);
try (DirectoryStream<Path> ds2 = Files.newDirectoryStream(pkgUnderMod)) {
for (Path p : ds2) {
String str = p.getFileName().toString();
// get rid of ".class", if any
if (str.endsWith(".class")) {
final String clsName = str.substring(0, str.length() - ".class".length());
if (clsName.indexOf('$') == -1 && isClassAccessible(pkg + "." + clsName)) {
props.add(str);
}
} else if (isPackageAccessible(pkg + "." + str)) {
props.add(str);
}
}
}
}
}
}
}
return props;
}
private void listPackage(final Location loc, final String pkg, final Set<String> props)
throws IOException {
for (JavaFileObject file : fm.list(loc, pkg, fileKinds, true)) {
final String binaryName = fm.inferBinaryName(loc, file);
// does not start with the given package prefix
if (!binaryName.startsWith(pkg + ".")) {
continue;
}
final int nextDot = binaryName.indexOf('.', pkg.length() + 1);
final int start = pkg.length() + 1;
if (nextDot != -1) {
// subpackage - eg. "regex" for "java.util"
final String pkgName = binaryName.substring(start, nextDot);
if (isPackageAccessible(binaryName.substring(0, nextDot))) {
props.add(binaryName.substring(start, nextDot));
}
} else {
// class - filter out nested, inner, anonymous, local classes.
// Dynalink supported public nested classes as properties of
// StaticClass object anyway. We don't want to expose those
// "$" internal names as properties of package object.
final String clsName = binaryName.substring(start);
if (clsName.indexOf('$') == -1 && isClassAccessible(binaryName)) {
props.add(clsName);
}
}
}
}
// return list of File objects for the given class path
private static List<File> getFiles(final String classPath) {
return Stream.of(classPath.split(File.pathSeparator))
.map(File::new)
.collect(Collectors.toList());
}
private boolean isClassAccessible(final String className) {
try {
final Class<?> clz = context.findClass(className);
return Modifier.isPublic(clz.getModifiers());
} catch (final ClassNotFoundException cnfe) {
}
return false;
}
private boolean isPackageAccessible(final String pkgName) {
try {
Context.checkPackageAccess(pkgName);
return true;
} catch (final SecurityException se) {
return false;
}
}
}

View file

@ -0,0 +1,203 @@
/*
* 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.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.WeakHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.objects.NativeJava;
/*
* A helper class to get properties of a given object for source code completion.
*/
final class PropertiesHelper {
// Java package properties helper, may be null
private PackagesHelper pkgsHelper;
// cached properties list
private final WeakHashMap<Object, List<String>> propsCache = new WeakHashMap<>();
/**
* Construct a new PropertiesHelper.
*
* @param context the current nashorn Context
*/
PropertiesHelper(final Context context) {
try {
this.pkgsHelper = new PackagesHelper(context);
} catch (final IOException exp) {
if (Main.DEBUG) {
exp.printStackTrace();
}
this.pkgsHelper = null;
}
}
void close() throws Exception {
propsCache.clear();
pkgsHelper.close();
}
/**
* returns the list of properties of the given object.
*
* @param obj object whose property list is returned
* @return the list of properties of the given object
*/
List<String> getProperties(final Object obj) {
assert obj != null && obj != ScriptRuntime.UNDEFINED;
// wrap JS primitives as objects before gettting properties
if (JSType.isPrimitive(obj)) {
return getProperties(JSType.toScriptObject(obj));
}
// Handle Java package prefix case first. Should do it before checking
// for its super class ScriptObject!
if (obj instanceof NativeJavaPackage) {
if (pkgsHelper != null) {
return pkgsHelper.getPackageProperties(((NativeJavaPackage)obj).getName());
} else {
return Collections.<String>emptyList();
}
}
// script object - all inherited and non-enumerable, non-index properties
if (obj instanceof ScriptObject) {
final ScriptObject sobj = (ScriptObject)obj;
final PropertyMap pmap = sobj.getMap();
if (propsCache.containsKey(pmap)) {
return propsCache.get(pmap);
}
final String[] keys = sobj.getAllKeys();
List<String> props = Arrays.asList(keys);
props = props.stream()
.filter(s -> Character.isJavaIdentifierStart(s.charAt(0)))
.collect(Collectors.toList());
Collections.sort(props);
// cache properties against the PropertyMap
propsCache.put(pmap, props);
return props;
}
// java class case - don't refer to StaticClass directly
if (NativeJava.isType(ScriptRuntime.UNDEFINED, obj)) {
if (propsCache.containsKey(obj)) {
return propsCache.get(obj);
}
final List<String> props = NativeJava.getProperties(obj);
Collections.sort(props);
// cache properties against the StaticClass representing the class
propsCache.put(obj, props);
return props;
}
// any other Java object
final Class<?> clazz = obj.getClass();
if (propsCache.containsKey(clazz)) {
return propsCache.get(clazz);
}
final List<String> props = NativeJava.getProperties(obj);
Collections.sort(props);
// cache properties against the Class object
propsCache.put(clazz, props);
return props;
}
// This method creates a regex Pattern to use to do CamelCase
// matching. The pattern is derived from user supplied string
// containing one or more upper case characters in it.
private static Pattern makeCamelCasePattern(final String str) {
assert !str.isEmpty();
final char[] chars = str.toCharArray();
final StringBuilder buf = new StringBuilder();
boolean seenUpperCase = false;
// Skip first char for case check. Even if it is upper case,
// we do not want to put lower case matching pattern before
// the first letter!
buf.append(chars[0]);
for (int idx = 1; idx < chars.length; idx++) {
final char ch = chars[idx];
if (ch >= 'A' && ch <= 'Z') {
seenUpperCase = true;
buf.append("[^A-Z]*");
}
buf.append(ch);
}
if (seenUpperCase) {
// match anything at the end!
buf.append(".*");
try {
return Pattern.compile(buf.toString());
} catch (Exception exp) {
}
}
return null;
}
/**
* Returns the list of properties of the given object that start with the given prefix.
*
* @param obj object whose property list is returned
* @param prefix property prefix to be matched
* @return the list of properties of the given object
*/
List<String> getProperties(final Object obj, final String prefix) {
assert prefix != null && !prefix.isEmpty();
List<String> allProps = getProperties(obj);
List<String> props = allProps.stream()
.filter(s -> s.startsWith(prefix))
.collect(Collectors.toList());
// If no match, try CamelCase completion..
if (props.isEmpty()) {
final Pattern pat = makeCamelCasePattern(prefix);
if (pat != null) {
return allProps.stream()
.filter(s -> pat.matcher(s).matches())
.collect(Collectors.toList());
}
}
return props;
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2014, 2017, 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.
*/
/**
* Defines Nashorn shell module.
*
* <p>This module includes the command line tool <em>{@index jjs jjs tool}</em>
* to invoke the Nashorn engine.
*
* <dl style="font-family:'DejaVu Sans', Arial, Helvetica, sans serif">
* <dt class="simpleTagLabel">Tool Guides:
* <dd>{@extLink jjs_tool_reference jjs}
* </dl>
*
* @moduleGraph
* @since 9
*/
module jdk.scripting.nashorn.shell {
requires java.compiler;
requires java.desktop;
requires jdk.internal.le;
requires jdk.scripting.nashorn;
requires jdk.internal.ed;
uses jdk.internal.editor.spi.BuildInEditorProvider;
}