Skip to content

Commit

Permalink
Rewrite how implicitly permitted subtypes are discovered and wired.
Browse files Browse the repository at this point in the history
  • Loading branch information
srikanth-sankaran committed Oct 17, 2024
1 parent 0b34789 commit d1a55f5
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,9 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
Expand Down Expand Up @@ -115,8 +111,6 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding
} else {
anonymousType.setSuperClass(supertype);
anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
if (supertype.isEnum() && supertype instanceof SourceTypeBinding superEnum)
implicitlySealEnumHierarchy(superEnum, anonymousType);
TypeReference typeReference = this.referenceContext.allocation.type;
if (typeReference != null) { // no check for enum constant body
this.referenceContext.superclass = typeReference;
Expand Down Expand Up @@ -152,21 +146,6 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding
anonymousType.verifyMethods(environment().methodVerifier());
}

private void implicitlySealEnumHierarchy(SourceTypeBinding superEnum, LocalTypeBinding anonymousType) {
if (JavaFeature.SEALED_CLASSES.isSupported(compilerOptions())) {
ReferenceBinding[] permittedTypes = superEnum.permittedTypes();
int sz = permittedTypes == null ? 0 : permittedTypes.length;
if (sz == 0) {
permittedTypes = new ReferenceBinding[] { anonymousType };
} else {
System.arraycopy(permittedTypes, 0, permittedTypes = new ReferenceBinding[sz + 1], 0, sz);
permittedTypes[sz] = anonymousType;
}
anonymousType.modifiers |= ClassFileConstants.AccFinal;
superEnum.setPermittedTypes(permittedTypes);
}
}

void buildComponents() {
SourceTypeBinding sourceType = this.referenceContext.binding;
if (!sourceType.isRecord()) return;
Expand Down Expand Up @@ -368,7 +347,6 @@ void buildLocalTypeBinding(SourceTypeBinding enclosingType) {

LocalTypeBinding localType = buildLocalType(enclosingType, enclosingType.fPackage);
connectTypeHierarchy();
connectImplicitPermittedTypes();
if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
checkParameterizedTypeBounds();
checkParameterizedSuperTypeCollisions();
Expand Down Expand Up @@ -1267,49 +1245,12 @@ private boolean connectEnumSuperclass() {
}
return !foundCycle;
}
private void connectImplicitPermittedTypes(SourceTypeBinding sourceType) {
List<SourceTypeBinding> types = new ArrayList<>();
for (TypeDeclaration typeDecl : this.referenceCompilationUnit().types) {
types.addAll(sourceType.collectAllTypeBindings(typeDecl, this.compilationUnitScope()));
}
Set<ReferenceBinding> permSubTypes = new LinkedHashSet<>();
for (ReferenceBinding type : types) {
if (!TypeBinding.equalsEquals(type, sourceType) && type.findSuperTypeOriginatingFrom(sourceType) != null) {
permSubTypes.add(type);
}
}
if (sourceType.isSealed() && sourceType.isLocalType()) {
// bug xxxx flag Error and return;
}
if (permSubTypes.size() == 0) {
if (!sourceType.isLocalType() && !sourceType.isRecord() && !sourceType.isEnum()) // error flagged already
problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext);
return;
}
sourceType.setPermittedTypes(permSubTypes.toArray(new ReferenceBinding[0]));
}
void connectImplicitPermittedTypes() {
TypeDeclaration typeDecl = this.referenceContext;
SourceTypeBinding sourceType = typeDecl.binding;
if (sourceType.id == TypeIds.T_JavaLangObject) // already handled
return;
if (sourceType.isSealed() && (typeDecl.permittedTypes == null ||
typeDecl.permittedTypes.length == 0 || typeDecl.permittedTypes[0].isImplicit())) {
connectImplicitPermittedTypes(sourceType);
}
ReferenceBinding[] memberTypes = sourceType.memberTypes;
if (memberTypes != null && memberTypes != Binding.NO_MEMBER_TYPES) {
for (ReferenceBinding memberType : memberTypes)
((SourceTypeBinding) memberType).scope.connectImplicitPermittedTypes();
}
}

void connectPermittedTypes() {
SourceTypeBinding sourceType = this.referenceContext.binding;
sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES);
if (sourceType.id == TypeIds.T_JavaLangObject || sourceType.isEnum()) // already handled
return;

if (this.referenceContext.permittedTypes != null) {
if (this.referenceContext.permittedTypes != null && !this.referenceContext.permittedTypes[0].isImplicit()) {
sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES);
try {
sourceType.tagBits |= TagBits.SealingTypeHierarchy;
int length = this.referenceContext.permittedTypes.length;
Expand Down Expand Up @@ -1340,6 +1281,15 @@ void connectPermittedTypes() {
} finally {
sourceType.tagBits &= ~TagBits.SealingTypeHierarchy;
}
} else {
ReferenceBinding[] permittedTypes = sourceType.permittedTypes();
if (permittedTypes == null || permittedTypes.length == 0) {
sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES);
if (sourceType.isSealed()) {
if (!sourceType.isLocalType() && !sourceType.isRecord() && !sourceType.isEnum()) // error flagged alread
problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext);
}
}
}
ReferenceBinding[] memberTypes = sourceType.memberTypes;
if (memberTypes != null && memberTypes != Binding.NO_MEMBER_TYPES) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,8 +426,6 @@ void sealTypeHierarchy() {
for (SourceTypeBinding sourceType : this.topLevelTypes) {
sourceType.scope.connectPermittedTypes();
}
for (SourceTypeBinding topLevelType : this.topLevelTypes)
topLevelType.scope.connectImplicitPermittedTypes();
}
void integrateAnnotationsInHierarchy() {
// Only now that all hierarchy information is built we're ready for ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition;
Expand Down Expand Up @@ -1277,40 +1276,6 @@ private void reportSealedSuperTypeDoesNotPermitProblem(TypeReference superTypeRe
}
}

public List<SourceTypeBinding> collectAllTypeBindings(TypeDeclaration typeDecl, CompilationUnitScope unitScope) {
class TypeBindingsCollector extends ASTVisitor {
List<SourceTypeBinding> types = new ArrayList<>();
@Override
public boolean visit(
TypeDeclaration localTypeDeclaration,
BlockScope scope1) {
checkAndAddBinding(localTypeDeclaration.binding);
return true;
}
@Override
public boolean visit(
TypeDeclaration memberTypeDeclaration,
ClassScope scope1) {
checkAndAddBinding(memberTypeDeclaration.binding);
return true;
}
@Override
public boolean visit(
TypeDeclaration typeDeclaration,
CompilationUnitScope scope1) {
checkAndAddBinding(typeDeclaration.binding);
return true; // do nothing by default, keep traversing
}
private void checkAndAddBinding(SourceTypeBinding stb) {
if (stb != null)
this.types.add(stb);
}
}
TypeBindingsCollector typeCollector = new TypeBindingsCollector();
typeDecl.traverse(typeCollector, unitScope);
return typeCollector.types;
}

private boolean checkPermitsAndAdd(ReferenceBinding superType) {
if (superType == null
|| superType.equals(this.scope.getJavaLangObject())
Expand Down Expand Up @@ -3263,9 +3228,31 @@ public ReferenceBinding setSuperClass(ReferenceBinding superClass) {
annotatedType.superclass = superClass;
}
}
if (superClass != null && superClass.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed()
&& (sourceSuperType.scope.referenceContext.permittedTypes == null || sourceSuperType.scope.referenceContext.permittedTypes[0].isImplicit())) {
sourceSuperType.setImplicitPermittedType(this);
if (this.isAnonymousType() && superClass.isEnum())
this.modifiers |= ClassFileConstants.AccFinal;
}
return this.superclass = superClass;
}

private void setImplicitPermittedType(SourceTypeBinding permittedType) {
ReferenceBinding[] typesPermitted = this.permittedTypes();
int sz = typesPermitted == null ? 0 : typesPermitted.length;
if (this.scope.referenceCompilationUnit() == permittedType.scope.referenceCompilationUnit()) {
if (sz == 0) {
typesPermitted = new ReferenceBinding[] { permittedType };
} else {
System.arraycopy(typesPermitted, 0, typesPermitted = new ReferenceBinding[sz + 1], 0, sz);
typesPermitted[sz] = permittedType;
}
this.setPermittedTypes(typesPermitted);
} else if (sz == 0) {
this.setPermittedTypes(Binding.NO_PERMITTED_TYPES);
}
}

// Propagate writes to all annotated variants so the clones evolve along.
public ReferenceBinding [] setSuperInterfaces(ReferenceBinding [] superInterfaces) {

Expand All @@ -3279,6 +3266,13 @@ public ReferenceBinding setSuperClass(ReferenceBinding superClass) {
annotatedType.superInterfaces = superInterfaces;
}
}
for (int i = 0, length = superInterfaces == null ? 0 : superInterfaces.length; i < length; i++) {
ReferenceBinding superInterface = superInterfaces[i];
if (superInterface.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed()
&& (sourceSuperType.scope.referenceContext.permittedTypes == null || sourceSuperType.scope.referenceContext.permittedTypes[0].isImplicit())) {
sourceSuperType.setImplicitPermittedType(this);
}
}
return this.superInterfaces = superInterfaces;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1579,8 +1579,8 @@ public void testBug564498_2() throws IOException, ClassFormatException {
"");
String expectedOutput =
"PermittedSubclasses:\n" +
" #22 p1/A$Z,\n" +
" #24 p1/A$SubY\n" +
" #22 p1/A$SubY,\n" +
" #24 p1/A$Z\n" +
"}";
verifyClassFile(expectedOutput, "p1/A$Y.class", ClassFileBytesDisassembler.SYSTEM);
expectedOutput =
Expand All @@ -1607,9 +1607,9 @@ public void testBug564498_3() throws IOException, ClassFormatException {
"");
String expectedOutput =
"PermittedSubclasses:\n" +
" #24 p1/A$Y$SubInnerY,\n" +
" #26 p1/A$Z,\n" +
" #28 p1/A$SubY\n";
" #24 p1/A$SubY,\n" +
" #26 p1/A$Y$SubInnerY,\n" +
" #28 p1/A$Z\n";
verifyClassFile(expectedOutput, "p1/A$Y.class", ClassFileBytesDisassembler.SYSTEM);
}
public void testBug564498_4() throws IOException, ClassFormatException {
Expand Down

0 comments on commit d1a55f5

Please sign in to comment.