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)') + } +}
