From 8bbdcec6fad6edf8f335f9e0c6899fddc56e07c1 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Tue, 23 Jun 2009 08:04:44 +0200 Subject: Figured out that our previous act of just assigning TypeReference objects directly to other nodes (e.g. from a FieldDeclaration's type to a method argument) is NOT a good idea, as this screws up when the TypeReference object represents a generic type (like 'T') - each instance of a generic type has a different resolution, but 1 TypeReference object can only hold 1 resolution. Thus, a copyType() method has been written, and the Handle* classes have been updated to use it. Also, generateEquals() is half-finished in HandleData. --- src/lombok/eclipse/Eclipse.java | 93 +++++++++++++++++ src/lombok/eclipse/handlers/HandleData.java | 139 ++++++++++++++++++-------- src/lombok/eclipse/handlers/HandleGetter.java | 4 +- src/lombok/eclipse/handlers/HandleSetter.java | 2 +- 4 files changed, 193 insertions(+), 45 deletions(-) (limited to 'src/lombok') diff --git a/src/lombok/eclipse/Eclipse.java b/src/lombok/eclipse/Eclipse.java index 8457e28a..e717e491 100644 --- a/src/lombok/eclipse/Eclipse.java +++ b/src/lombok/eclipse/Eclipse.java @@ -22,14 +22,22 @@ import org.eclipse.core.runtime.Status; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; +import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.Literal; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.osgi.framework.Bundle; @@ -76,6 +84,91 @@ public class Eclipse { return sb.toString(); } + public static TypeParameter[] copyTypeParams(TypeParameter[] params) { + if ( params == null ) return null; + TypeParameter[] out = new TypeParameter[params.length]; + int idx = 0; + for ( TypeParameter param : params ) { + TypeParameter o = new TypeParameter(); + o.annotations = param.annotations; + o.bits = param.bits; + o.modifiers = param.modifiers; + o.name = param.name; + o.type = copyType(param.type); + if ( param.bounds != null ) { + TypeReference[] b = new TypeReference[param.bounds.length]; + int idx2 = 0; + for ( TypeReference ref : param.bounds ) b[idx2++] = copyType(ref); + o.bounds = b; + } + out[idx++] = o; + } + return out; + } + + public static TypeReference copyType(TypeReference ref) { + if ( ref instanceof QualifiedTypeReference ) { + QualifiedTypeReference iRef = (QualifiedTypeReference) ref; + return new QualifiedTypeReference(iRef.tokens, iRef.sourcePositions); + } + + if ( ref instanceof ArrayQualifiedTypeReference ) { + ArrayQualifiedTypeReference iRef = (ArrayQualifiedTypeReference) ref; + return new ArrayQualifiedTypeReference(iRef.tokens, iRef.dimensions(), iRef.sourcePositions); + } + + if ( ref instanceof ParameterizedQualifiedTypeReference ) { + ParameterizedQualifiedTypeReference iRef = (ParameterizedQualifiedTypeReference) ref; + TypeReference[][] args = null; + if ( iRef.typeArguments != null ) { + args = new TypeReference[iRef.typeArguments.length][]; + int idx = 0; + for ( TypeReference[] inRefArray : iRef.typeArguments ) { + if ( inRefArray == null ) args[idx++] = null; + else { + TypeReference[] outRefArray = new TypeReference[inRefArray.length]; + int idx2 = 0; + for ( TypeReference inRef : inRefArray ) { + outRefArray[idx2++] = copyType(inRef); + } + args[idx++] = outRefArray; + } + } + } + return new ParameterizedQualifiedTypeReference(iRef.tokens, args, iRef.dimensions(), iRef.sourcePositions); + } + + if ( ref instanceof SingleTypeReference ) { + SingleTypeReference iRef = (SingleTypeReference) ref; + return new SingleTypeReference(iRef.token, (long)iRef.sourceStart << 32 | iRef.sourceEnd); + } + + if ( ref instanceof ArrayTypeReference ) { + ArrayTypeReference iRef = (ArrayTypeReference) ref; + return new ArrayTypeReference(iRef.token, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd); + } + + if ( ref instanceof ParameterizedSingleTypeReference ) { + ParameterizedSingleTypeReference iRef = (ParameterizedSingleTypeReference) ref; + TypeReference[] args = null; + if ( iRef.typeArguments != null ) { + args = new TypeReference[iRef.typeArguments.length]; + int idx = 0; + for ( TypeReference inRef : iRef.typeArguments ) { + if ( inRef == null ) args[idx++] = null; + else args[idx++] = copyType(inRef); + } + } + return new ParameterizedSingleTypeReference(iRef.token, args, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd); + } + + if ( ref instanceof Wildcard ) { + return new Wildcard(((Wildcard)ref).kind); + } + + return ref; + } + public static boolean annotationTypeMatches(Class type, Node node) { if ( node.getKind() != Kind.ANNOTATION ) return false; TypeReference typeRef = ((Annotation)node.get()).type; diff --git a/src/lombok/eclipse/handlers/HandleData.java b/src/lombok/eclipse/handlers/HandleData.java index dd398d74..27757e7d 100644 --- a/src/lombok/eclipse/handlers/HandleData.java +++ b/src/lombok/eclipse/handlers/HandleData.java @@ -1,11 +1,11 @@ package lombok.eclipse.handlers; +import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.PKG.*; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import lombok.AccessLevel; @@ -15,22 +15,28 @@ import lombok.core.AST.Kind; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseAST.Node; +import lombok.eclipse.handlers.PKG.MethodExistsResult; import org.eclipse.jdt.internal.compiler.ast.ASTNode; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; -import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.BinaryExpression; +import org.eclipse.jdt.internal.compiler.ast.CastExpression; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; 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.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; @@ -39,15 +45,12 @@ import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.ThisReference; +import org.eclipse.jdt.internal.compiler.ast.TrueLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import org.eclipse.jdt.internal.compiler.ast.TypeParameter; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; -import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; -import org.eclipse.jdt.internal.compiler.lookup.MethodScope; -import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; -import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.mangosdk.spi.ProviderFor; @ProviderFor(EclipseAnnotationHandler.class) @@ -81,42 +84,22 @@ public class HandleData implements EclipseAnnotationHandler { new HandleSetter().generateSetterForField(child, annotationNode.get()); } - switch ( methodExists("toString", typeNode) ) { - case NOT_EXISTS: + if ( methodExists("toString", typeNode) == MethodExistsResult.NOT_EXISTS ) { MethodDeclaration toString = createToString(typeNode, nodesForConstructorAndToString, ast); injectMethod(typeNode, toString); - //fallthrough - case EXISTS_BY_LOMBOK: -// TypeBinding javaLangString = null; -// if ( typeDecl.scope != null ) javaLangString = typeDecl.scope.getJavaLangString(); -// fixMethodBinding(getExistingLombokMethod("toString", typeNode), javaLangString, Collections.emptyList()); } - switch ( constructorExists(typeNode) ) { - case NOT_EXISTS: + if ( constructorExists(typeNode) == MethodExistsResult.NOT_EXISTS ) { ConstructorDeclaration constructor = createConstructor( ann.staticConstructor().isEmpty(), typeNode, nodesForConstructorAndToString, ast); injectMethod(typeNode, constructor); - //fallthrough - case EXISTS_BY_LOMBOK: -// constructor = createConstructor( -// ann.staticConstructor().isEmpty(), typeNode, nodesForConstructorAndToString, ast); -// injectScopeIntoConstructor(constructor, nodesForConstructorAndToString, typeDecl); -// fixMethodBinding(getExistingLombokConstructor(typeNode), typeDecl.binding, nodesForConstructorAndToString); } if ( !ann.staticConstructor().isEmpty() ) { - switch ( methodExists("of", typeNode) ) { - case NOT_EXISTS: + if ( methodExists("of", typeNode) == MethodExistsResult.NOT_EXISTS ) { MethodDeclaration staticConstructor = createStaticConstructor( ann.staticConstructor(), typeNode, nodesForConstructorAndToString, ast); injectMethod(typeNode, staticConstructor); - //fallthrough - case EXISTS_BY_LOMBOK: -// fixMethodBinding(getExistingLombokMethod(ann.staticConstructor(), typeNode), typeDecl.binding, nodesForConstructorAndToString); -// injectScopeIntoStaticConstructor((MethodDeclaration) getExistingLombokMethod( -// ann.staticConstructor(), typeNode).get(), -// nodesForConstructorAndToString, typeDecl); } } @@ -190,7 +173,7 @@ public class HandleData implements EclipseAnnotationHandler { thisX.token = field.name; assigns.add(new Assignment(thisX, new SingleNameReference(field.name, p), (int)p)); long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd; - args.add(new Argument(field.name, fieldPos, field.type, 0)); + args.add(new Argument(field.name, fieldPos, copyType(field.type), 0)); } constructor.statements = assigns.toArray(new Statement[assigns.size()]); @@ -209,7 +192,7 @@ public class HandleData implements EclipseAnnotationHandler { constructor.annotations = null; constructor.selector = name.toCharArray(); constructor.thrownExceptions = null; - constructor.typeParameters = null; + constructor.typeParameters = copyTypeParams(((TypeDeclaration)type.get()).typeParameters); constructor.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = pos.sourceStart; constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = pos.sourceEnd; @@ -217,13 +200,13 @@ public class HandleData implements EclipseAnnotationHandler { List args = new ArrayList(); List assigns = new ArrayList(); AllocationExpression statement = new AllocationExpression(); - statement.type = constructor.returnType; + statement.type = copyType(constructor.returnType); for ( Node fieldNode : fields ) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd; assigns.add(new SingleNameReference(field.name, fieldPos)); - args.add(new Argument(field.name, fieldPos, field.type, 0)); + args.add(new Argument(field.name, fieldPos, copyType(field.type), 0)); } statement.arguments = assigns.toArray(new Expression[assigns.size()]); @@ -232,14 +215,86 @@ public class HandleData implements EclipseAnnotationHandler { return constructor; } - private MethodDeclaration createEquals(Collection fields) { - //If using an of() constructor, make private. - //method params - //on loop: Assignment(FieldReference(ThisReference, "x"), SingleNameReference("x")) - return null; + private MethodDeclaration createEquals(Node type, Collection fields, ASTNode pos) { + long p = (long)pos.sourceStart << 32 | pos.sourceEnd; + MethodDeclaration method = new MethodDeclaration( + ((CompilationUnitDeclaration) type.top().get()).compilationResult); + + method.modifiers = PKG.toModifier(AccessLevel.PUBLIC); + method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); + method.annotations = null; + method.selector = "equals".toCharArray(); + method.thrownExceptions = null; + method.typeParameters = null; + method.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + method.bodyStart = method.declarationSourceStart = method.sourceStart = pos.sourceStart; + method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = pos.sourceEnd; + method.arguments = new Argument[] { + new Argument(new char[] { 'o' }, 0, + new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, new long[] { 0, 0, 0 }), 0) + }; + + List statements = new ArrayList(); + + /* if ( o == this ) return true; */ { + EqualExpression otherEqualsThis = new EqualExpression( + new SingleNameReference(new char[] { 'o' }, 0), + new ThisReference(0, 0), OperatorIds.EQUAL_EQUAL); + + ReturnStatement returnTrue = new ReturnStatement(new TrueLiteral(0, 0), 0, 0); + IfStatement ifOtherEqualsThis = new IfStatement(otherEqualsThis, returnTrue, 0, 0); + statements.add(ifOtherEqualsThis); + } + + /* if ( o == null ) return false; */ { + EqualExpression otherEqualsNull = new EqualExpression( + new SingleNameReference(new char[] { 'o' }, 0), + new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); + + ReturnStatement returnFalse = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); + IfStatement ifOtherEqualsNull = new IfStatement(otherEqualsNull, returnFalse, 0, 0); + statements.add(ifOtherEqualsNull); + } + + /* if ( o.getClass() != getClass() ) return false; */ { + MessageSend otherGetClass = new MessageSend(); + otherGetClass.receiver = new SingleNameReference(new char[] { 'o' }, 0); + otherGetClass.selector = "getClass".toCharArray(); + MessageSend thisGetClass = new MessageSend(); + thisGetClass.receiver = new ThisReference(0, 0); + thisGetClass.selector = "getClass".toCharArray(); + EqualExpression classesNotEqual = new EqualExpression(otherGetClass, thisGetClass, OperatorIds.NOT_EQUAL); + ReturnStatement returnFalse = new ReturnStatement(new FalseLiteral(0, 0), 0, 0); + IfStatement ifClassesNotEqual = new IfStatement(classesNotEqual, returnFalse, 0, 0); + statements.add(ifClassesNotEqual); + } + + char[] otherN = "other".toCharArray(); + + //TODO fix generics raw type warnings by inserting Wildcards. + /* MyType other = (MyType) o; */ { + if ( !fields.isEmpty() ) { + LocalDeclaration other = new LocalDeclaration(otherN, 0, 0); + other.initialization = new CastExpression( + new SingleNameReference(new char[] { 'o' }, 0), + new SingleNameReference(((TypeDeclaration)type.get()).name, 0)); + } + } + + for ( Node field : fields ) { + FieldDeclaration f = (FieldDeclaration) field.get(); + //compare if primitive, write per-primitive special code, otherwise use == null ? other == null ? .equals(other). + //TODO I LEFT IT HERE + } + + /* return true; */ { + statements.add(new ReturnStatement(new TrueLiteral(0, 0), 0, 0)); + } + method.statements = statements.toArray(new Statement[statements.size()]); + return method; } - private MethodDeclaration createHashCode(Collection fields) { + private MethodDeclaration createHashCode(Node type, Collection fields, ASTNode pos) { //booleans: conditionalexpression that bounces between 1231 and 1237. //longs: (int) (lng ^ (lng >>> 32)); //doubles and floats: Double.doubleToLongBits, then as long. diff --git a/src/lombok/eclipse/handlers/HandleGetter.java b/src/lombok/eclipse/handlers/HandleGetter.java index d0ec673d..b0353a34 100644 --- a/src/lombok/eclipse/handlers/HandleGetter.java +++ b/src/lombok/eclipse/handlers/HandleGetter.java @@ -59,7 +59,7 @@ public class HandleGetter implements EclipseAnnotationHandler { } FieldDeclaration field = (FieldDeclaration) fieldNode.get(); - TypeReference fieldType = field.type; + TypeReference fieldType = Eclipse.copyType(field.type); String getterName = TransformationsUtil.toGetterName( new String(field.name), nameEquals(fieldType.getTypeName(), "boolean")); @@ -89,7 +89,7 @@ public class HandleGetter implements EclipseAnnotationHandler { int modifier, ASTNode pos) { MethodDeclaration method = new MethodDeclaration(parent.compilationResult); method.modifiers = modifier; - method.returnType = field.type; + method.returnType = Eclipse.copyType(field.type); method.annotations = null; method.arguments = null; method.selector = name.toCharArray(); diff --git a/src/lombok/eclipse/handlers/HandleSetter.java b/src/lombok/eclipse/handlers/HandleSetter.java index d33594b1..2367d66d 100644 --- a/src/lombok/eclipse/handlers/HandleSetter.java +++ b/src/lombok/eclipse/handlers/HandleSetter.java @@ -95,7 +95,7 @@ public class HandleSetter implements EclipseAnnotationHandler { method.modifiers = modifier; method.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0); method.annotations = null; - Argument param = new Argument(field.name, pos, field.type, 0); + Argument param = new Argument(field.name, pos, Eclipse.copyType(field.type), 0); method.arguments = new Argument[] { param }; method.selector = name.toCharArray(); method.binding = null; -- cgit