changeset 59296:f5b2d188240d

8235474: JShell does not handle records properly Reviewed-by: vromero, rfield
author jlahoda
date Wed, 11 Dec 2019 13:08:42 +0100
parents 6cf6761c444e
children 6afc12975478
files src/java.base/share/classes/module-info.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java src/jdk.jshell/share/classes/jdk/jshell/Eval.java src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java src/jdk.jshell/share/classes/jdk/jshell/Snippet.java test/langtools/jdk/jshell/CompletenessTest.java test/langtools/jdk/jshell/RecordsTest.java
diffstat 9 files changed, 136 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/module-info.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/src/java.base/share/classes/module-info.java	Wed Dec 11 13:08:42 2019 +0100
@@ -140,7 +140,8 @@
     exports jdk.internal to
         java.compiler,
         jdk.jfr,
-        jdk.compiler;
+        jdk.compiler,
+        jdk.jshell;
     exports jdk.internal.access to
         java.desktop,
         java.logging,
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Wed Dec 11 13:08:42 2019 +0100
@@ -1238,6 +1238,7 @@
             if ((flags & RECORD) != 0) {
                 // records can't be declared abstract
                 mask &= ~ABSTRACT;
+                implicit |= FINAL;
             }
             // Imply STRICTFP if owner has STRICTFP set.
             implicit |= sym.owner.flags_field & STRICTFP;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Dec 11 13:08:42 2019 +0100
@@ -3722,7 +3722,7 @@
     protected JCClassDecl recordDeclaration(JCModifiers mods, Comment dc) {
         int pos = token.pos;
         nextToken();
-        mods.flags |= Flags.RECORD | Flags.FINAL;
+        mods.flags |= Flags.RECORD;
         Name name = typeName();
 
         List<JCTypeParameter> typarams = typeParametersOpt();
@@ -4117,9 +4117,10 @@
         }
     }
 
-    boolean isRecordStart() {
+    protected boolean isRecordStart() {
      if (token.kind == IDENTIFIER && token.name() == names.record &&
             (peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN) ||
+             peekToken(TokenKind.IDENTIFIER, TokenKind.EOF) ||
              peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) {
           checkSourceLevel(Feature.RECORDS);
           return true;
--- a/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java	Wed Dec 11 13:08:42 2019 +0100
@@ -670,12 +670,12 @@
 
         public Completeness parseDeclaration() {
             boolean isImport = token.kind == IMPORT;
-            boolean isDatum = false;
+            boolean isRecord = false;
             boolean afterModifiers = false;
             boolean isBracesNeeded = false;
             while (token.kind.isDeclaration()) {
                 isBracesNeeded |= token.kind.isBracesNeeded();
-                isDatum |= !afterModifiers && token.kind == TK.IDENTIFIER && token.tok.name() == names.record;
+                isRecord |= !afterModifiers && token.kind == TK.IDENTIFIER && token.tok.name() == names.record;
                 afterModifiers |= !token.kind.isModifier();
                 nextToken();
             }
@@ -696,17 +696,11 @@
                         case SEMI:
                             return Completeness.COMPLETE;
                         case IDENTIFIER:
-                            return isBracesNeeded
+                            return isBracesNeeded || isRecord
                                     ? Completeness.DEFINITELY_INCOMPLETE
                                     : Completeness.COMPLETE_WITH_SEMI;
                         case BRACKETS:
                             return Completeness.COMPLETE_WITH_SEMI;
-                        case PARENS:
-                            if (isDatum) {
-                                return Completeness.COMPLETE_WITH_SEMI;
-                            } else {
-                                return Completeness.DEFINITELY_INCOMPLETE;
-                            }
                         case DOTSTAR:
                             if (isImport) {
                                 return Completeness.COMPLETE_WITH_SEMI;
@@ -780,6 +774,7 @@
                     case ENUM:
                     case ANNOTATION_TYPE:
                     case INTERFACE:
+                    case RECORD:
                     case METHOD:
                         return parseDeclaration();
                     default:
--- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Wed Dec 11 13:08:42 2019 +0100
@@ -229,6 +229,10 @@
                     return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
                 case INTERFACE:
                     return processClass(userSource, unitTree, compileSourceInt, SubKind.INTERFACE_SUBKIND, pt);
+                case RECORD:
+                    @SuppressWarnings("preview")
+                    List<Snippet> snippets = processClass(userSource, unitTree, compileSourceInt, SubKind.RECORD_SUBKIND, pt);
+                    return snippets;
                 case METHOD:
                     return processMethod(userSource, unitTree, compileSourceInt, pt);
                 default:
--- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java	Wed Dec 11 13:08:42 2019 +0100
@@ -179,7 +179,7 @@
             default:
                 JCModifiers mods = modifiersOpt(pmods);
                 if (token.kind == CLASS
-                        || token.kind == IDENTIFIER && token.name() == token.name().table.names.record
+                        || isRecordStart()
                         || token.kind == INTERFACE
                         || token.kind == ENUM) {
                     return List.<JCTree>of(classOrRecordOrInterfaceOrEnumDeclaration(mods, dc));
--- a/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java	Wed Dec 11 13:08:42 2019 +0100
@@ -236,6 +236,23 @@
         ENUM_SUBKIND(Kind.TYPE_DECL),
 
         /**
+         * {@preview Associated with records, a preview feature of the Java language.
+         *
+         *           This enum constant is associated with <i>records</i>, a preview
+         *           feature of the Java language. Preview features
+         *           may be removed in a future release, or upgraded to permanent
+         *           features of the Java language.}
+         *
+         * A record declaration.
+         * A {@code SubKind} of {@link Kind#TYPE_DECL}.
+         * @jls 8.10 Record Types
+         * @since 14
+         *
+         */
+        @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS)
+        RECORD_SUBKIND(Kind.TYPE_DECL),
+
+        /**
          * An annotation interface declaration. A {@code SubKind} of
          * {@link Kind#TYPE_DECL}.
          * @jls 9.6 Annotation Types
--- a/test/langtools/jdk/jshell/CompletenessTest.java	Wed Dec 11 12:12:39 2019 +0100
+++ b/test/langtools/jdk/jshell/CompletenessTest.java	Wed Dec 11 13:08:42 2019 +0100
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268 8223782
+ * @bug 8149524 8131024 8165211 8080071 8130454 8167343 8129559 8114842 8182268 8223782 8235474
  * @summary Test SourceCodeAnalysis
  * @build KullaTesting TestingInputStream
  * @run testng CompletenessTest
@@ -31,11 +31,15 @@
 
 import java.util.Map;
 import java.util.HashMap;
+import java.util.function.Consumer;
+import javax.lang.model.SourceVersion;
+import jdk.jshell.JShell;
 
 import org.testng.annotations.Test;
 import jdk.jshell.SourceCodeAnalysis.Completeness;
 
 import static jdk.jshell.SourceCodeAnalysis.Completeness.*;
+import org.testng.annotations.BeforeMethod;
 
 @Test
 public class CompletenessTest extends KullaTesting {
@@ -82,6 +86,10 @@
         "i >= 0 && Character.isWhitespace(s.charAt(i))",
         "int.class",
         "String.class",
+        "record.any",
+        "record()",
+        "record(1)",
+        "record.length()"
     };
 
     static final String[] complete_with_semi = new String[] {
@@ -123,7 +131,7 @@
         "int[] m = {1, 2}",
         "int[] m = {1, 2}, n = null",
         "int[] m = {1, 2}, n",
-        "int[] m = {1, 2}, n = {3, 4}"
+        "int[] m = {1, 2}, n = {3, 4}",
     };
 
     static final String[] considered_incomplete = new String[] {
@@ -193,6 +201,28 @@
         "var v = switch (x) { case ",
         "var v = switch (x) { case 0:",
         "var v = switch (x) { case 0: break 12; ",
+        "record D",
+        "record D(",
+        "record D(String",
+        "record D(String i",
+        "record D(String i,",
+        "record D(String i, String",
+        "record D(String i, String j",
+        "record D(String i)",
+        "record D(String i, String j)",
+        "record D(String i) {",
+        "record D(String i, String j) {",
+        "static record D",
+        "static record D(",
+        "static record D(String",
+        "static record D(String i",
+        "static record D(String i,",
+        "static record D(String i, String",
+        "static record D(String i, String j",
+        "static record D(String i)",
+        "static record D(String i, String j)",
+        "static record D(String i) {",
+        "static record D(String i, String j) {",
     };
 
     static final String[] unknown = new String[] {
@@ -241,7 +271,7 @@
     }
 
     public void test_complete() {
-        assertStatus(complete, COMPLETE);
+         assertStatus(complete, COMPLETE);
     }
 
     public void test_expression() {
@@ -349,4 +379,10 @@
         assertStatus("int[] m = {1, 2}, n = new int[0];  int i;", COMPLETE,
                      "int[] m = {1, 2}, n = new int[0];");
     }
+
+    @BeforeMethod
+    public void setUp() {
+        setUp(b -> b.compilerOptions("--enable-preview", "-source", String.valueOf(SourceVersion.latest().ordinal())));
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/jdk/jshell/RecordsTest.java	Wed Dec 11 13:08:42 2019 +0100
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019, 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 8235474
+ * @summary Tests for evalution of records
+ * @modules jdk.jshell
+ * @build KullaTesting TestingInputStream ExpectedDiagnostic
+ * @run testng RecordsTest
+ */
+
+import org.testng.annotations.Test;
+
+import javax.lang.model.SourceVersion;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.BeforeMethod;
+
+@Test
+public class RecordsTest extends KullaTesting {
+
+    public void testRecordClass() {
+        assertEval("record R(String s, int i) { }");
+        assertEquals(varKey(assertEval("R r = new R(\"r\", 42);")).name(), "r");
+        assertEval("r.s()", "\"r\"");
+        assertEval("r.i()", "42");
+    }
+
+    public void testRecordField() {
+        assertEquals(varKey(assertEval("String record = \"\";")).name(), "record");
+        assertEval("record.length()", "0");
+    }
+
+    public void testRecordMethod() {
+        assertEquals(methodKey(assertEval("String record(String record) { return record + record; }")).name(), "record");
+        assertEval("record(\"r\")", "\"rr\"");
+        assertEval("record(\"r\").length()", "2");
+    }
+
+    @BeforeMethod
+    public void setUp() {
+        setUp(b -> b.compilerOptions("--enable-preview", "-source", String.valueOf(SourceVersion.latest().ordinal()))
+                    .remoteVMOptions("--enable-preview"));
+    }
+}