8335989: Implement JEP 494: Module Import Declarations (Second Preview)

Reviewed-by: vromero, abimpoudis, mcimadamore, alanb
This commit is contained in:
Jan Lahoda 2024-11-14 06:14:33 +00:00
parent e7d90b941f
commit 1e97c1c913
28 changed files with 716 additions and 182 deletions

View File

@ -1896,6 +1896,11 @@ public class CreateSymbols {
continue; continue;
} }
if (ed.packageName.equals("jdk/internal/javac")) {
//keep jdk/internal/javac untouched. It is used to determine participates in preview:
continue;
}
Set<String> usingModules = package2ModulesUsingIt.getOrDefault(ed.packageName(), Set.of()); Set<String> usingModules = package2ModulesUsingIt.getOrDefault(ed.packageName(), Set.of());
ed.to.retainAll(usingModules); ed.to.retainAll(usingModules);

View File

@ -77,7 +77,7 @@ public @interface PreviewFeature {
@JEP(number=466, title="ClassFile API", status="Second Preview") @JEP(number=466, title="ClassFile API", status="Second Preview")
CLASSFILE_API, CLASSFILE_API,
STREAM_GATHERERS, STREAM_GATHERERS,
@JEP(number=476, title="Module Import Declarations", status="Preview") @JEP(number=494, title="Module Import Declarations", status="Second Preview")
MODULE_IMPORTS, MODULE_IMPORTS,
@JEP(number=478, title="Key Derivation Function API", status="Preview") @JEP(number=478, title="Key Derivation Function API", status="Preview")
KEY_DERIVATION, KEY_DERIVATION,

View File

@ -31,6 +31,7 @@ import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.lang.classfile.ClassFile;
import java.lang.module.InvalidModuleDescriptorException; import java.lang.module.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Builder; import java.lang.module.ModuleDescriptor.Builder;
@ -189,6 +190,7 @@ public final class ModuleInfo {
int minor_version = in.readUnsignedShort(); int minor_version = in.readUnsignedShort();
int major_version = in.readUnsignedShort(); int major_version = in.readUnsignedShort();
boolean isPreview = minor_version == ClassFile.PREVIEW_MINOR_VERSION;
if (!VM.isSupportedModuleDescriptorVersion(major_version, minor_version)) { if (!VM.isSupportedModuleDescriptorVersion(major_version, minor_version)) {
throw invalidModuleDescriptor("Unsupported major.minor version " throw invalidModuleDescriptor("Unsupported major.minor version "
+ major_version + "." + minor_version); + major_version + "." + minor_version);
@ -248,7 +250,7 @@ public final class ModuleInfo {
switch (attribute_name) { switch (attribute_name) {
case MODULE : case MODULE :
builder = readModuleAttribute(in, cpool, major_version); builder = readModuleAttribute(in, cpool, major_version, isPreview);
break; break;
case MODULE_PACKAGES : case MODULE_PACKAGES :
@ -344,7 +346,8 @@ public final class ModuleInfo {
* Reads the Module attribute, returning the ModuleDescriptor.Builder to * Reads the Module attribute, returning the ModuleDescriptor.Builder to
* build the corresponding ModuleDescriptor. * 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 throws IOException
{ {
// module_name // module_name
@ -405,14 +408,23 @@ public final class ModuleInfo {
throw invalidModuleDescriptor("The requires entry for java.base" throw invalidModuleDescriptor("The requires entry for java.base"
+ " has ACC_SYNTHETIC set"); + " 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 if (major >= 54
&& (mods.contains(Requires.Modifier.TRANSITIVE) && ((mods.contains(Requires.Modifier.TRANSITIVE)
&& !isPreview
&& !"java.se".equals(mn))
|| mods.contains(Requires.Modifier.STATIC))) { || mods.contains(Requires.Modifier.STATIC))) {
String flagName; String flagName;
if (mods.contains(Requires.Modifier.TRANSITIVE)) { if (mods.contains(Requires.Modifier.STATIC)) {
flagName = "ACC_TRANSITIVE";
} else {
flagName = "ACC_STATIC_PHASE"; flagName = "ACC_STATIC_PHASE";
} else {
flagName = "ACC_TRANSITIVE";
} }
throw invalidModuleDescriptor("The requires entry for java.base" throw invalidModuleDescriptor("The requires entry for java.base"
+ " has " + flagName + " set"); + " has " + flagName + " set");

View File

@ -154,6 +154,7 @@ module java.base {
exports jdk.internal.javac to exports jdk.internal.javac to
java.compiler, java.compiler,
java.desktop, // for ScopedValue java.desktop, // for ScopedValue
java.se, // for ParticipatesInPreview
jdk.compiler, jdk.compiler,
jdk.incubator.vector, // participates in preview features jdk.incubator.vector, // participates in preview features
jdk.jartool, // participates in preview features jdk.jartool, // participates in preview features

View File

@ -23,6 +23,8 @@
* questions. * questions.
*/ */
import jdk.internal.javac.ParticipatesInPreview;
/** /**
* Defines the API of the Java SE Platform. * Defines the API of the Java SE Platform.
* *
@ -38,7 +40,9 @@
* @moduleGraph * @moduleGraph
* @since 9 * @since 9
*/ */
@ParticipatesInPreview
module java.se { module java.se {
requires transitive java.base;
requires transitive java.compiler; requires transitive java.compiler;
requires transitive java.datatransfer; requires transitive java.datatransfer;
requires transitive java.desktop; requires transitive java.desktop;

View File

@ -87,12 +87,26 @@ public class JavacScope implements com.sun.source.tree.Scope {
} else { } else {
// synthesize an outermost "star-import" scope // synthesize an outermost "star-import" scope
return new JavacScope(env) { return new JavacScope(env) {
public boolean isStarImportScope() { @Override
return true; public ScopeType getScopeType() {
return ScopeType.STAR_IMPORT;
} }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public JavacScope getEnclosingScope() { 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<? extends Element> getLocalElements() {
return env.toplevel.moduleImportScope.getSymbols(VALIDATOR);
}
};
} }
@DefinedBy(Api.COMPILER_TREE) @DefinedBy(Api.COMPILER_TREE)
public Iterable<? extends Element> getLocalElements() { public Iterable<? extends Element> getLocalElements() {
@ -122,21 +136,27 @@ public class JavacScope implements com.sun.source.tree.Scope {
return env; return env;
} }
public boolean isStarImportScope() { public ScopeType getScopeType() {
return false; return ScopeType.ORDINARY;
} }
public boolean equals(Object other) { public boolean equals(Object other) {
return other instanceof JavacScope javacScope return other instanceof JavacScope javacScope
&& env.equals(javacScope.env) && env.equals(javacScope.env)
&& isStarImportScope() == javacScope.isStarImportScope(); && getScopeType()== javacScope.getScopeType();
} }
public int hashCode() { public int hashCode() {
return env.hashCode() + (isStarImportScope() ? 1 : 0); return env.hashCode() + getScopeType().hashCode();
} }
public String toString() { public String toString() {
return "JavacScope[env=" + env + ",starImport=" + isStarImportScope() + "]"; return "JavacScope[env=" + env + ", scope type=" + getScopeType() + "]";
}
private enum ScopeType {
ORDINARY,
STAR_IMPORT,
MODULE_IMPORT;
} }
} }

View File

@ -1402,6 +1402,7 @@ public class JavacTrees extends DocTrees {
jcCompilationUnit.namedImportScope = new NamedImportScope(psym); jcCompilationUnit.namedImportScope = new NamedImportScope(psym);
jcCompilationUnit.packge = psym; jcCompilationUnit.packge = psym;
jcCompilationUnit.starImportScope = new StarImportScope(psym); jcCompilationUnit.starImportScope = new StarImportScope(psym);
jcCompilationUnit.moduleImportScope = new StarImportScope(psym);
jcCompilationUnit.toplevelScope = WriteableScope.create(psym); jcCompilationUnit.toplevelScope = WriteableScope.create(psym);
return new TreePath(jcCompilationUnit); return new TreePath(jcCompilationUnit);
} }

View File

@ -27,6 +27,7 @@ package com.sun.tools.javac.code;
import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.code.Source.Feature; 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.jvm.Target;
import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.resources.CompilerProperties.Warnings; import com.sun.tools.javac.resources.CompilerProperties.Warnings;
@ -133,11 +134,23 @@ public class Preview {
return true; 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 // If java.base's jdk.internal.javac package is exported to s's module then
// s participates in the preview API // s participates in the preview API
return syms.java_base.exports.stream() return syms.java_base.exports.stream()
.filter(ed -> ed.packge.fullname == names.jdk_internal_javac) .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 FLEXIBLE_CONSTRUCTORS -> true;
case PRIMITIVE_PATTERNS -> true; case PRIMITIVE_PATTERNS -> true;
case MODULE_IMPORTS -> 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). //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' //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. //for those selected features, and 'false' for all the others.

View File

@ -262,6 +262,7 @@ public enum Source {
PRIMITIVE_PATTERNS(JDK23, Fragments.FeaturePrimitivePatterns, DiagKind.PLURAL), PRIMITIVE_PATTERNS(JDK23, Fragments.FeaturePrimitivePatterns, DiagKind.PLURAL),
FLEXIBLE_CONSTRUCTORS(JDK22, Fragments.FeatureFlexibleConstructors, DiagKind.NORMAL), FLEXIBLE_CONSTRUCTORS(JDK22, Fragments.FeatureFlexibleConstructors, DiagKind.NORMAL),
MODULE_IMPORTS(JDK23, Fragments.FeatureModuleImports, DiagKind.PLURAL), MODULE_IMPORTS(JDK23, Fragments.FeatureModuleImports, DiagKind.PLURAL),
JAVA_BASE_TRANSITIVE(JDK24, Fragments.FeatureJavaBaseTransitive, DiagKind.PLURAL),
PRIVATE_MEMBERS_IN_PERMITS_CLAUSE(JDK19), PRIVATE_MEMBERS_IN_PERMITS_CLAUSE(JDK19),
ERASE_POLY_SIG_RETURN_TYPE(JDK24), ERASE_POLY_SIG_RETURN_TYPE(JDK24),
; ;

View File

@ -223,6 +223,7 @@ public class Enter extends JCTree.Visitor {
tree.toplevelScope = WriteableScope.create(tree.packge); tree.toplevelScope = WriteableScope.create(tree.packge);
tree.namedImportScope = new NamedImportScope(tree.packge); tree.namedImportScope = new NamedImportScope(tree.packge);
tree.starImportScope = new StarImportScope(tree.packge); tree.starImportScope = new StarImportScope(tree.packge);
tree.moduleImportScope = new StarImportScope(tree.packge);
localEnv.info.scope = tree.toplevelScope; localEnv.info.scope = tree.toplevelScope;
localEnv.info.lint = lint; localEnv.info.lint = lint;
return localEnv; return localEnv;

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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.Directive.UsesDirective;
import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Flags.Flag; 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.Lint.LintCategory;
import com.sun.tools.javac.code.ModuleFinder; 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;
import com.sun.tools.javac.code.Source.Feature; import com.sun.tools.javac.code.Source.Feature;
import com.sun.tools.javac.code.Symbol; 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.CompletionFailure;
import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.ModuleFlags; 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.ModuleSymbol;
import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol;
import com.sun.tools.javac.code.Symtab; 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.tree.TreeInfo;
import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context; 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.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer; 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.PUBLIC;
import static com.sun.tools.javac.code.Flags.UNATTRIBUTED; 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.ERR;
import static com.sun.tools.javac.code.Kinds.Kind.MDL; import static com.sun.tools.javac.code.Kinds.Kind.MDL;
import static com.sun.tools.javac.code.Kinds.Kind.MTH; 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; 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 Symtab syms;
private final Attr attr; private final Attr attr;
private final Check chk; private final Check chk;
private final Preview preview;
private final DeferredLintHandler deferredLintHandler; private final DeferredLintHandler deferredLintHandler;
private final TypeEnvs typeEnvs; private final TypeEnvs typeEnvs;
private final Types types; private final Types types;
@ -150,6 +151,7 @@ public class Modules extends JCTree.Visitor {
private final Target target; private final Target target;
private final boolean allowModules; private final boolean allowModules;
private final boolean allowAccessIntoSystem; private final boolean allowAccessIntoSystem;
private final boolean allowRequiresTransitiveJavaBase;
public final boolean multiModuleMode; public final boolean multiModuleMode;
@ -192,6 +194,7 @@ public class Modules extends JCTree.Visitor {
syms = Symtab.instance(context); syms = Symtab.instance(context);
attr = Attr.instance(context); attr = Attr.instance(context);
chk = Check.instance(context); chk = Check.instance(context);
preview = Preview.instance(context);
deferredLintHandler = DeferredLintHandler.instance(context); deferredLintHandler = DeferredLintHandler.instance(context);
typeEnvs = TypeEnvs.instance(context); typeEnvs = TypeEnvs.instance(context);
moduleFinder = ModuleFinder.instance(context); moduleFinder = ModuleFinder.instance(context);
@ -203,6 +206,12 @@ public class Modules extends JCTree.Visitor {
Options options = Options.instance(context); Options options = Options.instance(context);
allowAccessIntoSystem = options.isUnset(Option.RELEASE); 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); lintOptions = options.isUnset(Option.XLINT_CUSTOM, "-" + LintCategory.OPTIONS.option);
multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH);
@ -249,7 +258,12 @@ public class Modules extends JCTree.Visitor {
public boolean enter(List<JCCompilationUnit> trees, ClassSymbol c) { public boolean enter(List<JCCompilationUnit> trees, ClassSymbol c) {
Assert.check(rootModules != null || inInitModules || !allowModules); 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<JCCompilationUnit> trees, Consumer<Set<ModuleSymbol>> init, ClassSymbol c) { private boolean enter(List<JCCompilationUnit> trees, Consumer<Set<ModuleSymbol>> init, ClassSymbol c) {
@ -807,11 +821,16 @@ public class Modules extends JCTree.Visitor {
allRequires.add(msym); allRequires.add(msym);
Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class); Set<RequiresFlag> flags = EnumSet.noneOf(RequiresFlag.class);
if (tree.isTransitive) { if (tree.isTransitive) {
if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) { if (msym == syms.java_base &&
log.error(tree.pos(), Errors.ModifierNotAllowedHere(names.transitive)); !allowRequiresTransitiveJavaBase &&
} else { !preview.participatesInPreview(syms, sym)) {
flags.add(RequiresFlag.TRANSITIVE); 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 (tree.isStaticPhase) {
if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) { if (msym == syms.java_base && source.compareTo(Source.JDK10) >= 0) {

View File

@ -2149,23 +2149,32 @@ public class Resolve {
return null; return null;
}; };
private final RecoveryLoadClass starImportScopeRecovery = (env, name) -> { private final RecoveryLoadClass starImportScopeRecovery =
Scope importScope = env.toplevel.starImportScope; onDemandImportScopeRecovery(false);
Symbol existing = importScope.findFirst(Convert.shortName(name),
sym -> sym.kind == TYP && sym.flatName() == name);
if (existing != null) { private final RecoveryLoadClass moduleImportScopeRecovery =
try { onDemandImportScopeRecovery(true);
existing = finder.loadClass(existing.packge().modle, name);
return new InvisibleSymbolError(env, true, existing); private RecoveryLoadClass onDemandImportScopeRecovery(boolean moduleImportScope) {
} catch (CompletionFailure cf) { return (env, name) -> {
//ignore 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<AttrContext> env, Name name) { Symbol lookupPackage(Env<AttrContext> env, Name name) {
PackageSymbol pack = syms.lookupPackage(env.toplevel.modle, name); PackageSymbol pack = syms.lookupPackage(env.toplevel.modle, name);
@ -2433,6 +2442,11 @@ public class Resolve {
sym = findGlobalType(env, env.toplevel.starImportScope, name, starImportScopeRecovery); sym = findGlobalType(env, env.toplevel.starImportScope, name, starImportScopeRecovery);
if (sym.exists()) return sym; if (sym.exists()) return sym;
else bestSoFar = bestOf(bestSoFar, 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; return bestSoFar;

View File

@ -220,6 +220,7 @@ public class TypeEnter implements Completer {
chk.checkImportedPackagesObservable(toplevel); chk.checkImportedPackagesObservable(toplevel);
toplevel.namedImportScope.finalizeScope(); toplevel.namedImportScope.finalizeScope();
toplevel.starImportScope.finalizeScope(); toplevel.starImportScope.finalizeScope();
toplevel.moduleImportScope.finalizeScope();
} catch (CompletionFailure cf) { } catch (CompletionFailure cf) {
chk.completionError(toplevel.pos(), cf); chk.completionError(toplevel.pos(), cf);
} finally { } finally {
@ -331,7 +332,7 @@ public class TypeEnter implements Completer {
throw new Abort(); throw new Abort();
} }
importAll(make.at(tree.pos()).Import(make.Select(make.QualIdent(javaLang.owner), javaLang), false), importAll(make.at(tree.pos()).Import(make.Select(make.QualIdent(javaLang.owner), javaLang), false),
javaLang, env); javaLang, env, false);
List<JCTree> defs = tree.getTypeDecls(); List<JCTree> defs = tree.getTypeDecls();
boolean isImplicitClass = !defs.isEmpty() && boolean isImplicitClass = !defs.isEmpty() &&
@ -341,7 +342,7 @@ public class TypeEnter implements Completer {
doModuleImport(make.ModuleImport(make.QualIdent(syms.java_base))); doModuleImport(make.ModuleImport(make.QualIdent(syms.java_base)));
if (peekTypeExists(syms.ioType.tsym)) { if (peekTypeExists(syms.ioType.tsym)) {
doImport(make.Import(make.Select(make.QualIdent(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) { if (imp instanceof JCModuleImport mimp) {
doModuleImport(mimp); doModuleImport(mimp);
} else { } 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()); 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; JCFieldAccess imp = tree.qualid;
Name name = TreeInfo.name(imp); Name name = TreeInfo.name(imp);
@ -450,16 +451,20 @@ public class TypeEnter implements Completer {
if (name == names.asterisk) { if (name == names.asterisk) {
// Import on demand. // Import on demand.
chk.checkCanonical(imp.selected); chk.checkCanonical(imp.selected);
if (tree.staticImport) if (tree.staticImport) {
Assert.check(!fromModuleImport);
importStaticAll(tree, p, env); importStaticAll(tree, p, env);
else } else {
importAll(tree, p, env); importAll(tree, p, env, fromModuleImport);
}
} else { } else {
// Named type import. // Named type import.
if (tree.staticImport) { if (tree.staticImport) {
Assert.check(!fromModuleImport);
importNamedStatic(tree, p, name, localEnv); importNamedStatic(tree, p, name, localEnv);
chk.checkCanonical(imp.selected); chk.checkCanonical(imp.selected);
} else { } else {
Assert.check(!fromModuleImport);
Type importedType = attribImportType(imp, localEnv); Type importedType = attribImportType(imp, localEnv);
Type originalType = importedType.getOriginalType(); Type originalType = importedType.getOriginalType();
TypeSymbol c = originalType.hasTag(CLASS) ? originalType.tsym : importedType.tsym; TypeSymbol c = originalType.hasTag(CLASS) ? originalType.tsym : importedType.tsym;
@ -506,7 +511,7 @@ public class TypeEnter implements Completer {
JCImport nestedImport = make.at(tree.pos) JCImport nestedImport = make.at(tree.pos)
.Import(make.Select(make.QualIdent(pkg), names.asterisk), false); .Import(make.Select(make.QualIdent(pkg), names.asterisk), false);
doImport(nestedImport); doImport(nestedImport, true);
} }
for (RequiresDirective requires : currentModule.requires) { for (RequiresDirective requires : currentModule.requires) {
@ -542,8 +547,13 @@ public class TypeEnter implements Completer {
*/ */
private void importAll(JCImport imp, private void importAll(JCImport imp,
final TypeSymbol tsym, final TypeSymbol tsym,
Env<AttrContext> env) { Env<AttrContext> env,
env.toplevel.starImportScope.importAll(types, tsym.members(), typeImportFilter, imp, cfHandler); 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. /** Import all static members of a class or package on demand.

View File

@ -199,6 +199,9 @@ public class ClassReader {
/** The minor version number of the class file being read. */ /** The minor version number of the class file being read. */
int minorVersion; int minorVersion;
/** true if the class file being read is a preview class file. */
boolean previewClassFile;
/** UTF-8 validation level */ /** UTF-8 validation level */
Convert.Validation utf8validation; Convert.Validation utf8validation;
@ -1200,7 +1203,9 @@ public class ClassReader {
ModuleSymbol rsym = poolReader.getModule(nextChar()); ModuleSymbol rsym = poolReader.getModule(nextChar());
Set<RequiresFlag> flags = readRequiresFlags(nextChar()); Set<RequiresFlag> flags = readRequiresFlags(nextChar());
if (rsym == syms.java_base && majorVersion >= V54.major) { 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); throw badClassFile("bad.requires.flag", RequiresFlag.TRANSITIVE);
} }
if (flags.contains(RequiresFlag.STATIC_PHASE)) { if (flags.contains(RequiresFlag.STATIC_PHASE)) {
@ -3185,7 +3190,7 @@ public class ClassReader {
majorVersion = nextChar(); majorVersion = nextChar();
int maxMajor = Version.MAX().major; int maxMajor = Version.MAX().major;
int maxMinor = Version.MAX().minor; int maxMinor = Version.MAX().minor;
boolean previewClassFile = previewClassFile =
minorVersion == ClassFile.PREVIEW_MINOR_VERSION; minorVersion == ClassFile.PREVIEW_MINOR_VERSION;
if (majorVersion > maxMajor || if (majorVersion > maxMajor ||
majorVersion * 1000 + minorVersion < majorVersion * 1000 + minorVersion <

View File

@ -921,10 +921,6 @@ compiler.misc.unexpected.ret.val=\
compiler.err.mod.not.allowed.here=\ compiler.err.mod.not.allowed.here=\
modifier {0} 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=\ compiler.err.intf.not.allowed.here=\
interface not allowed here interface not allowed here
@ -3252,6 +3248,9 @@ compiler.misc.feature.flexible.constructors=\
compiler.misc.feature.module.imports=\ compiler.misc.feature.module.imports=\
module imports module imports
compiler.misc.feature.java.base.transitive=\
transitive modifier for java.base
compiler.warn.underscore.as.identifier=\ compiler.warn.underscore.as.identifier=\
as of release 9, ''_'' is a keyword, and may not be used as an identifier as of release 9, ''_'' is a keyword, and may not be used as an identifier

View File

@ -538,6 +538,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public NamedImportScope namedImportScope; public NamedImportScope namedImportScope;
/** A scope for all import-on-demands. */ /** A scope for all import-on-demands. */
public StarImportScope starImportScope; public StarImportScope starImportScope;
/** A scope for all single module imports. */
public StarImportScope moduleImportScope;
/** Line starting positions, defined only if option -g is set. */ /** Line starting positions, defined only if option -g is set. */
public Position.LineMap lineMap = null; public Position.LineMap lineMap = null;
/** A table that stores all documentation comments indexed by the tree /** A table that stores all documentation comments indexed by the tree

View File

@ -1138,26 +1138,17 @@ public class TreeMaker implements JCTree.Factory {
sym.owner.kind == MTH || sym.owner.kind == VAR) { sym.owner.kind == MTH || sym.owner.kind == VAR) {
return true; return true;
} else if (sym.kind == TYP && toplevel != null) { } else if (sym.kind == TYP && toplevel != null) {
Iterator<Symbol> it = toplevel.namedImportScope.getSymbolsByName(sym.name).iterator(); for (Scope scope : new Scope[] {toplevel.namedImportScope,
if (it.hasNext()) { toplevel.packge.members(),
Symbol s = it.next(); toplevel.starImportScope,
return toplevel.moduleImportScope}) {
s == sym && Iterator<Symbol> it = scope.getSymbolsByName(sym.name).iterator();
!it.hasNext(); if (it.hasNext()) {
} Symbol s = it.next();
it = toplevel.packge.members().getSymbolsByName(sym.name).iterator(); return
if (it.hasNext()) { s == sym &&
Symbol s = it.next(); !it.hasNext();
return }
s == sym &&
!it.hasNext();
}
it = toplevel.starImportScope.getSymbolsByName(sym.name).iterator();
if (it.hasNext()) {
Symbol s = it.next();
return
s == sym &&
!it.hasNext();
} }
} }
return sym.kind == TYP && sym.isImplicit(); return sym.kind == TYP && sym.isImplicit();

View File

@ -31,10 +31,13 @@
* @summary Test parsing of module-info.class with different class file versions * @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.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Requires.Modifier; import java.lang.module.ModuleDescriptor.Requires.Modifier;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set; import java.util.Set;
import static java.lang.module.ModuleDescriptor.Requires.Modifier.*; import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
@ -46,6 +49,8 @@ import org.testng.annotations.Test;
import static org.testng.Assert.*; import static org.testng.Assert.*;
public class ClassFileVersionsTest { public class ClassFileVersionsTest {
private static final int PREVIEW_MINOR_VERSION =
ClassFile.PREVIEW_MINOR_VERSION;
private static final int FEATURE; private static final int FEATURE;
static { static {
FEATURE = Runtime.version().feature(); FEATURE = Runtime.version().feature();
@ -56,26 +61,34 @@ public class ClassFileVersionsTest {
@DataProvider(name = "supported") @DataProvider(name = "supported")
public Object[][] 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 * for each subsequent JDK version from JDK 10 to the current
* feature release for a total of (4 + (FEATURE - 9) ) => * feature release, and two tests for the current release with
* (feature - 5) rows. * a preview flag set, for a total of (4 + (FEATURE - 9) + 2)
* rows.
*/ */
Object[][] result = new Object[(FEATURE - 5)][]; List<Object[]> result = new ArrayList<>(4 + (FEATURE - 9) + 2);
// Class file version of JDK 9 is 53.0 // Class file version of JDK 9 is 53.0
result[0] = new Object[]{ 53, 0, Set.of()}; result.add(new Object[]{ 53, 0, Set.of()});
result[1] = new Object[]{ 53, 0, Set.of(STATIC) }; result.add(new Object[]{ 53, 0, Set.of(STATIC) });
result[2] = new Object[]{ 53, 0, Set.of(TRANSITIVE) }; result.add(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(STATIC, TRANSITIVE) });
// Major class file version of JDK N is 44 + n. Create rows // Major class file version of JDK N is 44 + n. Create rows
// for JDK 10 through FEATURE. // for JDK 10 through FEATURE.
for (int i = 4; i < (FEATURE - 5) ; i++) { for (int i = 10; i <= FEATURE; i++) {
result[i] = new Object[]{i + 50, 0, Set.of()}; 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 // 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, * There are three test cases for releases prior to JDK 9,
* three test cases for each JDK version from JDK 10 to the * three test cases for each JDK version from JDK 10 to the
* current feature release, plus one addition test case for * current feature release, two tests for the current release with
* the next release for a total of (3 + (FEATURE - 9) * 3 + 1) * the preview flag set, plus one addition test case for
* the next release for a total of (3 + (FEATURE - 9) * 3 + 2 + 1)
* rows. * rows.
*/ */
int unsupportedCount = 3 + (FEATURE - 9)*3 + 1; List<Object[]> result = new ArrayList<>(3 + (FEATURE - 9) * 3 + 2 + 1);
Object[][] result = new Object[unsupportedCount][];
result[0] = new Object[]{50, 0, Set.of()}; // JDK 6 result.add(new Object[]{50, 0, Set.of()}); // JDK 6
result[1] = new Object[]{51, 0, Set.of()}; // JDK 7 result.add(new Object[]{51, 0, Set.of()}); // JDK 7
result[2] = new Object[]{52, 0, Set.of()}; // JDK 8 result.add(new Object[]{52, 0, Set.of()}); // JDK 8
for (int i = 10; i <= FEATURE ; i++) { for (int i = 10; i <= FEATURE ; i++) {
int base = 3 + (i-10)*3;
// Major class file version of JDK N is 44+n // Major class file version of JDK N is 44+n
result[base] = new Object[]{i + 44, 0, Set.of(STATIC)}; result.add(new Object[]{i + 44, 0, Set.of(STATIC)});
result[base + 1] = new Object[]{i + 44, 0, Set.of(TRANSITIVE)}; result.add(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, TRANSITIVE)});
} }
result[unsupportedCount - 1] = new Object[]{FEATURE+1+44, 0, Set.of()}; result.add(new Object[]{FEATURE + 44,
return result; 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") @Test(dataProvider = "supported")

View File

@ -61,6 +61,8 @@ import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.access.JavaLangModuleAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
import java.lang.classfile.ClassFile; import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassFileVersion;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.attribute.ModuleAttribute; import java.lang.classfile.attribute.ModuleAttribute;
import java.lang.constant.PackageDesc; import java.lang.constant.PackageDesc;
import java.lang.constant.ModuleDesc; import java.lang.constant.ModuleDesc;
@ -1522,4 +1524,68 @@ public class ModuleDescriptorTest {
assertTrue(s.contains("p1")); 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);
}
}
} }

View File

@ -26,32 +26,32 @@
class Test { class Test {
void m1(int m1_arg) { void m1(int m1_arg) {
String x = "Test; 0; 0"; String x = "Test; 0; 0; 0";
String y = "Test; 0; 0"; String y = "Test; 0; 0; 0";
String z = "Test; 0; 0"; String z = "Test; 0; 0; 0";
Object o = new Object() { Object o = new Object() {
public boolean equals(Object other) { public boolean equals(Object other) {
String p = "-; Test; 0; 0"; String p = "-; Test; 0; 0; 0";
String q = "-; Test; 0; 0"; String q = "-; Test; 0; 0; 0";
String r = "-; Test; 0; 0"; String r = "-; Test; 0; 0; 0";
return (this == other); return (this == other);
} }
}; };
} }
String s = "Test; 0; 0"; String s = "Test; 0; 0; 0";
boolean b = new Object() { boolean b = new Object() {
public boolean equals(Object other) { public boolean equals(Object other) {
String p = "-; Test; 0; 0"; String p = "-; Test; 0; 0; 0";
String q = "-; Test; 0; 0"; String q = "-; Test; 0; 0; 0";
String r = "-; Test; 0; 0"; String r = "-; Test; 0; 0; 0";
return (this == other); return (this == other);
} }
}.equals(null); }.equals(null);
class Test2 { class Test2 {
String s = "Test.Test2; Test; 0; 0"; String s = "Test.Test2; Test; 0; 0; 0";
} }
} }

View File

@ -23,29 +23,29 @@
import java.util.List; import java.util.List;
import java.io.*; import java.io.*;
//TOPLEVEL_SCOPE:List, Test2, Test; java.io.*, java.lang.* //TOPLEVEL_SCOPE:List, Test2, Test; java.io.*, java.lang.*;
class Test { class Test {
void m1(int m1_arg) { void m1(int m1_arg) {
String x = "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 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 z = "z, y, x, m1_arg, super, this; List, Test2, Test; java.io.*, java.lang.*;";
Object o = new Object() { Object o = new Object() {
public boolean equals(Object other) { 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 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 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 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); 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() { boolean b = new Object() {
public boolean equals(Object other) { public boolean equals(Object other) {
String p = "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 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 r = "r, q, p, other, super, this; -, super, this; List, Test2, Test; java.io.*, java.lang.*;";
return (this == other); return (this == other);
} }

View File

@ -25,32 +25,32 @@
class Test { class Test {
void m1(int m1_arg) { void m1(int m1_arg) {
String x = "m1; 0; 0"; String x = "m1; 0; 0; 0";
String y = "m1; 0; 0"; String y = "m1; 0; 0; 0";
String z = "m1; 0; 0"; String z = "m1; 0; 0; 0";
Object o = new Object() { Object o = new Object() {
public boolean equals(Object other) { public boolean equals(Object other) {
String p = "equals; m1; 0; 0"; String p = "equals; m1; 0; 0; 0";
String q = "equals; m1; 0; 0"; String q = "equals; m1; 0; 0; 0";
String r = "equals; m1; 0; 0"; String r = "equals; m1; 0; 0; 0";
return (this == other); return (this == other);
} }
}; };
} }
String s = "0; 0; 0"; String s = "0; 0; 0; 0";
boolean b = new Object() { boolean b = new Object() {
public boolean equals(Object other) { public boolean equals(Object other) {
String p = "equals; 0; 0; 0"; String p = "equals; 0; 0; 0; 0";
String q = "equals; 0; 0; 0"; String q = "equals; 0; 0; 0; 0";
String r = "equals; 0; 0; 0"; String r = "equals; 0; 0; 0; 0";
return (this == other); return (this == other);
} }
}.equals(null); }.equals(null);
class Test2 { class Test2 {
String s = "0; 0; 0; 0"; String s = "0; 0; 0; 0; 0";
} }
} }

View File

@ -218,28 +218,14 @@ public class ImportModule extends TestRunner {
List<String> actualErrors; List<String> actualErrors;
List<String> expectedErrors; List<String> expectedErrors;
actualErrors = new JavacTask(tb)
new JavacTask(tb) .options("--enable-preview", "--release", SOURCE_VERSION,
.options("--enable-preview", "--release", SOURCE_VERSION, "-XDrawDiagnostics")
"-XDrawDiagnostics") .outdir(classes)
.outdir(classes) .files(tb.findJavaFiles(src))
.files(tb.findJavaFiles(src)) .run(Task.Expect.SUCCESS)
.run(Task.Expect.FAIL) .writeAll()
.writeAll() .getOutputLines(Task.OutputKind.DIRECT);
.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);
}
tb.writeJavaFiles(src, tb.writeJavaFiles(src,
""" """
@ -793,7 +779,7 @@ public class ImportModule extends TestRunner {
if (!Objects.equals(expectedErrors, actualErrors)) { if (!Objects.equals(expectedErrors, actualErrors)) {
throw new AssertionError("Incorrect Output, expected: " + expectedErrors + 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<String> 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<String> 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();
}
} }

View File

@ -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.tree.JCTree.JCStatement;
import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Context.Factory; 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; import static javax.tools.JavaFileObject.Kind.SOURCE;
@ -89,6 +92,7 @@ public class TestGetScopeResult {
new TestGetScopeResult().testLocalRecordAnnotation(); new TestGetScopeResult().testLocalRecordAnnotation();
new TestGetScopeResult().testRuleCases(); new TestGetScopeResult().testRuleCases();
new TestGetScopeResult().testNestedSwitchExpression(); new TestGetScopeResult().testNestedSwitchExpression();
new TestGetScopeResult().testModuleImportScope();
} }
public void run() throws IOException { 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<String> dumpScope(Scope scope) { private List<String> dumpScope(Scope scope) {
List<String> content = new ArrayList<>(); List<String> content = new ArrayList<>();
while (scope.getEnclosingClass() != null) { while (scope.getEnclosingClass() != null) {
@ -833,4 +895,17 @@ public class TestGetScopeResult {
} }
return content; 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.");
}
} }

View File

@ -21,7 +21,8 @@
* questions. * 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 { module m {
requires transitive java.base; requires transitive java.base;

View File

@ -432,6 +432,40 @@ public class ConvenientAccessErrorsTest extends ModuleTestBase {
throw new Exception("expected output not found; actual: " + log); 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<String> 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<String> 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 @Test
public void testUnusedImportOnDemand2(Path base) throws Exception { public void testUnusedImportOnDemand2(Path base) throws Exception {
Path src = base.resolve("src"); Path src = base.resolve("src");

View File

@ -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.Symbol.ModuleSymbol;
import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Symtab;
import java.util.ArrayList; 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.JarTask;
import toolbox.JavacTask; 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<String> l;
}
""");
Path classes = base.resolve("classes");
tb.createDirectories(classes);
AtomicBoolean seenJavaSEDependency = new AtomicBoolean();
List<String> 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<String> 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<com.sun.source.util.JavacTask> 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);
}
}
});
};
}
} }

View File

@ -23,7 +23,7 @@
/** /**
* @test * @test
* @bug 8193125 8196623 * @bug 8193125 8196623 8335989
* @summary test modifiers with java.base * @summary test modifiers with java.base
* @library /tools/lib * @library /tools/lib
* @enablePreview * @enablePreview
@ -49,6 +49,7 @@ import java.lang.classfile.attribute.*;
import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.platform.JDKPlatformProvider; import com.sun.tools.javac.platform.JDKPlatformProvider;
import java.util.function.Supplier;
import toolbox.JavacTask; import toolbox.JavacTask;
import toolbox.Task; import toolbox.Task;
@ -56,6 +57,8 @@ import toolbox.ToolBox;
public class JavaBaseTest { public class JavaBaseTest {
private static final String CURRENT_VERSION = System.getProperty("java.specification.version");
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
JavaBaseTest t = new JavaBaseTest(); JavaBaseTest t = new JavaBaseTest();
t.run(); t.run();
@ -75,13 +78,11 @@ public class JavaBaseTest {
void run() throws Exception { void run() throws Exception {
Set<String> targets = new LinkedHashSet<>(); Set<String> targets = new LinkedHashSet<>();
StreamSupport.stream(new JDKPlatformProvider().getSupportedPlatformNames() targets.add("9");
.spliterator(), targets.add("10");
false) targets.add("21");
.filter(p -> Integer.parseInt(p) >= 9)
.forEach(targets::add);
//run without --release:
targets.add("current"); targets.add("current");
targets.add("current-preview");
for (List<String> mods : modifiers) { for (List<String> mods : modifiers) {
for (String target : targets) { for (String target : targets) {
for (Mode mode : Mode.values()) { for (Mode mode : Mode.values()) {
@ -119,15 +120,43 @@ public class JavaBaseTest {
tb.writeJavaFiles(src, tb.writeJavaFiles(src,
"module m { requires " + String.join(" ", mods) + " java.base; }"); "module m { requires " + String.join(" ", mods) + " java.base; }");
Path modules = Files.createDirectories(base.resolve("modules")); Path modules = Files.createDirectories(base.resolve("modules"));
boolean expectOK = target.equals("9"); boolean expectOK;
JavacTask jct = new JavacTask(tb) JavacTask jct = new JavacTask(tb)
.outdir(modules); .outdir(modules);
if (target.equals("current")) List<String> options = new ArrayList<>();
jct.options("-XDrawDiagnostics");
else switch (target) {
jct.options("-XDrawDiagnostics", "--release", 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)) String log = jct.files(tb.findJavaFiles(src))
.run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL) .run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL)
@ -138,9 +167,9 @@ public class JavaBaseTest {
boolean foundErrorMessage = false; boolean foundErrorMessage = false;
for (String mod : mods) { for (String mod : mods) {
String key = mod.equals("static") String key = mod.equals("static")
? "compiler.err.mod.not.allowed.here" ? "compiler.err.mod.not.allowed.here: " + mod
: "compiler.err.modifier.not.allowed.here"; : "compiler.err.feature.not.supported.in.source.plural: (compiler.misc.feature.java.base.transitive)";
String message = "module-info.java:1:12: " + key + ": " + mod; String message = "module-info.java:1:12: " + key;
if (log.contains(message)) { if (log.contains(message)) {
foundErrorMessage = true; foundErrorMessage = true;
} }
@ -152,18 +181,57 @@ public class JavaBaseTest {
} }
void testClass(Path base, List<String> mods, String target) throws Exception { void testClass(Path base, List<String> mods, String target) throws Exception {
createClass(base, mods, target); boolean expectOK;
List<String> 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<String> 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"); Path src = base.resolve("src");
tb.writeJavaFiles(src, tb.writeJavaFiles(src,
"module mx { requires m; }"); "module mx { requires m; }");
Path modules = Files.createDirectories(base.resolve("modules")); Path modules = Files.createDirectories(base.resolve("modules"));
boolean expectOK = target.equals("9");
String log = new JavacTask(tb) String log = new JavacTask(tb)
.outdir(modules) .outdir(modules)
.options("-XDrawDiagnostics", .options(testOptions)
"--module-path", base.resolve("test-modules").toString())
.files(tb.findJavaFiles(src)) .files(tb.findJavaFiles(src))
.run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL) .run(expectOK ? Task.Expect.SUCCESS : Task.Expect.FAIL)
.writeAll() .writeAll()
@ -188,7 +256,7 @@ public class JavaBaseTest {
} }
} }
void createClass(Path base, List<String> mods, String target) throws Exception { void createClass(Path base, List<String> mods, List<String> options) throws Exception {
Path src1 = base.resolve("interim-src"); Path src1 = base.resolve("interim-src");
tb.writeJavaFiles(src1, tb.writeJavaFiles(src1,
"module m { requires java.base; }"); "module m { requires java.base; }");
@ -197,9 +265,7 @@ public class JavaBaseTest {
JavacTask jct = new JavacTask(tb) JavacTask jct = new JavacTask(tb)
.outdir(modules1); .outdir(modules1);
if (!target.equals("current")) { jct.options(options);
jct.options("--release", target);
}
jct.files(tb.findJavaFiles(src1)) jct.files(tb.findJavaFiles(src1))
.run(Task.Expect.SUCCESS); .run(Task.Expect.SUCCESS);
@ -226,6 +292,8 @@ public class JavaBaseTest {
requires.set(i, e2); requires.set(i, e2);
} }
boolean preview = options.contains("--enable-preview");
ModuleAttribute modAttr2 = ModuleAttribute.of( ModuleAttribute modAttr2 = ModuleAttribute.of(
modAttr1.moduleName(), modAttr1.moduleName(),
modAttr1.moduleFlagsMask(), modAttr1.moduleFlagsMask(),
@ -237,8 +305,14 @@ public class JavaBaseTest {
modAttr1.provides()); modAttr1.provides());
Path modInfo = base.resolve("test-modules").resolve("module-info.class"); Path modInfo = base.resolve("test-modules").resolve("module-info.class");
Files.createDirectories(modInfo.getParent()); Files.createDirectories(modInfo.getParent());
byte[] newBytes = ClassFile.of().transformClass(cm1, ClassTransform.dropping(ce -> ce instanceof ModuleAttribute). ClassTransform replace = (builder, element) -> {
andThen(ClassTransform.endHandler(classBuilder -> classBuilder.with(modAttr2)))); 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)) { try (OutputStream out = Files.newOutputStream(modInfo)) {
out.write(newBytes); out.write(newBytes);
} }