You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2018/01/31 12:40:50 UTC
[2/5] groovy git commit: GROOVY-8440: Groovy's @Immutable annotation
should be re-vamped to be a meta-annotation (additional refactoring to remove
dup and plumb in pre/post conditions)
GROOVY-8440: Groovy's @Immutable annotation should be re-vamped to be a meta-annotation (additional refactoring to remove dup and plumb in pre/post conditions)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/8cd0820f
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/8cd0820f
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/8cd0820f
Branch: refs/heads/GROOVY_2_6_X
Commit: 8cd0820f988db59a668daf71af0cf07ade15e8c8
Parents: 4be8c77
Author: paulk <pa...@asert.com.au>
Authored: Mon Jan 29 23:57:26 2018 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Wed Jan 31 22:40:31 2018 +1000
----------------------------------------------------------------------
.../groovy/groovy/transform/Immutable.groovy | 4 +-
.../groovy/groovy/transform/MapConstructor.java | 5 -
.../groovy/transform/TupleConstructor.java | 7 --
.../codehaus/groovy/ast/tools/GeneralUtils.java | 17 +++
.../transform/ImmutableASTTransformation.java | 107 +++++++++----------
.../MapConstructorASTTransformation.java | 69 +++++++-----
.../TupleConstructorASTTransformation.java | 25 +++--
7 files changed, 127 insertions(+), 107 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/8cd0820f/src/main/groovy/groovy/transform/Immutable.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/Immutable.groovy b/src/main/groovy/groovy/transform/Immutable.groovy
index e59342b..3fa518a 100644
--- a/src/main/groovy/groovy/transform/Immutable.groovy
+++ b/src/main/groovy/groovy/transform/Immutable.groovy
@@ -174,8 +174,8 @@ package groovy.transform
@ToString(cache = true, includeSuperProperties = true)
@EqualsAndHashCode(cache = true)
@ImmutableBase
-@TupleConstructor(makeImmutable = true, defaults = false)
-@MapConstructor(makeImmutable = true, noArg = true, includeSuperProperties = true)
+@TupleConstructor(defaults = false)
+@MapConstructor(noArg = true, includeSuperProperties = true)
@KnownImmutable
@AnnotationCollector(mode=AnnotationCollectorMode.PREFER_EXPLICIT_MERGED)
@interface Immutable { }
http://git-wip-us.apache.org/repos/asf/groovy/blob/8cd0820f/src/main/groovy/groovy/transform/MapConstructor.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/MapConstructor.java b/src/main/groovy/groovy/transform/MapConstructor.java
index 55dee78..d4d5657 100644
--- a/src/main/groovy/groovy/transform/MapConstructor.java
+++ b/src/main/groovy/groovy/transform/MapConstructor.java
@@ -96,11 +96,6 @@ public @interface MapConstructor {
boolean includeFields() default false;
/**
- * Whether immutable pre-cautions (defensive copying, cloning, etc.) should be applied to incoming/outgoing properties.
- */
- boolean makeImmutable() default false;
-
- /**
* Include properties in the constructor.
*/
boolean includeProperties() default true;
http://git-wip-us.apache.org/repos/asf/groovy/blob/8cd0820f/src/main/groovy/groovy/transform/TupleConstructor.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/TupleConstructor.java b/src/main/groovy/groovy/transform/TupleConstructor.java
index bad5518..eb7cb6e 100644
--- a/src/main/groovy/groovy/transform/TupleConstructor.java
+++ b/src/main/groovy/groovy/transform/TupleConstructor.java
@@ -232,13 +232,6 @@ public @interface TupleConstructor {
boolean force() default false;
/**
- * Whether immutable pre-cautions (defensive copying, cloning, etc.) should be applied to incoming/outgoing properties.
- *
- * @since 2.5.0
- */
- boolean makeImmutable() default false;
-
- /**
* Used to set whether default value processing is enabled (the default) or disabled.
*
* By default, every constructor parameter is given a default value. This value will
http://git-wip-us.apache.org/repos/asf/groovy/blob/8cd0820f/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
index 4c55515..8c949e7 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java
@@ -42,6 +42,7 @@ 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.FieldExpression;
+import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.NotExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
@@ -604,6 +605,22 @@ public class GeneralUtils {
return new BooleanExpression(new BinaryExpression(expr, EQ, new ConstantExpression(0)));
}
+ public static ListExpression list2args(List args) {
+ ListExpression result = new ListExpression();
+ for (Object o : args) {
+ result.addExpression(new ConstantExpression(o));
+ }
+ return result;
+ }
+
+ public static ListExpression classList2args(List<String> args) {
+ ListExpression result = new ListExpression();
+ for (Object o : args) {
+ result.addExpression(new ClassExpression(ClassHelper.make(o.toString())));
+ }
+ return result;
+ }
+
public static BinaryExpression ltX(Expression lhv, Expression rhv) {
return new BinaryExpression(lhv, LT, rhv);
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/8cd0820f/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
index dcbe6d0..5536783 100644
--- a/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/ImmutableASTTransformation.java
@@ -73,6 +73,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classList2args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.createConstructorStatementDefault;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
@@ -89,6 +90,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.isInstanceOfX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.isOneX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements;
import static org.codehaus.groovy.ast.tools.GeneralUtils.isTrueX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.list2args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.neX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.notX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.orX;
@@ -136,7 +138,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
private static final Class MY_CLASS = ImmutableBase.class;
private static final Class<? extends Annotation> KNOWN_IMMUTABLE_CLASS = KnownImmutable.class;
private static final Class<? extends Annotation> IMMUTABLE_BASE_CLASS = ImmutableBase.class;
- private static final ClassNode IMMUTABLE_BASE_TYPE = makeWithoutCaching(IMMUTABLE_BASE_CLASS, false);
public static final ClassNode MY_TYPE = make(MY_CLASS);
static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
private static final String MEMBER_KNOWN_IMMUTABLE_CLASSES = "knownImmutableClasses";
@@ -192,10 +193,8 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
for (FieldNode fNode : fList) {
ensureNotPublic(this, cName, fNode);
}
- boolean includeSuperProperties = false;
if (hasAnnotation(cNode, TupleConstructorASTTransformation.MY_TYPE)) {
AnnotationNode tupleCons = cNode.getAnnotations(TupleConstructorASTTransformation.MY_TYPE).get(0);
-// includeSuperProperties = memberHasValue(tupleCons, "includeSuperProperties", true);
if (unsupportedTupleAttribute(tupleCons, "excludes")) return;
if (unsupportedTupleAttribute(tupleCons, "includes")) return;
if (unsupportedTupleAttribute(tupleCons, "includeFields")) return;
@@ -205,7 +204,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
if (unsupportedTupleAttribute(tupleCons, "force")) return;
}
if (!validateConstructors(cNode)) return;
-// createConstructors(this, cNode, knownImmutableClasses, knownImmutables, includeSuperProperties);
if (memberHasValue(node, MEMBER_ADD_COPY_WITH, true) && !pList.isEmpty() &&
!hasDeclaredMethod(cNode, COPY_WITH_METHOD, 1)) {
createCopyWith(cNode, pList);
@@ -255,6 +253,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
static List<String> getKnownImmutableClasses(AbstractASTTransformation xform, AnnotationNode node) {
final List<String> immutableClasses = new ArrayList<String>();
+ if (node == null) return immutableClasses;
final Expression expression = node.getMember(MEMBER_KNOWN_IMMUTABLE_CLASSES);
if (expression == null) return immutableClasses;
@@ -276,6 +275,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
static List<String> getKnownImmutables(AbstractASTTransformation xform, AnnotationNode node) {
final List<String> immutables = new ArrayList<String>();
+ if (node == null) return immutables;
final Expression expression = node.getMember(MEMBER_KNOWN_IMMUTABLES);
if (expression == null) return immutables;
@@ -305,17 +305,6 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
}
}
- private static void createConstructors(AbstractASTTransformation xform, ClassNode cNode, List<String> knownImmutableClasses, List<String> knownImmutables, boolean includeSuperProperties, boolean allProperties) {
- List<PropertyNode> list = getProperties(cNode, includeSuperProperties, allProperties);
- boolean specialHashMapCase = isSpecialHashMapCase(list);
- if (specialHashMapCase) {
- createConstructorMapSpecial(cNode, list);
- } else {
- createConstructorMap(xform, cNode, list, knownImmutableClasses, knownImmutables);
- createConstructorOrdered(cNode, list);
- }
- }
-
static boolean isSpecialHashMapCase(List<PropertyNode> list) {
return list.size() == 1 && list.get(0).getField().getType().equals(HASHMAP_TYPE);
}
@@ -388,39 +377,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
return castX(type, smce);
}
- static void createConstructorMapSpecial(ClassNode cNode, List<PropertyNode> list) {
- final BlockStatement body = new BlockStatement();
- body.addStatement(createConstructorStatementMapSpecial(list.get(0).getField()));
- createConstructorMapCommon(cNode, body);
- }
-
- static void createConstructorMap(AbstractASTTransformation xform, ClassNode cNode, List<PropertyNode> list) {
- AnnotationNode annoImmutable = cNode.getAnnotations(IMMUTABLE_BASE_TYPE).get(0);
- final List<String> knownImmutableClasses = getKnownImmutableClasses(xform, annoImmutable);
- final List<String> knownImmutables = getKnownImmutables(xform, annoImmutable);
- createConstructorMap(xform, cNode, list, knownImmutableClasses, knownImmutables);
- }
-
- private static void createConstructorMap(AbstractASTTransformation xform, ClassNode cNode, List<PropertyNode> list, List<String> knownImmutableClasses, List<String> knownImmutables) {
- final BlockStatement body = new BlockStatement();
- body.addStatement(ifS(equalsNullX(varX("args")), assignS(varX("args"), new MapExpression())));
- for (PropertyNode pNode : list) {
- body.addStatement(createConstructorStatement(xform, cNode, pNode, knownImmutableClasses, knownImmutables));
- }
- // check for missing properties
- body.addStatement(stmt(callX(SELF_TYPE, "checkPropNames", args("this", "args"))));
- createConstructorMapCommon(cNode, body);
- if (!list.isEmpty()) {
- createNoArgConstructor(cNode);
- }
- }
-
- private static void createNoArgConstructor(ClassNode cNode) {
- Statement body = stmt(ctorX(ClassNode.THIS, args(new MapExpression())));
- doAddConstructor(cNode, new ConstructorNode(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body));
- }
-
- private static void createConstructorMapCommon(ClassNode cNode, BlockStatement body) {
+ static void createConstructorMapCommon(ClassNode cNode, BlockStatement body) {
final List<FieldNode> fList = cNode.getFields();
for (FieldNode fNode : fList) {
if (fNode.isPublic()) continue; // public fields will be rejected elsewhere
@@ -444,7 +401,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
)));
}
- private static Statement createConstructorStatementMapSpecial(FieldNode fNode) {
+ static Statement createConstructorStatementMapSpecial(FieldNode fNode) {
final Expression fieldExpr = varX(fNode);
final ClassNode fieldType = fieldExpr.getType();
final Expression initExpr = fNode.getInitialValueExpression();
@@ -505,7 +462,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
return true;
}
- private static Statement createConstructorStatement(AbstractASTTransformation xform, ClassNode cNode, PropertyNode pNode, List<String> knownImmutableClasses, List<String> knownImmutables) {
+ static Statement createConstructorStatement(AbstractASTTransformation xform, ClassNode cNode, PropertyNode pNode, List<String> knownImmutables, List<String> knownImmutableClasses) {
FieldNode fNode = pNode.getField();
final ClassNode fieldType = fNode.getType();
Statement statement;
@@ -521,26 +478,26 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
xform.addError(createErrorMessage(cNode.getName(), fNode.getName(), fieldType.getName(), "compiling"), fNode);
statement = EmptyStatement.INSTANCE;
} else {
- statement = createConstructorStatementGuarded(cNode, fNode);
+ statement = createConstructorStatementGuarded(cNode, fNode, knownImmutables, knownImmutableClasses);
}
return statement;
}
- private static Statement createConstructorStatementGuarded(ClassNode cNode, FieldNode fNode) {
+ private static Statement createConstructorStatementGuarded(ClassNode cNode, FieldNode fNode, List<String> knownImmutables, List<String> knownImmutableClasses) {
final Expression fieldExpr = varX(fNode);
Expression initExpr = fNode.getInitialValueExpression();
final Statement assignInit;
if (initExpr == null || (initExpr instanceof ConstantExpression && ((ConstantExpression) initExpr).isNullExpression())) {
assignInit = assignS(fieldExpr, ConstantExpression.EMPTY_EXPRESSION);
} else {
- assignInit = assignS(fieldExpr, checkUnresolved(fNode, initExpr));
+ assignInit = assignS(fieldExpr, checkUnresolved(fNode, initExpr, knownImmutables, knownImmutableClasses));
}
Expression unknown = findArg(fNode.getName());
- return ifElseS(equalsNullX(unknown), assignInit, assignS(fieldExpr, checkUnresolved(fNode, unknown)));
+ return ifElseS(equalsNullX(unknown), assignInit, assignS(fieldExpr, checkUnresolved(fNode, unknown, knownImmutables, knownImmutableClasses)));
}
- private static Expression checkUnresolved(FieldNode fNode, Expression value) {
- Expression args = args(callThisX("getClass"), constX(fNode.getName()), value);
+ private static Expression checkUnresolved(FieldNode fNode, Expression value, List<String> knownImmutables, List<String> knownImmutableClasses) {
+ Expression args = args(callThisX("getClass"), constX(fNode.getName()), value, list2args(knownImmutables), classList2args(knownImmutableClasses));
return callX(SELF_TYPE, "checkImmutable", args);
}
@@ -794,6 +751,44 @@ public class ImmutableASTTransformation extends AbstractASTTransformation {
}
@SuppressWarnings("Unchecked")
+ public static Object checkImmutable(Class<?> clazz, String fieldName, Object field, List<String> knownImmutableFieldNames, List<Class> knownImmutableClasses) {
+ if (field == null || field instanceof Enum || knownImmutable(field.getClass()) || knownImmutableFieldNames.contains(fieldName) || knownImmutableClasses.contains(field.getClass())) {
+ return field;
+ }
+
+ boolean isImmutable = false;
+ for (Annotation an : field.getClass().getAnnotations()) {
+ if (an.getClass().getName().startsWith("groovy.transform.Immutable")) {
+ isImmutable = true;
+ break;
+ }
+ }
+ if (isImmutable) return field;
+
+ if (field instanceof Collection) {
+ Field declaredField;
+ try {
+ declaredField = clazz.getDeclaredField(fieldName);
+ Class<?> fieldType = declaredField.getType();
+ if (Collection.class.isAssignableFrom(fieldType)) {
+ return DefaultGroovyMethods.asImmutable((Collection) field);
+ }
+ // potentially allow Collection coercion for a constructor
+ if (knownImmutable(fieldType)) {
+ return field;
+ }
+ } catch (NoSuchFieldException ignore) {
+ // ignore
+ }
+ }
+ final String typeName = field.getClass().getName();
+ throw new RuntimeException(createErrorMessage(clazz.getName(), fieldName, typeName, "constructing"));
+ }
+
+ /*
+ * For compatibility with pre 2.5 compiled classes
+ */
+ @SuppressWarnings("Unchecked")
public static Object checkImmutable(Class<?> clazz, String fieldName, Object field) {
if (field == null || field instanceof Enum || knownImmutable(field.getClass())) {
return field;
http://git-wip-us.apache.org/repos/asf/groovy/blob/8cd0820f/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
index fb2f94b..8f24342 100644
--- a/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/MapConstructorASTTransformation.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.transform;
+import groovy.transform.ImmutableBase;
import groovy.transform.MapConstructor;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
@@ -26,7 +27,6 @@ import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.DynamicVariable;
-import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
@@ -40,6 +40,7 @@ import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -65,6 +66,11 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.transform.ImmutableASTTransformation.createConstructorMapCommon;
+import static org.codehaus.groovy.transform.ImmutableASTTransformation.createConstructorStatement;
+import static org.codehaus.groovy.transform.ImmutableASTTransformation.createConstructorStatementMapSpecial;
+import static org.codehaus.groovy.transform.ImmutableASTTransformation.getKnownImmutableClasses;
+import static org.codehaus.groovy.transform.ImmutableASTTransformation.getKnownImmutables;
/**
* Handles generation of code for the @MapConstructor annotation.
@@ -76,6 +82,9 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation {
static final ClassNode MY_TYPE = make(MY_CLASS);
static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage();
private static final ClassNode MAP_TYPE = makeWithoutCaching(Map.class, false);
+ private static final Class<? extends Annotation> IMMUTABLE_BASE_CLASS = ImmutableBase.class;
+ private static final ClassNode IMMUTABLE_BASE_TYPE = makeWithoutCaching(IMMUTABLE_BASE_CLASS, false);
+ private static final ClassNode IMMUTABLE_XFORM_TYPE = make(ImmutableASTTransformation.class);
public void visit(ASTNode[] nodes, SourceUnit source) {
init(nodes, source);
@@ -96,7 +105,6 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation {
List<String> excludes = getMemberStringList(anno, "excludes");
List<String> includes = getMemberStringList(anno, "includes");
boolean allNames = memberHasValue(anno, "allNames", true);
- boolean makeImmutable = memberHasValue(anno, "makeImmutable", true);
if (!checkIncludeExcludeUndefinedAware(anno, excludes, includes, MY_TYPE_NAME)) return;
if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields, includeSuperProperties, false))
return;
@@ -114,7 +122,7 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation {
return;
}
- createConstructors(this, cNode, includeFields, includeProperties, includeSuperProperties, includeSuperFields, useSetters, noArg, allNames, allProperties, makeImmutable, excludes, includes, (ClosureExpression) pre, (ClosureExpression) post, source);
+ createConstructors(this, cNode, includeFields, includeProperties, includeSuperProperties, includeSuperFields, useSetters, noArg, allNames, allProperties, excludes, includes, (ClosureExpression) pre, (ClosureExpression) post, source);
if (pre != null) {
anno.setMember("pre", new ClosureExpression(new Parameter[0], EmptyStatement.INSTANCE));
@@ -125,7 +133,7 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation {
}
}
- private static void createConstructors(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperProperties, boolean includeSuperFields, boolean useSetters, boolean noArg, boolean allNames, boolean allProperties, boolean makeImmutable, List<String> excludes, List<String> includes, ClosureExpression pre, ClosureExpression post, SourceUnit source) {
+ private static void createConstructors(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields, boolean includeProperties, boolean includeSuperProperties, boolean includeSuperFields, boolean useSetters, boolean noArg, boolean allNames, boolean allProperties, List<String> excludes, List<String> includes, ClosureExpression pre, ClosureExpression post, SourceUnit source) {
List<ConstructorNode> constructors = cNode.getDeclaredConstructors();
boolean foundEmpty = constructors.size() == 1 && constructors.get(0).getFirstStatement() == null;
// HACK: JavaStubGenerator could have snuck in a constructor we don't want
@@ -140,17 +148,11 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation {
}
List<PropertyNode> list = getAllProperties(names, cNode, true, includeFields, allProperties, false, true);
- if (makeImmutable) {
- boolean specialHashMapCase = (ImmutableASTTransformation.isSpecialHashMapCase(list) && superList.isEmpty()) ||
- (ImmutableASTTransformation.isSpecialHashMapCase(superList) && list.isEmpty());
- superList.addAll(list);
- if (specialHashMapCase) {
- ImmutableASTTransformation.createConstructorMapSpecial(cNode, superList);
- } else {
- ImmutableASTTransformation.createConstructorMap(xform, cNode, superList);
- }
- return;
- }
+ List<AnnotationNode> annotations = cNode.getAnnotations(IMMUTABLE_BASE_TYPE);
+ AnnotationNode annoImmutable = annotations.isEmpty() ? null : annotations.get(0);
+ boolean makeImmutable = annoImmutable != null;
+ final List<String> knownImmutableClasses = getKnownImmutableClasses(xform, annoImmutable);
+ final List<String> knownImmutables = getKnownImmutables(xform, annoImmutable);
Parameter map = param(MAP_TYPE, "args");
final BlockStatement body = new BlockStatement();
@@ -160,27 +162,42 @@ public class MapConstructorASTTransformation extends AbstractASTTransformation {
copyStatementsWithSuperAdjustment(transformed, body);
}
final BlockStatement inner = new BlockStatement();
- for (PropertyNode pNode : superList) {
- String name = pNode.getName();
- if (shouldSkip(name, excludes, includes, allNames)) continue;
- assignField(useSetters, map, inner, name);
- }
- for (PropertyNode pNode : list) {
- String name = pNode.getName();
- if (shouldSkip(name, excludes, includes, allNames)) continue;
- assignField(useSetters, map, inner, name);
+ superList.addAll(list);
+ boolean specialHashMapCase = ImmutableASTTransformation.isSpecialHashMapCase(superList);
+ if (!specialHashMapCase) {
+ processProps(xform, cNode, makeImmutable && !specialHashMapCase, useSetters, allNames, excludes, includes, superList, map, inner, knownImmutables, knownImmutableClasses);
}
body.addStatement(ifS(notNullX(varX("args")), inner));
if (post != null) {
ClosureExpression transformed = (ClosureExpression) transformer.transform(post);
body.addStatement(transformed.getCode());
}
- cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, params(map), ClassNode.EMPTY_ARRAY, body));
- if (noArg && !(list.isEmpty() && superList.isEmpty()) && !hasNoArgConstructor(cNode)) {
+ if (makeImmutable && !specialHashMapCase) {
+ body.addStatement(stmt(callX(IMMUTABLE_XFORM_TYPE, "checkPropNames", args("this", "args"))));
+ createConstructorMapCommon(cNode, body);
+ } else if (specialHashMapCase) {
+ inner.addStatement(createConstructorStatementMapSpecial(superList.get(0).getField()));
+ createConstructorMapCommon(cNode, body);
+ } else {
+ cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, params(map), ClassNode.EMPTY_ARRAY, body));
+ }
+ if (noArg && !superList.isEmpty() && !hasNoArgConstructor(cNode) && !specialHashMapCase) {
createNoArgConstructor(cNode);
}
}
+ private static void processProps(AbstractASTTransformation xform, ClassNode cNode, boolean makeImmutable, boolean useSetters, boolean allNames, List<String> excludes, List<String> includes, List<PropertyNode> superList, Parameter map, BlockStatement inner, List<String> knownImmutables, List<String> knownImmutableClasses) {
+ for (PropertyNode pNode : superList) {
+ String name = pNode.getName();
+ if (shouldSkip(name, excludes, includes, allNames)) continue;
+ if (makeImmutable) {
+ inner.addStatement(createConstructorStatement(xform, cNode, pNode, knownImmutables, knownImmutableClasses));
+ } else {
+ assignField(useSetters, map, inner, name);
+ }
+ }
+ }
+
private static void createNoArgConstructor(ClassNode cNode) {
Statement body = stmt(ctorX(ClassNode.THIS, args(new MapExpression())));
cNode.addConstructor(new ConstructorNode(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body));
http://git-wip-us.apache.org/repos/asf/groovy/blob/8cd0820f/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
index ec6a4c0..5f0e381 100644
--- a/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/TupleConstructorASTTransformation.java
@@ -18,7 +18,7 @@
*/
package org.codehaus.groovy.transform;
-import groovy.transform.KnownImmutable;
+import groovy.transform.ImmutableBase;
import groovy.transform.TupleConstructor;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
@@ -40,6 +40,7 @@ import org.codehaus.groovy.classgen.VariableScopeVisitor;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -83,7 +84,8 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
private static final ClassNode LHMAP_TYPE = makeWithoutCaching(LinkedHashMap.class, false);
private static final ClassNode HMAP_TYPE = makeWithoutCaching(HashMap.class, false);
private static final ClassNode CHECK_METHOD_TYPE = make(ImmutableASTTransformation.class);
- private static final ClassNode IMMUTABLE_CLASS_TYPE = makeWithoutCaching(KnownImmutable.class, false);
+ private static final Class<? extends Annotation> IMMUTABLE_BASE_CLASS = ImmutableBase.class;
+ private static final ClassNode IMMUTABLE_BASE_TYPE = makeWithoutCaching(IMMUTABLE_BASE_CLASS, false);
private static final Map<Class<?>, Expression> primitivesInitialValues;
static {
@@ -118,7 +120,6 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
boolean defaults = !memberHasValue(anno, "defaults", false);
boolean useSetters = memberHasValue(anno, "useSetters", true);
boolean allProperties = memberHasValue(anno, "allProperties", true);
- boolean makeImmutable = memberHasValue(anno, "makeImmutable", true);
List<String> excludes = getMemberStringList(anno, "excludes");
List<String> includes = getMemberStringList(anno, "includes");
boolean allNames = memberHasValue(anno, "allNames", true);
@@ -137,7 +138,7 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
}
createConstructor(this, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties,
- callSuper, force, excludes, includes, useSetters, defaults, allNames, allProperties, makeImmutable,
+ callSuper, force, excludes, includes, useSetters, defaults, allNames, allProperties,
sourceUnit, (ClosureExpression) pre, (ClosureExpression) post);
if (pre != null) {
@@ -163,18 +164,17 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
}
public static void createConstructor(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields,
- boolean includeProperties, boolean includeSuperFields, boolean
- includeSuperProperties, boolean callSuper, boolean force,
- List<String> excludes, final List<String> includes, boolean useSetters, boolean
- defaults, boolean allNames, SourceUnit sourceUnit, ClosureExpression
- pre, ClosureExpression post) {
- createConstructor(xform, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force, excludes, includes, useSetters, defaults, allNames, false, false, sourceUnit, pre, post);
+ boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties,
+ boolean callSuper, boolean force, List<String> excludes, final List<String> includes,
+ boolean useSetters, boolean defaults, boolean allNames,
+ SourceUnit sourceUnit, ClosureExpression pre, ClosureExpression post) {
+ createConstructor(xform, cNode, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force, excludes, includes, useSetters, defaults, allNames,false, sourceUnit, pre, post);
}
public static void createConstructor(AbstractASTTransformation xform, ClassNode cNode, boolean includeFields,
boolean includeProperties, boolean includeSuperFields, boolean includeSuperProperties,
boolean callSuper, boolean force, List<String> excludes, final List<String> includes,
- boolean useSetters, boolean defaults, boolean allNames, boolean allProperties, boolean makeImmutable,
+ boolean useSetters, boolean defaults, boolean allNames, boolean allProperties,
SourceUnit sourceUnit, ClosureExpression pre, ClosureExpression post) {
Set<String> names = new HashSet<String>();
List<PropertyNode> superList;
@@ -186,6 +186,9 @@ public class TupleConstructorASTTransformation extends AbstractASTTransformation
List<PropertyNode> list = getAllProperties(names, cNode, true, includeFields, allProperties, false, true);
+ List<AnnotationNode> annotations = cNode.getAnnotations(IMMUTABLE_BASE_TYPE);
+ AnnotationNode annoImmutable = annotations.isEmpty() ? null : annotations.get(0);
+ boolean makeImmutable = annoImmutable != null;
if (makeImmutable) {
boolean specialHashMapCase = (ImmutableASTTransformation.isSpecialHashMapCase(list) && superList.isEmpty()) ||
(ImmutableASTTransformation.isSpecialHashMapCase(superList) && list.isEmpty());