Bug 564047 - [15] JEP 360 - Sealed Types - BinaryTypeBinding
Change-Id: Idac6c2fa41b5a8637561745f094ffba1db6a25fd
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_15.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_15.java
new file mode 100644
index 0000000..5cffd46
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_15.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2018, 2020 IBM Corporation.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.compiler.regression;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.tests.util.Util;
+import junit.framework.Test;
+
+@SuppressWarnings({ "rawtypes" })
+public class BatchCompilerTest_15 extends AbstractBatchCompilerTest {
+
+ static {
+// TESTS_NAMES = new String[] { "test440477" };
+// TESTS_NUMBERS = new int[] { 306 };
+// TESTS_RANGE = new int[] { 298, -1 };
+ }
+
+ /**
+ * This test suite only needs to be run on one compliance.
+ * As it includes some specific 1.5 tests, it must be used with a least a 1.5 VM
+ * and not be duplicated in general test suite.
+ * @see TestAll
+ */
+ public static Test suite() {
+ return buildMinimalComplianceTestSuite(testClass(), F_15);
+ }
+ public static Class testClass() {
+ return BatchCompilerTest_15.class;
+ }
+ public BatchCompilerTest_15(String name) {
+ super(name);
+ }
+ public void testBug564047_001(){
+ String currentWorkingDirectoryPath = System.getProperty("user.dir");
+ if (currentWorkingDirectoryPath == null) {
+ System.err.println("BatchCompilerTest#testBug564047_001 could not access the current working directory " + currentWorkingDirectoryPath);
+ } else if (!new File(currentWorkingDirectoryPath).isDirectory()) {
+ System.err.println("BatchCompilerTest#testBug564047_001 current working directory is not a directory " + currentWorkingDirectoryPath);
+ } else {
+ String lib1Path = currentWorkingDirectoryPath + File.separator + "lib1.jar";
+ try {
+ Util.createJar(
+ new String[] {
+ "p/Y.java",
+ "package p;\n" +
+ "public sealed class Y permits Z{}",
+ "p/Z.java",
+ "package p;\n" +
+ "public final class Z extends Y{}",
+ },
+ lib1Path,
+ JavaCore.VERSION_15,
+ true);
+ this.runNegativeTest(
+ new String[] {
+ "src/p/X.java",
+ "package p;\n" +
+ "public class X extends Y {\n" +
+ " public static void main(String[] args){\n" +
+ " System.out.println(0);\n" +
+ " }\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "src/p/X.java\""
+ + " -cp " + lib1Path // relative
+ + " -sourcepath \"" + OUTPUT_DIR + File.separator + "src\""
+ + " --release 15 --enable-preview -g -preserveAllLocals"
+ + " -proceedOnError -referenceInfo"
+ + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 2)\n" +
+ " public class X extends Y {\n" +
+ " ^\n" +
+ "The class X with a sealed direct superclass or a sealed direct superinterface Y should be declared either final, sealed, or non-sealed\n" +
+ "----------\n" +
+ "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 2)\n" +
+ " public class X extends Y {\n" +
+ " ^\n" +
+ "The type X extending a sealed class Y should be a permitted subtype of Y\n" +
+ "----------\n" +
+ "2 problems (2 errors)\n",
+ true);
+ } catch (IOException e) {
+ System.err.println("BatchCompilerTest#testBug563430_001 could not write to current working directory " + currentWorkingDirectoryPath);
+ } finally {
+ new File(lib1Path).delete();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java
index 3e37a42..19ce0df 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypes15Tests.java
@@ -20,6 +20,8 @@
import java.io.IOException;
import java.util.Map;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.tests.util.Util;
import org.eclipse.jdt.core.util.ClassFileBytesDisassembler;
@@ -1353,4 +1355,54 @@
"The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" +
"----------\n");
}
+ public void testBug564047_001() throws CoreException, IOException {
+ String outputDirectory = Util.getOutputDirectory();
+ String lib1Path = outputDirectory + File.separator + "lib1.jar";
+ try {
+ Util.createJar(
+ new String[] {
+ "p/Y.java",
+ "package p;\n" +
+ "public sealed class Y permits Z{}",
+ "p/Z.java",
+ "package p;\n" +
+ "public final class Z extends Y{}",
+ },
+ lib1Path,
+ JavaCore.VERSION_15,
+ true);
+ String[] libs = getDefaultClassPaths();
+ int len = libs.length;
+ System.arraycopy(libs, 0, libs = new String[len+1], 0, len);
+ libs[len] = lib1Path;
+ this.runNegativeTest(
+ new String[] {
+ "src/p/X.java",
+ "package p;\n" +
+ "public class X extends Y {\n" +
+ " public static void main(String[] args){\n" +
+ " System.out.println(0);\n" +
+ " }\n" +
+ "}",
+ },
+ "----------\n" +
+ "1. ERROR in src\\p\\X.java (at line 2)\n" +
+ " public class X extends Y {\n" +
+ " ^\n" +
+ "The class X with a sealed direct superclass or a sealed direct superinterface Y should be declared either final, sealed, or non-sealed\n" +
+ "----------\n" +
+ "2. ERROR in src\\p\\X.java (at line 2)\n" +
+ " public class X extends Y {\n" +
+ " ^\n" +
+ "The type X extending a sealed class Y should be a permitted subtype of Y\n" +
+ "----------\n",
+ libs,
+ true);
+ } catch (IOException e) {
+ System.err.println("could not write to current working directory ");
+ } finally {
+ new File(lib1Path).delete();
+ }
+
+ }
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
index 0f01891..1925861 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java
@@ -1546,6 +1546,9 @@
protected IJavaProject createJava14Project(String name, String[] srcFolders) throws CoreException {
return createJava9ProjectWithJREAttributes(name, srcFolders, null, "14");
}
+ protected IJavaProject createJava15Project(String name, String[] srcFolders) throws CoreException {
+ return createJava9ProjectWithJREAttributes(name, srcFolders, null, "15");
+ }
protected IJavaProject createJava9ProjectWithJREAttributes(String name, String[] srcFolders, IClasspathAttribute[] attributes) throws CoreException {
return createJava9ProjectWithJREAttributes(name, srcFolders, attributes, "9");
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
index 5938323..2218fa4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
@@ -97,6 +101,7 @@
protected ReferenceBinding superclass;
protected ReferenceBinding enclosingType;
protected ReferenceBinding[] superInterfaces;
+ protected ReferenceBinding[] permittedSubtypes;
protected FieldBinding[] fields;
protected MethodBinding[] methods;
protected ReferenceBinding[] memberTypes;
@@ -261,6 +266,7 @@
this.superclass = prototype.superclass;
this.enclosingType = prototype.enclosingType;
this.superInterfaces = prototype.superInterfaces;
+ this.permittedSubtypes = prototype.permittedSubtypes;
this.fields = prototype.fields;
this.methods = prototype.methods;
this.memberTypes = prototype.memberTypes;
@@ -442,6 +448,7 @@
// and still want to use binaries passed that point (e.g. type hierarchy resolver, see bug 63748).
this.typeVariables = Binding.NO_TYPE_VARIABLES;
this.superInterfaces = Binding.NO_SUPERINTERFACES;
+ this.permittedSubtypes = Binding.NO_PERMITTEDTYPES;
// must retrieve member types in case superclass/interfaces need them
this.memberTypes = Binding.NO_MEMBER_TYPES;
@@ -526,6 +533,19 @@
this.tagBits |= TagBits.HasUnresolvedSuperinterfaces;
}
}
+
+ this.permittedSubtypes = Binding.NO_PERMITTEDTYPES;
+ char[][] permittedSubtypeNames = binaryType.getPermittedSubtypeNames();
+ if (permittedSubtypeNames != null) {
+ int size = permittedSubtypeNames.length;
+ if (size > 0) {
+ this.permittedSubtypes = new ReferenceBinding[size];
+ for (short i = 0; i < size; i++)
+ // attempt to find each superinterface if it exists in the cache (otherwise - resolve it when requested)
+ this.permittedSubtypes[i] = this.environment.getTypeFromConstantPoolName(permittedSubtypeNames[i], 0, -1, false, missingTypeNames, toplevelWalker.toSupertype(i, superclassName));
+ this.extendedTagBits |= ExtendedTagBits.HasUnresolvedPermittedSubtypes;
+ }
+ }
} else {
// attempt to find the superclass if it exists in the cache (otherwise - resolve it when requested)
this.superclass = (ReferenceBinding) this.environment.getTypeFromTypeSignature(wrapper, typeVars, this, missingTypeNames,
@@ -544,6 +564,20 @@
types.toArray(this.superInterfaces);
this.tagBits |= TagBits.HasUnresolvedSuperinterfaces;
}
+
+ this.permittedSubtypes = Binding.NO_PERMITTEDTYPES;
+ if (!wrapper.atEnd()) {
+ // attempt to find each permitted type if it exists in the cache (otherwise - resolve it when requested)
+ java.util.ArrayList types = new java.util.ArrayList(2);
+ short rank = 0;
+ do {
+ types.add(this.environment.getTypeFromTypeSignature(wrapper, typeVars, this, missingTypeNames, toplevelWalker.toSupertype(rank++, wrapper.peekFullType())));
+ } while (!wrapper.atEnd());
+ this.permittedSubtypes = new ReferenceBinding[types.size()];
+ types.toArray(this.permittedSubtypes);
+ this.extendedTagBits |= ExtendedTagBits.HasUnresolvedPermittedSubtypes;
+ }
+
}
boolean canUseNullTypeAnnotations = this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled && this.environment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_8;
if (canUseNullTypeAnnotations && this.externalAnnotationStatus.isPotentiallyUnannotatedLib()) {
@@ -556,6 +590,12 @@
break;
}
}
+ for (TypeBinding permsub : this.permittedSubtypes) {
+ if (permsub.hasNullTypeAnnotations()) {
+ this.externalAnnotationStatus = ExternalAnnotationStatus.TYPE_IS_ANNOTATED;
+ break;
+ }
+ }
}
}
@@ -1267,6 +1307,7 @@
refScope.recordTypeReference(this.superclass);
return this.superclass.getExactMethod(selector, argumentTypes, refScope);
}
+ // NOTE: not adding permitted types here since the search is up the hierarchy while permitted ones are down.
}
return null;
}
@@ -1630,6 +1671,12 @@
}
@Override
+public boolean isSealed() {
+ ReferenceBinding[] permittedSubTypes = permittedTypes();
+ return !(permittedSubTypes == null || permittedSubTypes == Binding.NO_PERMITTEDTYPES);
+}
+
+@Override
public ReferenceBinding containerAnnotationType() {
if (!isPrototype()) throw new IllegalStateException();
if (this.containerAnnotationType instanceof UnresolvedReferenceBinding) {
@@ -2199,6 +2246,16 @@
this.tagBits &= ~TagBits.HasUnresolvedSuperinterfaces;
return this.superInterfaces;
}
+//NOTE: permitted subtypes of binary types are resolved when needed
+@Override
+public ReferenceBinding[] permittedTypes() {
+
+ if (!isPrototype()) {
+ return this.permittedSubtypes = this.prototype.permittedTypes();
+ }
+ // Note: unlike for superinterfaces() hierarchy check not required here since these are subtypes
+ return this.permittedSubtypes;
+}
@Override
public TypeVariableBinding[] typeVariables() {
@@ -2267,6 +2324,19 @@
buffer.append("NULL SUPERINTERFACES"); //$NON-NLS-1$
}
+ if (this.permittedSubtypes != null) {
+ if (this.permittedSubtypes != Binding.NO_PERMITTEDTYPES) {
+ buffer.append("\n\tpermits : "); //$NON-NLS-1$
+ for (int i = 0, length = this.permittedSubtypes.length; i < length; i++) {
+ if (i > 0)
+ buffer.append(", "); //$NON-NLS-1$
+ buffer.append((this.permittedSubtypes[i] != null) ? this.permittedSubtypes[i].debugName() : "NULL TYPE"); //$NON-NLS-1$
+ }
+ }
+ } else {
+ buffer.append("NULL PERMITTEDSUBTYPES"); //$NON-NLS-1$
+ }
+
if (this.enclosingType != null) {
buffer.append("\n\tenclosing type : "); //$NON-NLS-1$
buffer.append(this.enclosingType.debugName());
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java
index 42a43d4..626530a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExtendedTagBits.java
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
@@ -18,5 +22,6 @@
public interface ExtendedTagBits {
int AreRecordComponentsComplete = ASTNode.Bit1;
+ int HasUnresolvedPermittedSubtypes = ASTNode.Bit2;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java
index 2ba5a78..1d1e1ff 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
@@ -38,6 +42,7 @@
this.modifiers = ClassFileConstants.AccPublic;
this.superclass = null; // will be fixed up using #setMissingSuperclass(...)
this.superInterfaces = Binding.NO_SUPERINTERFACES;
+ this.permittedSubtypes = Binding.NO_PERMITTEDTYPES;
this.typeVariables = Binding.NO_TYPE_VARIABLES;
this.memberTypes = Binding.NO_MEMBER_TYPES;
this.fields = Binding.NO_FIELDS;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 6fb6ec2..bcabd64 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -1523,7 +1523,7 @@
/**
* Answer true if the receiver has sealed modifier
*/
-public final boolean isSealed() {
+public boolean isSealed() {
return (this.modifiers & ExtraCompilerModifiers.AccSealed) != 0;
}