# HG changeset patch # User lana # Date 1440098998 25200 # Node ID 9b3eca69b88b2d1bebce92d58280ae66fc0b6091 # Parent 576d1aa23516aa5a84f16fff02e734732b7db4ed# Parent 5b0c3dc04a73deba0ad621a33c07b5282683b6a5 Merge diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Console.java --- a/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Console.java Thu Aug 20 11:38:25 2015 -0700 +++ b/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Console.java Thu Aug 20 12:29:58 2015 -0700 @@ -25,6 +25,7 @@ package jdk.nashorn.tools.jjs; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; @@ -33,71 +34,41 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.prefs.BackingStoreException; -import java.util.prefs.Preferences; import jdk.internal.jline.console.ConsoleReader; -import jdk.internal.jline.console.history.History.Entry; -import jdk.internal.jline.console.history.MemoryHistory; +import jdk.internal.jline.console.completer.Completer; +import jdk.internal.jline.console.history.FileHistory; class Console implements AutoCloseable { private final ConsoleReader in; - private final PersistentHistory history; + private final FileHistory history; - Console(InputStream cmdin, PrintStream cmdout, Preferences prefs) throws IOException { + Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile, + final Completer completer) throws IOException { in = new ConsoleReader(cmdin, cmdout); in.setExpandEvents(false); in.setHandleUserInterrupt(true); - in.setHistory(history = new PersistentHistory(prefs)); - Runtime.getRuntime().addShutdownHook(new Thread(()->close())); + in.setBellEnabled(true); + in.setHistory(history = new FileHistory(historyFile)); + in.addCompleter(completer); + Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory)); } - String readLine(String prompt) throws IOException { + String readLine(final String prompt) throws IOException { return in.readLine(prompt); } - @Override public void close() { - history.save(); + saveHistory(); } - public static class PersistentHistory extends MemoryHistory { - - private final Preferences prefs; - - protected PersistentHistory(Preferences prefs) { - this.prefs = prefs; - load(); - } - - private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_"; + private void saveHistory() { + try { + getHistory().flush(); + } catch (final IOException exp) {} + } - public final void load() { - try { - List keys = new ArrayList<>(Arrays.asList(prefs.keys())); - Collections.sort(keys); - for (String key : keys) { - if (!key.startsWith(HISTORY_LINE_PREFIX)) - continue; - CharSequence line = prefs.get(key, ""); - add(line); - } - } catch (BackingStoreException ex) { - throw new IllegalStateException(ex); - } - } - - public void save() { - Iterator entries = iterator(); - if (entries.hasNext()) { - int len = (int) Math.ceil(Math.log10(size()+1)); - String format = HISTORY_LINE_PREFIX + "%0" + len + "d"; - while (entries.hasNext()) { - Entry entry = entries.next(); - prefs.put(String.format(format, entry.index()), entry.value().toString()); - } - } - } - + FileHistory getHistory() { + return (FileHistory) in.getHistory(); } } diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,107 @@ +/* + * 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.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; +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.ScriptRuntime.UNDEFINED; + +/* + * A script friendly object that exposes history of commands to scripts. + */ +final class HistoryObject extends AbstractJSObject { + private static final Set props; + static { + final HashSet s = new HashSet<>(); + s.add("clear"); + s.add("forEach"); + s.add("print"); + s.add("size"); + props = Collections.unmodifiableSet(s); + } + + private final FileHistory hist; + + HistoryObject(final FileHistory hist) { + this.hist = hist; + } + + @Override + public Object getMember(final String name) { + switch (name) { + case "clear": + return (Runnable)hist::clear; + case "forEach": + return (Function)this::iterate; + case "print": + return (Runnable)this::print; + case "size": + return hist.size(); + } + return UNDEFINED; + } + + @Override + public Object getDefaultValue(final Class hint) { + if (hint == String.class) { + return toString(); + } + return UNDEFINED; + } + + @Override + public String toString() { + return "[object history]"; + } + + @Override + public Set keySet() { + return props; + } + + private void print() { + for (History.Entry e : hist) { + System.out.println(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; + } +} diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java --- a/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java Thu Aug 20 11:38:25 2015 -0700 +++ b/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java Thu Aug 20 12:29:58 2015 -0700 @@ -26,20 +26,21 @@ package jdk.nashorn.tools.jjs; import java.io.BufferedReader; +import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; -import java.util.prefs.Preferences; +import jdk.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.ErrorManager; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.tools.Shell; -import jdk.internal.jline.console.UserInterruptException; /** * Interactive command line Shell for Nashorn. @@ -47,7 +48,8 @@ public final class Main extends Shell { private Main() {} - static final Preferences PREFS = Preferences.userRoot().node("tool/jjs"); + // 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. @@ -83,6 +85,7 @@ return new Main().run(in, out, err, args); } + /** * read-eval-print loop for Nashorn shell. * @@ -96,13 +99,16 @@ final PrintWriter err = context.getErr(); final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); + final Completer completer = new NashornCompleter(context, global, this); - try (final Console in = new Console(System.in, System.out, PREFS)) { + try (final Console in = new Console(System.in, System.out, HIST_FILE, completer)) { if (globalChanged) { Context.setGlobal(global); } global.addShellBuiltins(); + // expose history object for reflecting on command line history + global.put("history", new HistoryObject(in.getHistory()), false); while (true) { String source = ""; diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,231 @@ +/* + * 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.List; +import java.util.regex.Pattern; +import jdk.internal.jline.console.completer.Completer; +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.Global; +import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.ScriptRuntime; + +// A simple source completer for nashorn +final class NashornCompleter implements Completer { + private final Context context; + private final Global global; + private final PartialParser partialParser; + private final Parser parser; + + NashornCompleter(final Context context, final Global global, final PartialParser partialParser) { + this.context = context; + this.global = global; + this.partialParser = partialParser; + this.parser = Parser.create(); + } + + // 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*"); + + @Override + public int complete(final String test, final int cursor, final List 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) { + // 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; + } + } + + private int completeMemberSelect(final String exprStr, final int cursor, final List 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, ""); + } catch (Exception ignored) { + // throw the exception - this is during tab-completion + } + + if (obj != null && obj != ScriptRuntime.UNDEFINED) { + if (endsWithDot) { + // no user specified "prefix". List all properties of the object + result.addAll(PropertiesHelper.getProperties(obj)); + return cursor; + } else { + // list of properties matching the user specified prefix + final String prefix = select.getIdentifier(); + result.addAll(PropertiesHelper.getProperties(obj, prefix)); + return cursor - prefix.length(); + } + } + + return cursor; + } + + private int completeIdentifier(final String test, final int cursor, final List result, + final IdentifierTree ident) { + final String name = ident.getName(); + result.addAll(PropertiesHelper.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, null); + final List 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; + } + + private Tree getRightMostExpression(final ExpressionTree expr) { + return expr.accept(new SimpleTreeVisitorES5_1() { + @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); + } +} diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/PropertiesHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/PropertiesHelper.java Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,104 @@ +/* + * 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.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.WeakHashMap; +import java.util.stream.Collectors; +import jdk.nashorn.internal.runtime.JSType; +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 { + private PropertiesHelper() {} + + // cached properties list + private static final WeakHashMap> propsCache = new WeakHashMap<>(); + + // returns the list of properties of the given object + static List getProperties(final Object obj) { + assert obj != null && obj != ScriptRuntime.UNDEFINED; + + if (JSType.isPrimitive(obj)) { + return getProperties(JSType.toScriptObject(obj)); + } + + 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 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; + } + + if (NativeJava.isType(ScriptRuntime.UNDEFINED, obj)) { + if (propsCache.containsKey(obj)) { + return propsCache.get(obj); + } + final List props = NativeJava.getProperties(obj); + Collections.sort(props); + // cache properties against the StaticClass representing the class + propsCache.put(obj, props); + return props; + } + + final Class clazz = obj.getClass(); + if (propsCache.containsKey(clazz)) { + return propsCache.get(clazz); + } + + final List props = NativeJava.getProperties(obj); + Collections.sort(props); + // cache properties against the Class object + propsCache.put(clazz, props); + return props; + } + + // returns the list of properties of the given object that start with the given prefix + static List getProperties(final Object obj, final String prefix) { + assert prefix != null && !prefix.isEmpty(); + return getProperties(obj).stream() + .filter(s -> s.startsWith(prefix)) + .collect(Collectors.toList()); + } +} diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Thu Aug 20 11:38:25 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Thu Aug 20 12:29:58 2015 -0700 @@ -398,7 +398,7 @@ } else if(protocol.equals("jrt")) { return getJrtVersionDirName(); } else { - throw new AssertionError(); + throw new AssertionError("unknown protocol"); } } @@ -556,13 +556,15 @@ return Math.max(0, Integer.parseInt(str)); } + private static final String JRT_NASHORN_DIR = "/modules/jdk.scripting.nashorn"; + // version directory name if nashorn is loaded from jrt:/ URL private static String getJrtVersionDirName() throws Exception { final FileSystem fs = getJrtFileSystem(); // consider all .class resources under nashorn module to compute checksum - final Path nashorn = fs.getPath("/jdk.scripting.nashorn"); + final Path nashorn = fs.getPath(JRT_NASHORN_DIR); if (! Files.isDirectory(nashorn)) { - throw new FileNotFoundException("missing /jdk.scripting.nashorn dir in jrt fs"); + throw new FileNotFoundException("missing " + JRT_NASHORN_DIR + " dir in jrt fs"); } final MessageDigest digest = MessageDigest.getInstance("SHA-1"); Files.walk(nashorn).forEach(new Consumer() { diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java Thu Aug 20 11:38:25 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java Thu Aug 20 12:29:58 2015 -0700 @@ -30,11 +30,14 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Queue; +import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.support.TypeUtilities; import jdk.nashorn.api.scripting.JSObject; @@ -443,6 +446,47 @@ throw typeError("cant.convert.to.javascript.array", objArray.getClass().getName()); } + /** + * Return properties of the given object. Properties also include "method names". + * This is meant for source code completion in interactive shells or editors. + * + * @param object the object whose properties are returned. + * @return list of properties + */ + public static List getProperties(final Object object) { + if (object instanceof StaticClass) { + // static properties of the given class + final Class clazz = ((StaticClass)object).getRepresentedClass(); + final ArrayList props = new ArrayList<>(); + try { + Bootstrap.checkReflectionAccess(clazz, true); + // Usually writable properties are a subset as 'write-only' properties are rare + props.addAll(BeansLinker.getReadableStaticPropertyNames(clazz)); + props.addAll(BeansLinker.getStaticMethodNames(clazz)); + } catch (Exception ignored) {} + return props; + } else if (object instanceof JSObject) { + final JSObject jsObj = ((JSObject)object); + final ArrayList props = new ArrayList<>(); + props.addAll(jsObj.keySet()); + return props; + } else if (object != null && object != UNDEFINED) { + // instance properties of the given object + final Class clazz = object.getClass(); + final ArrayList props = new ArrayList<>(); + try { + Bootstrap.checkReflectionAccess(clazz, false); + // Usually writable properties are a subset as 'write-only' properties are rare + props.addAll(BeansLinker.getReadableInstancePropertyNames(clazz)); + props.addAll(BeansLinker.getInstanceMethodNames(clazz)); + } catch (Exception ignored) {} + return props; + } + + // don't know about that object + return Collections.emptyList(); + } + private static int[] copyArray(final byte[] in) { final int[] out = new int[in.length]; for(int i = 0; i < in.length; ++i) { diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Thu Aug 20 11:38:25 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Thu Aug 20 12:29:58 2015 -0700 @@ -3237,6 +3237,7 @@ } /** + * {@code * MultiplicativeExpression : * UnaryExpression * MultiplicativeExpression * UnaryExpression @@ -3323,11 +3324,15 @@ * Expression , AssignmentExpression * * See 11.14 + * } * * Parse expression. * @return Expression node. */ - private Expression expression() { + protected Expression expression() { + // This method is protected so that subclass can get details + // at expression start point! + // TODO - Destructuring array. // Include commas in expression parsing. return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false); @@ -3398,7 +3403,10 @@ return lhs; } - private Expression assignmentExpression(final boolean noIn) { + protected Expression assignmentExpression(final boolean noIn) { + // This method is protected so that subclass can get details + // at assignment expression start point! + // TODO - Handle decompose. // Exclude commas in expression parsing. return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn); diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Aug 20 11:38:25 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Aug 20 12:29:58 2015 -0700 @@ -1340,6 +1340,21 @@ } /** + * return an array of all property keys - all inherited, non-enumerable included. + * This is meant for source code completion by interactive shells or editors. + * + * @return Array of keys, order of properties is undefined. + */ + public String[] getAllKeys() { + final Set keys = new HashSet<>(); + final Set nonEnumerable = new HashSet<>(); + for (ScriptObject self = this; self != null; self = self.getProto()) { + keys.addAll(Arrays.asList(self.getOwnKeys(true, nonEnumerable))); + } + return keys.toArray(new String[keys.size()]); + } + + /** * return an array of own property keys associated with the object. * * @param all True if to include non-enumerable keys. diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/PartialParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/PartialParser.java Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,42 @@ +/* + * 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; + +import jdk.nashorn.internal.runtime.Context; + +/** + * Partial parsing support for code completion of expressions. + */ +public interface PartialParser { + /** + * Parse potentially partial code and keep track of the start of last expression. + * + * @param context the nashorn context + * @param code code that is to be parsed + * @return the start index of the last expression parsed in the (incomplete) code. + */ + public int getLastExpressionStart(final Context context, final String code); +} diff -r 576d1aa23516 -r 9b3eca69b88b src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java Thu Aug 20 11:38:25 2015 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java Thu Aug 20 12:29:58 2015 -0700 @@ -43,6 +43,7 @@ import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.objects.Global; @@ -59,7 +60,7 @@ /** * Command line Shell for processing JavaScript files. */ -public class Shell { +public class Shell implements PartialParser { /** * Resource name for properties file @@ -397,6 +398,42 @@ } /** + * Parse potentially partial code and keep track of the start of last expression. + * This 'partial' parsing support is meant to be used for code-completion. + * + * @param context the nashorn context + * @param code code that is to be parsed + * @return the start index of the last expression parsed in the (incomplete) code. + */ + @Override + public final int getLastExpressionStart(final Context context, final String code) { + final int[] exprStart = { -1 }; + + final Parser p = new Parser(context.getEnv(), sourceFor("", code),new Context.ThrowErrorManager()) { + @Override + protected Expression expression() { + exprStart[0] = this.start; + return super.expression(); + } + + @Override + protected Expression assignmentExpression(final boolean noIn) { + exprStart[0] = this.start; + return super.expression(); + } + }; + + try { + p.parse(); + } catch (final Exception ignored) { + // throw any parser exception, but we are partial parsing anyway + } + + return exprStart[0]; + } + + + /** * read-eval-print loop for Nashorn shell. * * @param context the nashorn context diff -r 576d1aa23516 -r 9b3eca69b88b test/script/currently-failing/JDK-8055034.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/currently-failing/JDK-8055034.js Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, 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-8055034: jjs exits interactive mode if exception was thrown when trying to print value of last evaluated expression + * + * @test + * @option -scripting + * @run + */ + +// assume that this script is run with "nashorn.jar" System +// property set to relative or absolute path of nashorn.jar + +if (typeof fail != 'function') { + fail = print; +} + +var System = java.lang.System; +var File = java.io.File; +var javahome = System.getProperty("java.home"); +var nashornJar = new File(System.getProperty("nashorn.jar")); +if (! nashornJar.isAbsolute()) { + nashornJar = new File(".", nashornJar); +} + +// we want to use nashorn.jar passed and not the one that comes with JRE +var jjsCmd = javahome + "/../bin/jjs"; +jjsCmd = jjsCmd.toString().replace(/\//g, File.separator); +if (! new File(jjsCmd).isFile()) { + jjsCmd = javahome + "/bin/jjs"; + jjsCmd = jjsCmd.toString().replace(/\//g, File.separator); +} +jjsCmd += " -J-Xbootclasspath/p:" + nashornJar; + +$ENV.PWD=System.getProperty("user.dir") // to avoid RE on Cygwin +$EXEC(jjsCmd, "var x = Object.create(null);\nx;\nprint('PASSED');\nexit(0)"); + +// $ERR has all interactions including prompts! Just check for error substring. +var err = $ERR.trim(); +if (! err.contains("TypeError: Cannot get default string value")) { + fail("Error stream does not contain expected error message"); +} + +// should print "PASSED" +print($OUT.trim()); +// exit code should be 0 +print("exit code = " + $EXIT); diff -r 576d1aa23516 -r 9b3eca69b88b test/script/currently-failing/JDK-8055034.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/currently-failing/JDK-8055034.js.EXPECTED Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,2 @@ +PASSED +exit code = 0 diff -r 576d1aa23516 -r 9b3eca69b88b test/script/currently-failing/JDK-8130127.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/currently-failing/JDK-8130127.js Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,55 @@ +/* + * 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-8130127: streamline input parameter of Nashorn scripting $EXEC function + * + * Test different variants of stdin passing to $EXEC. + * + * @test + * @option -scripting + * @run + */ + +var File = java.io.File, + sep = File.separator, + System = java.lang.System, + os = System.getProperty("os.name"), + win = os.startsWith("Windows"), + jjsName = "jjs" + (win ? ".exe" : ""), + javaHome = System.getProperty("java.home") + +var jjs = javaHome + "/../bin/".replace(/\//g, sep) + jjsName +if (!new File(jjs).isFile()) { + jjs = javaHome + "/bin/".replace(/\//g, sep) + jjsName +} + +var jjsCmd = jjs + " readprint.js" + +print($EXEC(jjsCmd)) +print($EXEC(jjsCmd, null)) +print($EXEC(jjsCmd, undefined)) +print($EXEC(jjsCmd, "")) + +print($EXEC(jjs, "print('hello')")) + diff -r 576d1aa23516 -r 9b3eca69b88b test/script/currently-failing/JDK-8130127.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/currently-failing/JDK-8130127.js.EXPECTED Thu Aug 20 12:29:58 2015 -0700 @@ -0,0 +1,6 @@ + + + + +hello + diff -r 576d1aa23516 -r 9b3eca69b88b test/script/nosecurity/JDK-8055034.js --- a/test/script/nosecurity/JDK-8055034.js Thu Aug 20 11:38:25 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2014, 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-8055034: jjs exits interactive mode if exception was thrown when trying to print value of last evaluated expression - * - * @test - * @option -scripting - * @run - */ - -// assume that this script is run with "nashorn.jar" System -// property set to relative or absolute path of nashorn.jar - -if (typeof fail != 'function') { - fail = print; -} - -var System = java.lang.System; -var File = java.io.File; -var javahome = System.getProperty("java.home"); -var nashornJar = new File(System.getProperty("nashorn.jar")); -if (! nashornJar.isAbsolute()) { - nashornJar = new File(".", nashornJar); -} - -// we want to use nashorn.jar passed and not the one that comes with JRE -var jjsCmd = javahome + "/../bin/jjs"; -jjsCmd = jjsCmd.toString().replace(/\//g, File.separator); -if (! new File(jjsCmd).isFile()) { - jjsCmd = javahome + "/bin/jjs"; - jjsCmd = jjsCmd.toString().replace(/\//g, File.separator); -} -jjsCmd += " -J-Xbootclasspath/p:" + nashornJar; - -$ENV.PWD=System.getProperty("user.dir") // to avoid RE on Cygwin -$EXEC(jjsCmd, "var x = Object.create(null);\nx;\nprint('PASSED');\nexit(0)"); - -// $ERR has all interactions including prompts! Just check for error substring. -var err = $ERR.trim(); -if (! err.contains("TypeError: Cannot get default string value")) { - fail("Error stream does not contain expected error message"); -} - -// should print "PASSED" -print($OUT.trim()); -// exit code should be 0 -print("exit code = " + $EXIT); diff -r 576d1aa23516 -r 9b3eca69b88b test/script/nosecurity/JDK-8055034.js.EXPECTED --- a/test/script/nosecurity/JDK-8055034.js.EXPECTED Thu Aug 20 11:38:25 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -PASSED -exit code = 0 diff -r 576d1aa23516 -r 9b3eca69b88b test/script/nosecurity/JDK-8130127.js --- a/test/script/nosecurity/JDK-8130127.js Thu Aug 20 11:38:25 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * 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-8130127: streamline input parameter of Nashorn scripting $EXEC function - * - * Test different variants of stdin passing to $EXEC. - * - * @test - * @option -scripting - * @run - */ - -var File = java.io.File, - sep = File.separator, - System = java.lang.System, - os = System.getProperty("os.name"), - win = os.startsWith("Windows"), - jjsName = "jjs" + (win ? ".exe" : ""), - javaHome = System.getProperty("java.home") - -var jjs = javaHome + "/../bin/".replace(/\//g, sep) + jjsName -if (!new File(jjs).isFile()) { - jjs = javaHome + "/bin/".replace(/\//g, sep) + jjsName -} - -var jjsCmd = jjs + " readprint.js" - -print($EXEC(jjsCmd)) -print($EXEC(jjsCmd, null)) -print($EXEC(jjsCmd, undefined)) -print($EXEC(jjsCmd, "")) - -print($EXEC(jjs, "print('hello')")) - diff -r 576d1aa23516 -r 9b3eca69b88b test/script/nosecurity/JDK-8130127.js.EXPECTED --- a/test/script/nosecurity/JDK-8130127.js.EXPECTED Thu Aug 20 11:38:25 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - - - - -hello - diff -r 576d1aa23516 -r 9b3eca69b88b test/src/jdk/nashorn/internal/runtime/test/CodeStoreAndPathTest.java --- a/test/src/jdk/nashorn/internal/runtime/test/CodeStoreAndPathTest.java Thu Aug 20 11:38:25 2015 -0700 +++ b/test/src/jdk/nashorn/internal/runtime/test/CodeStoreAndPathTest.java Thu Aug 20 12:29:58 2015 -0700 @@ -26,6 +26,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.nio.file.DirectoryStream; @@ -38,7 +39,6 @@ import org.testng.annotations.Test; /** - * @ignore Fails with jtreg, but passes with ant test run. Ignore for now. * @test * @bug 8039185 8039403 * @summary Test for persistent code cache and path handling @@ -113,7 +113,8 @@ assertEquals(actualCodeCachePath, expectedCodeCachePath); // Check that code cache dir exists and it's not empty final File file = new File(actualCodeCachePath.toUri()); - assertFalse(!file.isDirectory(), "No code cache directory was created!"); + assertTrue(file.exists(), "No code cache directory was created!"); + assertTrue(file.isDirectory(), "Code cache location is not a directory!"); assertFalse(file.list().length == 0, "Code cache directory is empty!"); } @@ -174,7 +175,7 @@ return codeCachePath.resolve(file); } } - throw new AssertionError("Code cache path not found"); + throw new AssertionError("Code cache path not found: " + codeCachePath.toString()); } private static void checkCompiledScripts(final DirectoryStream stream, final int numberOfScripts) throws IOException {