mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-27 19:33:39 +00:00
8325324: Implement JEP 477: Implicitly Declared Classes and Instance Main Methods (Third Preview)
Reviewed-by: asotona, vromero, mcimadamore
This commit is contained in:
parent
612ae9289a
commit
4e6d851f3f
@ -69,9 +69,7 @@ public @interface PreviewFeature {
|
||||
FOREIGN,
|
||||
@JEP(number=459, title="String Templates", status="Second Preview")
|
||||
STRING_TEMPLATES,
|
||||
@JEP(number=445, title="Unnamed Classes and Instance Main Methods", status="Deprecated")
|
||||
UNNAMED_CLASSES,
|
||||
@JEP(number=463, title="Implicitly Declared Classes and Instance Main Methods", status="Preview")
|
||||
@JEP(number=477, title="Implicitly Declared Classes and Instance Main Methods", status="Third Preview")
|
||||
IMPLICIT_CLASSES,
|
||||
@JEP(number=464, title="Scoped Values", status="Second Preview")
|
||||
SCOPED_VALUES,
|
||||
|
||||
@ -230,6 +230,7 @@ public class Symtab {
|
||||
public final Type valueBasedInternalType;
|
||||
public final Type classDescType;
|
||||
public final Type enumDescType;
|
||||
public final Type ioType;
|
||||
|
||||
// For serialization lint checking
|
||||
public final Type objectStreamFieldType;
|
||||
@ -616,6 +617,7 @@ public class Symtab {
|
||||
valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation");
|
||||
classDescType = enterClass("java.lang.constant.ClassDesc");
|
||||
enumDescType = enterClass("java.lang.Enum$EnumDesc");
|
||||
ioType = enterClass("java.io.IO");
|
||||
// For serialization lint checking
|
||||
objectStreamFieldType = enterClass("java.io.ObjectStreamField");
|
||||
objectInputStreamType = enterClass("java.io.ObjectInputStream");
|
||||
|
||||
@ -323,7 +323,7 @@ public class TypeEnter implements Completer {
|
||||
sym.owner.complete();
|
||||
}
|
||||
|
||||
private void importJavaLang(JCCompilationUnit tree, Env<AttrContext> env, ImportFilter typeImportFilter) {
|
||||
private void implicitImports(JCCompilationUnit tree, Env<AttrContext> env) {
|
||||
// Import-on-demand java.lang.
|
||||
PackageSymbol javaLang = syms.enterPackage(syms.java_base, names.java_lang);
|
||||
if (javaLang.members().isEmpty() && !javaLang.exists()) {
|
||||
@ -332,6 +332,28 @@ public class TypeEnter implements Completer {
|
||||
}
|
||||
importAll(make.at(tree.pos()).Import(make.Select(make.QualIdent(javaLang.owner), javaLang), false),
|
||||
javaLang, env);
|
||||
|
||||
List<JCTree> defs = tree.getTypeDecls();
|
||||
boolean isImplicitClass = !defs.isEmpty() &&
|
||||
defs.head instanceof JCClassDecl cls &&
|
||||
(cls.mods.flags & IMPLICIT_CLASS) != 0;
|
||||
if (isImplicitClass) {
|
||||
doModuleImport(make.ModuleImport(make.QualIdent(syms.java_base)));
|
||||
if (peekTypeExists(syms.ioType.tsym)) {
|
||||
doImport(make.Import(make.Select(make.QualIdent(syms.ioType.tsym),
|
||||
names.asterisk), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean peekTypeExists(TypeSymbol type) {
|
||||
try {
|
||||
type.complete();
|
||||
return !type.type.isErroneous();
|
||||
} catch (CompletionFailure cf) {
|
||||
//does not exist
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveImports(JCCompilationUnit tree, Env<AttrContext> env) {
|
||||
@ -356,7 +378,7 @@ public class TypeEnter implements Completer {
|
||||
(origin, sym) -> sym.kind == TYP &&
|
||||
chk.importAccessible(sym, packge);
|
||||
|
||||
importJavaLang(tree, env, typeImportFilter);
|
||||
implicitImports(tree, env);
|
||||
|
||||
JCModuleDecl decl = tree.getModuleDecl();
|
||||
|
||||
|
||||
285
test/langtools/tools/javac/ImplicitClass/ImplicitImports.java
Normal file
285
test/langtools/tools/javac/ImplicitClass/ImplicitImports.java
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 8325324
|
||||
* @summary Verify behavior w.r.t. implicit imports
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.util
|
||||
* @build toolbox.ToolBox toolbox.JavacTask
|
||||
* @run main ImplicitImports
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import toolbox.TestRunner;
|
||||
import toolbox.JavacTask;
|
||||
import toolbox.JavaTask;
|
||||
import toolbox.Task;
|
||||
import toolbox.Task.OutputKind;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class ImplicitImports extends TestRunner {
|
||||
|
||||
private static final String SOURCE_VERSION = System.getProperty("java.specification.version");
|
||||
private ToolBox tb;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
new ImplicitImports().runTests();
|
||||
}
|
||||
|
||||
ImplicitImports() {
|
||||
super(System.err);
|
||||
tb = new ToolBox();
|
||||
}
|
||||
|
||||
public void runTests() throws Exception {
|
||||
runTests(m -> new Object[] { Paths.get(m.getName()) });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplicitJavaBaseImport(Path base) throws Exception {
|
||||
Path current = base.resolve(".");
|
||||
Path src = current.resolve("src");
|
||||
Path classes = current.resolve("classes");
|
||||
tb.writeFile(src.resolve("Test.java"),
|
||||
"""
|
||||
public static void main(String... args) {
|
||||
List<String> l = new ArrayList<>();
|
||||
System.out.println(l.getClass().getName());
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
{//with --release:
|
||||
new JavacTask(tb)
|
||||
.options("--enable-preview", "--release", SOURCE_VERSION)
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
var out = new JavaTask(tb)
|
||||
.classpath(classes.toString())
|
||||
.className("Test")
|
||||
.vmOptions("--enable-preview")
|
||||
.run()
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.STDOUT);
|
||||
|
||||
var expectedOut = List.of("java.util.ArrayList");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{//with --source:
|
||||
new JavacTask(tb)
|
||||
.options("--enable-preview", "--source", SOURCE_VERSION)
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
var out = new JavaTask(tb)
|
||||
.classpath(classes.toString())
|
||||
.className("Test")
|
||||
.vmOptions("--enable-preview")
|
||||
.run()
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.STDOUT);
|
||||
|
||||
var expectedOut = List.of("java.util.ArrayList");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplicitSimpleIOImport(Path base) throws Exception {
|
||||
Path current = base.resolve(".");
|
||||
|
||||
Path patchClasses = prepareIOPatch(current);
|
||||
|
||||
Path src = current.resolve("src");
|
||||
Path classes = current.resolve("classes");
|
||||
tb.writeFile(src.resolve("Test.java"),
|
||||
"""
|
||||
public static void main(String... args) {
|
||||
println("Hello, World!");
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
new JavacTask(tb)
|
||||
.options("--enable-preview", "--release", SOURCE_VERSION,
|
||||
"--patch-module", "java.base=" + patchClasses)
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
var out = new JavaTask(tb)
|
||||
.classpath(classes.toString())
|
||||
.className("Test")
|
||||
.vmOptions("--enable-preview",
|
||||
"--patch-module", "java.base=" + patchClasses)
|
||||
.run()
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.STDOUT);
|
||||
|
||||
var expectedOut = List.of("Hello, World!");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoImplicitImportsForOrdinaryClasses(Path base) throws Exception {
|
||||
Path current = base.resolve(".");
|
||||
|
||||
Path patchClasses = prepareIOPatch(current);
|
||||
|
||||
Path src = current.resolve("src");
|
||||
Path classes = current.resolve("classes");
|
||||
tb.writeFile(src.resolve("Test.java"),
|
||||
"""
|
||||
public class Test {
|
||||
public static void main(String... args) {
|
||||
List<String> l = new ArrayList<>();
|
||||
println("Hello, World!");
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
var log = new JavacTask(tb)
|
||||
.options("--enable-preview", "--release", SOURCE_VERSION,
|
||||
"--patch-module", "java.base=" + patchClasses,
|
||||
"-XDrawDiagnostics")
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.FAIL)
|
||||
.writeAll()
|
||||
.getOutputLines(OutputKind.DIRECT);
|
||||
|
||||
var expectedLog = List.of(
|
||||
"Test.java:3:9: compiler.err.cant.resolve.location: kindname.class, List, , , (compiler.misc.location: kindname.class, Test, null)",
|
||||
"Test.java:3:30: compiler.err.cant.resolve.location: kindname.class, ArrayList, , , (compiler.misc.location: kindname.class, Test, null)",
|
||||
"Test.java:4:9: compiler.err.cant.resolve.location.args: kindname.method, println, , java.lang.String, (compiler.misc.location: kindname.class, Test, null)",
|
||||
"3 errors"
|
||||
);
|
||||
|
||||
if (!Objects.equals(expectedLog, log)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedLog +
|
||||
", actual: " + log);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Path prepareIOPatch(Path base) throws IOException {
|
||||
Path patchSrc = base.resolve("patch-src");
|
||||
Path patchClasses = base.resolve("patch-classes");
|
||||
tb.writeJavaFiles(patchSrc,
|
||||
"""
|
||||
package java.io;
|
||||
public class IO {
|
||||
public static void println(Object o) {
|
||||
System.out.println(o);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(patchClasses);
|
||||
|
||||
new JavacTask(tb)
|
||||
.options("--patch-module", "java.base=" + patchSrc)
|
||||
.outdir(patchClasses)
|
||||
.files(tb.findJavaFiles(patchSrc))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
return patchClasses;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithExplicitImport(Path base) throws Exception {
|
||||
Path current = base.resolve(".");
|
||||
Path src = current.resolve("src");
|
||||
Path classes = current.resolve("classes");
|
||||
tb.writeFile(src.resolve("Test.java"),
|
||||
"""
|
||||
import java.lang.*;
|
||||
public static void main(String... args) {
|
||||
List<String> l = new ArrayList<>();
|
||||
System.out.println(l.getClass().getName());
|
||||
}
|
||||
""");
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
new JavacTask(tb)
|
||||
.options("--enable-preview", "--release", SOURCE_VERSION)
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
var out = new JavaTask(tb)
|
||||
.classpath(classes.toString())
|
||||
.className("Test")
|
||||
.vmOptions("--enable-preview")
|
||||
.run()
|
||||
.writeAll()
|
||||
.getOutputLines(Task.OutputKind.STDOUT);
|
||||
|
||||
var expectedOut = List.of("java.util.ArrayList");
|
||||
|
||||
if (!Objects.equals(expectedOut, out)) {
|
||||
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
|
||||
", actual: " + out);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,6 +85,10 @@ public class TestSymtabItems {
|
||||
if (f.getName().toLowerCase().contains("methodhandle"))
|
||||
continue;
|
||||
|
||||
// Temporarily ignore java.io.IO:
|
||||
if (f.getName().equals("ioType"))
|
||||
continue;
|
||||
|
||||
//both noModule and unnamedModule claim the unnamed package, ignore noModule for now:
|
||||
if (f.getName().equals("noModule"))
|
||||
continue;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user