You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2023/10/25 16:00:12 UTC
[groovy] branch GROOVY_4_0_X updated: GROOVY-10750, GROOVY-11206: class header constant inlining
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new 5c7be2ff9b GROOVY-10750, GROOVY-11206: class header constant inlining
5c7be2ff9b is described below
commit 5c7be2ff9b42e2d3c6492e0a3db7506e77d70ed1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Oct 25 09:51:29 2023 -0500
GROOVY-10750, GROOVY-11206: class header constant inlining
4_0_X backport
---
.../codehaus/groovy/control/ResolveVisitor.java | 58 ++--
src/test/gls/annotations/AnnotationTest.groovy | 61 +++-
.../groovy/classgen/asm/TypeAnnotationsTest.groovy | 327 ++++++++++-----------
3 files changed, 240 insertions(+), 206 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 70c1c35a09..2cbeeb3731 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -263,13 +263,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
return source;
}
- @Override
- public void visitMethod(final MethodNode node) {
- super.visitMethod(node);
- visitGenericsTypeAnnotations(node);
- visitTypeAnnotations(node.getReturnType());
- }
-
@Override
protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
VariableScope oldScope = currentScope;
@@ -334,7 +327,9 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
}
private void resolveOrFail(final ClassNode type, final String msg, final ASTNode node, final boolean preferImports) {
- visitTypeAnnotations(type);
+ if (type.isRedirectNode() || !type.isPrimaryClassNode()) {
+ visitTypeAnnotations(type); // JSR 308 support
+ }
if (preferImports) {
resolveGenericsTypes(type.getGenericsTypes());
if (resolveAliasFromModule(type)) return;
@@ -997,13 +992,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
visitAnnotations(ve);
Variable v = ve.getAccessedVariable();
- if(!(v instanceof DynamicVariable) && !checkingVariableTypeInDeclaration) {
- /*
- * GROOVY-4009: when a normal variable is simply being used, there is no need to try to
- * resolve its type. Variable type resolve should proceed only if the variable is being declared.
- */
- return ve;
- }
if (v instanceof DynamicVariable) {
String name = ve.getName();
ClassNode t = ClassHelper.make(name);
@@ -1029,8 +1017,14 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
for (VariableScope scope = currentScope; scope != null && !scope.isRoot(); scope = scope.getParent()) {
if (scope.removeReferencedClassVariable(ve.getName()) == null) break;
}
- return new ClassExpression(t);
+ ClassExpression ce = new ClassExpression(t);
+ ce.setSourcePosition(ve);
+ return ce;
}
+ } else if (!checkingVariableTypeInDeclaration) {
+ // GROOVY-4009: When a normal variable is simply being used, there is no need to try to
+ // resolve its type. Variable type resolve should proceed only if the variable is being declared.
+ return ve;
}
resolveOrFail(ve.getType(), ve);
ClassNode origin = ve.getOriginType();
@@ -1189,27 +1183,6 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
private void visitTypeAnnotations(final ClassNode node) {
visitAnnotations(node.getTypeAnnotations());
- visitGenericsTypeAnnotations(node);
- }
-
- private void visitGenericsTypeAnnotations(final ClassNode node) {
- GenericsType[] genericsTypes = node.getGenericsTypes();
- if (node.isUsingGenerics() && genericsTypes != null) {
- visitGenericsTypeAnnotations(genericsTypes);
- }
- }
-
- private void visitGenericsTypeAnnotations(final MethodNode node) {
- GenericsType[] genericsTypes = node.getGenericsTypes();
- if (genericsTypes != null) {
- visitGenericsTypeAnnotations(genericsTypes);
- }
- }
-
- private void visitGenericsTypeAnnotations(final GenericsType[] genericsTypes) {
- for (GenericsType gt : genericsTypes) {
- visitTypeAnnotations(gt.getType());
- }
}
@Override
@@ -1330,8 +1303,12 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
// initialize scopes/variables now that imports and super types are resolved
new VariableScopeVisitor(source).visitClass(node);
- visitTypeAnnotations(node);
- super.visitClass(node);
+ visitPackage(node.getPackage());
+ visitImports(node.getModule());
+
+ node.visitContents(this);
+ visitObjectInitializerStatements(node);
+ visitAnnotations(node); // GROOVY-10750, GROOVY-11206
}
currentClass = oldNode;
}
@@ -1416,6 +1393,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
String name = type.getName();
ClassNode typeType = type.getType();
+ visitTypeAnnotations(typeType); // JSR 308 support
GenericsTypeName gtn = new GenericsTypeName(name);
boolean isWildcardGT = QUESTION_MARK.equals(name);
boolean dealWithGenerics = (level == 0 || (level > 0 && genericParameterNames.get(gtn) != null));
@@ -1466,7 +1444,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
if (genericsType.isResolved()) return true;
currentClass.setUsingGenerics(true);
ClassNode type = genericsType.getType();
- visitTypeAnnotations(type); // JSR-308 support
+ visitTypeAnnotations(type); // JSR 308 support
GenericsType tp = genericParameterNames.get(new GenericsTypeName(type.getName()));
if (tp != null) {
ClassNode[] bounds = tp.getUpperBounds();
diff --git a/src/test/gls/annotations/AnnotationTest.groovy b/src/test/gls/annotations/AnnotationTest.groovy
index cd4e49a871..a23e1da172 100644
--- a/src/test/gls/annotations/AnnotationTest.groovy
+++ b/src/test/gls/annotations/AnnotationTest.groovy
@@ -664,7 +664,7 @@ final class AnnotationTest {
}
// GROOVY-10750
- @NotYetImplemented @Test
+ @Test
void testAttributeValueConstants10() {
assertScript shell, '''
@Retention(RUNTIME)
@@ -687,9 +687,29 @@ final class AnnotationTest {
'''
}
- // GROOVY-11207
+ // GROOVY-11206
@Test
void testAttributeValueConstants11() {
+ assertScript shell, '''
+ @Retention(RUNTIME)
+ @interface Foo {
+ String value()
+ }
+ @groovy.transform.CompileStatic
+ @Foo(BarController.SOME_URL)
+ class BarController {
+ public static final String BASE_URL = '/bars'
+ public static final String SOME_URL = BASE_URL + '/{barId}/bazs'
+ }
+
+ def foo = BarController.getAnnotation(Foo)
+ assert foo.value() == '/bars/{barId}/bazs'
+ '''
+ }
+
+ // GROOVY-11207
+ @Test
+ void testAttributeValueConstants12() {
assertScript shell, '''
@Retention(RUNTIME)
@interface Foo {
@@ -998,6 +1018,43 @@ final class AnnotationTest {
'''
}
+ // GROOVY-11178
+ @NotYetImplemented @Test
+ void testAnnotationOnConstructorType() {
+ assertScript shell, '''package p
+ @Target(TYPE_USE)
+ @interface Tag {}
+
+ @groovy.transform.ASTTest(phase=CLASS_GENERATION, value={
+ def cce = node.rightExpression
+ assert cce.type.typeAnnotations.size() == 1
+ assert cce.type.typeAnnotations[0].classNode.name == 'p.Tag'
+ })
+ Object o = new @Tag Object()
+ '''
+
+ def err = shouldFail shell, '''\
+ @Target(PARAMETER)
+ @interface Tag {}
+
+ Object o = new @Tag Object()
+ '''
+ assert err =~ /Annotation @Tag is not allowed on element TYPE/
+ }
+
+ // GROOVY-9155
+ @Test
+ void testAnnotationOnTypeArgumentType() {
+ def err = shouldFail shell, '''package p
+ @Target(TYPE_USE)
+ @interface Tag {}
+
+ def m(List<@Tag(foo="") String> strings) {
+ }
+ '''
+ assert err =~ /'foo' is not part of the annotation Tag in @p.Tag/
+ }
+
// GROOVY-8234
@Test
void testAnnotationWithRepeatableSupported() {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
index 6e806ae38b..4d3ff7fe2b 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy
@@ -18,28 +18,72 @@
*/
package org.codehaus.groovy.classgen.asm
-class TypeAnnotationsTest extends AbstractBytecodeTestCase {
+final class TypeAnnotationsTest extends AbstractBytecodeTestCase {
+
+ private final String imports = '''\
+ |import java.lang.annotation.*
+ |import static java.lang.annotation.ElementType.*
+ |import static java.lang.annotation.RetentionPolicy.*
+ |'''.stripMargin()
+
+ void testTypeAnnotationsForConstructor1() {
+ def bytecode = compile(classNamePattern: 'HasConstructor', method: '<init>', imports + '''
+ @Retention(RUNTIME) @Target(CONSTRUCTOR) @interface CtorAnno { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+
+ class HasConstructor {
+ @CtorAnno @TypeAnno0 @TypeAnno1 HasConstructor() { }
+ }
+ ''')
+ assert bytecode.hasSequence([
+ 'public <init>()V',
+ '@LCtorAnno;()',
+ '@LTypeAnno0;() : METHOD_RETURN',
+ '@LTypeAnno1;() : METHOD_RETURN'
+ ])
+ }
+
+ void testTypeAnnotationsForConstructor2() {
+ def bytecode = compile(classNamePattern: 'Foo.Bar', method: '<init>', imports + '''
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+
+ class Foo {
+ class Bar {
+ @TypeAnno0
+ Bar(@TypeAnno1 that) {
+ }
+ }
+ }
+ ''')
+ assert bytecode.hasStrictSequence([
+ 'public <init>(LFoo;Ljava/lang/Object;)V',
+ //'@LTypeAnno0;() : METHOD_RETURN', TODO
+ '@LTypeAnno1;() : METHOD_FORMAL_PARAMETER 1, null', // TODO: 0
+ 'L0'
+ ])
+ }
+
void testTypeAnnotationsForMethod1() {
- assert compile(method: 'foo','''
- import java.lang.annotation.*
- import static java.lang.annotation.RetentionPolicy.RUNTIME
- import static java.lang.annotation.ElementType.*
-
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno8 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno9 { }
-
- <@TypeAnno0 X extends @TypeAnno1 Number> @TypeAnno2 List<@TypeAnno3 X> foo(
- @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Map<@TypeAnno7 X, @TypeAnno8 ?>> arg
- ) throws @TypeAnno9 Exception {}
- ''').hasSequence([
+ def bytecode = compile(method: 'foo', imports + '''
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno2 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno3 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno7 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno8 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno9 { }
+
+ <@TypeAnno0 X extends @TypeAnno1 Number> @TypeAnno2 List<@TypeAnno3 X> foo(
+ @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Map<@TypeAnno7 X, @TypeAnno8 ?>> arg
+ ) throws @TypeAnno9 Exception {
+ }
+ ''')
+ assert bytecode.hasSequence([
'public foo(Ljava/util/List;)Ljava/util/List;',
'@LTypeAnno0;() : METHOD_TYPE_PARAMETER 0, null',
'@LTypeAnno1;() : METHOD_TYPE_PARAMETER_BOUND 0, 0, null',
@@ -55,30 +99,27 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
}
void testTypeAnnotationsForMethod2() {
- assert compile(method: 'foo','''
- import java.lang.annotation.*
- import static java.lang.annotation.RetentionPolicy.RUNTIME
- import static java.lang.annotation.ElementType.*
-
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeParameterAnno { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
-
- <@TypeAnno0 @TypeParameterAnno E extends @TypeAnno1 Number & @TypeAnno2 List<@TypeAnno3 E>> void foo(
- @TypeAnno4 E arg1, @TypeAnno5 int arg2, @TypeAnno6 int arg3
- ) {
- // TODO support in code examples below
- try {
- numbers.addAll(new @TypeAnno5 ArrayList<@TypeAnno6 Integer>());
- } catch (@TypeAnno7 Throwable ignore) {
+ def bytecode = compile(method: 'foo', imports + '''
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeParameterAnno { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno2 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno3 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+
+ <@TypeAnno0 @TypeParameterAnno E extends @TypeAnno1 Number & @TypeAnno2 List<@TypeAnno3 E>> void foo(
+ @TypeAnno4 E arg1, @TypeAnno5 int arg2, @TypeAnno6 int arg3
+ ) {
+ // TODO support in code examples below
+ try {
+ numbers.addAll(new @TypeAnno5 ArrayList<@TypeAnno6 Integer>());
+ } catch (@TypeAnno7 Throwable ignore) {
+ }
}
- }
- ''').hasSequence([
+ ''')
+ assert bytecode.hasSequence([
'public foo(Ljava/lang/Number;II)V',
'@LTypeAnno0;() : METHOD_TYPE_PARAMETER 0, null',
'@LTypeParameterAnno;() : METHOD_TYPE_PARAMETER 0, null',
@@ -92,39 +133,36 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
}
void testTypeAnnotationsForMethod3() {
- assert compile(method: 'get', classNamePattern: 'Foo','''
- import java.lang.annotation.*
- import static java.lang.annotation.RetentionPolicy.RUNTIME
- import static java.lang.annotation.ElementType.*
-
- @Retention(RUNTIME) @Target([PARAMETER]) @interface ParameterAnno { }
- @Retention(RUNTIME) @Target([TYPE_PARAMETER, TYPE_USE]) @interface TypeUseAndParameterAnno { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno8 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno9 { }
-
- class Foo<X, Y> {
- @TypeAnno0 Map<@TypeAnno1 ? extends X, ? super @TypeAnno2 Y>
- get(@TypeAnno3 @ParameterAnno @TypeUseAndParameterAnno List<@TypeAnno4 List<@TypeAnno5 X>> arg1,
- Map<@TypeAnno6 ? extends @TypeAnno7 X, @TypeAnno8 ? super @TypeAnno9 Y> arg2
- ) throws @TypeAnno0 RuntimeException, @TypeAnno1 IOException {
- // TODO support in code example below
- @TypeAnno0 List<@TypeAnno1 Integer> numbers = Arrays.<@TypeAnno3 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18);
- if (arg2 instanceof @TypeAnno2 Number) {
- return (@TypeAnno3 Map<@TypeAnno4 ? extends X, ? super @TypeAnno5 Y>) null;
+ def bytecode = compile(classNamePattern: 'Foo', method: 'get', imports + '''
+ @Retention(RUNTIME) @Target(PARAMETER) @interface ParameterAnno { }
+ @Retention(RUNTIME) @Target([TYPE_PARAMETER, TYPE_USE]) @interface TypeUseAndParameterAnno { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno2 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno3 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno7 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno8 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno9 { }
+
+ class Foo<X, Y> {
+ @TypeAnno0 Map<@TypeAnno1 ? extends X, ? super @TypeAnno2 Y>
+ get(@TypeAnno3 @ParameterAnno @TypeUseAndParameterAnno List<@TypeAnno4 List<@TypeAnno5 X>> arg1,
+ Map<@TypeAnno6 ? extends @TypeAnno7 X, @TypeAnno8 ? super @TypeAnno9 Y> arg2
+ ) throws @TypeAnno0 RuntimeException, @TypeAnno1 IOException {
+ // TODO support in code example below
+ @TypeAnno0 List<@TypeAnno1 Integer> numbers = Arrays.<@TypeAnno3 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18);
+ if (arg2 instanceof @TypeAnno2 Number) {
+ return (@TypeAnno3 Map<@TypeAnno4 ? extends X, ? super @TypeAnno5 Y>) null;
+ }
+ //if (numbers.stream().sorted(@TypeAnno6 Integer::compareTo).count() > 0) return null; // needs grammar tweak
+ return arg2;
}
- //if (numbers.stream().sorted(@TypeAnno6 Integer::compareTo).count() > 0) return null; // needs grammar tweak
- return arg2;
}
- }
- ''').hasSequence([
+ ''')
+ assert bytecode.hasSequence([
'public get(Ljava/util/List;Ljava/util/Map;)Ljava/util/Map; throws java/lang/RuntimeException',
'@LTypeAnno0;() : METHOD_RETURN, null',
'@LTypeAnno1;() : METHOD_RETURN, 0;',
@@ -142,44 +180,48 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
])
}
- void testTypeAnnotationsForConstructor() {
- assert compile(method: '<init>', classNamePattern: 'HasCons', '''
- import java.lang.annotation.*
- import static java.lang.annotation.RetentionPolicy.RUNTIME
- import static java.lang.annotation.ElementType.*
-
- @Retention(RUNTIME) @Target([CONSTRUCTOR]) @interface ConstructorAnno { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
-
- class HasCons {
- @ConstructorAnno @TypeAnno0 @TypeAnno1 HasCons() { }
- }
- ''').hasSequence([
- 'public <init>()V',
- '@LConstructorAnno;()',
- '@LTypeAnno0;() : METHOD_RETURN',
- '@LTypeAnno1;() : METHOD_RETURN'
+ // GROOVY-9154
+ void testTypeAnnotationsForMethod4() {
+ def bytecode = compile(classNamePattern: 'Foo', method: 'sizeZeroOrPositive', '''\
+ @Grab("net.jqwik:jqwik:1.1.4")
+ import net.jqwik.api.ForAll
+ import net.jqwik.api.Property
+ import net.jqwik.api.constraints.IntRange
+
+ @groovy.transform.CompileStatic
+ class Foo {
+ @Property
+ boolean sizeZeroOrPositive(@ForAll List<@IntRange(min=0, max=10) Integer> items) {
+ items.size() >= 0
+ }
+ }
+ ''')
+ assert bytecode.hasSequence([
+ 'public sizeZeroOrPositive(Ljava/util/List;)Z',
+ '@Lnet/jqwik/api/Property;()',
+ '@Lnet/jqwik/api/constraints/IntRange;(min=0, max=10) : METHOD_FORMAL_PARAMETER 0, 0;',
+ '// annotable parameter count: 1 (visible)',
+ '@Lnet/jqwik/api/ForAll;() // parameter 0'
])
}
void testTypeAnnotationsForField1() {
- assert compile(field: 'documents', classNamePattern: 'Foo', '''
- import java.lang.annotation.*
- import static java.lang.annotation.RetentionPolicy.RUNTIME
- import static java.lang.annotation.ElementType.*
-
- @Retention(RUNTIME) @Target([FIELD]) @interface FieldAnno { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno0 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno1 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno2 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno3 { }
-
- class Foo {
- public @FieldAnno Map<@TypeAnno0 ? extends @TypeAnno1 CharSequence, @TypeAnno2 List<@TypeAnno3 ?>> documents = null
- }
- ''').hasSequence([
- 'public Ljava/util/Map; documents',
+ def bytecode = compile(classNamePattern: 'Foo', field: 'foo', imports + '''
+ @Retention(RUNTIME) @Target(FIELD) @interface FieldAnno { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno0 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno1 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno2 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno3 { }
+
+ class Foo {
+ public @FieldAnno Map<
+ @TypeAnno0 ? extends @TypeAnno1 CharSequence,
+ @TypeAnno2 List<@TypeAnno3 ?>
+ > foo
+ }
+ ''')
+ assert bytecode.hasSequence([
+ 'public Ljava/util/Map; foo',
'@LFieldAnno;()',
'@LTypeAnno0;() : FIELD, 0;',
'@LTypeAnno1;() : FIELD, 0;*',
@@ -189,65 +231,22 @@ class TypeAnnotationsTest extends AbstractBytecodeTestCase {
}
void testTypeAnnotationsForField2() {
- assert compile(field: 'numbers', classNamePattern: 'Bar','''
- import java.lang.annotation.*
- import static java.lang.annotation.RetentionPolicy.RUNTIME
- import static java.lang.annotation.ElementType.*
-
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno4 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno5 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno6 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeAnno7 { }
-
- class Bar {
- // TODO support type annotations for code (TypeAnno7 below)
- public @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Integer> numbers = Arrays.<@TypeAnno7 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18)
- }
- ''').hasSequence([
+ def bytecode = compile(classNamePattern: 'Bar', field: 'numbers', imports + '''
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno4 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno5 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno6 { }
+ @Retention(RUNTIME) @Target(TYPE_USE) @interface TypeAnno7 { }
+
+ class Bar {
+ // TODO support type annotations for code (TypeAnno7 below)
+ public @TypeAnno4 List<@TypeAnno5 ? super @TypeAnno6 Integer> numbers = Arrays.<@TypeAnno7 Integer>asList(5, 3, 50, 24, 40, 2, 9, 18)
+ }
+ ''')
+ assert bytecode.hasSequence([
'public Ljava/util/List; numbers',
'@LTypeAnno4;() : FIELD, null',
'@LTypeAnno5;() : FIELD, 0;',
'@LTypeAnno6;() : FIELD, 0;*'
])
}
-
- void testTypeAnnotationsForClass() {
- assert compile(classNamePattern: 'MyClass','''
- import java.lang.annotation.*
- import java.rmi.Remote
- import static java.lang.annotation.RetentionPolicy.RUNTIME
- import static java.lang.annotation.ElementType.*
-
- @Retention(RUNTIME) @Target([TYPE]) @interface TypeAnno { }
- @Retention(RUNTIME) @Target([TYPE_PARAMETER]) @interface TypeParameterAnno1 { }
- @Retention(RUNTIME) @Target([TYPE_PARAMETER]) @interface TypeParameterAnno2 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno0 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno1 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno2 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno3 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno4 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno5 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno6 { }
- @Retention(RUNTIME) @Target([TYPE_USE]) @interface TypeUseAnno7 { }
-
- @TypeAnno @TypeUseAnno0 @TypeUseAnno1
- class MyClass<@TypeParameterAnno1 @TypeParameterAnno2 X, @TypeParameterAnno2 Y extends @TypeUseAnno2 File>
- extends @TypeUseAnno3 ArrayList<@TypeUseAnno4 X>
- implements @TypeUseAnno5 Remote, @TypeUseAnno6 List<@TypeUseAnno7 X> { }
- ''').hasSequence([
- 'public class MyClass extends java/util/ArrayList implements java/rmi/Remote java/util/List groovy/lang/GroovyObject {',
- '@LTypeAnno;()',
- '@LTypeUseAnno0;()',
- '@LTypeUseAnno1;()',
- '@LTypeParameterAnno1;() : CLASS_TYPE_PARAMETER 0, null',
- '@LTypeParameterAnno2;() : CLASS_TYPE_PARAMETER 0, null',
- '@LTypeParameterAnno2;() : CLASS_TYPE_PARAMETER 1, null',
- '@LTypeUseAnno2;() : CLASS_TYPE_PARAMETER_BOUND 1, 0, null',
- '@LTypeUseAnno3;() : CLASS_EXTENDS -1, null',
- '@LTypeUseAnno4;() : CLASS_EXTENDS -1, 0;',
- '@LTypeUseAnno5;() : CLASS_EXTENDS 0, null',
- '@LTypeUseAnno6;() : CLASS_EXTENDS 1, null',
- '@LTypeUseAnno7;() : CLASS_EXTENDS 1, 0;'
- ])
- }
}