diff --git a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java index de3ff4ca524..0ff0c75efd4 100644 --- a/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java +++ b/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java @@ -3350,7 +3350,8 @@ public class JShellTool implements MessageHandler { String val = state.status(vk) == Status.VALID ? feedback.truncateVarValue(state.varValue(vk)) : getResourceString("jshell.msg.vars.not.active"); - hard(" %s %s = %s", vk.typeName(), vk.name(), val); + String varName = vk.name(); + hard(" %s %s = %s", vk.typeName(), varName.isEmpty() ? "_" : varName, val); }); return true; } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java index b175bd563dc..3c45fbdbd14 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java @@ -385,21 +385,28 @@ class Eval { subkind = SubKind.VAR_DECLARATION_SUBKIND; } Wrap wname; - int nameStart = compileSource.lastIndexOf(name, nameMax); - if (nameStart < 0) { - // the name has been transformed (e.g. unicode). - // Use it directly - wname = Wrap.identityWrap(name); + String fieldName; + if (name.isEmpty()) { + fieldName = "$UNNAMED"; + wname = Wrap.simpleWrap(fieldName); } else { - int nameEnd = nameStart + name.length(); - Range rname = new Range(nameStart, nameEnd); - wname = new Wrap.RangeWrap(compileSource, rname); + fieldName = name; + int nameStart = compileSource.lastIndexOf(name, nameMax); + if (nameStart < 0) { + // the name has been transformed (e.g. unicode). + // Use it directly + wname = Wrap.identityWrap(name); + } else { + int nameEnd = nameStart + name.length(); + Range rname = new Range(nameStart, nameEnd); + wname = new Wrap.RangeWrap(compileSource, rname); + } } Wrap guts = Wrap.varWrap(compileSource, typeWrap, sbBrackets.toString(), wname, winit, enhancedDesugaring, anonDeclareWrap); DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true); Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, - name, subkind, displayType, hasEnhancedType ? fullTypeName : null, anonymousClasses, + name, fieldName, subkind, displayType, hasEnhancedType ? fullTypeName : null, anonymousClasses, tds.declareReferences(), modDiag); snippets.add(snip); } @@ -659,7 +666,7 @@ class Eval { } Collection declareReferences = null; //TODO snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, - name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, displayTypeName, fullTypeName, anonymousClasses, declareReferences, null); + name, name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, displayTypeName, fullTypeName, anonymousClasses, declareReferences, null); } else { guts = Wrap.methodReturnWrap(compileSource); snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts, diff --git a/src/jdk.jshell/share/classes/jdk/jshell/JShell.java b/src/jdk.jshell/share/classes/jdk/jshell/JShell.java index 4a53eb62726..ffa46cf8be4 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/JShell.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/JShell.java @@ -726,7 +726,7 @@ public class JShell implements AutoCloseable { } String value; try { - value = executionControl().varValue(snippet.classFullName(), snippet.name()); + value = executionControl().varValue(snippet.classFullName(), snippet.fieldName()); } catch (EngineTerminationException ex) { throw new IllegalStateException(ex.getMessage()); } catch (ExecutionControlException ex) { diff --git a/src/jdk.jshell/share/classes/jdk/jshell/KeyMap.java b/src/jdk.jshell/share/classes/jdk/jshell/KeyMap.java index 8b9db679ee3..a2011a111bf 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/KeyMap.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/KeyMap.java @@ -65,6 +65,10 @@ class KeyMap { } VarKey keyForVariable(String name) { + if (name.isEmpty()) { + return new VarKey(state, name); + } + return varMap.computeIfAbsent(name, k -> new VarKey(state, name)); } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java index f846f109122..4177e667710 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java @@ -229,7 +229,7 @@ class ReplParser extends JavacParser { } else if ((isVoid || (lastmode & TYPE) != 0) && LAX_IDENTIFIER.test(token.kind)) { // we have "Type Ident", so we can assume it is variable or method declaration pos = token.pos; - Name name = ident(); + Name name = identOrUnderscore(); if (token.kind == LPAREN) { // method declaration //mods.flags |= Flags.STATIC; diff --git a/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java b/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java index ae281933b2c..c801e489e20 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/SnippetMaps.java @@ -101,7 +101,7 @@ final class SnippetMaps { StringBuilder sb = new StringBuilder(); sb.append("package ").append(REPL_PACKAGE).append(";\n"); for (Snippet si : keyIndexToSnippet) { - if (si != null && si.status().isDefined() && (except == null || !except.contains(si.key()))) { + if (si != null && si.status().isDefined() && (except == null || !except.contains(si.key())) && si.name() != null && !si.name().isEmpty()) { sb.append(si.importLine(state)); } } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java b/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java index bfd8f0f73c5..27af7b1a440 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java @@ -58,8 +58,10 @@ public class VarSnippet extends DeclarationSnippet { */ final Set anonymousClasses; + final String fieldName; + VarSnippet(VarKey key, String userSource, Wrap guts, - String name, SubKind subkind, String typeName, String fullTypeName, + String name, String fieldName, SubKind subkind, String typeName, String fullTypeName, Set anonymousClasses, Collection declareReferences, DiagList syntheticDiags) { super(key, userSource, guts, name, subkind, null, declareReferences, @@ -67,6 +69,11 @@ public class VarSnippet extends DeclarationSnippet { this.typeName = typeName; this.fullTypeName = fullTypeName; this.anonymousClasses = anonymousClasses; + this.fieldName = fieldName; + } + + String fieldName() { + return fieldName; } /** diff --git a/test/langtools/jdk/jshell/UnnamedTest.java b/test/langtools/jdk/jshell/UnnamedTest.java new file mode 100644 index 00000000000..87ce6f68cad --- /dev/null +++ b/test/langtools/jdk/jshell/UnnamedTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, 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. + */ + +/* + * @test + * @bug 9999999 + * @summary Tests for unnamed variables + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.jshell + * @build Compiler KullaTesting TestingInputStream ExpectedDiagnostic + * @run testng UnnamedTest + */ + +import java.util.function.Consumer; +import jdk.jshell.VarSnippet; +import org.testng.Assert; +import org.testng.annotations.Test; + +import jdk.jshell.JShell; + +public class UnnamedTest extends KullaTesting { + + @Test + public void unnamed() { + VarSnippet sn1 = varKey(assertEval("int _ = 0;")); + VarSnippet sn2 = varKey(assertEval("String _ = \"x\";")); + Assert.assertEquals(getState().varValue(sn1), "0"); + Assert.assertEquals(getState().varValue(sn2), "\"x\""); + } + + @Override + public void setUp(Consumer bc) { + super.setUp(bc.andThen(b -> b.compilerOptions("--enable-preview", "--source", System.getProperty("java.specification.version")))); + } +}