Skip to content

Commit 4e6d851

Browse files
committedMay 23, 2024
8325324: Implement JEP 477: Implicitly Declared Classes and Instance Main Methods (Third Preview)
Reviewed-by: asotona, vromero, mcimadamore
1 parent 612ae92 commit 4e6d851

File tree

5 files changed

+316
-5
lines changed

5 files changed

+316
-5
lines changed
 

‎src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ public enum Feature {
6969
FOREIGN,
7070
@JEP(number=459, title="String Templates", status="Second Preview")
7171
STRING_TEMPLATES,
72-
@JEP(number=445, title="Unnamed Classes and Instance Main Methods", status="Deprecated")
73-
UNNAMED_CLASSES,
74-
@JEP(number=463, title="Implicitly Declared Classes and Instance Main Methods", status="Preview")
72+
@JEP(number=477, title="Implicitly Declared Classes and Instance Main Methods", status="Third Preview")
7573
IMPLICIT_CLASSES,
7674
@JEP(number=464, title="Scoped Values", status="Second Preview")
7775
SCOPED_VALUES,

‎src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java

+2
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ public static Symtab instance(Context context) {
230230
public final Type valueBasedInternalType;
231231
public final Type classDescType;
232232
public final Type enumDescType;
233+
public final Type ioType;
233234

234235
// For serialization lint checking
235236
public final Type objectStreamFieldType;
@@ -616,6 +617,7 @@ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
616617
valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation");
617618
classDescType = enterClass("java.lang.constant.ClassDesc");
618619
enumDescType = enterClass("java.lang.Enum$EnumDesc");
620+
ioType = enterClass("java.io.IO");
619621
// For serialization lint checking
620622
objectStreamFieldType = enterClass("java.io.ObjectStreamField");
621623
objectInputStreamType = enterClass("java.io.ObjectInputStream");

‎src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java

+24-2
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ protected void runPhase(Env<AttrContext> env) {
323323
sym.owner.complete();
324324
}
325325

326-
private void importJavaLang(JCCompilationUnit tree, Env<AttrContext> env, ImportFilter typeImportFilter) {
326+
private void implicitImports(JCCompilationUnit tree, Env<AttrContext> env) {
327327
// Import-on-demand java.lang.
328328
PackageSymbol javaLang = syms.enterPackage(syms.java_base, names.java_lang);
329329
if (javaLang.members().isEmpty() && !javaLang.exists()) {
@@ -332,6 +332,28 @@ private void importJavaLang(JCCompilationUnit tree, Env<AttrContext> env, Import
332332
}
333333
importAll(make.at(tree.pos()).Import(make.Select(make.QualIdent(javaLang.owner), javaLang), false),
334334
javaLang, env);
335+
336+
List<JCTree> defs = tree.getTypeDecls();
337+
boolean isImplicitClass = !defs.isEmpty() &&
338+
defs.head instanceof JCClassDecl cls &&
339+
(cls.mods.flags & IMPLICIT_CLASS) != 0;
340+
if (isImplicitClass) {
341+
doModuleImport(make.ModuleImport(make.QualIdent(syms.java_base)));
342+
if (peekTypeExists(syms.ioType.tsym)) {
343+
doImport(make.Import(make.Select(make.QualIdent(syms.ioType.tsym),
344+
names.asterisk), true));
345+
}
346+
}
347+
}
348+
349+
private boolean peekTypeExists(TypeSymbol type) {
350+
try {
351+
type.complete();
352+
return !type.type.isErroneous();
353+
} catch (CompletionFailure cf) {
354+
//does not exist
355+
return false;
356+
}
335357
}
336358

337359
private void resolveImports(JCCompilationUnit tree, Env<AttrContext> env) {
@@ -356,7 +378,7 @@ private void resolveImports(JCCompilationUnit tree, Env<AttrContext> env) {
356378
(origin, sym) -> sym.kind == TYP &&
357379
chk.importAccessible(sym, packge);
358380

359-
importJavaLang(tree, env, typeImportFilter);
381+
implicitImports(tree, env);
360382

361383
JCModuleDecl decl = tree.getModuleDecl();
362384

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8325324
27+
* @summary Verify behavior w.r.t. implicit imports
28+
* @library /tools/lib
29+
* @modules jdk.compiler/com.sun.tools.javac.api
30+
* jdk.compiler/com.sun.tools.javac.main
31+
* jdk.compiler/com.sun.tools.javac.util
32+
* @build toolbox.ToolBox toolbox.JavacTask
33+
* @run main ImplicitImports
34+
*/
35+
36+
import java.io.IOException;
37+
import java.nio.file.Files;
38+
import java.nio.file.Path;
39+
import java.nio.file.Paths;
40+
import java.util.List;
41+
import java.util.Objects;
42+
43+
import toolbox.TestRunner;
44+
import toolbox.JavacTask;
45+
import toolbox.JavaTask;
46+
import toolbox.Task;
47+
import toolbox.Task.OutputKind;
48+
import toolbox.ToolBox;
49+
50+
public class ImplicitImports extends TestRunner {
51+
52+
private static final String SOURCE_VERSION = System.getProperty("java.specification.version");
53+
private ToolBox tb;
54+
55+
public static void main(String... args) throws Exception {
56+
new ImplicitImports().runTests();
57+
}
58+
59+
ImplicitImports() {
60+
super(System.err);
61+
tb = new ToolBox();
62+
}
63+
64+
public void runTests() throws Exception {
65+
runTests(m -> new Object[] { Paths.get(m.getName()) });
66+
}
67+
68+
@Test
69+
public void testImplicitJavaBaseImport(Path base) throws Exception {
70+
Path current = base.resolve(".");
71+
Path src = current.resolve("src");
72+
Path classes = current.resolve("classes");
73+
tb.writeFile(src.resolve("Test.java"),
74+
"""
75+
public static void main(String... args) {
76+
List<String> l = new ArrayList<>();
77+
System.out.println(l.getClass().getName());
78+
}
79+
""");
80+
81+
Files.createDirectories(classes);
82+
83+
{//with --release:
84+
new JavacTask(tb)
85+
.options("--enable-preview", "--release", SOURCE_VERSION)
86+
.outdir(classes)
87+
.files(tb.findJavaFiles(src))
88+
.run(Task.Expect.SUCCESS)
89+
.writeAll();
90+
91+
var out = new JavaTask(tb)
92+
.classpath(classes.toString())
93+
.className("Test")
94+
.vmOptions("--enable-preview")
95+
.run()
96+
.writeAll()
97+
.getOutputLines(Task.OutputKind.STDOUT);
98+
99+
var expectedOut = List.of("java.util.ArrayList");
100+
101+
if (!Objects.equals(expectedOut, out)) {
102+
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
103+
", actual: " + out);
104+
105+
}
106+
}
107+
108+
{//with --source:
109+
new JavacTask(tb)
110+
.options("--enable-preview", "--source", SOURCE_VERSION)
111+
.outdir(classes)
112+
.files(tb.findJavaFiles(src))
113+
.run(Task.Expect.SUCCESS)
114+
.writeAll();
115+
116+
var out = new JavaTask(tb)
117+
.classpath(classes.toString())
118+
.className("Test")
119+
.vmOptions("--enable-preview")
120+
.run()
121+
.writeAll()
122+
.getOutputLines(Task.OutputKind.STDOUT);
123+
124+
var expectedOut = List.of("java.util.ArrayList");
125+
126+
if (!Objects.equals(expectedOut, out)) {
127+
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
128+
", actual: " + out);
129+
130+
}
131+
}
132+
}
133+
134+
@Test
135+
public void testImplicitSimpleIOImport(Path base) throws Exception {
136+
Path current = base.resolve(".");
137+
138+
Path patchClasses = prepareIOPatch(current);
139+
140+
Path src = current.resolve("src");
141+
Path classes = current.resolve("classes");
142+
tb.writeFile(src.resolve("Test.java"),
143+
"""
144+
public static void main(String... args) {
145+
println("Hello, World!");
146+
}
147+
""");
148+
149+
Files.createDirectories(classes);
150+
151+
new JavacTask(tb)
152+
.options("--enable-preview", "--release", SOURCE_VERSION,
153+
"--patch-module", "java.base=" + patchClasses)
154+
.outdir(classes)
155+
.files(tb.findJavaFiles(src))
156+
.run(Task.Expect.SUCCESS)
157+
.writeAll();
158+
159+
var out = new JavaTask(tb)
160+
.classpath(classes.toString())
161+
.className("Test")
162+
.vmOptions("--enable-preview",
163+
"--patch-module", "java.base=" + patchClasses)
164+
.run()
165+
.writeAll()
166+
.getOutputLines(Task.OutputKind.STDOUT);
167+
168+
var expectedOut = List.of("Hello, World!");
169+
170+
if (!Objects.equals(expectedOut, out)) {
171+
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
172+
", actual: " + out);
173+
174+
}
175+
}
176+
177+
@Test
178+
public void testNoImplicitImportsForOrdinaryClasses(Path base) throws Exception {
179+
Path current = base.resolve(".");
180+
181+
Path patchClasses = prepareIOPatch(current);
182+
183+
Path src = current.resolve("src");
184+
Path classes = current.resolve("classes");
185+
tb.writeFile(src.resolve("Test.java"),
186+
"""
187+
public class Test {
188+
public static void main(String... args) {
189+
List<String> l = new ArrayList<>();
190+
println("Hello, World!");
191+
}
192+
}
193+
""");
194+
195+
Files.createDirectories(classes);
196+
197+
var log = new JavacTask(tb)
198+
.options("--enable-preview", "--release", SOURCE_VERSION,
199+
"--patch-module", "java.base=" + patchClasses,
200+
"-XDrawDiagnostics")
201+
.outdir(classes)
202+
.files(tb.findJavaFiles(src))
203+
.run(Task.Expect.FAIL)
204+
.writeAll()
205+
.getOutputLines(OutputKind.DIRECT);
206+
207+
var expectedLog = List.of(
208+
"Test.java:3:9: compiler.err.cant.resolve.location: kindname.class, List, , , (compiler.misc.location: kindname.class, Test, null)",
209+
"Test.java:3:30: compiler.err.cant.resolve.location: kindname.class, ArrayList, , , (compiler.misc.location: kindname.class, Test, null)",
210+
"Test.java:4:9: compiler.err.cant.resolve.location.args: kindname.method, println, , java.lang.String, (compiler.misc.location: kindname.class, Test, null)",
211+
"3 errors"
212+
);
213+
214+
if (!Objects.equals(expectedLog, log)) {
215+
throw new AssertionError("Incorrect Output, expected: " + expectedLog +
216+
", actual: " + log);
217+
218+
}
219+
}
220+
221+
private Path prepareIOPatch(Path base) throws IOException {
222+
Path patchSrc = base.resolve("patch-src");
223+
Path patchClasses = base.resolve("patch-classes");
224+
tb.writeJavaFiles(patchSrc,
225+
"""
226+
package java.io;
227+
public class IO {
228+
public static void println(Object o) {
229+
System.out.println(o);
230+
}
231+
}
232+
""");
233+
234+
Files.createDirectories(patchClasses);
235+
236+
new JavacTask(tb)
237+
.options("--patch-module", "java.base=" + patchSrc)
238+
.outdir(patchClasses)
239+
.files(tb.findJavaFiles(patchSrc))
240+
.run(Task.Expect.SUCCESS)
241+
.writeAll();
242+
243+
return patchClasses;
244+
}
245+
246+
@Test
247+
public void testWithExplicitImport(Path base) throws Exception {
248+
Path current = base.resolve(".");
249+
Path src = current.resolve("src");
250+
Path classes = current.resolve("classes");
251+
tb.writeFile(src.resolve("Test.java"),
252+
"""
253+
import java.lang.*;
254+
public static void main(String... args) {
255+
List<String> l = new ArrayList<>();
256+
System.out.println(l.getClass().getName());
257+
}
258+
""");
259+
260+
Files.createDirectories(classes);
261+
262+
new JavacTask(tb)
263+
.options("--enable-preview", "--release", SOURCE_VERSION)
264+
.outdir(classes)
265+
.files(tb.findJavaFiles(src))
266+
.run(Task.Expect.SUCCESS)
267+
.writeAll();
268+
269+
var out = new JavaTask(tb)
270+
.classpath(classes.toString())
271+
.className("Test")
272+
.vmOptions("--enable-preview")
273+
.run()
274+
.writeAll()
275+
.getOutputLines(Task.OutputKind.STDOUT);
276+
277+
var expectedOut = List.of("java.util.ArrayList");
278+
279+
if (!Objects.equals(expectedOut, out)) {
280+
throw new AssertionError("Incorrect Output, expected: " + expectedOut +
281+
", actual: " + out);
282+
283+
}
284+
}
285+
}

‎test/langtools/tools/javac/processing/model/TestSymtabItems.java

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ void run() throws Exception {
8585
if (f.getName().toLowerCase().contains("methodhandle"))
8686
continue;
8787

88+
// Temporarily ignore java.io.IO:
89+
if (f.getName().equals("ioType"))
90+
continue;
91+
8892
//both noModule and unnamedModule claim the unnamed package, ignore noModule for now:
8993
if (f.getName().equals("noModule"))
9094
continue;

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on May 23, 2024

@openjdk-notifier[bot]
Please sign in to comment.