8338981: Access to private classes should be permitted inside the permits clause of the enclosing top-level class
Reviewed-by: vromero, mcimadamore
This commit is contained in:
parent
3ccd2f757d
commit
b2694934b5
@ -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),
|
||||
PRIVATE_MEMBERS_IN_PERMITS_CLAUSE(JDK19),
|
||||
;
|
||||
|
||||
enum DiagKind {
|
||||
|
@ -92,6 +92,10 @@ public class AttrContext {
|
||||
*/
|
||||
boolean allowProtectedAccess = false;
|
||||
|
||||
/** Are we attributing a permits clause?
|
||||
*/
|
||||
boolean isPermitsClause = false;
|
||||
|
||||
/** Are arguments to current function applications boxed into an array for varargs?
|
||||
*/
|
||||
Resolve.MethodResolutionPhase pendingResolutionPhase = null;
|
||||
@ -149,6 +153,7 @@ public class AttrContext {
|
||||
info.preferredTreeForDiagnostics = preferredTreeForDiagnostics;
|
||||
info.visitingServiceImplementation = visitingServiceImplementation;
|
||||
info.allowProtectedAccess = allowProtectedAccess;
|
||||
info.isPermitsClause = isPermitsClause;
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,7 @@ public class Resolve {
|
||||
private final boolean compactMethodDiags;
|
||||
private final boolean allowLocalVariableTypeInference;
|
||||
private final boolean allowYieldStatement;
|
||||
private final boolean allowPrivateMembersInPermitsClause;
|
||||
final EnumSet<VerboseResolutionMode> verboseResolutionMode;
|
||||
final boolean dumpMethodReferenceSearchResults;
|
||||
final boolean dumpStacktraceOnError;
|
||||
@ -147,6 +148,7 @@ public class Resolve {
|
||||
Target target = Target.instance(context);
|
||||
allowLocalVariableTypeInference = Feature.LOCAL_VARIABLE_TYPE_INFERENCE.allowedInSource(source);
|
||||
allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source);
|
||||
allowPrivateMembersInPermitsClause = Feature.PRIVATE_MEMBERS_IN_PERMITS_CLAUSE.allowedInSource(source);
|
||||
polymorphicSignatureScope = WriteableScope.create(syms.noSymbol);
|
||||
allowModules = Feature.MODULES.allowedInSource(source);
|
||||
allowRecords = Feature.RECORDS.allowedInSource(source);
|
||||
@ -425,7 +427,9 @@ public class Resolve {
|
||||
(env.enclClass.sym == sym.owner // fast special case
|
||||
||
|
||||
env.enclClass.sym.outermostClass() ==
|
||||
sym.owner.outermostClass())
|
||||
sym.owner.outermostClass()
|
||||
||
|
||||
privateMemberInPermitsClauseIfAllowed(env, sym))
|
||||
&&
|
||||
sym.isInheritedIn(site.tsym, types);
|
||||
case 0:
|
||||
@ -458,6 +462,13 @@ public class Resolve {
|
||||
return isAccessible(env, site, checkInner) && notOverriddenIn(site, sym);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean privateMemberInPermitsClauseIfAllowed(Env<AttrContext> env, Symbol sym) {
|
||||
return allowPrivateMembersInPermitsClause &&
|
||||
env.info.isPermitsClause &&
|
||||
((JCClassDecl) env.tree).sym.outermostClass() == sym.owner.outermostClass();
|
||||
}
|
||||
|
||||
//where
|
||||
/* `sym' is accessible only if not overridden by
|
||||
* another symbol which is a member of `site'
|
||||
|
@ -979,11 +979,17 @@ public class TypeEnter implements Completer {
|
||||
if (sym.isPermittedExplicit) {
|
||||
ListBuffer<Symbol> permittedSubtypeSymbols = new ListBuffer<>();
|
||||
List<JCExpression> permittedTrees = tree.permitting;
|
||||
var isPermitsClause = baseEnv.info.isPermitsClause;
|
||||
try {
|
||||
baseEnv.info.isPermitsClause = true;
|
||||
for (JCExpression permitted : permittedTrees) {
|
||||
Type pt = attr.attribBase(permitted, baseEnv, false, false, false);
|
||||
permittedSubtypeSymbols.append(pt.tsym);
|
||||
}
|
||||
sym.setPermittedSubclasses(permittedSubtypeSymbols.toList());
|
||||
} finally {
|
||||
baseEnv.info.isPermitsClause = isPermitsClause;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8338981
|
||||
* @summary Access to private classes should be permitted inside the permits clause of the enclosing top-level class.
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.util
|
||||
* @build toolbox.ToolBox toolbox.JavacTask toolbox.Task
|
||||
* @run main PrivateMembersInPermitClause -source 19+
|
||||
*/
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import toolbox.Task;
|
||||
import java.util.List;
|
||||
|
||||
public class PrivateMembersInPermitClause extends toolbox.TestRunner {
|
||||
|
||||
private final toolbox.ToolBox tb;
|
||||
|
||||
public PrivateMembersInPermitClause() {
|
||||
super(System.err);
|
||||
tb = new toolbox.ToolBox();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
new PrivateMembersInPermitClause().runTests();
|
||||
}
|
||||
|
||||
public void runTests() throws Exception {
|
||||
runTests(_ -> new Object[] {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a private class in the permits clause compiles successfully.
|
||||
*/
|
||||
@Test
|
||||
public void privateClassPermitted() throws Exception {
|
||||
var root = Path.of("src");
|
||||
tb.writeJavaFiles(root,
|
||||
"""
|
||||
sealed class S permits S.A {
|
||||
private static final class A extends S {}
|
||||
}
|
||||
"""
|
||||
);
|
||||
|
||||
new toolbox.JavacTask(tb)
|
||||
.files(root.resolve("S.java"))
|
||||
.run(toolbox.Task.Expect.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a private class from another top-level class in the permits clause fails to compile.
|
||||
*/
|
||||
@Test
|
||||
public void otherTopLevelPrivateClassFails() throws Exception {
|
||||
var root = Path.of("src");
|
||||
tb.writeJavaFiles(root,
|
||||
"""
|
||||
public class S {
|
||||
private static final class A extends S {}
|
||||
}
|
||||
sealed class T permits S.A {
|
||||
}
|
||||
"""
|
||||
);
|
||||
var expectedErrors = List.of(
|
||||
"S.java:4:25: compiler.err.report.access: S.A, private, S",
|
||||
"1 error"
|
||||
);
|
||||
|
||||
var compileErrors = new toolbox.JavacTask(tb)
|
||||
.files(root.resolve("S.java"))
|
||||
.options("-XDrawDiagnostics")
|
||||
.run(toolbox.Task.Expect.FAIL)
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
if (!Objects.equals(compileErrors, expectedErrors)) {
|
||||
throw new AssertionError("Expected errors: " + expectedErrors + ", but got: " + compileErrors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a private class in the permits clause of an inner class compiles successfully.
|
||||
*/
|
||||
@Test
|
||||
public void privateClassInInnerPermitted() throws Exception {
|
||||
var root = Path.of("src");
|
||||
tb.writeJavaFiles(root,
|
||||
"""
|
||||
public sealed class S permits S.T.A {
|
||||
static class T {
|
||||
private static final class A extends S {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
);
|
||||
|
||||
new toolbox.JavacTask(tb)
|
||||
.files(root.resolve("S.java"))
|
||||
.run(toolbox.Task.Expect.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a private class in the permits clause contained in a sibling private inner class compiles successfully.
|
||||
*/
|
||||
@Test
|
||||
public void siblingPrivateClassesPermitted() throws Exception {
|
||||
var root = Path.of("src");
|
||||
tb.writeJavaFiles(root,
|
||||
"""
|
||||
public class S {
|
||||
private static class A {
|
||||
private static class B extends C.D {}
|
||||
}
|
||||
private static class C {
|
||||
private static class D {}
|
||||
}
|
||||
}
|
||||
"""
|
||||
);
|
||||
|
||||
new toolbox.JavacTask(tb)
|
||||
.files(root.resolve("S.java"))
|
||||
.run(toolbox.Task.Expect.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a private class in the permits clause of a sealed class does not compile when the release is lower than 19.
|
||||
*/
|
||||
@Test
|
||||
public void testSourceLowerThan19() throws Exception {
|
||||
var root = Path.of("src");
|
||||
tb.writeJavaFiles(root,
|
||||
"""
|
||||
sealed class S permits S.A {
|
||||
private static final class A extends S {}
|
||||
}
|
||||
"""
|
||||
);
|
||||
|
||||
var expectedErrors = List.of(
|
||||
"S.java:1:25: compiler.err.report.access: S.A, private, S",
|
||||
"S.java:2:26: compiler.err.cant.inherit.from.sealed: S",
|
||||
"2 errors"
|
||||
);
|
||||
|
||||
var actualOutput = new toolbox.JavacTask(tb)
|
||||
.files(root.resolve("S.java"))
|
||||
.options("--release", "18", "-XDrawDiagnostics")
|
||||
.run(toolbox.Task.Expect.FAIL)
|
||||
.getOutputLines(Task.OutputKind.DIRECT);
|
||||
|
||||
if (!Objects.equals(actualOutput, expectedErrors)) {
|
||||
throw new AssertionError("Expected errors: " + expectedErrors + ", but got: " + actualOutput);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user