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;
}
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());
ed.to.retainAll(usingModules);

View File

@ -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,

View File

@ -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");

View File

@ -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

View File

@ -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;

View File

@ -87,13 +87,27 @@ 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 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)
public Iterable<? extends Element> getLocalElements() {
return env.toplevel.starImportScope.getSymbols(VALIDATOR);
@ -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;
}
}

View File

@ -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);
}

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.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.

View File

@ -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),
;

View File

@ -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;

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.
*
* 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<JCCompilationUnit> 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<JCCompilationUnit> trees, Consumer<Set<ModuleSymbol>> init, ClassSymbol c) {
@ -807,12 +821,17 @@ public class Modules extends JCTree.Visitor {
allRequires.add(msym);
Set<RequiresFlag> 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) {
log.error(tree.pos(), Errors.ModNotAllowedHere(EnumSet.of(Flag.STATIC)));

View File

@ -2149,8 +2149,16 @@ public class Resolve {
return null;
};
private final RecoveryLoadClass starImportScopeRecovery = (env, name) -> {
Scope importScope = env.toplevel.starImportScope;
private final RecoveryLoadClass starImportScopeRecovery =
onDemandImportScopeRecovery(false);
private final RecoveryLoadClass moduleImportScopeRecovery =
onDemandImportScopeRecovery(true);
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);
@ -2166,6 +2174,7 @@ public class Resolve {
return null;
};
}
Symbol lookupPackage(Env<AttrContext> 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;

View File

@ -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<JCTree> 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<AttrContext> env) {
env.toplevel.starImportScope.importAll(types, tsym.members(), typeImportFilter, imp, cfHandler);
Env<AttrContext> 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.

View File

@ -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<RequiresFlag> 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 <

View File

@ -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

View File

@ -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

View File

@ -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<Symbol> it = toplevel.namedImportScope.getSymbolsByName(sym.name).iterator();
for (Scope scope : new Scope[] {toplevel.namedImportScope,
toplevel.packge.members(),
toplevel.starImportScope,
toplevel.moduleImportScope}) {
Iterator<Symbol> it = scope.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();
}
}
return sym.kind == TYP && sym.isImplicit();

View File

@ -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<Object[]> 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<Object[]> 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")

View File

@ -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);
}
}
}

View File

@ -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";
}
}

View File

@ -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);
}

View File

@ -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";
}
}

View File

@ -218,29 +218,15 @@ public class ImportModule extends TestRunner {
List<String> actualErrors;
List<String> expectedErrors;
actualErrors =
new JavacTask(tb)
.options("--enable-preview", "--release", SOURCE_VERSION,
"-XDrawDiagnostics")
.outdir(classes)
.files(tb.findJavaFiles(src))
.run(Task.Expect.FAIL)
.run(Task.Expect.SUCCESS)
.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);
}
tb.writeJavaFiles(src,
"""
package test;
@ -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<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.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<String> dumpScope(Scope scope) {
List<String> 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.");
}
}

View File

@ -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;

View File

@ -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<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
public void testUnusedImportOnDemand2(Path base) throws Exception {
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.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<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
* @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<String> 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<String> 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<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;
}
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<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");
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<String> mods, String target) throws Exception {
void createClass(Path base, List<String> mods, List<String> 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);
}