You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2024/01/20 15:39:22 UTC
(groovy) 01/01: GROOVY-11292: Class without sealed parent cannot be non-sealed
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch GROOVY-11292
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit c213f8e5ee73949057c7cbd8824991d70c41cbe5
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Jan 20 23:38:52 2024 +0800
GROOVY-11292: Class without sealed parent cannot be non-sealed
---
.github/workflows/groovy-build-test-main.yml | 2 +-
.../java/org/codehaus/groovy/ast/ClassNode.java | 12 ++++++
.../groovy/ast/decompiled/DecompiledClassNode.java | 7 ++++
.../groovy/classgen/ClassCompletionVerifier.java | 3 +-
src/test/groovy/bugs/Groovy11292.groovy | 48 ++++++++++++++++++++++
5 files changed, 69 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/groovy-build-test-main.yml b/.github/workflows/groovy-build-test-main.yml
index 5e342f8fec..10e379cff2 100644
--- a/.github/workflows/groovy-build-test-main.yml
+++ b/.github/workflows/groovy-build-test-main.yml
@@ -26,7 +26,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-22.04]
- java: [11, 17]
+ java: [11, 17, 21]
runs-on: ${{ matrix.os }}
env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }}
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index 0b726ef719..8832a62512 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.ast;
+import groovy.transform.NonSealed;
import org.apache.groovy.ast.tools.ClassNodeUtils;
import org.apache.groovy.lang.annotation.Incubating;
import org.codehaus.groovy.GroovyBugError;
@@ -47,6 +48,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import static java.lang.reflect.Modifier.isFinal;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
@@ -1333,6 +1335,16 @@ public class ClassNode extends AnnotatedNode {
return !getAnnotations(ClassHelper.SEALED_TYPE).isEmpty() || !getPermittedSubclasses().isEmpty();
}
+ @Incubating
+ public boolean isNonSealed() {
+ if (redirect != null) return redirect.isNonSealed();
+ if (Boolean.TRUE.equals(this.getNodeMetaData(NonSealed.class))) return true;
+
+ final ClassNode superClass = this.getSuperClass();
+ if (null == superClass) return false;
+ return !(isFinal(this.getModifiers()) || this.isSealed()) && superClass.isSealed();
+ }
+
public boolean isResolved() {
if (clazz != null) return true;
if (redirect != null) return redirect.isResolved();
diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java b/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java
index e919d090db..ea81fdc7dc 100644
--- a/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java
@@ -109,6 +109,13 @@ public class DecompiledClassNode extends ClassNode {
return false;
}
+ @Override
+ public boolean isNonSealed() {
+ Class<?> typeClass = getTypeClass();
+ return !(Modifier.isFinal(typeClass.getModifiers()) || ReflectionUtils.isSealed(typeClass))
+ && ReflectionUtils.isSealed(typeClass.getSuperclass());
+ }
+
@Override
public String setName(String name) {
throw new UnsupportedOperationException();
diff --git a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
index 870704af79..33dbafa944 100644
--- a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -18,7 +18,6 @@
*/
package org.codehaus.groovy.classgen;
-import groovy.transform.NonSealed;
import groovy.transform.Sealed;
import org.apache.groovy.ast.tools.AnnotatedNodeUtils;
import org.apache.groovy.ast.tools.ClassNodeUtils;
@@ -368,7 +367,7 @@ public class ClassCompletionVerifier extends ClassCodeVisitorSupport {
}
private boolean nonSealed(final ClassNode node) {
- return Boolean.TRUE.equals(node.getNodeMetaData(NonSealed.class));
+ return node.isNonSealed();
}
private void checkSealedParent(final ClassNode cn, final ClassNode parent) {
diff --git a/src/test/groovy/bugs/Groovy11292.groovy b/src/test/groovy/bugs/Groovy11292.groovy
new file mode 100644
index 0000000000..07e2606aac
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy11292.groovy
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package bugs
+
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy11292 {
+ @Test
+ void testClassWithoutSealedParent() {
+ assertScript '''
+ import org.codehaus.groovy.util.Finalizable
+ class TestReference<T>
+ extends java.lang.ref.SoftReference<T>
+ implements org.codehaus.groovy.util.Reference<T, Finalizable> {
+
+ final Finalizable handler
+
+ TestReference(T referent) {
+ super(referent)
+ }
+
+ @Override
+ Finalizable getHandler() {
+ return handler
+ }
+ }
+ assert new TestReference(null)
+ '''
+ }
+}