use of com.sun.tools.javac.tree.JCTree.JCAnnotation in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method createEquals.
public JCMethodDecl createEquals(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JavacNode source, List<JCAnnotation> onParam) {
JavacTreeMaker maker = typeNode.getTreeMaker();
Name oName = typeNode.toName("o");
Name otherName = typeNode.toName("other");
Name thisName = typeNode.toName("this");
List<JCAnnotation> annsOnParamOnMethod = List.nil();
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation);
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode);
if (checkerFramework.generateSideEffectFree()) {
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
}
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
JCExpression objectType;
if (annsOnParamOnMethod.isEmpty()) {
objectType = genJavaLangTypeRef(typeNode, "Object");
} else {
objectType = chainDots(typeNode, "java", "lang", "Object");
objectType = maker.AnnotatedType(annsOnParamOnMethod, objectType);
}
JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN);
long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
JCVariableDecl param = maker.VarDef(maker.Modifiers(finalFlag | Flags.PARAMETER, onParam), oName, objectType, null);
JavacHandlerUtil.createRelevantNullableAnnotation(typeNode, param);
final List<JCVariableDecl> params = List.of(param);
/* if (o == this) return true; */
{
statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(oName), maker.Ident(thisName)), returnBool(maker, true), null));
}
/* if (!(o instanceof Outer.Inner.MyType)) return false; */
{
JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.Parens(maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode, false))));
statements.append(maker.If(notInstanceOf, returnBool(maker, false), null));
}
/* Outer.Inner.MyType<?> other = (Outer.Inner.MyType<?>) o; */
{
if (!members.isEmpty() || needsCanEqual) {
final JCExpression selfType1 = createTypeReference(typeNode, true), selfType2 = createTypeReference(typeNode, true);
statements.append(maker.VarDef(maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
}
}
/* if (!other.canEqual((java.lang.Object) this)) return false; */
{
if (needsCanEqual) {
List<JCExpression> exprNil = List.nil();
JCExpression thisRef = maker.Ident(thisName);
JCExpression castThisRef = maker.TypeCast(genJavaLangTypeRef(typeNode, "Object"), thisRef);
JCExpression equalityCheck = maker.Apply(exprNil, maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")), List.of(castThisRef));
statements.append(maker.If(maker.Unary(CTC_NOT, equalityCheck), returnBool(maker, false), null));
}
}
/* if (!super.equals(o)) return false; */
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")), List.<JCExpression>of(maker.Ident(oName)));
JCUnary superNotEqual = maker.Unary(CTC_NOT, callToSuper);
statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
}
for (Included<JavacNode, EqualsAndHashCode.Include> member : members) {
JavacNode memberNode = member.getNode();
boolean isMethod = memberNode.getKind() == Kind.METHOD;
JCExpression fType = unnotate(getFieldType(memberNode, fieldAccess));
JCExpression thisFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess);
JCExpression otherFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode, maker.Ident(otherName)) : createFieldAccessor(maker, memberNode, fieldAccess, maker.Ident(otherName));
if (fType instanceof JCPrimitiveTypeTree) {
switch(((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
case FLOAT:
/* if (Float.compare(this.fieldName, other.fieldName) != 0) return false; */
statements.append(generateCompareFloatOrDouble(thisFieldAccessor, otherFieldAccessor, maker, typeNode, false));
break;
case DOUBLE:
/* if (Double.compare(this.fieldName, other.fieldName) != 0) return false; */
statements.append(generateCompareFloatOrDouble(thisFieldAccessor, otherFieldAccessor, maker, typeNode, true));
break;
default:
/* if (this.fieldName != other.fieldName) return false; */
statements.append(maker.If(maker.Binary(CTC_NOT_EQUAL, thisFieldAccessor, otherFieldAccessor), returnBool(maker, false), null));
break;
}
} else if (fType instanceof JCArrayTypeTree) {
JCArrayTypeTree array = (JCArrayTypeTree) fType;
/* if (!java.util.Arrays.deepEquals(this.fieldName, other.fieldName)) return false; //use equals for primitive arrays. */
boolean multiDim = unnotate(array.elemtype) instanceof JCArrayTypeTree;
boolean primitiveArray = unnotate(array.elemtype) instanceof JCPrimitiveTypeTree;
boolean useDeepEquals = multiDim || !primitiveArray;
JCExpression eqMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals");
List<JCExpression> args = List.of(thisFieldAccessor, otherFieldAccessor);
statements.append(maker.If(maker.Unary(CTC_NOT, maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null));
} else /* objects */
{
/* final java.lang.Object this$fieldName = this.fieldName; */
/* final java.lang.Object other$fieldName = other.fieldName; */
/* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
Name thisDollarFieldName = memberNode.toName("this" + (isMethod ? "$$" : "$") + memberNode.getName());
Name otherDollarFieldName = memberNode.toName("other" + (isMethod ? "$$" : "$") + memberNode.getName());
statements.append(maker.VarDef(maker.Modifiers(finalFlag), thisDollarFieldName, genJavaLangTypeRef(typeNode, "Object"), thisFieldAccessor));
statements.append(maker.VarDef(maker.Modifiers(finalFlag), otherDollarFieldName, genJavaLangTypeRef(typeNode, "Object"), otherFieldAccessor));
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(thisDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression otherNotEqualsNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(otherDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression thisEqualsThat = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")), List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
JCExpression fieldsAreNotEqual = maker.Conditional(thisEqualsNull, otherNotEqualsNull, maker.Unary(CTC_NOT, thisEqualsThat));
statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null));
}
}
/* return true; */
{
statements.append(returnBool(maker, true));
}
JCBlock body = maker.Block(0, statements.toList());
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("equals"), returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source);
}
use of com.sun.tools.javac.tree.JCTree.JCAnnotation in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method handle.
@Override
public void handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class);
deleteImportFromCompilationUnit(annotationNode, CacheStrategy.class.getName());
EqualsAndHashCode ann = annotation.getInstance();
java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode);
JavacNode typeNode = annotationNode.up();
List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
Boolean callSuper = ann.callSuper();
if (!annotation.isExplicit("callSuper"))
callSuper = null;
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
boolean cacheHashCode = ann.cacheStrategy() == CacheStrategy.LAZY;
generateMethods(typeNode, annotationNode, members, callSuper, true, cacheHashCode, fieldAccess, onParam);
}
use of com.sun.tools.javac.tree.JCTree.JCAnnotation in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method createHashCode.
public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, FieldAccess fieldAccess, JavacNode source) {
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation);
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode);
if (cacheHashCode && checkerFramework.generatePure()) {
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()));
} else if (checkerFramework.generateSideEffectFree()) {
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
}
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
JCExpression returnType = maker.TypeIdent(CTC_INT);
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
Name primeName = typeNode.toName(PRIME_NAME);
Name resultName = typeNode.toName(RESULT_NAME);
long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());
boolean isEmpty = members.isEmpty();
/* if (this.$hashCodeCache != 0) return this.$hashCodeCache; */
{
if (cacheHashCode) {
JCIdent receiver = maker.Ident(typeNode.toName("this"));
JCFieldAccess cacheHashCodeFieldAccess = maker.Select(receiver, typeNode.toName(HASH_CODE_CACHE_NAME));
JCExpression cacheNotZero = maker.Binary(CTC_NOT_EQUAL, cacheHashCodeFieldAccess, maker.Literal(CTC_INT, 0));
statements.append(maker.If(cacheNotZero, maker.Return(cacheHashCodeFieldAccess), null));
}
}
/* final int PRIME = X; */
{
if (!isEmpty) {
statements.append(maker.VarDef(maker.Modifiers(finalFlag), primeName, maker.TypeIdent(CTC_INT), maker.Literal(HandlerUtil.primeForHashcode())));
}
}
/* int result = ... */
{
final JCExpression init;
if (callSuper) {
/* ... super.hashCode(); */
init = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), List.<JCExpression>nil());
} else {
/* ... 1; */
init = maker.Literal(1);
}
statements.append(maker.VarDef(maker.Modifiers(isEmpty && !cacheHashCode ? finalFlag : 0L), resultName, maker.TypeIdent(CTC_INT), init));
}
for (Included<JavacNode, EqualsAndHashCode.Include> member : members) {
JavacNode memberNode = member.getNode();
JCExpression fType = unnotate(getFieldType(memberNode, fieldAccess));
boolean isMethod = memberNode.getKind() == Kind.METHOD;
JCExpression fieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess);
if (fType instanceof JCPrimitiveTypeTree) {
switch(((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
case BOOLEAN:
/* this.fieldName ? X : Y */
statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(fieldAccessor, maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse())))));
break;
case LONG:
{
Name dollarFieldName = memberNode.toName((isMethod ? "$$" : "$") + memberNode.getName());
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), fieldAccessor));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
break;
case FLOAT:
/* Float.floatToIntBits(this.fieldName) */
statements.append(createResultCalculation(typeNode, maker.Apply(List.<JCExpression>nil(), genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"), List.of(fieldAccessor))));
break;
case DOUBLE:
{
/* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */
Name dollarFieldName = memberNode.toName((isMethod ? "$$" : "$") + memberNode.getName());
JCExpression init = maker.Apply(List.<JCExpression>nil(), genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"), List.of(fieldAccessor));
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), init));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
break;
default:
case BYTE:
case SHORT:
case INT:
case CHAR:
/* just the field */
statements.append(createResultCalculation(typeNode, fieldAccessor));
break;
}
} else if (fType instanceof JCArrayTypeTree) {
JCArrayTypeTree array = (JCArrayTypeTree) fType;
/* java.util.Arrays.deepHashCode(this.fieldName) //use just hashCode() for primitive arrays. */
boolean multiDim = unnotate(array.elemtype) instanceof JCArrayTypeTree;
boolean primitiveArray = unnotate(array.elemtype) instanceof JCPrimitiveTypeTree;
boolean useDeepHC = multiDim || !primitiveArray;
JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepHC ? "deepHashCode" : "hashCode");
statements.append(createResultCalculation(typeNode, maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor))));
} else /* objects */
{
/* final java.lang.Object $fieldName = this.fieldName; */
/* ($fieldName == null ? NULL_PRIME : $fieldName.hashCode()) */
Name dollarFieldName = memberNode.toName((isMethod ? "$$" : "$") + memberNode.getName());
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, genJavaLangTypeRef(typeNode, "Object"), fieldAccessor));
JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")), List.<JCExpression>nil());
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(dollarFieldName), maker.Literal(CTC_BOT, null));
statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(thisEqualsNull, maker.Literal(HandlerUtil.primeForNull()), hcCall))));
}
}
/*
* if (result == 0) result = Integer.MIN_VALUE;
* this.$hashCodeCache = result;
*
*/
{
if (cacheHashCode) {
statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(resultName), maker.Literal(CTC_INT, 0)), maker.Exec(maker.Assign(maker.Ident(resultName), genJavaLangTypeRef(typeNode, "Integer", "MIN_VALUE"))), null));
JCFieldAccess cacheHashCodeFieldAccess = maker.Select(maker.Ident(typeNode.toName("this")), typeNode.toName(HASH_CODE_CACHE_NAME));
statements.append(maker.Exec(maker.Assign(cacheHashCodeFieldAccess, maker.Ident(resultName))));
}
}
/* return result; */
{
statements.append(maker.Return(maker.Ident(resultName)));
}
JCBlock body = maker.Block(0, statements.toList());
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("hashCode"), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source);
}
use of com.sun.tools.javac.tree.JCTree.JCAnnotation in project lombok by rzwitserloot.
the class HandleEqualsAndHashCode method createCanEqual.
public JCMethodDecl createCanEqual(JavacNode typeNode, JavacNode source, List<JCAnnotation> onParam) {
/* protected boolean canEqual(final java.lang.Object other) {
* return other instanceof Outer.Inner.MyType;
* }
*/
JavacTreeMaker maker = typeNode.getTreeMaker();
List<JCAnnotation> annsOnMethod = List.nil();
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode);
if (checkerFramework.generatePure()) {
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()));
}
JCModifiers mods = maker.Modifiers(Flags.PROTECTED, annsOnMethod);
JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN);
Name canEqualName = typeNode.toName("canEqual");
JCExpression objectType = genJavaLangTypeRef(typeNode, "Object");
Name otherName = typeNode.toName("other");
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, onParam), otherName, objectType, null);
createRelevantNullableAnnotation(typeNode, param);
List<JCVariableDecl> params = List.of(param);
JCBlock body = maker.Block(0, List.<JCStatement>of(maker.Return(maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode, false)))));
return recursiveSetGeneratedBy(maker.MethodDef(mods, canEqualName, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source);
}
use of com.sun.tools.javac.tree.JCTree.JCAnnotation in project lombok by rzwitserloot.
the class HandleFieldDefaults method visitType.
@Override
public void visitType(JavacNode typeNode, JCClassDecl type) {
AnnotationValues<FieldDefaults> fieldDefaults = null;
JavacNode source = typeNode;
boolean levelIsExplicit = false;
boolean makeFinalIsExplicit = false;
FieldDefaults fd = null;
for (JavacNode jn : typeNode.down()) {
if (jn.getKind() != Kind.ANNOTATION)
continue;
JCAnnotation ann = (JCAnnotation) jn.get();
JCTree typeTree = ann.annotationType;
if (typeTree == null)
continue;
String typeTreeToString = typeTree.toString();
if (!typeTreeToString.equals("FieldDefaults") && !typeTreeToString.equals("lombok.experimental.FieldDefaults"))
continue;
if (!typeMatches(FieldDefaults.class, jn, typeTree))
continue;
source = jn;
fieldDefaults = createAnnotation(FieldDefaults.class, jn);
levelIsExplicit = fieldDefaults.isExplicit("level");
makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
fd = fieldDefaults.getInstance();
if (!levelIsExplicit && !makeFinalIsExplicit) {
jn.addError("This does nothing; provide either level or makeFinal or both.");
}
if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
levelIsExplicit = false;
}
deleteAnnotationIfNeccessary(jn, FieldDefaults.class);
deleteImportFromCompilationUnit(jn, "lombok.AccessLevel");
break;
}
if (fd == null && (type.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0)
return;
boolean defaultToPrivate = levelIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
boolean defaultToFinal = makeFinalIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null)
return;
// Do not apply field defaults to records if set using the the config system
if (fieldDefaults == null && !isClassOrEnum(typeNode))
return;
AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
}
Aggregations