diff options
Diffstat (limited to 'src/core/lombok/eclipse')
-rwxr-xr-x | src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java | 105 |
1 files changed, 98 insertions, 7 deletions
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 7147343e..deb19c00 100755 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -36,6 +36,7 @@ import java.util.Set; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.EqualsAndHashCode; +import lombok.EqualsAndHashCode.CacheStrategy; import lombok.core.AST.Kind; import lombok.core.handlers.HandlerUtil; import lombok.core.handlers.InclusionExclusionUtils; @@ -59,6 +60,8 @@ import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression; import org.eclipse.jdt.internal.compiler.ast.IntLiteral; @@ -94,6 +97,9 @@ import org.mangosdk.spi.ProviderFor; @ProviderFor(EclipseAnnotationHandler.class) public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndHashCode> { + private static final String HASH_CODE_CACHE_NAME = "$hashCodeCache"; + + private final char[] HASH_CODE_CACHE_NAME_ARR = HASH_CODE_CACHE_NAME.toCharArray(); private final char[] PRIME = "PRIME".toCharArray(); private final char[] RESULT = "result".toCharArray(); @@ -116,7 +122,9 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, fieldAccess, onParam); + boolean cacheHashCode = ann.cacheStrategy() == CacheStrategy.LAZY; + + generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, cacheHashCode, fieldAccess, onParam); } public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) { @@ -130,11 +138,11 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; - generateMethods(typeNode, errorNode, members, null, false, access, new ArrayList<Annotation>()); + generateMethods(typeNode, errorNode, members, null, false, false, access, new ArrayList<Annotation>()); } public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, EqualsAndHashCode.Include>> members, - Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) { + Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List<Annotation> onParam) { TypeDeclaration typeDecl = null; @@ -222,12 +230,33 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH injectMethod(typeNode, canEqualMethod); } - MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, errorNode.get(), fieldAccess); + if (cacheHashCode){ + if (fieldExists(HASH_CODE_CACHE_NAME, typeNode) != MemberExistsResult.NOT_EXISTS) { + String msg = String.format("Not caching the result of hashCode: A field named %s already exists.", HASH_CODE_CACHE_NAME); + errorNode.addWarning(msg); + cacheHashCode = false; + } else { + createHashCodeCacheField(typeNode, errorNode.get()); + } + } + + MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, cacheHashCode, errorNode.get(), fieldAccess); hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope); injectMethod(typeNode, hashCodeMethod); } + + private void createHashCodeCacheField(EclipseNode typeNode, ASTNode source) { + FieldDeclaration hashCodeCacheDecl = new FieldDeclaration(HASH_CODE_CACHE_NAME_ARR, 0, 0); + hashCodeCacheDecl.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccTransient; + hashCodeCacheDecl.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + hashCodeCacheDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); + hashCodeCacheDecl.declarationSourceEnd = -1; + injectFieldAndMarkGenerated(typeNode, hashCodeCacheDecl); + setGeneratedBy(hashCodeCacheDecl, source); + setGeneratedBy(hashCodeCacheDecl.type, source); + } - public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess) { + public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, ASTNode source, FieldAccess fieldAccess) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -238,7 +267,10 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH method.returnType = TypeReference.baseTypeReference(TypeIds.T_int, 0); setGeneratedBy(method.returnType, source); Annotation overrideAnnotation = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source); - if (getCheckerFrameworkVersion(type).generateSideEffectFree()) { + CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(type); + if (cacheHashCode && checkerFramework.generatePure()) { + method.annotations = new Annotation[] { overrideAnnotation, generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__PURE) }; + } else if (checkerFramework.generateSideEffectFree()) { method.annotations = new Annotation[] { overrideAnnotation, generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) }; } else { method.annotations = new Annotation[] { overrideAnnotation }; @@ -262,6 +294,22 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH } } + /* if (this.$hashCodeCache != 0) return this.$hashCodeCache; */ { + if (cacheHashCode) { + FieldReference hashCodeCacheRef = new FieldReference(HASH_CODE_CACHE_NAME_ARR, p); + hashCodeCacheRef.receiver = new ThisReference(pS, pE); + setGeneratedBy(hashCodeCacheRef, source); + setGeneratedBy(hashCodeCacheRef.receiver, source); + EqualExpression cacheNotZero = new EqualExpression(hashCodeCacheRef, makeIntLiteral("0".toCharArray(), source), OperatorIds.NOT_EQUAL); + setGeneratedBy(cacheNotZero, source); + ReturnStatement returnCache = new ReturnStatement(hashCodeCacheRef, pS, pE); + setGeneratedBy(returnCache, source); + IfStatement ifStatement = new IfStatement(cacheNotZero, returnCache, pS, pE); + setGeneratedBy(ifStatement, source); + statements.add(ifStatement); + } + } + /* final int PRIME = X; */ { /* Without members, PRIME isn't used, as that would trigger a 'local variable not used' warning. */ if (!isEmpty) { @@ -296,7 +344,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH resultDecl.initialization = init; resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); resultDecl.type.sourceStart = pS; resultDecl.type.sourceEnd = pE; - if (isEmpty) resultDecl.modifiers |= Modifier.FINAL; + if (isEmpty && !cacheHashCode) resultDecl.modifiers |= Modifier.FINAL; setGeneratedBy(resultDecl.type, source); statements.add(resultDecl); } @@ -391,6 +439,49 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH } } + /* + * if (result == 0) result = Integer.MIN_VALUE; + * this.$hashCodeCache = result; + * + */ { + if (cacheHashCode) { + SingleNameReference resultRef = new SingleNameReference(RESULT, p); + setGeneratedBy(resultRef, source); + + EqualExpression resultIsZero = new EqualExpression(resultRef, makeIntLiteral("0".toCharArray(), source), OperatorIds.EQUAL_EQUAL); + setGeneratedBy(resultIsZero, source); + + resultRef = new SingleNameReference(RESULT, p); + setGeneratedBy(resultRef, source); + + FieldReference integerMinValue = new FieldReference("MIN_VALUE".toCharArray(), p); + integerMinValue.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_INTEGER); + setGeneratedBy(integerMinValue, source); + + Assignment newResult = new Assignment(resultRef, integerMinValue, pE); + newResult.sourceStart = pS; newResult.statementEnd = newResult.sourceEnd = pE; + setGeneratedBy(newResult, source); + + IfStatement ifStatement = new IfStatement(resultIsZero, newResult, pS, pE); + setGeneratedBy(ifStatement, source); + statements.add(ifStatement); + + + FieldReference hashCodeCacheRef = new FieldReference(HASH_CODE_CACHE_NAME_ARR, p); + hashCodeCacheRef.receiver = new ThisReference(pS, pE); + setGeneratedBy(hashCodeCacheRef, source); + setGeneratedBy(hashCodeCacheRef.receiver, source); + + resultRef = new SingleNameReference(RESULT, p); + setGeneratedBy(resultRef, source); + + Assignment cacheResult = new Assignment(hashCodeCacheRef, resultRef, pE); + cacheResult.sourceStart = pS; cacheResult.statementEnd = cacheResult.sourceEnd = pE; + setGeneratedBy(cacheResult, source); + statements.add(cacheResult); + } + } + /* return result; */ { SingleNameReference resultRef = new SingleNameReference(RESULT, p); setGeneratedBy(resultRef, source); |