From 1e97c1c913220b07ff0c1c977cea80bc9436729d Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Thu, 14 Nov 2024 06:14:33 +0000 Subject: [PATCH] 8335989: Implement JEP 494: Module Import Declarations (Second Preview) Reviewed-by: vromero, abimpoudis, mcimadamore, alanb --- .../tools/symbolgenerator/CreateSymbols.java | 5 + .../jdk/internal/javac/PreviewFeature.java | 2 +- .../jdk/internal/module/ModuleInfo.java | 24 +++- src/java.base/share/classes/module-info.java | 1 + src/java.se/share/classes/module-info.java | 4 + .../com/sun/tools/javac/api/JavacScope.java | 36 +++-- .../com/sun/tools/javac/api/JavacTrees.java | 1 + .../com/sun/tools/javac/code/Preview.java | 16 ++- .../com/sun/tools/javac/code/Source.java | 1 + .../com/sun/tools/javac/comp/Enter.java | 1 + .../com/sun/tools/javac/comp/Modules.java | 41 ++++-- .../com/sun/tools/javac/comp/Resolve.java | 40 ++++-- .../com/sun/tools/javac/comp/TypeEnter.java | 30 +++-- .../com/sun/tools/javac/jvm/ClassReader.java | 9 +- .../tools/javac/resources/compiler.properties | 7 +- .../com/sun/tools/javac/tree/JCTree.java | 2 + .../com/sun/tools/javac/tree/TreeMaker.java | 31 ++--- .../lang/module/ClassFileVersionsTest.java | 68 ++++++---- .../lang/module/ModuleDescriptorTest.java | 66 ++++++++++ .../tools/javac/6402516/TestClass.java | 22 ++-- .../javac/6402516/TestLocalElements.java | 22 ++-- .../tools/javac/6402516/TestMethod.java | 22 ++-- test/langtools/tools/javac/ImportModule.java | 122 +++++++++++++---- .../tools/javac/api/TestGetScopeResult.java | 75 +++++++++++ .../ModifierNotAllowed/module-info.java | 3 +- .../modules/ConvenientAccessErrorsTest.java | 34 +++++ .../tools/javac/modules/EdgeCases.java | 89 +++++++++++++ .../tools/javac/modules/JavaBaseTest.java | 124 ++++++++++++++---- 28 files changed, 716 insertions(+), 182 deletions(-) diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index 039c531301f..114c82c5229 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -1896,6 +1896,11 @@ public class CreateSymbols { continue; } + if (ed.packageName.equals("jdk/internal/javac")) { + //keep jdk/internal/javac untouched. It is used to determine participates in preview: + continue; + } + Set usingModules = package2ModulesUsingIt.getOrDefault(ed.packageName(), Set.of()); ed.to.retainAll(usingModules); diff --git a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java index 4b2a0629706..aa196bd910b 100644 --- a/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java +++ b/src/java.base/share/classes/jdk/internal/javac/PreviewFeature.java @@ -77,7 +77,7 @@ public @interface PreviewFeature { @JEP(number=466, title="ClassFile API", status="Second Preview") CLASSFILE_API, STREAM_GATHERERS, - @JEP(number=476, title="Module Import Declarations", status="Preview") + @JEP(number=494, title="Module Import Declarations", status="Second Preview") MODULE_IMPORTS, @JEP(number=478, title="Key Derivation Function API", status="Preview") KEY_DERIVATION, diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java index 26cb12f484a..75b3b8fc993 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java @@ -31,6 +31,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.lang.classfile.ClassFile; import java.lang.module.InvalidModuleDescriptorException; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Builder; @@ -189,6 +190,7 @@ public final class ModuleInfo { int minor_version = in.readUnsignedShort(); int major_version = in.readUnsignedShort(); + boolean isPreview = minor_version == ClassFile.PREVIEW_MINOR_VERSION; if (!VM.isSupportedModuleDescriptorVersion(major_version, minor_version)) { throw invalidModuleDescriptor("Unsupported major.minor version " + major_version + "." + minor_version); @@ -248,7 +250,7 @@ public final class ModuleInfo { switch (attribute_name) { case MODULE : - builder = readModuleAttribute(in, cpool, major_version); + builder = readModuleAttribute(in, cpool, major_version, isPreview); break; case MODULE_PACKAGES : @@ -344,7 +346,8 @@ public final class ModuleInfo { * Reads the Module attribute, returning the ModuleDescriptor.Builder to * build the corresponding ModuleDescriptor. */ - private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major) + private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major, + boolean isPreview) throws IOException { // module_name @@ -405,14 +408,23 @@ public final class ModuleInfo { throw invalidModuleDescriptor("The requires entry for java.base" + " has ACC_SYNTHETIC set"); } + // requires transitive java.base is illegal unless: + // - the major version is 53 (JDK 9), or: + // - the classfile is a preview classfile, or: + // - the module is deemed to be participating in preview + // (i.e. the module is a java.* module) + // requires static java.base is illegal unless: + // - the major version is 53 (JDK 9), or: if (major >= 54 - && (mods.contains(Requires.Modifier.TRANSITIVE) + && ((mods.contains(Requires.Modifier.TRANSITIVE) + && !isPreview + && !"java.se".equals(mn)) || mods.contains(Requires.Modifier.STATIC))) { String flagName; - if (mods.contains(Requires.Modifier.TRANSITIVE)) { - flagName = "ACC_TRANSITIVE"; - } else { + if (mods.contains(Requires.Modifier.STATIC)) { flagName = "ACC_STATIC_PHASE"; + } else { + flagName = "ACC_TRANSITIVE"; } throw invalidModuleDescriptor("The requires entry for java.base" + " has " + flagName + " set"); diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index f3b62c37fa8..85ccb2192fb 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -154,6 +154,7 @@ module java.base { exports jdk.internal.javac to java.compiler, java.desktop, // for ScopedValue + java.se, // for ParticipatesInPreview jdk.compiler, jdk.incubator.vector, // participates in preview features jdk.jartool, // participates in preview features diff --git a/src/java.se/share/classes/module-info.java b/src/java.se/share/classes/module-info.java index 81b1bd3cb8a..9a2704660b7 100644 --- a/src/java.se/share/classes/module-info.java +++ b/src/java.se/share/classes/module-info.java @@ -23,6 +23,8 @@ * questions. */ +import jdk.internal.javac.ParticipatesInPreview; + /** * Defines the API of the Java SE Platform. * @@ -38,7 +40,9 @@ * @moduleGraph * @since 9 */ +@ParticipatesInPreview module java.se { + requires transitive java.base; requires transitive java.compiler; requires transitive java.datatransfer; requires transitive java.desktop; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacScope.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacScope.java index 3dfbbc1ff52..9b3a50a035a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacScope.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacScope.java @@ -87,12 +87,26 @@ public class JavacScope implements com.sun.source.tree.Scope { } else { // synthesize an outermost "star-import" scope return new JavacScope(env) { - public boolean isStarImportScope() { - return true; + @Override + public ScopeType getScopeType() { + return ScopeType.STAR_IMPORT; } @DefinedBy(Api.COMPILER_TREE) public JavacScope getEnclosingScope() { - return null; + return new JavacScope(env) { + @Override + public ScopeType getScopeType() { + return ScopeType.MODULE_IMPORT; + } + @Override @DefinedBy(Api.COMPILER_TREE) + public JavacScope getEnclosingScope() { + return null; + } + @Override @DefinedBy(Api.COMPILER_TREE) + public Iterable getLocalElements() { + return env.toplevel.moduleImportScope.getSymbols(VALIDATOR); + } + }; } @DefinedBy(Api.COMPILER_TREE) public Iterable getLocalElements() { @@ -122,21 +136,27 @@ public class JavacScope implements com.sun.source.tree.Scope { return env; } - public boolean isStarImportScope() { - return false; + public ScopeType getScopeType() { + return ScopeType.ORDINARY; } public boolean equals(Object other) { return other instanceof JavacScope javacScope && env.equals(javacScope.env) - && isStarImportScope() == javacScope.isStarImportScope(); + && getScopeType()== javacScope.getScopeType(); } public int hashCode() { - return env.hashCode() + (isStarImportScope() ? 1 : 0); + return env.hashCode() + getScopeType().hashCode(); } public String toString() { - return "JavacScope[env=" + env + ",starImport=" + isStarImportScope() + "]"; + return "JavacScope[env=" + env + ", scope type=" + getScopeType() + "]"; + } + + private enum ScopeType { + ORDINARY, + STAR_IMPORT, + MODULE_IMPORT; } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 8f84f5d7fbd..ce719230455 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -1402,6 +1402,7 @@ public class JavacTrees extends DocTrees { jcCompilationUnit.namedImportScope = new NamedImportScope(psym); jcCompilationUnit.packge = psym; jcCompilationUnit.starImportScope = new StarImportScope(psym); + jcCompilationUnit.moduleImportScope = new StarImportScope(psym); jcCompilationUnit.toplevelScope = WriteableScope.create(psym); return new TreePath(jcCompilationUnit); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java index 5e9a75a0b6b..c66e1758616 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java @@ -27,6 +27,7 @@ package com.sun.tools.javac.code; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Source.Feature; +import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Warnings; @@ -133,11 +134,23 @@ public class Preview { return true; } + return participatesInPreview(syms, s.packge().modle); + } + + /** + * Returns true if module {@code m} is deemed to participate in the preview, and + * therefore no warnings or errors will be produced. + * + * @param syms the symbol table + * @param m the module to check + * @return true if {@code m} is participating in the preview of {@code previewSymbol} + */ + public boolean participatesInPreview(Symtab syms, ModuleSymbol m) { // If java.base's jdk.internal.javac package is exported to s's module then // s participates in the preview API return syms.java_base.exports.stream() .filter(ed -> ed.packge.fullname == names.jdk_internal_javac) - .anyMatch(ed -> ed.modules.contains(s.packge().modle)); + .anyMatch(ed -> ed.modules.contains(m)); } /** @@ -211,6 +224,7 @@ public class Preview { case FLEXIBLE_CONSTRUCTORS -> true; case PRIMITIVE_PATTERNS -> true; case MODULE_IMPORTS -> true; + case JAVA_BASE_TRANSITIVE -> true; //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). //When real preview features will be added, this method can be implemented to return 'true' //for those selected features, and 'false' for all the others. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java index 4a226e4de3b..572a7b12675 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Source.java @@ -262,6 +262,7 @@ public enum Source { PRIMITIVE_PATTERNS(JDK23, Fragments.FeaturePrimitivePatterns, DiagKind.PLURAL), FLEXIBLE_CONSTRUCTORS(JDK22, Fragments.FeatureFlexibleConstructors, DiagKind.NORMAL), MODULE_IMPORTS(JDK23, Fragments.FeatureModuleImports, DiagKind.PLURAL), + JAVA_BASE_TRANSITIVE(JDK24, Fragments.FeatureJavaBaseTransitive, DiagKind.PLURAL), PRIVATE_MEMBERS_IN_PERMITS_CLAUSE(JDK19), ERASE_POLY_SIG_RETURN_TYPE(JDK24), ; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java index 926be3b6e26..1042a9747ba 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Enter.java @@ -223,6 +223,7 @@ public class Enter extends JCTree.Visitor { tree.toplevelScope = WriteableScope.create(tree.packge); tree.namedImportScope = new NamedImportScope(tree.packge); tree.starImportScope = new StarImportScope(tree.packge); + tree.moduleImportScope = new StarImportScope(tree.packge); localEnv.info.scope = tree.toplevelScope; localEnv.info.lint = lint; return localEnv; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java index 3d893252218..bfad334d194 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -64,8 +64,11 @@ import com.sun.tools.javac.code.Directive.RequiresFlag; import com.sun.tools.javac.code.Directive.UsesDirective; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Flags.Flag; +import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.ModuleFinder; +import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Symbol; @@ -74,6 +77,7 @@ import com.sun.tools.javac.code.Symbol.Completer; import com.sun.tools.javac.code.Symbol.CompletionFailure; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.ModuleFlags; +import com.sun.tools.javac.code.Symbol.ModuleResolutionFlags; import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symtab; @@ -99,6 +103,7 @@ import com.sun.tools.javac.tree.JCTree.Tag; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; @@ -112,14 +117,9 @@ import static com.sun.tools.javac.code.Flags.ENUM; import static com.sun.tools.javac.code.Flags.PUBLIC; import static com.sun.tools.javac.code.Flags.UNATTRIBUTED; -import com.sun.tools.javac.code.Kinds; - import static com.sun.tools.javac.code.Kinds.Kind.ERR; import static com.sun.tools.javac.code.Kinds.Kind.MDL; import static com.sun.tools.javac.code.Kinds.Kind.MTH; -import com.sun.tools.javac.code.Lint; - -import com.sun.tools.javac.code.Symbol.ModuleResolutionFlags; import static com.sun.tools.javac.code.TypeTag.CLASS; @@ -141,6 +141,7 @@ public class Modules extends JCTree.Visitor { private final Symtab syms; private final Attr attr; private final Check chk; + private final Preview preview; private final DeferredLintHandler deferredLintHandler; private final TypeEnvs typeEnvs; private final Types types; @@ -150,6 +151,7 @@ public class Modules extends JCTree.Visitor { private final Target target; private final boolean allowModules; private final boolean allowAccessIntoSystem; + private final boolean allowRequiresTransitiveJavaBase; public final boolean multiModuleMode; @@ -192,6 +194,7 @@ public class Modules extends JCTree.Visitor { syms = Symtab.instance(context); attr = Attr.instance(context); chk = Check.instance(context); + preview = Preview.instance(context); deferredLintHandler = DeferredLintHandler.instance(context); typeEnvs = TypeEnvs.instance(context); moduleFinder = ModuleFinder.instance(context); @@ -203,6 +206,12 @@ public class Modules extends JCTree.Visitor { Options options = Options.instance(context); allowAccessIntoSystem = options.isUnset(Option.RELEASE); + + Preview preview = Preview.instance(context); + + allowRequiresTransitiveJavaBase = + Feature.JAVA_BASE_TRANSITIVE.allowedInSource(source) && + (!preview.isPreview(Feature.JAVA_BASE_TRANSITIVE) || preview.isEnabled()); lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option); multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); @@ -249,7 +258,12 @@ public class Modules extends JCTree.Visitor { public boolean enter(List trees, ClassSymbol c) { Assert.check(rootModules != null || inInitModules || !allowModules); - return enter(trees, modules -> {}, c); + return enter(trees, modules -> { + //make sure java.base is completed in all cases before continuing. + //the next steps may query if the current module participates in preview, + //and that requires a completed java.base: + syms.java_base.complete(); + }, c); } private boolean enter(List trees, Consumer> init, ClassSymbol c) { @@ -807,11 +821,16 @@ public class Modules extends JCTree.Visitor { allRequires.add(msym); Set flags = EnumSet.noneOf(RequiresFlag.class); if (tree.isTransitive) { - if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) { - log.error(tree.pos(), Errors.ModifierNotAllowedHere(names.transitive)); - } else { - flags.add(RequiresFlag.TRANSITIVE); + if (msym == syms.java_base && + !allowRequiresTransitiveJavaBase && + !preview.participatesInPreview(syms, sym)) { + if (source.compareTo(Source.JDK10) >= 0) { + log.error(DiagnosticFlag.SOURCE_LEVEL, + tree.pos(), + Feature.JAVA_BASE_TRANSITIVE.error(source.name)); + } } + flags.add(RequiresFlag.TRANSITIVE); } if (tree.isStaticPhase) { if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index b6d7e166902..a9527171749 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -2149,23 +2149,32 @@ public class Resolve { return null; }; - private final RecoveryLoadClass starImportScopeRecovery = (env, name) -> { - Scope importScope = env.toplevel.starImportScope; - Symbol existing = importScope.findFirst(Convert.shortName(name), - sym -> sym.kind == TYP && sym.flatName() == name); + private final RecoveryLoadClass starImportScopeRecovery = + onDemandImportScopeRecovery(false); - if (existing != null) { - try { - existing = finder.loadClass(existing.packge().modle, name); + private final RecoveryLoadClass moduleImportScopeRecovery = + onDemandImportScopeRecovery(true); - return new InvisibleSymbolError(env, true, existing); - } catch (CompletionFailure cf) { - //ignore + private RecoveryLoadClass onDemandImportScopeRecovery(boolean moduleImportScope) { + return (env, name) -> { + Scope importScope = moduleImportScope ? env.toplevel.moduleImportScope + : env.toplevel.starImportScope; + Symbol existing = importScope.findFirst(Convert.shortName(name), + sym -> sym.kind == TYP && sym.flatName() == name); + + if (existing != null) { + try { + existing = finder.loadClass(existing.packge().modle, name); + + return new InvisibleSymbolError(env, true, existing); + } catch (CompletionFailure cf) { + //ignore + } } - } - return null; - }; + return null; + }; + } Symbol lookupPackage(Env env, Name name) { PackageSymbol pack = syms.lookupPackage(env.toplevel.modle, name); @@ -2433,6 +2442,11 @@ public class Resolve { sym = findGlobalType(env, env.toplevel.starImportScope, name, starImportScopeRecovery); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); + + sym = findGlobalType(env, env.toplevel.moduleImportScope, name, moduleImportScopeRecovery); + if (sym.exists()) return sym; + + else bestSoFar = bestOf(bestSoFar, sym); } return bestSoFar; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java index b518d7edb4b..40dcf854ff9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java @@ -220,6 +220,7 @@ public class TypeEnter implements Completer { chk.checkImportedPackagesObservable(toplevel); toplevel.namedImportScope.finalizeScope(); toplevel.starImportScope.finalizeScope(); + toplevel.moduleImportScope.finalizeScope(); } catch (CompletionFailure cf) { chk.completionError(toplevel.pos(), cf); } finally { @@ -331,7 +332,7 @@ public class TypeEnter implements Completer { throw new Abort(); } importAll(make.at(tree.pos()).Import(make.Select(make.QualIdent(javaLang.owner), javaLang), false), - javaLang, env); + javaLang, env, false); List defs = tree.getTypeDecls(); boolean isImplicitClass = !defs.isEmpty() && @@ -341,7 +342,7 @@ public class TypeEnter implements Completer { 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)); + names.asterisk), true), false); } } } @@ -413,7 +414,7 @@ public class TypeEnter implements Completer { if (imp instanceof JCModuleImport mimp) { doModuleImport(mimp); } else { - doImport((JCImport) imp); + doImport((JCImport) imp, false); } } } @@ -438,7 +439,7 @@ public class TypeEnter implements Completer { annotate.annotateLater(tree.annotations, env, env.toplevel.packge, tree.pos()); } - private void doImport(JCImport tree) { + private void doImport(JCImport tree, boolean fromModuleImport) { JCFieldAccess imp = tree.qualid; Name name = TreeInfo.name(imp); @@ -450,16 +451,20 @@ public class TypeEnter implements Completer { if (name == names.asterisk) { // Import on demand. chk.checkCanonical(imp.selected); - if (tree.staticImport) + if (tree.staticImport) { + Assert.check(!fromModuleImport); importStaticAll(tree, p, env); - else - importAll(tree, p, env); + } else { + importAll(tree, p, env, fromModuleImport); + } } else { // Named type import. if (tree.staticImport) { + Assert.check(!fromModuleImport); importNamedStatic(tree, p, name, localEnv); chk.checkCanonical(imp.selected); } else { + Assert.check(!fromModuleImport); Type importedType = attribImportType(imp, localEnv); Type originalType = importedType.getOriginalType(); TypeSymbol c = originalType.hasTag(CLASS) ? originalType.tsym : importedType.tsym; @@ -506,7 +511,7 @@ public class TypeEnter implements Completer { JCImport nestedImport = make.at(tree.pos) .Import(make.Select(make.QualIdent(pkg), names.asterisk), false); - doImport(nestedImport); + doImport(nestedImport, true); } for (RequiresDirective requires : currentModule.requires) { @@ -542,8 +547,13 @@ public class TypeEnter implements Completer { */ private void importAll(JCImport imp, final TypeSymbol tsym, - Env env) { - env.toplevel.starImportScope.importAll(types, tsym.members(), typeImportFilter, imp, cfHandler); + Env env, + boolean fromModuleImport) { + StarImportScope targetScope = + fromModuleImport ? env.toplevel.moduleImportScope + : env.toplevel.starImportScope; + + targetScope.importAll(types, tsym.members(), typeImportFilter, imp, cfHandler); } /** Import all static members of a class or package on demand. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 233629e10bb..f70b96697b3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -199,6 +199,9 @@ public class ClassReader { /** The minor version number of the class file being read. */ int minorVersion; + /** true if the class file being read is a preview class file. */ + boolean previewClassFile; + /** UTF-8 validation level */ Convert.Validation utf8validation; @@ -1200,7 +1203,9 @@ public class ClassReader { ModuleSymbol rsym = poolReader.getModule(nextChar()); Set flags = readRequiresFlags(nextChar()); if (rsym == syms.java_base && majorVersion >= V54.major) { - if (flags.contains(RequiresFlag.TRANSITIVE)) { + if (flags.contains(RequiresFlag.TRANSITIVE) && + (majorVersion != Version.MAX().major || !previewClassFile) && + !preview.participatesInPreview(syms, msym)) { throw badClassFile("bad.requires.flag", RequiresFlag.TRANSITIVE); } if (flags.contains(RequiresFlag.STATIC_PHASE)) { @@ -3185,7 +3190,7 @@ public class ClassReader { majorVersion = nextChar(); int maxMajor = Version.MAX().major; int maxMinor = Version.MAX().minor; - boolean previewClassFile = + previewClassFile = minorVersion == ClassFile.PREVIEW_MINOR_VERSION; if (majorVersion > maxMajor || majorVersion * 1000 + minorVersion < diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index a24dd2f95bd..9e42c2f377a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -921,10 +921,6 @@ compiler.misc.unexpected.ret.val=\ compiler.err.mod.not.allowed.here=\ modifier {0} not allowed here -# 0: name -compiler.err.modifier.not.allowed.here=\ - modifier {0} not allowed here - compiler.err.intf.not.allowed.here=\ interface not allowed here @@ -3252,6 +3248,9 @@ compiler.misc.feature.flexible.constructors=\ compiler.misc.feature.module.imports=\ module imports +compiler.misc.feature.java.base.transitive=\ + transitive modifier for java.base + compiler.warn.underscore.as.identifier=\ as of release 9, ''_'' is a keyword, and may not be used as an identifier diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 003f70eeecc..a2837013506 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -538,6 +538,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public NamedImportScope namedImportScope; /** A scope for all import-on-demands. */ public StarImportScope starImportScope; + /** A scope for all single module imports. */ + public StarImportScope moduleImportScope; /** Line starting positions, defined only if option -g is set. */ public Position.LineMap lineMap = null; /** A table that stores all documentation comments indexed by the tree diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java index b4c6f804a2f..218099ac662 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java @@ -1138,26 +1138,17 @@ public class TreeMaker implements JCTree.Factory { sym.owner.kind == MTH || sym.owner.kind == VAR) { return true; } else if (sym.kind == TYP && toplevel != null) { - Iterator it = toplevel.namedImportScope.getSymbolsByName(sym.name).iterator(); - if (it.hasNext()) { - Symbol s = it.next(); - return - s == sym && - !it.hasNext(); - } - it = toplevel.packge.members().getSymbolsByName(sym.name).iterator(); - if (it.hasNext()) { - Symbol s = it.next(); - return - s == sym && - !it.hasNext(); - } - it = toplevel.starImportScope.getSymbolsByName(sym.name).iterator(); - if (it.hasNext()) { - Symbol s = it.next(); - return - s == sym && - !it.hasNext(); + for (Scope scope : new Scope[] {toplevel.namedImportScope, + toplevel.packge.members(), + toplevel.starImportScope, + toplevel.moduleImportScope}) { + Iterator it = scope.getSymbolsByName(sym.name).iterator(); + if (it.hasNext()) { + Symbol s = it.next(); + return + s == sym && + !it.hasNext(); + } } } return sym.kind == TYP && sym.isImplicit(); diff --git a/test/jdk/java/lang/module/ClassFileVersionsTest.java b/test/jdk/java/lang/module/ClassFileVersionsTest.java index 849a56301a1..1ffc5878651 100644 --- a/test/jdk/java/lang/module/ClassFileVersionsTest.java +++ b/test/jdk/java/lang/module/ClassFileVersionsTest.java @@ -31,10 +31,13 @@ * @summary Test parsing of module-info.class with different class file versions */ +import java.lang.classfile.ClassFile; import java.lang.module.InvalidModuleDescriptorException; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor.Requires.Modifier; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import static java.lang.module.ModuleDescriptor.Requires.Modifier.*; @@ -46,6 +49,8 @@ import org.testng.annotations.Test; import static org.testng.Assert.*; public class ClassFileVersionsTest { + private static final int PREVIEW_MINOR_VERSION = + ClassFile.PREVIEW_MINOR_VERSION; private static final int FEATURE; static { FEATURE = Runtime.version().feature(); @@ -56,26 +61,34 @@ public class ClassFileVersionsTest { @DataProvider(name = "supported") public Object[][] supported() { /* - * There are four test cases for JDK 9 and then one test case + * There are four test cases for JDK 9, one test case * for each subsequent JDK version from JDK 10 to the current - * feature release for a total of (4 + (FEATURE - 9) ) => - * (feature - 5) rows. + * feature release, and two tests for the current release with + * a preview flag set, for a total of (4 + (FEATURE - 9) + 2) + * rows. */ - Object[][] result = new Object[(FEATURE - 5)][]; + List result = new ArrayList<>(4 + (FEATURE - 9) + 2); // Class file version of JDK 9 is 53.0 - result[0] = new Object[]{ 53, 0, Set.of()}; - result[1] = new Object[]{ 53, 0, Set.of(STATIC) }; - result[2] = new Object[]{ 53, 0, Set.of(TRANSITIVE) }; - result[3] = new Object[]{ 53, 0, Set.of(STATIC, TRANSITIVE) }; + result.add(new Object[]{ 53, 0, Set.of()}); + result.add(new Object[]{ 53, 0, Set.of(STATIC) }); + result.add(new Object[]{ 53, 0, Set.of(TRANSITIVE) }); + result.add(new Object[]{ 53, 0, Set.of(STATIC, TRANSITIVE) }); // Major class file version of JDK N is 44 + n. Create rows // for JDK 10 through FEATURE. - for (int i = 4; i < (FEATURE - 5) ; i++) { - result[i] = new Object[]{i + 50, 0, Set.of()}; + for (int i = 10; i <= FEATURE; i++) { + result.add(new Object[]{ 44 + i, 0, Set.of()}); } - return result; + result.add(new Object[]{ 44 + FEATURE, + PREVIEW_MINOR_VERSION, + Set.of()}); + result.add(new Object[]{ 44 + FEATURE, + PREVIEW_MINOR_VERSION, + Set.of(TRANSITIVE) }); + + return result.toArray(s -> new Object[s][]); } // major, minor, modifiers for requires java.base @@ -84,27 +97,34 @@ public class ClassFileVersionsTest { /* * There are three test cases for releases prior to JDK 9, * three test cases for each JDK version from JDK 10 to the - * current feature release, plus one addition test case for - * the next release for a total of (3 + (FEATURE - 9) * 3 + 1) + * current feature release, two tests for the current release with + * the preview flag set, plus one addition test case for + * the next release for a total of (3 + (FEATURE - 9) * 3 + 2 + 1) * rows. */ - int unsupportedCount = 3 + (FEATURE - 9)*3 + 1; - Object[][] result = new Object[unsupportedCount][]; + List result = new ArrayList<>(3 + (FEATURE - 9) * 3 + 2 + 1); - result[0] = new Object[]{50, 0, Set.of()}; // JDK 6 - result[1] = new Object[]{51, 0, Set.of()}; // JDK 7 - result[2] = new Object[]{52, 0, Set.of()}; // JDK 8 + result.add(new Object[]{50, 0, Set.of()}); // JDK 6 + result.add(new Object[]{51, 0, Set.of()}); // JDK 7 + result.add(new Object[]{52, 0, Set.of()}); // JDK 8 for (int i = 10; i <= FEATURE ; i++) { - int base = 3 + (i-10)*3; // Major class file version of JDK N is 44+n - result[base] = new Object[]{i + 44, 0, Set.of(STATIC)}; - result[base + 1] = new Object[]{i + 44, 0, Set.of(TRANSITIVE)}; - result[base + 2] = new Object[]{i + 44, 0, Set.of(STATIC, TRANSITIVE)}; + result.add(new Object[]{i + 44, 0, Set.of(STATIC)}); + result.add(new Object[]{i + 44, 0, Set.of(TRANSITIVE)}); + result.add(new Object[]{i + 44, 0, Set.of(STATIC, TRANSITIVE)}); } - result[unsupportedCount - 1] = new Object[]{FEATURE+1+44, 0, Set.of()}; - return result; + result.add(new Object[]{FEATURE + 44, + PREVIEW_MINOR_VERSION, + Set.of(STATIC)}); + result.add(new Object[]{FEATURE + 44, + PREVIEW_MINOR_VERSION, + Set.of(STATIC, TRANSITIVE)}); + + result.add(new Object[]{FEATURE+1+44, 0, Set.of()}); + + return result.toArray(s -> new Object[s][]); } @Test(dataProvider = "supported") diff --git a/test/jdk/java/lang/module/ModuleDescriptorTest.java b/test/jdk/java/lang/module/ModuleDescriptorTest.java index c9267f3db3d..c46bb4a1e9e 100644 --- a/test/jdk/java/lang/module/ModuleDescriptorTest.java +++ b/test/jdk/java/lang/module/ModuleDescriptorTest.java @@ -61,6 +61,8 @@ import static java.lang.module.ModuleDescriptor.Requires.Modifier.*; import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.access.SharedSecrets; import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassFileVersion; +import java.lang.classfile.ClassTransform; import java.lang.classfile.attribute.ModuleAttribute; import java.lang.constant.PackageDesc; import java.lang.constant.ModuleDesc; @@ -1522,4 +1524,68 @@ public class ModuleDescriptorTest { assertTrue(s.contains("p1")); } + @Test(expectedExceptions = InvalidModuleDescriptorException.class) + public void testRequiresTransitiveJavaBaseNotPermitted1() throws Exception { + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo") + .requires(Set.of(Modifier.TRANSITIVE), "java.base") + .build(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ModuleInfoWriter.write(descriptor, baos); + ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); + + ModuleDescriptor.read(bb, () -> Set.of("p", "q")); + } + + @Test(expectedExceptions = InvalidModuleDescriptorException.class) + public void testRequiresTransitiveJavaBaseNotPermitted2() throws Exception { + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo") + .requires(Set.of(Modifier.TRANSITIVE), "java.base") + .build(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ModuleInfoWriter.write(descriptor, baos); + byte[] bytecode = baos.toByteArray(); + ByteBuffer bb = ByteBuffer.wrap(bytecode); + setClassFileVersion(bb, ClassFile.JAVA_21_VERSION, -1); + + ModuleDescriptor.read(bb, () -> Set.of("p", "q")); + } + + public void testRequiresTransitiveJavaBasePermitted() throws Exception { + ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo") + .requires(Set.of(Modifier.TRANSITIVE), "java.base") + .build(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ModuleInfoWriter.write(descriptor, baos); + byte[] bytecode = baos.toByteArray(); + ByteBuffer bb = ByteBuffer.wrap(bytecode); + setClassFileVersion(bb, -1, ClassFile.PREVIEW_MINOR_VERSION); + + descriptor = ModuleDescriptor.read(bb, () -> Set.of("p", "q")); + + assertEquals(descriptor.requires().size(), 1); + Requires javaBase = descriptor.requires().iterator().next(); + assertEquals(javaBase.name(), "java.base"); + assertEquals(javaBase.modifiers(), Set.of(Modifier.TRANSITIVE)); + } + + /**Change the classfile versions of the provided classfile to the provided + * values. + * + * @param bytecode the classfile content to modify + * @param major the major classfile version to set, + * -1 if the existing version should be kept + * @param minor the minor classfile version to set, + * -1 if the existing version should be kept + */ + private void setClassFileVersion(ByteBuffer bb, int major, int minor) { + if (minor != (-1)) { + bb.putShort(4, (short) minor); + } + if (major != (-1)) { + bb.putShort(6, (short) major); + } + } } diff --git a/test/langtools/tools/javac/6402516/TestClass.java b/test/langtools/tools/javac/6402516/TestClass.java index f080fbc22b4..57131cddaf9 100644 --- a/test/langtools/tools/javac/6402516/TestClass.java +++ b/test/langtools/tools/javac/6402516/TestClass.java @@ -26,32 +26,32 @@ class Test { void m1(int m1_arg) { - String x = "Test; 0; 0"; - String y = "Test; 0; 0"; - String z = "Test; 0; 0"; + String x = "Test; 0; 0; 0"; + String y = "Test; 0; 0; 0"; + String z = "Test; 0; 0; 0"; Object o = new Object() { public boolean equals(Object other) { - String p = "-; Test; 0; 0"; - String q = "-; Test; 0; 0"; - String r = "-; Test; 0; 0"; + String p = "-; Test; 0; 0; 0"; + String q = "-; Test; 0; 0; 0"; + String r = "-; Test; 0; 0; 0"; return (this == other); } }; } - String s = "Test; 0; 0"; + String s = "Test; 0; 0; 0"; boolean b = new Object() { public boolean equals(Object other) { - String p = "-; Test; 0; 0"; - String q = "-; Test; 0; 0"; - String r = "-; Test; 0; 0"; + String p = "-; Test; 0; 0; 0"; + String q = "-; Test; 0; 0; 0"; + String r = "-; Test; 0; 0; 0"; return (this == other); } }.equals(null); class Test2 { - String s = "Test.Test2; Test; 0; 0"; + String s = "Test.Test2; Test; 0; 0; 0"; } } diff --git a/test/langtools/tools/javac/6402516/TestLocalElements.java b/test/langtools/tools/javac/6402516/TestLocalElements.java index 589d2e993cb..7b835bfdcc3 100644 --- a/test/langtools/tools/javac/6402516/TestLocalElements.java +++ b/test/langtools/tools/javac/6402516/TestLocalElements.java @@ -23,29 +23,29 @@ import java.util.List; import java.io.*; -//TOPLEVEL_SCOPE:List, Test2, Test; java.io.*, java.lang.* +//TOPLEVEL_SCOPE:List, Test2, Test; java.io.*, java.lang.*; class Test { void m1(int m1_arg) { - String x = "x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*"; - String y = "y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*"; - String z = "z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*"; + String x = "x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*;"; + String y = "y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*;"; + String z = "z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*;"; Object o = new Object() { public boolean equals(Object other) { - String p = "p, other, super, this; -, o, z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*"; - String q = "q, p, other, super, this; -, o, z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*"; - String r = "r, q, p, other, super, this; -, o, z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*"; + String p = "p, other, super, this; -, o, z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*;"; + String q = "q, p, other, super, this; -, o, z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*;"; + String r = "r, q, p, other, super, this; -, o, z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*;"; return (this == other); } }; } - String s = "super, this; List, Test2, Test; java.io.*, java.lang.*"; + String s = "super, this; List, Test2, Test; java.io.*, java.lang.*;"; boolean b = new Object() { public boolean equals(Object other) { - String p = "p, other, super, this; -, super, this; List, Test2, Test; java.io.*, java.lang.*"; - String q = "q, p, other, super, this; -, super, this; List, Test2, Test; java.io.*, java.lang.*"; - String r = "r, q, p, other, super, this; -, super, this; List, Test2, Test; java.io.*, java.lang.*"; + String p = "p, other, super, this; -, super, this; List, Test2, Test; java.io.*, java.lang.*;"; + String q = "q, p, other, super, this; -, super, this; List, Test2, Test; java.io.*, java.lang.*;"; + String r = "r, q, p, other, super, this; -, super, this; List, Test2, Test; java.io.*, java.lang.*;"; return (this == other); } diff --git a/test/langtools/tools/javac/6402516/TestMethod.java b/test/langtools/tools/javac/6402516/TestMethod.java index 9aec1d5d881..304b0abd9cb 100644 --- a/test/langtools/tools/javac/6402516/TestMethod.java +++ b/test/langtools/tools/javac/6402516/TestMethod.java @@ -25,32 +25,32 @@ class Test { void m1(int m1_arg) { - String x = "m1; 0; 0"; - String y = "m1; 0; 0"; - String z = "m1; 0; 0"; + String x = "m1; 0; 0; 0"; + String y = "m1; 0; 0; 0"; + String z = "m1; 0; 0; 0"; Object o = new Object() { public boolean equals(Object other) { - String p = "equals; m1; 0; 0"; - String q = "equals; m1; 0; 0"; - String r = "equals; m1; 0; 0"; + String p = "equals; m1; 0; 0; 0"; + String q = "equals; m1; 0; 0; 0"; + String r = "equals; m1; 0; 0; 0"; return (this == other); } }; } - String s = "0; 0; 0"; + String s = "0; 0; 0; 0"; boolean b = new Object() { public boolean equals(Object other) { - String p = "equals; 0; 0; 0"; - String q = "equals; 0; 0; 0"; - String r = "equals; 0; 0; 0"; + String p = "equals; 0; 0; 0; 0"; + String q = "equals; 0; 0; 0; 0"; + String r = "equals; 0; 0; 0; 0"; return (this == other); } }.equals(null); class Test2 { - String s = "0; 0; 0; 0"; + String s = "0; 0; 0; 0; 0"; } } diff --git a/test/langtools/tools/javac/ImportModule.java b/test/langtools/tools/javac/ImportModule.java index dc1ed9bd0c1..1224a9d7470 100644 --- a/test/langtools/tools/javac/ImportModule.java +++ b/test/langtools/tools/javac/ImportModule.java @@ -218,28 +218,14 @@ public class ImportModule extends TestRunner { List actualErrors; List expectedErrors; - actualErrors = - new JavacTask(tb) - .options("--enable-preview", "--release", SOURCE_VERSION, - "-XDrawDiagnostics") - .outdir(classes) - .files(tb.findJavaFiles(src)) - .run(Task.Expect.FAIL) - .writeAll() - .getOutputLines(Task.OutputKind.DIRECT); - - expectedErrors = List.of( - "Test.java:5:5: compiler.err.ref.ambiguous: Logger, kindname.interface, java.lang.System.Logger, java.lang.System, kindname.class, java.util.logging.Logger, java.util.logging", - "- compiler.note.preview.filename: Test.java, DEFAULT", - "- compiler.note.preview.recompile", - "1 error" - ); - - if (!Objects.equals(expectedErrors, actualErrors)) { - throw new AssertionError("Incorrect Output, expected: " + expectedErrors + - ", actual: " + out); - - } + new JavacTask(tb) + .options("--enable-preview", "--release", SOURCE_VERSION, + "-XDrawDiagnostics") + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); tb.writeJavaFiles(src, """ @@ -793,7 +779,7 @@ public class ImportModule extends TestRunner { if (!Objects.equals(expectedErrors, actualErrors)) { throw new AssertionError("Incorrect Output, expected: " + expectedErrors + - ", actual: " + out); + ", actual: " + actualErrors); } } @@ -843,4 +829,94 @@ public class ImportModule extends TestRunner { } } + public void testPackageImportDisambiguates(Path base) throws Exception { + Path current = base.resolve("."); + Path src = current.resolve("src"); + Path classes = current.resolve("classes"); + Path ma = src.resolve("ma"); + tb.writeJavaFiles(ma, + """ + module ma { + exports ma.p1; + } + """, + """ + package ma.p1; + public class A {} + """); + Path mb = src.resolve("mb"); + tb.writeJavaFiles(mb, + """ + module mb { + exports mb.p1; + } + """, + """ + package mb.p1; + public class A {} + """); + Path test = src.resolve("test"); + tb.writeJavaFiles(test, + """ + module test { + requires ma; + requires mb; + } + """, + """ + package test; + import module ma; + import module mb; + public class Test { + A a; + } + """); + + Files.createDirectories(classes); + + List actualErrors = new JavacTask(tb) + .options("-XDrawDiagnostics", + "--enable-preview", "--release", SOURCE_VERSION, + "--module-source-path", src.toString()) + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expectedErrors = List.of( + "Test.java:5:5: compiler.err.ref.ambiguous: A, kindname.class, mb.p1.A, mb.p1, kindname.class, ma.p1.A, ma.p1", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error" + ); + + if (!Objects.equals(expectedErrors, actualErrors)) { + throw new AssertionError("Incorrect Output, expected: " + expectedErrors + + ", actual: " + actualErrors); + + } + + tb.writeJavaFiles(test, + """ + package test; + import module ma; + import module mb; + import mb.p1.*; + public class Test { + A a; + } + """); + + Files.createDirectories(classes); + + new JavacTask(tb) + .options("-XDrawDiagnostics", + "--enable-preview", "--release", SOURCE_VERSION, + "--module-source-path", src.toString()) + .outdir(classes) + .files(tb.findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll(); + } } diff --git a/test/langtools/tools/javac/api/TestGetScopeResult.java b/test/langtools/tools/javac/api/TestGetScopeResult.java index 8b53ba0a752..daa3e761b2a 100644 --- a/test/langtools/tools/javac/api/TestGetScopeResult.java +++ b/test/langtools/tools/javac/api/TestGetScopeResult.java @@ -73,6 +73,9 @@ import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context.Factory; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.ElementFilter; +import javax.tools.JavaFileObject; import static javax.tools.JavaFileObject.Kind.SOURCE; @@ -89,6 +92,7 @@ public class TestGetScopeResult { new TestGetScopeResult().testLocalRecordAnnotation(); new TestGetScopeResult().testRuleCases(); new TestGetScopeResult().testNestedSwitchExpression(); + new TestGetScopeResult().testModuleImportScope(); } public void run() throws IOException { @@ -823,6 +827,64 @@ public class TestGetScopeResult { } } + void testModuleImportScope() throws IOException { + JavacTool c = JavacTool.create(); + try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) { + String code = """ + import module java.compiler; + import java.util.*; + import java.lang.System; + class Test { + } + """; + Context ctx = new Context(); + TestAnalyzer.preRegister(ctx); + JavaFileObject input = + SimpleJavaFileObject.forSource(URI.create("myfo:///Test.java"), code); + JavacTask t = (JavacTask) c.getTask(null, fm, null, null, null, + List.of(input), + ctx); + CompilationUnitTree cut = t.parse().iterator().next(); + t.analyze(); + + TreePath topLevelClass = new TreePath(new TreePath(cut), cut.getTypeDecls().get(0)); + Scope scope = Trees.instance(t).getScope(topLevelClass); + + if (scope.getEnclosingClass() == null) { + throw new AssertionError("Expected an enclosing class."); + } + + scope = scope.getEnclosingScope(); + + if (scope.getEnclosingClass() != null) { + throw new AssertionError("Did not expect an enclosing class."); + } + + asssertScopeContainsTypeWithFQN(scope, "java.lang.System"); + asssertScopeContainsTypeWithFQN(scope, "Test"); + + scope = scope.getEnclosingScope(); + + if (scope.getEnclosingClass() != null) { + throw new AssertionError("Did not expect an enclosing class."); + } + + asssertScopeContainsTypeWithFQN(scope, "java.util.List"); + + scope = scope.getEnclosingScope(); + + if (scope.getEnclosingClass() != null) { + throw new AssertionError("Did not expect an enclosing class."); + } + + asssertScopeContainsTypeWithFQN(scope, "javax.tools.ToolProvider"); + + if (scope.getEnclosingScope() != null) { + throw new AssertionError("Did not expect an enclosing scope."); + } + } + } + private List dumpScope(Scope scope) { List content = new ArrayList<>(); while (scope.getEnclosingClass() != null) { @@ -833,4 +895,17 @@ public class TestGetScopeResult { } return content; } + + private void asssertScopeContainsTypeWithFQN(Scope scope, String fqn) { + for (TypeElement type : ElementFilter.typesIn(scope.getLocalElements())) { + if (type.getQualifiedName().contentEquals(fqn)) { + return ; + } + } + + throw new AssertionError("Expected to find: " + fqn + + " in: " + scope.getLocalElements() + + ", but it is missing."); + } + } diff --git a/test/langtools/tools/javac/diags/examples/ModifierNotAllowed/module-info.java b/test/langtools/tools/javac/diags/examples/ModifierNotAllowed/module-info.java index 44bcfae8d44..f2e5899a915 100644 --- a/test/langtools/tools/javac/diags/examples/ModifierNotAllowed/module-info.java +++ b/test/langtools/tools/javac/diags/examples/ModifierNotAllowed/module-info.java @@ -21,7 +21,8 @@ * questions. */ -// key: compiler.err.modifier.not.allowed.here +// key: compiler.err.feature.not.supported.in.source.plural +// key: compiler.misc.feature.java.base.transitive module m { requires transitive java.base; diff --git a/test/langtools/tools/javac/modules/ConvenientAccessErrorsTest.java b/test/langtools/tools/javac/modules/ConvenientAccessErrorsTest.java index 24c2b045800..f558ddb20ac 100644 --- a/test/langtools/tools/javac/modules/ConvenientAccessErrorsTest.java +++ b/test/langtools/tools/javac/modules/ConvenientAccessErrorsTest.java @@ -432,6 +432,40 @@ public class ConvenientAccessErrorsTest extends ModuleTestBase { throw new Exception("expected output not found; actual: " + log); } + @Test + public void testInModuleImport(Path base) throws Exception { + Path src = base.resolve("src"); + Path src_m1 = src.resolve("m1x"); + tb.writeJavaFiles(src_m1, + "module m1x { }", + "package api; public class Api { public String test() { return null; } }"); + Path src_m2 = src.resolve("m2x"); + tb.writeJavaFiles(src_m2, + "module m2x { requires m1x; }", + "package test; import module m1x; public class Test { Api api; { api.test().length(); } }"); + Path classes = base.resolve("classes"); + tb.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "--enable-preview", "--source", System.getProperty("java.specification.version"), + "--module-source-path", src.toString()) + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = Arrays.asList( + "Test.java:1:54: compiler.err.cant.resolve.location: kindname.class, Api, , , (compiler.misc.location: kindname.class, test.Test, null)", + "- compiler.note.preview.filename: Test.java, DEFAULT", + "- compiler.note.preview.recompile", + "1 error"); + + if (!expected.equals(log)) + throw new Exception("expected output not found; actual: " + log); + } + @Test public void testUnusedImportOnDemand2(Path base) throws Exception { Path src = base.resolve("src"); diff --git a/test/langtools/tools/javac/modules/EdgeCases.java b/test/langtools/tools/javac/modules/EdgeCases.java index 0dc5d02ce4a..dd68a5142b2 100644 --- a/test/langtools/tools/javac/modules/EdgeCases.java +++ b/test/langtools/tools/javac/modules/EdgeCases.java @@ -73,6 +73,9 @@ import com.sun.tools.javac.api.JavacTaskImpl; import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symtab; import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import javax.lang.model.element.ModuleElement.DirectiveKind; import toolbox.JarTask; import toolbox.JavacTask; @@ -1153,4 +1156,90 @@ public class EdgeCases extends ModuleTestBase { } } + @Test + public void testJavaSEHasRequiresTransitiveJavaBase(Path base) throws Exception { + Path src = base.resolve("src"); + Path a = src.resolve("a"); + tb.writeJavaFiles(a, + "module a { requires java.se; }", + """ + package test; + import module java.se; + public class Test { + ArrayList l; + } + """); + Path classes = base.resolve("classes"); + tb.createDirectories(classes); + + AtomicBoolean seenJavaSEDependency = new AtomicBoolean(); + + List log; + + log = new JavacTask(tb) + .outdir(classes) + .options("-XDrawDiagnostics", "-XDshould-stop.at=FLOW") + .callback(verifyJavaSEDependency(true, seenJavaSEDependency)) + .files(findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = List.of( + "Test.java:2:8: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.module.imports)", + "1 error"); + + if (!expected.equals(log)) + throw new Exception("expected output not found: " + log); + + if (!seenJavaSEDependency.get()) { + throw new AssertionError("Didn't find the java.se dependency!"); + } + + seenJavaSEDependency.set(false); + + new JavacTask(tb) + .outdir(classes) + .options("--enable-preview", + "--source", System.getProperty("java.specification.version")) + .callback(verifyJavaSEDependency(true, seenJavaSEDependency)) + .files(findJavaFiles(src)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + if (!seenJavaSEDependency.get()) { + throw new AssertionError("Didn't find the java.se dependency!"); + } + } + private Consumer verifyJavaSEDependency( + boolean expectedTransitive, + AtomicBoolean seenJavaSEDependency) { + return t -> { + t.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() == TaskEvent.Kind.ANALYZE) { + ModuleElement javaBase = + t.getElements().getModuleElement("java.base"); + ModuleElement javaSE = + t.getElements().getModuleElement("java.se"); + RequiresDirective requiresJavaBase = + javaSE.getDirectives() + .stream() + .filter(d -> d.getKind() == DirectiveKind.REQUIRES) + .map(d -> (RequiresDirective) d) + .filter(d -> d.getDependency() == javaBase) + .findAny() + .orElseThrow(); + if (requiresJavaBase.isTransitive() != expectedTransitive) { + throw new AssertionError("Expected: " + expectedTransitive + ", " + + "but got: " + requiresJavaBase.isTransitive()); + } + seenJavaSEDependency.set(true); + } + } + }); + }; + } } diff --git a/test/langtools/tools/javac/modules/JavaBaseTest.java b/test/langtools/tools/javac/modules/JavaBaseTest.java index 6d9f574705d..484225fcd0c 100644 --- a/test/langtools/tools/javac/modules/JavaBaseTest.java +++ b/test/langtools/tools/javac/modules/JavaBaseTest.java @@ -23,7 +23,7 @@ /** * @test - * @bug 8193125 8196623 + * @bug 8193125 8196623 8335989 * @summary test modifiers with java.base * @library /tools/lib * @enablePreview @@ -49,6 +49,7 @@ import java.lang.classfile.attribute.*; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.platform.JDKPlatformProvider; +import java.util.function.Supplier; import toolbox.JavacTask; import toolbox.Task; @@ -56,6 +57,8 @@ import toolbox.ToolBox; public class JavaBaseTest { + private static final String CURRENT_VERSION = System.getProperty("java.specification.version"); + public static void main(String... args) throws Exception { JavaBaseTest t = new JavaBaseTest(); t.run(); @@ -75,13 +78,11 @@ public class JavaBaseTest { void run() throws Exception { Set targets = new LinkedHashSet<>(); - StreamSupport.stream(new JDKPlatformProvider().getSupportedPlatformNames() - .spliterator(), - false) - .filter(p -> Integer.parseInt(p) >= 9) - .forEach(targets::add); - //run without --release: + targets.add("9"); + targets.add("10"); + targets.add("21"); targets.add("current"); + targets.add("current-preview"); for (List mods : modifiers) { for (String target : targets) { for (Mode mode : Mode.values()) { @@ -119,15 +120,43 @@ public class JavaBaseTest { tb.writeJavaFiles(src, "module m { requires " + String.join(" ", mods) + " java.base; }"); Path modules = Files.createDirectories(base.resolve("modules")); - boolean expectOK = target.equals("9"); + boolean expectOK; JavacTask jct = new JavacTask(tb) .outdir(modules); - if (target.equals("current")) - jct.options("-XDrawDiagnostics"); - else - jct.options("-XDrawDiagnostics", "--release", target); + List options = new ArrayList<>(); + + switch (target) { + case "current": + options.add("--release"); + options.add(CURRENT_VERSION); + expectOK = false; + break; + case "current-preview": + options.add("--enable-preview"); + options.add("--release"); + options.add(CURRENT_VERSION); + expectOK = true; + break; + case "9": + options.add("--release"); + options.add(target); + expectOK = true; + break; + default: + options.add("--release"); + options.add(target); + expectOK = false; + break; + } + + if (mods.contains("static") && !"9".equals(target)) { + expectOK = false; + } + + options.add("-XDrawDiagnostics"); + jct.options(options); String log = jct.files(tb.findJavaFiles(src)) .run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL) @@ -138,9 +167,9 @@ public class JavaBaseTest { boolean foundErrorMessage = false; for (String mod : mods) { String key = mod.equals("static") - ? "compiler.err.mod.not.allowed.here" - : "compiler.err.modifier.not.allowed.here"; - String message = "module-info.java:1:12: " + key + ": " + mod; + ? "compiler.err.mod.not.allowed.here: " + mod + : "compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.java.base.transitive)"; + String message = "module-info.java:1:12: " + key; if (log.contains(message)) { foundErrorMessage = true; } @@ -152,18 +181,57 @@ public class JavaBaseTest { } void testClass(Path base, List mods, String target) throws Exception { - createClass(base, mods, target); + boolean expectOK; + List options = new ArrayList<>(); + + switch (target) { + case "current": + options.add("--release"); + options.add(CURRENT_VERSION); + expectOK = false; + break; + case "current-preview": + options.add("--enable-preview"); + options.add("--release"); + options.add(CURRENT_VERSION); + expectOK = true; + break; + case "9": + options.add("--release"); + options.add(target); + expectOK = true; + break; + default: + options.add("--release"); + options.add(target); + expectOK = false; + break; + } + + if (mods.contains("static") && !"9".equals(target)) { + expectOK = false; + } + + createClass(base, mods, options); + + List testOptions = new ArrayList<>(); + + testOptions.add("-XDrawDiagnostics"); + testOptions.add("--module-path"); testOptions.add(base.resolve("test-modules").toString()); + + if (options.contains("--enable-preview")) { + testOptions.add("--enable-preview"); + testOptions.add("--source"); testOptions.add(CURRENT_VERSION); + } Path src = base.resolve("src"); tb.writeJavaFiles(src, "module mx { requires m; }"); Path modules = Files.createDirectories(base.resolve("modules")); - boolean expectOK = target.equals("9"); String log = new JavacTask(tb) .outdir(modules) - .options("-XDrawDiagnostics", - "--module-path", base.resolve("test-modules").toString()) + .options(testOptions) .files(tb.findJavaFiles(src)) .run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL) .writeAll() @@ -188,7 +256,7 @@ public class JavaBaseTest { } } - void createClass(Path base, List mods, String target) throws Exception { + void createClass(Path base, List mods, List options) throws Exception { Path src1 = base.resolve("interim-src"); tb.writeJavaFiles(src1, "module m { requires java.base; }"); @@ -197,9 +265,7 @@ public class JavaBaseTest { JavacTask jct = new JavacTask(tb) .outdir(modules1); - if (!target.equals("current")) { - jct.options("--release", target); - } + jct.options(options); jct.files(tb.findJavaFiles(src1)) .run(Task.Expect.SUCCESS); @@ -226,6 +292,8 @@ public class JavaBaseTest { requires.set(i, e2); } + boolean preview = options.contains("--enable-preview"); + ModuleAttribute modAttr2 = ModuleAttribute.of( modAttr1.moduleName(), modAttr1.moduleFlagsMask(), @@ -237,8 +305,14 @@ public class JavaBaseTest { modAttr1.provides()); Path modInfo = base.resolve("test-modules").resolve("module-info.class"); Files.createDirectories(modInfo.getParent()); - byte[] newBytes = ClassFile.of().transformClass(cm1, ClassTransform.dropping(ce -> ce instanceof ModuleAttribute). - andThen(ClassTransform.endHandler(classBuilder -> classBuilder.with(modAttr2)))); + ClassTransform replace = (builder, element) -> { + switch (element) { + case ClassFileVersion cfv when preview -> builder.withVersion(cfv.majorVersion(), 0xFFFF); + case ModuleAttribute _ -> builder.with(modAttr2); + default -> builder.with(element); + } + }; + byte[] newBytes = ClassFile.of().transformClass(cm1, replace); try (OutputStream out = Files.newOutputStream(modInfo)) { out.write(newBytes); }