Repository: groovy
Updated Branches:
  refs/heads/GROOVY_2_5_X dafabb2eb -> 13522f887


http://git-wip-us.apache.org/repos/asf/groovy/blob/13522f88/pr617.patch
----------------------------------------------------------------------
diff --git a/pr617.patch b/pr617.patch
new file mode 100644
index 0000000..23f72a7
--- /dev/null
+++ b/pr617.patch
@@ -0,0 +1,293 @@
+From 2e36707b9446f8b48a85bee26f6ec746d56e8b95 Mon Sep 17 00:00:00 2001
+From: aalmiray <[email protected]>
+Date: Wed, 11 Oct 2017 16:34:27 +0200
+Subject: [PATCH 1/2] GROOVY-8352: add a @Generated annotation
+
+---
+ src/main/groovy/transform/Generated.java           | 36 ++++++++++
+ .../org/codehaus/groovy/classgen/Verifier.java     | 22 +++---
+ .../groovy/tools/javac/JavaStubGenerator.java      | 10 +--
+ .../groovy/transform/GeneratedTransformTest.groovy | 81 ++++++++++++++++++++++
+ 4 files changed, 136 insertions(+), 13 deletions(-)
+ create mode 100644 src/main/groovy/transform/Generated.java
+ create mode 100644 
src/test/org/codehaus/groovy/transform/GeneratedTransformTest.groovy
+
+diff --git a/src/main/groovy/transform/Generated.java 
b/src/main/groovy/transform/Generated.java
+new file mode 100644
+index 0000000000..f7ea40af6d
+--- /dev/null
++++ b/src/main/groovy/transform/Generated.java
+@@ -0,0 +1,36 @@
++/*
++ *  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 groovy.transform;
++
++import java.lang.annotation.ElementType;
++import java.lang.annotation.Retention;
++import java.lang.annotation.RetentionPolicy;
++import java.lang.annotation.Target;
++
++/**
++ * The Generated annotation is used to mark members that have been generated.
++ *
++ * @author Andres Almiray
++ * @author Jochen Theodorou
++ * @author Mark Hoffmann
++ */
++@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE, 
ElementType.FIELD})
++@Retention(RetentionPolicy.RUNTIME)
++public @interface Generated {
++}
+diff --git a/src/main/org/codehaus/groovy/classgen/Verifier.java 
b/src/main/org/codehaus/groovy/classgen/Verifier.java
+index b98e784577..f1251d7e7a 100644
+--- a/src/main/org/codehaus/groovy/classgen/Verifier.java
++++ b/src/main/org/codehaus/groovy/classgen/Verifier.java
+@@ -21,6 +21,7 @@
+ import groovy.lang.GroovyClassLoader;
+ import groovy.lang.GroovyObject;
+ import groovy.lang.MetaClass;
++import groovy.transform.Generated;
+ import org.codehaus.groovy.GroovyBugError;
+ import org.codehaus.groovy.ast.*;
+ import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+@@ -124,6 +125,8 @@
+             new Parameter(ClassHelper.METACLASS_TYPE, "mc")
+     };
+ 
++    private static final Class GENERATED_ANNOTATION = Generated.class;
++
+     private ClassNode classNode;
+     private MethodNode methodNode;
+ 
+@@ -384,6 +387,7 @@ public void visit(MethodVisitor mv) {
+     protected void addGroovyObjectInterfaceAndMethods(ClassNode node, final 
String classInternalName) {
+         if (!node.isDerivedFromGroovyObject()) 
node.addInterface(ClassHelper.make(GroovyObject.class));
+         FieldNode metaClassField = getMetaClassField(node);
++        AnnotationNode generatedAnnotation = new 
AnnotationNode(ClassHelper.make(GENERATED_ANNOTATION));
+ 
+         if (!node.hasMethod("getMetaClass", Parameter.EMPTY_ARRAY)) {
+             metaClassField = setMetaClassFieldIfNotExists(node, 
metaClassField);
+@@ -425,7 +429,7 @@ public void visit(MethodVisitor mv) {
+                             mv.visitInsn(ARETURN);
+                         }
+                     })
+-            );
++            ).addAnnotation(generatedAnnotation);
+         }
+ 
+         Parameter[] parameters = new Parameter[]{new 
Parameter(ClassHelper.METACLASS_TYPE, "mc")};
+@@ -459,7 +463,7 @@ public void visit(MethodVisitor mv) {
+                     ACC_PUBLIC, ClassHelper.VOID_TYPE,
+                     SET_METACLASS_PARAMS, ClassNode.EMPTY_ARRAY,
+                     setMetaClassCode
+-            );
++            ).addAnnotation(generatedAnnotation);
+         }
+ 
+         if (!node.hasMethod("invokeMethod", INVOKE_METHOD_PARAMS)) {
+@@ -485,7 +489,7 @@ public void visit(MethodVisitor mv) {
+                             mv.visitInsn(ARETURN);
+                         }
+                     })
+-            );
++            ).addAnnotation(generatedAnnotation);
+         }
+ 
+         if (!node.hasMethod("getProperty", GET_PROPERTY_PARAMS)) {
+@@ -505,7 +509,7 @@ public void visit(MethodVisitor mv) {
+                             mv.visitInsn(ARETURN);
+                         }
+                     })
+-            );
++            ).addAnnotation(generatedAnnotation);
+         }
+ 
+         if (!node.hasMethod("setProperty", SET_PROPERTY_PARAMS)) {
+@@ -526,7 +530,7 @@ public void visit(MethodVisitor mv) {
+                             mv.visitInsn(RETURN);
+                         }
+                     })
+-            );
++            ).addAnnotation(generatedAnnotation);
+         }
+     }
+ 
+@@ -535,12 +539,12 @@ public void visit(MethodVisitor mv) {
+      * call will either be made to ClassNode.addSyntheticMethod() or 
ClassNode.addMethod(). If a non-synthetic method
+      * is to be added the ACC_SYNTHETIC modifier is removed if it has been 
accidentally supplied.
+      */
+-    protected void addMethod(ClassNode node, boolean shouldBeSynthetic, 
String name, int modifiers, ClassNode returnType, Parameter[] parameters,
+-                             ClassNode[] exceptions, Statement code) {
++    protected MethodNode addMethod(ClassNode node, boolean shouldBeSynthetic, 
String name, int modifiers, ClassNode returnType, Parameter[] parameters,
++                                   ClassNode[] exceptions, Statement code) {
+         if (shouldBeSynthetic) {
+-            node.addSyntheticMethod(name, modifiers, returnType, parameters, 
exceptions, code);
++            return node.addSyntheticMethod(name, modifiers, returnType, 
parameters, exceptions, code);
+         } else {
+-            node.addMethod(name, modifiers & ~ACC_SYNTHETIC, returnType, 
parameters, exceptions, code);
++            return node.addMethod(name, modifiers & ~ACC_SYNTHETIC, 
returnType, parameters, exceptions, code);
+         }
+     }
+ 
+diff --git a/src/main/org/codehaus/groovy/tools/javac/JavaStubGenerator.java 
b/src/main/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+index b672793c18..89d45aeba2 100644
+--- a/src/main/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
++++ b/src/main/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
+@@ -155,8 +155,8 @@ protected void addPropertyMethod(MethodNode method) {
+                     doAddMethod(method);
+                 }
+                 protected void addReturnIfNeeded(MethodNode node) {}
+-                protected void addMethod(ClassNode node, boolean 
shouldBeSynthetic, String name, int modifiers, ClassNode returnType, 
Parameter[] parameters, ClassNode[] exceptions, Statement code) {
+-                    doAddMethod(new MethodNode(name, modifiers, returnType, 
parameters, exceptions, code));
++                protected MethodNode addMethod(ClassNode node, boolean 
shouldBeSynthetic, String name, int modifiers, ClassNode returnType, 
Parameter[] parameters, ClassNode[] exceptions, Statement code) {
++                    return doAddMethod(new MethodNode(name, modifiers, 
returnType, parameters, exceptions, code));
+                 }
+ 
+                 protected void addConstructor(Parameter[] newParams, 
ConstructorNode ctor, Statement code, ClassNode node) {
+@@ -184,13 +184,15 @@ protected void addDefaultParameters(DefaultArgsAction 
action, MethodNode method)
+                     }
+                 }
+ 
+-                private void doAddMethod(MethodNode method) {
++                private MethodNode doAddMethod(MethodNode method) {
+                     String sig = method.getTypeDescriptor();
+ 
+-                    if (propertyMethodsWithSigs.containsKey(sig)) return;
++                    if (propertyMethodsWithSigs.containsKey(sig)) return 
method;
+ 
+                     propertyMethods.add(method);
+                     propertyMethodsWithSigs.put(sig, method);
++
++                    return method;
+                 }
+ 
+                 @Override
+diff --git 
a/src/test/org/codehaus/groovy/transform/GeneratedTransformTest.groovy 
b/src/test/org/codehaus/groovy/transform/GeneratedTransformTest.groovy
+new file mode 100644
+index 0000000000..34e9f0ea0c
+--- /dev/null
++++ b/src/test/org/codehaus/groovy/transform/GeneratedTransformTest.groovy
+@@ -0,0 +1,81 @@
++/*
++ *  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 org.codehaus.groovy.transform
++
++import org.junit.After
++import org.junit.Before
++import org.junit.Rule
++import org.junit.Test
++import org.junit.rules.TestName
++import org.junit.runner.RunWith
++import org.junit.runners.JUnit4
++
++import static org.junit.Assume.assumeTrue
++
++/**
++ * Tests for the @Generated annotation.
++ */
++@RunWith(JUnit4)
++class GeneratedTransformTest extends GroovyShellTestCase {
++    @Rule public TestName nameRule = new TestName()
++
++    @Before
++    void setUp() {
++        super.setUp()
++        // check java version requirements
++        def v = System.getProperty("java.specification.version")
++        assert v
++        assumeTrue('Test requires jre8+', 
nameRule.methodName.endsWith('_vm8').implies(new BigDecimal(v) >= 1.8))
++    }
++
++    @After
++    void tearDown() {
++        super.tearDown()
++    }
++
++    @Test
++    void testDefaultGroovyMethodsAreAnnotatedWithGenerated() {
++        def person = evaluate('''
++            class Person {
++                String name
++            }
++            new Person()
++        ''')
++
++        GroovyObject.declaredMethods.each { m ->
++            def method = person.class.declaredMethods.find { it.name == 
m.name }
++            assert method.annotations*.annotationType().name == 
['groovy.transform.Generated']
++        }
++    }
++
++    @Test
++    void testOverridenDefaultGroovyMethodsAreNotAnnotatedWithGenerated() {
++        def person = evaluate('''
++            class Person {
++                String name
++                
++                def invokeMethod(String name, args) { }
++            }
++            new Person()
++        ''')
++
++        def method = person.class.declaredMethods.find { it.name == 
'invokeMethod' }
++        assert !('groovy.transform.Generated' in 
method.annotations*.annotationType().name)
++    }
++}
+\ No newline at end of file
+
+From d2097c8cb1dc6080dc491d5fbae6b0732d697c23 Mon Sep 17 00:00:00 2001
+From: aalmiray <[email protected]>
+Date: Thu, 12 Oct 2017 10:07:07 +0200
+Subject: [PATCH 2/2] Remove author tags from javadoc
+
+---
+ src/main/groovy/transform/Generated.java | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/src/main/groovy/transform/Generated.java 
b/src/main/groovy/transform/Generated.java
+index f7ea40af6d..98eaf5b0df 100644
+--- a/src/main/groovy/transform/Generated.java
++++ b/src/main/groovy/transform/Generated.java
+@@ -25,10 +25,6 @@
+ 
+ /**
+  * The Generated annotation is used to mark members that have been generated.
+- *
+- * @author Andres Almiray
+- * @author Jochen Theodorou
+- * @author Mark Hoffmann
+  */
+ @Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE, 
ElementType.FIELD})
+ @Retention(RetentionPolicy.RUNTIME)

http://git-wip-us.apache.org/repos/asf/groovy/blob/13522f88/pr622.patch
----------------------------------------------------------------------
diff --git a/pr622.patch b/pr622.patch
new file mode 100644
index 0000000..1d3df05
--- /dev/null
+++ b/pr622.patch
@@ -0,0 +1,237 @@
+From 3839297520e325b8520d856bd2d74fc5ee63cced Mon Sep 17 00:00:00 2001
+From: paulk <[email protected]>
+Date: Mon, 30 Oct 2017 08:47:28 +1000
+Subject: [PATCH] GROOVY-8112: NPE in Groovy compiler when referencing @Field
+ in aic
+
+---
+ .../groovy/transform/FieldASTTransformation.java   | 104 +++++++++++++++++----
+ .../groovy/transform/FieldTransformTest.groovy     |  39 +++++++-
+ 2 files changed, 122 insertions(+), 21 deletions(-)
+
+diff --git 
a/src/main/org/codehaus/groovy/transform/FieldASTTransformation.java 
b/src/main/org/codehaus/groovy/transform/FieldASTTransformation.java
+index 52b4406b94..8e6b58e8b5 100644
+--- a/src/main/org/codehaus/groovy/transform/FieldASTTransformation.java
++++ b/src/main/org/codehaus/groovy/transform/FieldASTTransformation.java
+@@ -27,15 +27,19 @@
+ import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+ import org.codehaus.groovy.ast.ClassHelper;
+ import org.codehaus.groovy.ast.ClassNode;
++import org.codehaus.groovy.ast.ConstructorNode;
+ import org.codehaus.groovy.ast.FieldNode;
+ import org.codehaus.groovy.ast.MethodNode;
++import org.codehaus.groovy.ast.Parameter;
+ import org.codehaus.groovy.ast.Variable;
+ import org.codehaus.groovy.ast.VariableScope;
+-import org.codehaus.groovy.ast.expr.BinaryExpression;
++import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+ import org.codehaus.groovy.ast.expr.ClosureExpression;
+ import org.codehaus.groovy.ast.expr.ConstantExpression;
++import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+ import org.codehaus.groovy.ast.expr.DeclarationExpression;
+ import org.codehaus.groovy.ast.expr.Expression;
++import org.codehaus.groovy.ast.expr.TupleExpression;
+ import org.codehaus.groovy.ast.expr.VariableExpression;
+ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+ import org.codehaus.groovy.classgen.VariableScopeVisitor;
+@@ -44,6 +48,7 @@
+ import org.codehaus.groovy.runtime.MetaClassHelper;
+ import org.objectweb.asm.Opcodes;
+ 
++import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Iterator;
+ import java.util.List;
+@@ -59,9 +64,6 @@
+ 
+ /**
+  * Handles transformation for the @Field annotation.
+- *
+- * @author Paul King
+- * @author Cedric Champeau
+  */
+ @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+ public class FieldASTTransformation extends ClassCodeExpressionTransformer 
implements ASTTransformation, Opcodes {
+@@ -77,6 +79,7 @@
+     private String variableName;
+     private FieldNode fieldNode;
+     private ClosureExpression currentClosure;
++    private ConstructorCallExpression currentAIC;
+ 
+     public void visit(ASTNode[] nodes, SourceUnit source) {
+         sourceUnit = source;
+@@ -166,23 +169,76 @@ public Expression transform(Expression expr) {
+         } else if (insideScriptBody && expr instanceof VariableExpression && 
currentClosure != null) {
+             VariableExpression ve = (VariableExpression) expr;
+             if (ve.getName().equals(variableName)) {
+-                // we may only check the variable name because the Groovy 
compiler
+-                // already fails if a variable with the same name already 
exists in the scope.
+-                // this means that a closure cannot shadow a class variable
+-                ve.setAccessedVariable(fieldNode);
+-                final VariableScope variableScope = 
currentClosure.getVariableScope();
+-                final Iterator<Variable> iterator = 
variableScope.getReferencedLocalVariablesIterator();
+-                while (iterator.hasNext()) {
+-                    Variable next = iterator.next();
+-                    if (next.getName().equals(variableName)) 
iterator.remove();
+-                }
+-                variableScope.putReferencedClassVariable(fieldNode);
++                adjustToClassVar(ve);
+                 return ve;
+             }
++        } else if (currentAIC != null && expr instanceof 
ArgumentListExpression) {
++            // if a match is found, the compiler will have already set up aic 
constructor to hav
++            // an argument which isn't needed since we'll be accessing the 
field; we must undo it
++            Expression skip = null;
++            List<Expression> origArgList = ((ArgumentListExpression) 
expr).getExpressions();
++            for (int i = 0; i < origArgList.size(); i++) {
++                Expression arg = origArgList.get(i);
++                if (matchesCandidate(arg)) {
++                    skip = arg;
++                    adjustConstructorAndFields(i, currentAIC.getType());
++                    break;
++                }
++            }
++            if (skip != null) {
++                return adjustedArgList(skip, origArgList);
++            }
+         }
+         return expr.transformExpression(this);
+     }
+ 
++    private boolean matchesCandidate(Expression arg) {
++        return arg instanceof VariableExpression && ((VariableExpression) 
arg).getAccessedVariable() == 
candidate.getVariableExpression().getAccessedVariable();
++    }
++
++    private Expression adjustedArgList(Expression skip, List<Expression> 
origArgs) {
++        List<Expression> newArgs = new ArrayList<Expression>(origArgs.size() 
- 1);
++        for (Expression origArg : origArgs) {
++            if (skip != origArg) {
++                newArgs.add(origArg);
++            }
++        }
++        return new ArgumentListExpression(newArgs);
++    }
++
++    private void adjustConstructorAndFields(int skipIndex, ClassNode type) {
++        List<ConstructorNode> constructors = type.getDeclaredConstructors();
++        if (constructors.size() == 1) {
++            ConstructorNode constructor = constructors.get(0);
++            Parameter[] params = constructor.getParameters();
++            Parameter[] newParams = new Parameter[params.length - 1];
++            int to = 0;
++            for (int from = 0; from < params.length; from++) {
++                if (from != skipIndex) {
++                    newParams[to++] = params[from];
++                }
++            }
++            type.removeConstructor(constructor);
++            // code doesn't mention the removed param at this point, okay to 
leave as is
++            type.addConstructor(constructor.getModifiers(), newParams, 
constructor.getExceptions(), constructor.getCode());
++            type.removeField(variableName);
++        }
++    }
++
++    private void adjustToClassVar(VariableExpression expr) {
++        // we only need to check the variable name because the Groovy compiler
++        // already fails if a variable with the same name already exists in 
the scope.
++        // this means that a closure cannot shadow a class variable
++        expr.setAccessedVariable(fieldNode);
++        final VariableScope variableScope = currentClosure.getVariableScope();
++        final Iterator<Variable> iterator = 
variableScope.getReferencedLocalVariablesIterator();
++        while (iterator.hasNext()) {
++            Variable next = iterator.next();
++            if (next.getName().equals(variableName)) iterator.remove();
++        }
++        variableScope.putReferencedClassVariable(fieldNode);
++    }
++
+     @Override
+     public void visitClosureExpression(final ClosureExpression expression) {
+         ClosureExpression old = currentClosure;
+@@ -192,6 +248,20 @@ public void visitClosureExpression(final 
ClosureExpression expression) {
+     }
+ 
+     @Override
++    public void visitConstructorCallExpression(final 
ConstructorCallExpression cce) {
++        if (!insideScriptBody || !cce.isUsingAnonymousInnerClass()) return;
++        ConstructorCallExpression old = currentAIC;
++        currentAIC = cce;
++        Expression newArgs = transform(cce.getArguments());
++        if (cce.getArguments() instanceof TupleExpression && newArgs 
instanceof TupleExpression) {
++            List<Expression> argList = ((TupleExpression) 
cce.getArguments()).getExpressions();
++            argList.clear();
++            argList.addAll(((TupleExpression) newArgs).getExpressions());
++        }
++        currentAIC = old;
++    }
++
++    @Override
+     public void visitMethod(MethodNode node) {
+         Boolean oldInsideScriptBody = insideScriptBody;
+         if (node.isScriptBody()) insideScriptBody = true;
+@@ -202,9 +272,7 @@ public void visitMethod(MethodNode node) {
+     @Override
+     public void visitExpressionStatement(ExpressionStatement es) {
+         Expression exp = es.getExpression();
+-        if (exp instanceof BinaryExpression) {
+-            exp.visit(this);
+-        }
++        exp.visit(this);
+         super.visitExpressionStatement(es);
+     }
+ 
+diff --git a/src/test/org/codehaus/groovy/transform/FieldTransformTest.groovy 
b/src/test/org/codehaus/groovy/transform/FieldTransformTest.groovy
+index 2821f6e11c..6575720bfb 100644
+--- a/src/test/org/codehaus/groovy/transform/FieldTransformTest.groovy
++++ b/src/test/org/codehaus/groovy/transform/FieldTransformTest.groovy
+@@ -21,8 +21,7 @@ package org.codehaus.groovy.transform
+ import gls.CompilableTestSupport
+ 
+ /**
+- * @author Paul King
+- * @author C�dric Champeau
++ * Tests for the {@code @Field} transformation
+  */
+ class FieldTransformTest extends CompilableTestSupport {
+ 
+@@ -226,4 +225,38 @@ class FieldTransformTest extends CompilableTestSupport {
+             assert foo + bar + baz == 'foobarbaz'
+         '''
+     }
+-}
+\ No newline at end of file
++
++    void testAnonymousInnerClassReferencesToField() {
++        // GROOVY-8112
++        assertScript '''
++            @groovy.transform.Field
++            StringBuilder logger = new StringBuilder()
++            logger.append('a')
++            ['b'].each {
++                logger.append(it)
++                new Object() {
++                    String toString() {
++                        logger.append('c')
++                        ['d'].each { logger.append(it) }
++                    }
++                }.toString()
++            }
++            Closure c = { logger.append('e') }
++            c()
++            // control: worked previously, make sure we didn't break
++            def method() {
++                logger.append('f')
++                ['g'].each {
++                    logger.append(it)
++                    new Object() {
++                        String toString() {
++                            logger.append('h')
++                        }
++                    }.toString()
++                }
++            }
++            method()
++            assert logger.toString() == 'abcdefgh'
++        '''
++    }
++}

http://git-wip-us.apache.org/repos/asf/groovy/blob/13522f88/pr625.patch
----------------------------------------------------------------------
diff --git a/pr625.patch b/pr625.patch
new file mode 100644
index 0000000..4d0e8d2
--- /dev/null
+++ b/pr625.patch
@@ -0,0 +1,65 @@
+From 03fd268d814c491f1424f9fe8d8a0d30c5c333a5 Mon Sep 17 00:00:00 2001
+From: Shil Sinha <[email protected]>
+Date: Tue, 31 Oct 2017 13:29:19 -0400
+Subject: [PATCH] GROOVY-8369: Statically compiled property access on enum
+ class throws NoSuchFieldError
+
+---
+ .../classgen/asm/sc/StaticTypesCallSiteWriter.java      | 10 +++++-----
+ .../asm/sc/FieldsAndPropertiesStaticCompileTest.groovy  | 17 +++++++++++++++++
+ 2 files changed, 22 insertions(+), 5 deletions(-)
+
+diff --git 
a/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java 
b/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
+index 8d003ebc75..2eaa9348e6 100644
+--- 
a/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
++++ 
b/src/main/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
+@@ -148,11 +148,6 @@ public void makeGetPropertySite(Expression receiver, 
final String methodName, fi
+         }
+         if (makeGetPropertyWithGetter(receiver, receiverType, methodName, 
safe, implicitThis)) return;
+         if (makeGetField(receiver, receiverType, methodName, safe, 
implicitThis, samePackages(receiverType.getPackageName(), 
classNode.getPackageName()))) return;
+-        if (receiverType.isEnum()) {
+-            mv.visitFieldInsn(GETSTATIC, 
BytecodeHelper.getClassInternalName(receiverType), methodName, 
BytecodeHelper.getTypeDescription(receiverType));
+-            controller.getOperandStack().push(receiverType);
+-            return;
+-        }
+         if (receiver instanceof ClassExpression) {
+             if (makeGetField(receiver, receiver.getType(), methodName, safe, 
implicitThis, samePackages(receiver.getType().getPackageName(), 
classNode.getPackageName()))) return;
+             if (makeGetPropertyWithGetter(receiver, receiver.getType(), 
methodName, safe, implicitThis)) return;
+@@ -163,6 +158,11 @@ public void makeGetPropertySite(Expression receiver, 
final String methodName, fi
+             if (makeGetPropertyWithGetter(receiver, CLASS_Type, methodName, 
safe, implicitThis)) return;
+             if (makeGetField(receiver, CLASS_Type, methodName, safe, false, 
true)) return;
+         }
++        if (receiverType.isEnum()) {
++            mv.visitFieldInsn(GETSTATIC, 
BytecodeHelper.getClassInternalName(receiverType), methodName, 
BytecodeHelper.getTypeDescription(receiverType));
++            controller.getOperandStack().push(receiverType);
++            return;
++        }
+         if (makeGetPrivateFieldWithBridgeMethod(receiver, receiverType, 
methodName, safe, implicitThis)) return;
+ 
+         // GROOVY-5580, it is still possible that we're calling a 
superinterface property
+diff --git 
a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
 
b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
+index 6af5f135f1..2bf0a867bd 100644
+--- 
a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
++++ 
b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
+@@ -696,4 +696,21 @@ import 
org.codehaus.groovy.transform.sc.ListOfExpressionsExpression
+             assert astTrees['A$_closure1'][1].contains('INVOKESTATIC 
A.pfaccess$02 (LA;Ljava/lang/String;)Ljava/lang/String;')
+         }
+     }
++
++    //GROOVY-8369
++    void testPropertyAccessOnEnumClass() {
++        try {
++            assertScript '''
++                enum Foo {}
++
++                def test() {
++                    println Foo.getModifiers() // => 16401 // ENUM | FINAL | 
PUBLIC (see GROOVY_8360 wrt STATIC)
++                    println Foo.modifiers      // java.lang.NoSuchFieldError: 
modifiers
++                }    
++                test()
++            '''
++        } finally {
++            //println astTrees
++        }
++    }
+ }

http://git-wip-us.apache.org/repos/asf/groovy/blob/13522f88/src/main/groovy/transform/MapArguments.java.bak
----------------------------------------------------------------------
diff --git a/src/main/groovy/transform/MapArguments.java.bak 
b/src/main/groovy/transform/MapArguments.java.bak
new file mode 100644
index 0000000..c4d097d
--- /dev/null
+++ b/src/main/groovy/transform/MapArguments.java.bak
@@ -0,0 +1,33 @@
+/*
+ *  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 groovy.transform;
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@GroovyASTTransformationClass("org.apache.groovy.transform.MapArgumentsASTTransformation")
+public @interface MapArguments {
+    Class typeHint() default Object.class;
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/13522f88/src/main/org/apache/groovy/transform/MapArgumentsASTTransformation.java.bak
----------------------------------------------------------------------
diff --git 
a/src/main/org/apache/groovy/transform/MapArgumentsASTTransformation.java.bak 
b/src/main/org/apache/groovy/transform/MapArgumentsASTTransformation.java.bak
new file mode 100644
index 0000000..bd9b293
--- /dev/null
+++ 
b/src/main/org/apache/groovy/transform/MapArgumentsASTTransformation.java.bak
@@ -0,0 +1,165 @@
+/*
+ *  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 org.apache.groovy.transform;
+
+import groovy.transform.MapArguments;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.groovy.ast.tools.FactoryUtils.args;
+import static org.apache.groovy.ast.tools.FactoryUtils.callThisX;
+import static org.apache.groovy.ast.tools.FactoryUtils.castX;
+import static org.apache.groovy.ast.tools.FactoryUtils.classX;
+import static org.apache.groovy.ast.tools.FactoryUtils.constX;
+import static org.apache.groovy.ast.tools.FactoryUtils.ctorX;
+import static org.apache.groovy.ast.tools.FactoryUtils.stmt;
+import static org.apache.groovy.ast.tools.FactoryUtils.varX;
+
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+public class MapArgumentsASTTransformation extends AbstractASTTransformation {
+
+    @Override
+    public void visit(ASTNode[] nodes, SourceUnit source) {
+        init(nodes, source);
+
+        MethodNode methodNode = (MethodNode) nodes[1];
+
+        Parameter[] parameters = methodNode.getParameters();
+        Parameter closureParameter = null;
+
+        switch (parameters.length) {
+            case 0:
+                addError("MapArguments parameters should not be empty", 
methodNode);
+                return;
+            case 1:
+                //TODO more object checks
+                break;
+            case 2:
+                closureParameter = parameters[1];
+                if 
(!(closureParameter.getType().isDerivedFrom(ClassHelper.CLOSURE_TYPE)) && 
!ClassHelper.isSAMType(closureParameter.getType())) {
+                    addError("MapArguments second parameter can be a closure 
only", closureParameter);
+                    return;
+                }
+                break;
+            default:
+                addError("MapArguments parameters length should not be greater 
than 2", methodNode);
+                return;
+        }
+
+        Parameter mapParameter = new 
Parameter(nonGeneric(ClassHelper.MAP_TYPE), "__namedArgs");
+
+        Parameter[] mapBasedMethodParameters = closureParameter != null ? new 
Parameter[]{mapParameter, closureParameter} : new Parameter[]{mapParameter};
+
+        ClassNode declaringClass = methodNode.getDeclaringClass();
+
+        if (declaringClass.hasMethod(methodNode.getName(), 
mapBasedMethodParameters)) {
+            addError("This class already has a Map-based method", methodNode);
+            return;
+        }
+
+        Expression convertedValueExpression;
+
+        ClassNode parameterType = parameters[0].getType();
+        if (checkForMapConstructor(parameterType)) {
+            ArgumentListExpression convertedArguments = 
args(varX(mapParameter));
+            if ((parameterType.getModifiers() & Opcodes.ACC_STATIC) == 0) {
+                if(methodNode.isStatic()) {
+                    addError("You can't use inner class as map argument since 
it's impossible to instantiate it", methodNode);
+                    return;
+                }
+                convertedArguments.getExpressions().add(0, 
VariableExpression.THIS_EXPRESSION);
+            }
+            convertedValueExpression = ctorX(parameterType, 
convertedArguments);
+        } else {
+            convertedValueExpression = castX(parameterType, 
varX(mapParameter));
+        }
+
+        ArgumentListExpression oldMethodArguments = 
FactoryUtils.args(convertedValueExpression);
+
+        List<MethodNode> generatedMethods = new ArrayList<MethodNode>();
+        generatedMethods.add(declaringClass.addMethod(
+                methodNode.getName(),
+                methodNode.getModifiers(),
+                methodNode.getReturnType(),
+                mapBasedMethodParameters,
+                methodNode.getExceptions(),
+                stmt(callThisX(methodNode.getName(), oldMethodArguments))
+        ));
+
+        if (closureParameter != null) {
+            oldMethodArguments.addExpression(varX(closureParameter));
+            generatedMethods.add(declaringClass.addMethod(
+                    methodNode.getName(),
+                    methodNode.getModifiers(),
+                    methodNode.getReturnType(),
+                    new Parameter[]{mapBasedMethodParameters[0]},
+                    methodNode.getExceptions(),
+                    stmt(callThisX(
+                            methodNode.getName(),
+                            args(oldMethodArguments.getExpression(0), 
castX(closureParameter.getType(), constX(null)))
+                    ))
+            ));
+        }
+
+        for (MethodNode generatedMethod : generatedMethods) {
+            AnnotationNode mapArgumentsAnnotationNode = new 
AnnotationNode(ClassHelper.makeWithoutCaching(MapArguments.class));
+            mapArgumentsAnnotationNode.setMember("typeHint", 
classX(parameterType));
+            generatedMethod.addAnnotation(mapArgumentsAnnotationNode);
+        }
+    }
+
+    protected boolean checkForMapConstructor(ClassNode parameterType) {
+        List<ConstructorNode> declaredConstructors = 
parameterType.getDeclaredConstructors();
+
+        for (ConstructorNode declaredConstructor : declaredConstructors) {
+            Parameter[] declaredConstructorParameters = 
declaredConstructor.getParameters();
+            if (declaredConstructorParameters.length != 1) {
+                continue;
+            }
+
+            ClassNode declaredConstructorParameterType = 
declaredConstructorParameters[0].getType();
+
+            if (declaredConstructorParameterType == null) {
+                continue;
+            }
+
+            if (declaredConstructorParameterType.equals(ClassHelper.MAP_TYPE)) 
{
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/13522f88/src/test/org/codehaus/groovy/tools/stubgenerator/Groovy8224Bug.groovy.bak
----------------------------------------------------------------------
diff --git 
a/src/test/org/codehaus/groovy/tools/stubgenerator/Groovy8224Bug.groovy.bak 
b/src/test/org/codehaus/groovy/tools/stubgenerator/Groovy8224Bug.groovy.bak
new file mode 100644
index 0000000..8fe711b
--- /dev/null
+++ b/src/test/org/codehaus/groovy/tools/stubgenerator/Groovy8224Bug.groovy.bak
@@ -0,0 +1,53 @@
+/*
+ *  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 org.codehaus.groovy.tools.stubgenerator
+
+/**
+ * Test that traits include getters/setters.
+ */
+class Groovy8224Bug extends StringSourcesStubTestCase {
+
+    Map<String, String> provideSources() {
+        [
+                'Foo.groovy': '''
+                    trait GroovyXTrait {
+                        int foo
+                    }
+                ''',
+                'Main.java': '''
+                    class Main {
+                        public static void main(String[] args) {
+                            new GroovyXImpl().getFoo();
+                        }
+                    }
+                ''',
+                'GroovyXImpl.groovy': '''
+                    class GroovyXImpl implements GroovyXTrait { }
+                ''',
+        ]
+    }
+
+    void verifyStubs() {
+        def stubSource = stubJavaSourceFor('GroovyXImpl')
+        assert stubSource.contains('interface Foo')
+        assert stubSource.contains('int foo()')
+        assert stubSource.contains('void baz(int y)')
+        assert stubSource.contains('void bar(int x)')
+    }
+}

Reply via email to