Merge
This commit is contained in:
commit
559fdad1c4
47
nashorn/samples/classes.js
Normal file
47
nashorn/samples/classes.js
Normal file
@ -0,0 +1,47 @@
|
||||
// Usage: jjs classes.js [ -- <java_package_name > ]
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of Oracle nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// print all Java classes of the given package and its subpackages.
|
||||
var pkg = arguments.length > 0? arguments[0] : "java.lang";
|
||||
|
||||
with (new JavaImporter(javax.tools, java.util)) {
|
||||
var compiler = ToolProvider.systemJavaCompiler;
|
||||
var fm = compiler.getStandardFileManager(null, null, null);
|
||||
var kinds = EnumSet.of(JavaFileObject.Kind.CLASS);
|
||||
var loc = StandardLocation.PLATFORM_CLASS_PATH;
|
||||
var itr = fm.list(loc, pkg, kinds, true).iterator();
|
||||
while(itr.hasNext()) {
|
||||
print(fm.inferBinaryName(loc, itr.next()));
|
||||
}
|
||||
fm.close();
|
||||
}
|
@ -34,6 +34,11 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
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.completer.Completer;
|
||||
import jdk.internal.jline.console.history.FileHistory;
|
||||
@ -45,6 +50,8 @@ class Console implements AutoCloseable {
|
||||
Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile,
|
||||
final Completer completer) throws IOException {
|
||||
in = new ConsoleReader(cmdin, cmdout);
|
||||
TerminalFactory.registerFlavor(Flavor.WINDOWS, JJSWindowsTerminal :: new);
|
||||
TerminalFactory.registerFlavor(Flavor.UNIX, JJSUnixTerminal :: new);
|
||||
in.setExpandEvents(false);
|
||||
in.setHandleUserInterrupt(true);
|
||||
in.setBellEnabled(true);
|
||||
@ -71,4 +78,60 @@ class Console implements AutoCloseable {
|
||||
FileHistory getHistory() {
|
||||
return (FileHistory) 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.nashorn.api.scripting.AbstractJSObject;
|
||||
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) {
|
||||
EditPad.edit(errorHandler, initText, saveHandler);
|
||||
} else {
|
||||
errorHandler.accept(Main.getMessage("no.editor"));
|
||||
}
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFunction() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.function.Consumer;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* A minimal Swing editor as a fallback when the user does not specify an
|
||||
* external editor.
|
||||
*/
|
||||
final class EditPad extends JFrame implements Runnable {
|
||||
private static final long serialVersionUID = 1;
|
||||
private final Consumer<String> errorHandler;
|
||||
private final String initialText;
|
||||
private final boolean[] closeLock;
|
||||
private final Consumer<String> saveHandler;
|
||||
|
||||
EditPad(Consumer<String> errorHandler, String initialText,
|
||||
boolean[] closeLock, Consumer<String> saveHandler) {
|
||||
super("Edit Pad (Experimental)");
|
||||
this.errorHandler = errorHandler;
|
||||
this.initialText = initialText;
|
||||
this.closeLock = closeLock;
|
||||
this.saveHandler = saveHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
EditPad.this.dispose();
|
||||
notifyClose();
|
||||
}
|
||||
});
|
||||
setLocationRelativeTo(null);
|
||||
setLayout(new BorderLayout());
|
||||
JTextArea textArea = new JTextArea(initialText);
|
||||
add(new JScrollPane(textArea), BorderLayout.CENTER);
|
||||
add(buttons(textArea), BorderLayout.SOUTH);
|
||||
|
||||
setSize(800, 600);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
private JPanel buttons(JTextArea textArea) {
|
||||
FlowLayout flow = new FlowLayout();
|
||||
flow.setHgap(35);
|
||||
JPanel buttons = new JPanel(flow);
|
||||
JButton cancel = new JButton("Cancel");
|
||||
cancel.setMnemonic(KeyEvent.VK_C);
|
||||
JButton accept = new JButton("Accept");
|
||||
accept.setMnemonic(KeyEvent.VK_A);
|
||||
JButton exit = new JButton("Exit");
|
||||
exit.setMnemonic(KeyEvent.VK_X);
|
||||
buttons.add(cancel);
|
||||
buttons.add(accept);
|
||||
buttons.add(exit);
|
||||
|
||||
cancel.addActionListener(e -> {
|
||||
close();
|
||||
});
|
||||
accept.addActionListener(e -> {
|
||||
saveHandler.accept(textArea.getText());
|
||||
});
|
||||
exit.addActionListener(e -> {
|
||||
saveHandler.accept(textArea.getText());
|
||||
close();
|
||||
});
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
private void close() {
|
||||
setVisible(false);
|
||||
dispose();
|
||||
notifyClose();
|
||||
}
|
||||
|
||||
private void notifyClose() {
|
||||
synchronized (closeLock) {
|
||||
closeLock[0] = true;
|
||||
closeLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
static void edit(Consumer<String> errorHandler, String initialText,
|
||||
Consumer<String> saveHandler) {
|
||||
boolean[] closeLock = new boolean[1];
|
||||
SwingUtilities.invokeLater(
|
||||
new EditPad(errorHandler, initialText, closeLock, saveHandler));
|
||||
synchronized (closeLock) {
|
||||
while (!closeLock[0]) {
|
||||
try {
|
||||
closeLock.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
// ignore and loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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(Consumer<String> errorHandler, Consumer<String> saveHandler, Console input) {
|
||||
this.errorHandler = errorHandler;
|
||||
this.saveHandler = saveHandler;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
private void edit(String cmd, 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(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 (ClosedWatchServiceException ex) {
|
||||
break;
|
||||
} catch (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(String cmd) throws IOException {
|
||||
ProcessBuilder pb = new ProcessBuilder(cmd, tmpfile.toString());
|
||||
pb = pb.inheritIO();
|
||||
|
||||
try {
|
||||
input.suspend();
|
||||
Process process = pb.start();
|
||||
process.waitFor();
|
||||
} catch (IOException ex) {
|
||||
errorHandler.accept("process IO failure: " + ex.getMessage());
|
||||
} catch (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 (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(String cmd, Consumer<String> errorHandler, String initialText,
|
||||
Consumer<String> saveHandler, Console input) {
|
||||
ExternalEditor ed = new ExternalEditor(errorHandler, saveHandler, input);
|
||||
ed.edit(cmd, initialText);
|
||||
}
|
||||
}
|
@ -25,16 +25,23 @@
|
||||
|
||||
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.FileHistory;
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -46,15 +53,51 @@ final class HistoryObject extends AbstractJSObject {
|
||||
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 FileHistory hist;
|
||||
private final PrintWriter err;
|
||||
private final Consumer<String> evaluator;
|
||||
|
||||
HistoryObject(final FileHistory hist) {
|
||||
HistoryObject(final FileHistory 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
|
||||
@ -64,10 +107,16 @@ final class HistoryObject extends AbstractJSObject {
|
||||
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;
|
||||
}
|
||||
@ -82,7 +131,11 @@ final class HistoryObject extends AbstractJSObject {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[object history]";
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for (History.Entry e : hist) {
|
||||
buf.append(e.value()).append('\n');
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -90,9 +143,32 @@ final class HistoryObject extends AbstractJSObject {
|
||||
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.println(e.value());
|
||||
System.out.printf("%3d %s\n", e.index() + 1, e.value());
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,4 +180,17 @@ final class HistoryObject extends AbstractJSObject {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.nashorn.tools.jjs;
|
||||
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
@ -32,12 +33,14 @@ import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Consumer;
|
||||
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.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.Property;
|
||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.tools.Shell;
|
||||
@ -48,6 +51,9 @@ import jdk.nashorn.tools.Shell;
|
||||
public final class Main extends Shell {
|
||||
private Main() {}
|
||||
|
||||
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");
|
||||
|
||||
@ -96,10 +102,12 @@ public final class Main extends Shell {
|
||||
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 Completer completer = new NashornCompleter(context, global, this);
|
||||
final PropertiesHelper propsHelper = new PropertiesHelper(env._classpath);
|
||||
final NashornCompleter completer = new NashornCompleter(context, global, this, propsHelper);
|
||||
|
||||
try (final Console in = new Console(System.in, System.out, HIST_FILE, completer)) {
|
||||
if (globalChanged) {
|
||||
@ -107,8 +115,30 @@ public final class Main extends Shell {
|
||||
}
|
||||
|
||||
global.addShellBuiltins();
|
||||
// expose history object for reflecting on command line history
|
||||
global.put("history", new HistoryObject(in.getHistory()), false);
|
||||
|
||||
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 = "";
|
||||
@ -133,10 +163,25 @@ public final class Main extends Shell {
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,8 +194,34 @@ public final class Main extends Shell {
|
||||
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 != ScriptRuntime.UNDEFINED) {
|
||||
err.println(JSType.toString(res));
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
err.println(e);
|
||||
if (doe) {
|
||||
e.printStackTrace(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,18 @@
|
||||
|
||||
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;
|
||||
@ -46,28 +55,143 @@ 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
|
||||
/**
|
||||
* 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) {
|
||||
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.parser = Parser.create();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// 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!
|
||||
@ -96,6 +220,19 @@ final class NashornCompleter implements Completer {
|
||||
|
||||
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;
|
||||
}
|
||||
@ -113,6 +250,38 @@ final class NashornCompleter implements Completer {
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
@ -122,19 +291,22 @@ final class NashornCompleter implements Completer {
|
||||
Object obj = null;
|
||||
try {
|
||||
obj = context.eval(global, objExprCode, global, "<suggestions>");
|
||||
} catch (Exception ignored) {
|
||||
// throw the exception - this is during tab-completion
|
||||
} 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(PropertiesHelper.getProperties(obj));
|
||||
result.addAll(propsHelper.getProperties(obj));
|
||||
return cursor;
|
||||
} else {
|
||||
// list of properties matching the user specified prefix
|
||||
final String prefix = select.getIdentifier();
|
||||
result.addAll(PropertiesHelper.getProperties(obj, prefix));
|
||||
result.addAll(propsHelper.getProperties(obj, prefix));
|
||||
return cursor - prefix.length();
|
||||
}
|
||||
}
|
||||
@ -142,10 +314,11 @@ final class NashornCompleter implements Completer {
|
||||
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(PropertiesHelper.getProperties(global, name));
|
||||
result.addAll(propsHelper.getProperties(global, name));
|
||||
return cursor - name.length();
|
||||
}
|
||||
|
||||
@ -169,6 +342,7 @@ final class NashornCompleter implements Completer {
|
||||
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
|
||||
@ -228,4 +402,27 @@ final class NashornCompleter implements Completer {
|
||||
}
|
||||
}, 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");
|
||||
}
|
||||
|
||||
return Parser.create(args.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.File;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
/**
|
||||
* Is Java package properties helper available?
|
||||
*
|
||||
* @return true if package properties support is available
|
||||
*/
|
||||
static boolean isAvailable() {
|
||||
return compiler != null;
|
||||
}
|
||||
|
||||
private final StandardJavaFileManager fm;
|
||||
private final Set<JavaFileObject.Kind> fileKinds;
|
||||
|
||||
/**
|
||||
* Construct a new PackagesHelper.
|
||||
*
|
||||
* @param classPath Class path to compute properties of java package objects
|
||||
*/
|
||||
PackagesHelper(final String classPath) throws IOException {
|
||||
assert isAvailable() : "no java compiler found!";
|
||||
|
||||
fm = compiler.getStandardFileManager(null, null, null);
|
||||
fileKinds = EnumSet.of(JavaFileObject.Kind.CLASS);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
fm.close();
|
||||
}
|
||||
|
||||
private Set<String> listPackage(final String pkg) throws IOException {
|
||||
final Set<String> props = new HashSet<>();
|
||||
listPackage(StandardLocation.PLATFORM_CLASS_PATH, pkg, props);
|
||||
listPackage(StandardLocation.CLASS_PATH, pkg, props);
|
||||
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"
|
||||
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) {
|
||||
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());
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.nashorn.tools.jjs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -32,6 +33,7 @@ import java.util.List;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
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;
|
||||
@ -41,19 +43,59 @@ import jdk.nashorn.internal.objects.NativeJava;
|
||||
* A helper class to get properties of a given object for source code completion.
|
||||
*/
|
||||
final class PropertiesHelper {
|
||||
private PropertiesHelper() {}
|
||||
|
||||
// Java package properties helper, may be null
|
||||
private PackagesHelper pkgsHelper;
|
||||
// cached properties list
|
||||
private static final WeakHashMap<Object, List<String>> propsCache = new WeakHashMap<>();
|
||||
private final WeakHashMap<Object, List<String>> propsCache = new WeakHashMap<>();
|
||||
|
||||
// returns the list of properties of the given object
|
||||
static List<String> getProperties(final Object obj) {
|
||||
/**
|
||||
* Construct a new PropertiesHelper.
|
||||
*
|
||||
* @param classPath Class path to compute properties of java package objects
|
||||
*/
|
||||
PropertiesHelper(final String classPath) {
|
||||
if (PackagesHelper.isAvailable()) {
|
||||
try {
|
||||
this.pkgsHelper = new PackagesHelper(classPath);
|
||||
} 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();
|
||||
@ -71,6 +113,7 @@ final class PropertiesHelper {
|
||||
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);
|
||||
@ -82,6 +125,7 @@ final class PropertiesHelper {
|
||||
return props;
|
||||
}
|
||||
|
||||
// any other Java object
|
||||
final Class<?> clazz = obj.getClass();
|
||||
if (propsCache.containsKey(clazz)) {
|
||||
return propsCache.get(clazz);
|
||||
@ -94,8 +138,14 @@ final class PropertiesHelper {
|
||||
return props;
|
||||
}
|
||||
|
||||
// returns the list of properties of the given object that start with the given prefix
|
||||
static List<String> getProperties(final Object obj, final String prefix) {
|
||||
/**
|
||||
* 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();
|
||||
return getProperties(obj).stream()
|
||||
.filter(s -> s.startsWith(prefix))
|
||||
|
@ -249,7 +249,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
private final Set<String> emittedMethods = new HashSet<>();
|
||||
|
||||
// Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
|
||||
private final Map<Integer, ContinuationInfo> fnIdToContinuationInfo = new HashMap<>();
|
||||
private ContinuationInfo continuationInfo;
|
||||
|
||||
private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
|
||||
|
||||
@ -349,11 +349,20 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final int flags = getScopeCallSiteFlags(symbol);
|
||||
if (isFastScope(symbol)) {
|
||||
// Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
|
||||
if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !isOptimisticOrRestOf()) {
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
// As shared scope vars are only used in non-optimistic compilation, we switch from using TypeBounds to
|
||||
if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !identNode.isOptimistic()) {
|
||||
// As shared scope vars are only used with non-optimistic identifiers, we switch from using TypeBounds to
|
||||
// just a single definitive type, resultBounds.widest.
|
||||
loadSharedScopeVar(resultBounds.widest, symbol, flags);
|
||||
new OptimisticOperation(identNode, TypeBounds.OBJECT) {
|
||||
@Override
|
||||
void loadStack() {
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
void consumeStack() {
|
||||
loadSharedScopeVar(resultBounds.widest, symbol, flags);
|
||||
}
|
||||
}.emit();
|
||||
} else {
|
||||
new LoadFastScopeVar(identNode, resultBounds, flags).emit();
|
||||
}
|
||||
@ -384,10 +393,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return continuationEntryPoints != null;
|
||||
}
|
||||
|
||||
private boolean isOptimisticOrRestOf() {
|
||||
return useOptimisticTypes() || isRestOf();
|
||||
}
|
||||
|
||||
private boolean isCurrentContinuationEntryPoint(final int programPoint) {
|
||||
return isRestOf() && getCurrentContinuationEntryPoint() == programPoint;
|
||||
}
|
||||
@ -464,12 +469,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
|
||||
assert !isOptimisticOrRestOf();
|
||||
if (isFastScope(symbol)) {
|
||||
method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol));
|
||||
} else {
|
||||
method.load(-1);
|
||||
}
|
||||
assert isFastScope(symbol);
|
||||
method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol));
|
||||
return lc.getScopeGet(unit, symbol, valueType, flags).generateInvoke(method);
|
||||
}
|
||||
|
||||
@ -1573,7 +1574,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
} else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
|
||||
|| !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD
|
||||
|| CodeGenerator.this.lc.inDynamicScope()
|
||||
|| isOptimisticOrRestOf()) {
|
||||
|| callNode.isOptimistic()) {
|
||||
scopeCall(node, flags);
|
||||
} else {
|
||||
sharedScopeCall(node, flags);
|
||||
@ -2070,8 +2071,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
final int fnId = functionNode.getId();
|
||||
|
||||
if (skipFunction(functionNode)) {
|
||||
// In case we are not generating code for the function, we must create or retrieve the function object and
|
||||
// load it on the stack here.
|
||||
@ -2109,9 +2108,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method.begin();
|
||||
|
||||
if (isRestOf()) {
|
||||
final ContinuationInfo ci = new ContinuationInfo();
|
||||
fnIdToContinuationInfo.put(fnId, ci);
|
||||
method.gotoLoopStart(ci.getHandlerLabel());
|
||||
assert continuationInfo == null;
|
||||
continuationInfo = new ContinuationInfo();
|
||||
method.gotoLoopStart(continuationInfo.getHandlerLabel());
|
||||
}
|
||||
}
|
||||
|
||||
@ -5308,7 +5307,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private ContinuationInfo getContinuationInfo() {
|
||||
return fnIdToContinuationInfo.get(lc.getCurrentFunction().getId());
|
||||
return continuationInfo;
|
||||
}
|
||||
|
||||
private void generateContinuationHandler() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 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
|
||||
@ -1451,9 +1451,22 @@ public class Lexer extends Scanner {
|
||||
skip(3);
|
||||
}
|
||||
|
||||
// Scan identifier.
|
||||
// Scan identifier. It might be quoted, indicating that no string editing should take place.
|
||||
final char quoteChar = ch0;
|
||||
final boolean noStringEditing = quoteChar == '"' || quoteChar == '\'';
|
||||
if (noStringEditing) {
|
||||
skip(1);
|
||||
}
|
||||
final int identStart = position;
|
||||
final int identLength = scanIdentifier();
|
||||
if (noStringEditing) {
|
||||
if (ch0 != quoteChar) {
|
||||
error(Lexer.message("here.non.matching.delimiter"), last, position, position);
|
||||
restoreState(saved);
|
||||
return false;
|
||||
}
|
||||
skip(1);
|
||||
}
|
||||
|
||||
// Check for identifier.
|
||||
if (identLength == 0) {
|
||||
@ -1523,7 +1536,7 @@ public class Lexer extends Scanner {
|
||||
}
|
||||
|
||||
// Edit string if appropriate.
|
||||
if (scripting && !stringState.isEmpty()) {
|
||||
if (!noStringEditing && !stringState.isEmpty()) {
|
||||
editString(STRING, stringState);
|
||||
} else {
|
||||
// Add here string.
|
||||
|
@ -46,6 +46,11 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
* and output and error writers, top level Namespace etc.
|
||||
*/
|
||||
public final class ScriptEnvironment {
|
||||
// Primarily intended to be used in test environments so that eager compilation tests work without an
|
||||
// error when tested with optimistic compilation.
|
||||
private static final boolean ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE = Options.getBooleanProperty(
|
||||
"nashorn.options.allowEagerCompilationSilentOverride", false);
|
||||
|
||||
/** Output writer for this environment */
|
||||
private final PrintWriter out;
|
||||
|
||||
@ -61,6 +66,9 @@ public final class ScriptEnvironment {
|
||||
/** Size of the per-global Class cache size */
|
||||
public final int _class_cache_size;
|
||||
|
||||
/** -classpath value. */
|
||||
public final String _classpath;
|
||||
|
||||
/** Only compile script, do not run it or generate other ScriptObjects */
|
||||
public final boolean _compile_only;
|
||||
|
||||
@ -220,6 +228,7 @@ public final class ScriptEnvironment {
|
||||
this.options = options;
|
||||
|
||||
_class_cache_size = options.getInteger("class.cache.size");
|
||||
_classpath = options.getString("classpath");
|
||||
_compile_only = options.getBoolean("compile.only");
|
||||
_const_as_var = options.getBoolean("const.as.var");
|
||||
_debug_lines = options.getBoolean("debug.lines");
|
||||
@ -237,8 +246,20 @@ public final class ScriptEnvironment {
|
||||
}
|
||||
_fx = options.getBoolean("fx");
|
||||
_global_per_engine = options.getBoolean("global.per.engine");
|
||||
_lazy_compilation = options.getBoolean("lazy.compilation");
|
||||
_optimistic_types = options.getBoolean("optimistic.types");
|
||||
final boolean lazy_compilation = options.getBoolean("lazy.compilation");
|
||||
if (!lazy_compilation && _optimistic_types) {
|
||||
if (!ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE) {
|
||||
throw new IllegalStateException(
|
||||
ECMAErrors.getMessage(
|
||||
"config.error.eagerCompilationConflictsWithOptimisticTypes",
|
||||
options.getOptionTemplateByKey("lazy.compilation").getName(),
|
||||
options.getOptionTemplateByKey("optimistic.types").getName()));
|
||||
}
|
||||
_lazy_compilation = true;
|
||||
} else {
|
||||
_lazy_compilation = lazy_compilation;
|
||||
}
|
||||
_loader_per_compile = options.getBoolean("loader.per.compile");
|
||||
_no_java = options.getBoolean("no.java");
|
||||
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
|
||||
|
@ -28,12 +28,14 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
@ -156,11 +158,12 @@ public final class Timing implements Loggable {
|
||||
}
|
||||
|
||||
final class TimeSupplier implements Supplier<String> {
|
||||
private final Map<String, Long> timings;
|
||||
|
||||
TimeSupplier() {
|
||||
timings = new LinkedHashMap<>();
|
||||
}
|
||||
private final Map<String, LongAdder> timings = new ConcurrentHashMap<>();
|
||||
private final LinkedBlockingQueue<String> orderedTimingNames = new LinkedBlockingQueue<>();
|
||||
private final Function<String, LongAdder> newTimingCreator = s -> {
|
||||
orderedTimingNames.add(s);
|
||||
return new LongAdder();
|
||||
};
|
||||
|
||||
String[] getStrings() {
|
||||
final List<String> strs = new ArrayList<>();
|
||||
@ -184,26 +187,26 @@ public final class Timing implements Loggable {
|
||||
int maxKeyLength = 0;
|
||||
int maxValueLength = 0;
|
||||
|
||||
for (final Map.Entry<String, Long> entry : timings.entrySet()) {
|
||||
for (final Map.Entry<String, LongAdder> entry : timings.entrySet()) {
|
||||
maxKeyLength = Math.max(maxKeyLength, entry.getKey().length());
|
||||
maxValueLength = Math.max(maxValueLength, toMillisPrint(entry.getValue()).length());
|
||||
maxValueLength = Math.max(maxValueLength, toMillisPrint(entry.getValue().longValue()).length());
|
||||
}
|
||||
maxKeyLength++;
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Accumulated compilation phase timings:\n\n");
|
||||
for (final Map.Entry<String, Long> entry : timings.entrySet()) {
|
||||
for (final String timingName: orderedTimingNames) {
|
||||
int len;
|
||||
|
||||
len = sb.length();
|
||||
sb.append(entry.getKey());
|
||||
sb.append(timingName);
|
||||
len = sb.length() - len;
|
||||
|
||||
while (len++ < maxKeyLength) {
|
||||
sb.append(' ');
|
||||
}
|
||||
|
||||
final Long duration = entry.getValue();
|
||||
final long duration = timings.get(timingName).longValue();
|
||||
final String strDuration = toMillisPrint(duration);
|
||||
len = strDuration.length();
|
||||
for (int i = 0; i < maxValueLength - len; i++) {
|
||||
@ -233,11 +236,7 @@ public final class Timing implements Loggable {
|
||||
}
|
||||
|
||||
private void accumulateTime(final String module, final long duration) {
|
||||
Long accumulatedTime = timings.get(module);
|
||||
if (accumulatedTime == null) {
|
||||
accumulatedTime = 0L;
|
||||
}
|
||||
timings.put(module, accumulatedTime + duration);
|
||||
timings.computeIfAbsent(module, newTimingCreator).add(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
@ -34,6 +33,7 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.Callable;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
|
||||
@ -80,6 +80,15 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
private final static MethodHandle INVOKE_LONG_SETTER = findOwnMH_S("invokeLongSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, long.class);
|
||||
private final static MethodHandle INVOKE_NUMBER_SETTER = findOwnMH_S("invokeNumberSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, double.class);
|
||||
|
||||
private static final Object OBJECT_GETTER_INVOKER_KEY = new Object();
|
||||
private static MethodHandle getObjectGetterInvoker() {
|
||||
return Context.getGlobal().getDynamicInvoker(OBJECT_GETTER_INVOKER_KEY, new Callable<MethodHandle>() {
|
||||
@Override
|
||||
public MethodHandle call() throws Exception {
|
||||
return getINVOKE_UA_GETTER(Object.class, INVALID_PROGRAM_POINT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static MethodHandle getINVOKE_UA_GETTER(final Class<?> returnType, final int programPoint) {
|
||||
if (UnwarrantedOptimismException.isValid(programPoint)) {
|
||||
@ -90,6 +99,16 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object OBJECT_SETTER_INVOKER_KEY = new Object();
|
||||
private static MethodHandle getObjectSetterInvoker() {
|
||||
return Context.getGlobal().getDynamicInvoker(OBJECT_SETTER_INVOKER_KEY, new Callable<MethodHandle>() {
|
||||
@Override
|
||||
public MethodHandle call() throws Exception {
|
||||
return getINVOKE_UA_SETTER(Object.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static MethodHandle getINVOKE_UA_SETTER(final Class<?> valueType) {
|
||||
return Bootstrap.createDynamicInvoker("dyn:call", void.class, Object.class, Object.class, valueType);
|
||||
}
|
||||
@ -181,7 +200,7 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
@Override
|
||||
public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
|
||||
try {
|
||||
return invokeObjectGetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_GETTER(Object.class, INVALID_PROGRAM_POINT), self);
|
||||
return invokeObjectGetter(getAccessors((owner != null) ? owner : self), getObjectGetterInvoker(), self);
|
||||
} catch (final Error | RuntimeException t) {
|
||||
throw t;
|
||||
} catch (final Throwable t) {
|
||||
@ -207,7 +226,7 @@ public final class UserAccessorProperty extends SpillProperty {
|
||||
@Override
|
||||
public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
|
||||
try {
|
||||
invokeObjectSetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_SETTER(Object.class), strict ? getKey() : null, self, value);
|
||||
invokeObjectSetter(getAccessors((owner != null) ? owner : self), getObjectSetterInvoker(), strict ? getKey() : null, self, value);
|
||||
} catch (final Error | RuntimeException t) {
|
||||
throw t;
|
||||
} catch (final Throwable t) {
|
||||
|
@ -304,8 +304,8 @@ public final class OptionTemplate implements Comparable<OptionTemplate> {
|
||||
}
|
||||
}
|
||||
|
||||
boolean matches(final String key0) {
|
||||
return key0.equals(this.shortName) || key0.equals(this.name);
|
||||
boolean nameMatches(final String aName) {
|
||||
return aName.equals(this.shortName) || aName.equals(this.name);
|
||||
}
|
||||
|
||||
private static final int LINE_BREAK = 64;
|
||||
|
@ -520,9 +520,25 @@ public final class Options {
|
||||
}
|
||||
}
|
||||
|
||||
private static OptionTemplate getOptionTemplate(final String key) {
|
||||
/**
|
||||
* Retrieves an option template identified by key.
|
||||
* @param shortKey the short (that is without the e.g. "nashorn.option." part) key
|
||||
* @return the option template identified by the key
|
||||
* @throws IllegalArgumentException if the key doesn't specify an existing template
|
||||
*/
|
||||
public OptionTemplate getOptionTemplateByKey(final String shortKey) {
|
||||
final String fullKey = key(shortKey);
|
||||
for(final OptionTemplate t: validOptions) {
|
||||
if(t.getKey().equals(fullKey)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(shortKey);
|
||||
}
|
||||
|
||||
private static OptionTemplate getOptionTemplateByName(final String name) {
|
||||
for (final OptionTemplate t : Options.validOptions) {
|
||||
if (t.matches(key)) {
|
||||
if (t.nameMatches(name)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@ -682,7 +698,7 @@ public final class Options {
|
||||
}
|
||||
|
||||
final String token = st.nextToken();
|
||||
this.template = Options.getOptionTemplate(token);
|
||||
this.template = getOptionTemplateByName(token);
|
||||
if (this.template == null) {
|
||||
throw new IllegalArgumentException(argument);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2010, 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
|
||||
@ -31,6 +31,7 @@ lexer.error.strict.no.octal=cannot use octal escapes in strict mode
|
||||
lexer.error.json.invalid.number=Invalid JSON number format
|
||||
lexer.error.invalid.escape.char=Invalid escape character
|
||||
lexer.error.illegal.identifier.character=Illegal character in identifier
|
||||
lexer.error.here.non.matching.delimiter=Quoted here string end marker must have matching delimiters
|
||||
|
||||
parser.error.illegal.continue.stmt=Illegal continue statement
|
||||
parser.error.illegal.break.stmt=Illegal break statement
|
||||
@ -172,7 +173,9 @@ syntax.error.redeclare.variable=Variable "{0}" has already been declared
|
||||
syntax.error.unprotected.switch.declaration=Unsupported {0} declaration in unprotected switch statement
|
||||
|
||||
io.error.cant.write=cannot write "{0}"
|
||||
|
||||
config.error.no.dest=no destination directory supplied
|
||||
config.error.eagerCompilationConflictsWithOptimisticTypes={0}=false (eager compilation) is not compatible with {1}=true.
|
||||
|
||||
uri.error.bad.uri=Bad URI "{0}" near offset {1}
|
||||
list.adapter.null.global=Attempted to create the adapter from outside a JavaScript execution context.
|
||||
|
@ -29,4 +29,6 @@ shell.usage=jjs [<options>] <files> [-- <arguments>]
|
||||
|
||||
shell.prompt=jjs>
|
||||
|
||||
shell.prompt2=...>
|
||||
|
||||
no.editor=AWT Headless mode set and no external editor is configured!
|
||||
|
@ -28,6 +28,7 @@
|
||||
* @runif external.octane
|
||||
* @fork
|
||||
* @option -Dnashorn.compiler.splitter.threshold=1000
|
||||
* @option -Dnashorn.options.allowEagerCompilationSilentOverride
|
||||
* @option -scripting
|
||||
* @option --lazy-compilation=false
|
||||
*/
|
||||
|
@ -26,7 +26,9 @@
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @fork
|
||||
* @option --lazy-compilation=false
|
||||
* @option -Dnashorn.options.allowEagerCompilationSilentOverride
|
||||
*/
|
||||
|
||||
// Just attempting to compile this caused the NPE
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @option -pcc
|
||||
* @option --lazy-compilation=false
|
||||
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
|
||||
* @option -Dnashorn.options.allowEagerCompilationSilentOverride
|
||||
* @fork
|
||||
*/
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @option -pcc
|
||||
* @option --lazy-compilation=false
|
||||
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
|
||||
* @option -Dnashorn.options.allowEagerCompilationSilentOverride
|
||||
* @fork
|
||||
*/
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @option -pcc
|
||||
* @option --lazy-compilation=false
|
||||
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
|
||||
* @option -Dnashorn.options.allowEagerCompilationSilentOverride
|
||||
* @fork
|
||||
*/
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
* @option -pcc
|
||||
* @option --lazy-compilation=false
|
||||
* @option -Dnashorn.persistent.code.cache=build/nashorn_code_cache
|
||||
* @option -Dnashorn.options.allowEagerCompilationSilentOverride
|
||||
* @fork
|
||||
*/
|
||||
|
||||
|
50
nashorn/test/script/nosecurity/JDK-8073613.js
Normal file
50
nashorn/test/script/nosecurity/JDK-8073613.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* JDK-8073613: Here documents: how to avoid string interpolation?
|
||||
*
|
||||
* @test
|
||||
* @option -scripting
|
||||
* @run
|
||||
*/
|
||||
|
||||
var a = 2,
|
||||
b = 3
|
||||
|
||||
print(<<EOD)
|
||||
${a}${b}
|
||||
EOD
|
||||
|
||||
print(<<"EOD")
|
||||
${a}${b}
|
||||
EOD
|
||||
|
||||
print(<<'EOM')
|
||||
${a}${b}
|
||||
EOM
|
||||
|
||||
print(<<"EOM")
|
||||
$\{a}
|
||||
EOM
|
||||
|
4
nashorn/test/script/nosecurity/JDK-8073613.js.EXPECTED
Normal file
4
nashorn/test/script/nosecurity/JDK-8073613.js.EXPECTED
Normal file
@ -0,0 +1,4 @@
|
||||
23
|
||||
${a}${b}
|
||||
${a}${b}
|
||||
$\{a}
|
Loading…
Reference in New Issue
Block a user