@Delegate, at least without value=, seems to work great in javac now!

This commit is contained in:
Reinier Zwitserloot committed 2010-11-25 12:47:07 +01:00
1 parent 4ded143def
commit c340c9c24f
4 files changed
+278 -14

No files matched your search

+6 -6
View File
@@ -317,8 +317,8 @@ public class JavacResolution {
return iterableParams.isEmpty() ? syms.objectType : types.upperBound(iterableParams.head);
}
public static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast) throws TypeNotConvertibleException {
return typeToJCTree(type, maker, ast, false);
public static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowVoid) throws TypeNotConvertibleException {
return typeToJCTree(type, maker, ast, false, allowVoid);
}
public static JCExpression createJavaLangObject(TreeMaker maker, JavacAST ast) {
@@ -328,7 +328,7 @@ public class JavacResolution {
return out;
}
private static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound) throws TypeNotConvertibleException {
private static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound, boolean allowVoid) throws TypeNotConvertibleException {
int dims = 0;
Type type0 = type;
while (type0 instanceof ArrayType) {
@@ -336,7 +336,7 @@ public class JavacResolution {
type0 = ((ArrayType)type0).elemtype;
}
JCExpression result = typeToJCTree0(type0, maker, ast, allowCompound);
JCExpression result = typeToJCTree0(type0, maker, ast, allowCompound, allowVoid);
while (dims > 0) {
result = maker.TypeArray(result);
dims--;
@@ -344,12 +344,12 @@ public class JavacResolution {
return result;
}
private static JCExpression typeToJCTree0(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound) throws TypeNotConvertibleException {
private static JCExpression typeToJCTree0(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound, boolean allowVoid) throws TypeNotConvertibleException {
// NB: There's such a thing as maker.Type(type), but this doesn't work very well; it screws up anonymous classes, captures, and adds an extra prefix dot for some reason too.
// -- so we write our own take on that here.
if (type.tag == TypeTags.BOT) return createJavaLangObject(maker, ast);
if (type.tag == TypeTags.VOID) return allowVoid ? primitiveToJCTree(type.getKind(), maker) : createJavaLangObject(maker, ast);
if (type.isPrimitive()) return primitiveToJCTree(type.getKind(), maker);
if (type.isErroneous()) throw new TypeNotConvertibleException("Type cannot be resolved");
@@ -0,0 +1,264 @@
/*
* Copyright © 2010 Reinier Zwitserloot, Roel Spilker and Robbert Jan Grootjans.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.javac.handlers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import lombok.Delegate;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacResolution;
import lombok.javac.JavacResolution.TypeNotConvertibleException;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ClassType;
import com.sun.tools.javac.code.Type.TypeVar;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
@ProviderFor(JavacAnnotationHandler.class)
public class HandleDelegate implements JavacAnnotationHandler<Delegate> {
@Override public boolean isResolutionBased() {
return true;
}
private static final List<String> METHODS_IN_OBJECT = Collections.unmodifiableList(Arrays.asList(
"hashCode()",
"equals(java.lang.Object)",
"wait()",
"wait(long)",
"wait(long, int)",
"notify()",
"notifyAll()",
"toString()",
"getClass()",
"clone()",
"finalize()"));
@Override public boolean handle(AnnotationValues<Delegate> annotation, JCAnnotation ast, JavacNode annotationNode) {
if (annotationNode.up().getKind() != Kind.FIELD) return false; // TODO error
List<Object> delegateTypes = annotation.getActualExpressions("value");
JavacResolution reso = new JavacResolution(annotationNode.getContext());
List<Type> resolved = new ArrayList<Type>();
if (delegateTypes.isEmpty()) {
Type type = ((JCVariableDecl)annotationNode.up().get()).type;
if (type == null) reso.resolveClassMember(annotationNode.up());
//TODO I'm fairly sure the above line (and that entire method) does effectively bupkis!
type = ((JCVariableDecl)annotationNode.up().get()).type;
if (type != null) resolved.add(type);
} else {
for (Object dt : delegateTypes) {
if (dt instanceof JCFieldAccess && ((JCFieldAccess)dt).name.toString().equals("class")) {
Type type = ((JCFieldAccess)dt).selected.type;
if (type == null) reso.resolveClassMember(annotationNode);
type = ((JCFieldAccess)dt).selected.type;
if (type != null) resolved.add(type);
}
}
}
List<MethodSig> signatures = new ArrayList<MethodSig>();
Set<String> banList = new HashSet<String>();
for (Type t : resolved) {
banList.addAll(METHODS_IN_OBJECT);
JavacNode typeNode = annotationNode.up().up();
for (Symbol member : ((JCClassDecl)typeNode.get()).sym.getEnclosedElements()) {
if (member instanceof MethodSymbol) {
MethodSymbol method = (MethodSymbol) member;
banList.add(printSig((ExecutableType) method.asType(), method.name, annotationNode.getTypesUtil()));
}
}
if (t instanceof ClassType) {
ClassType ct = (ClassType) t;
addMethodBindings(signatures, ct, annotationNode, banList);
} else {
annotationNode.addError("@Delegate can only use concrete class types, not wildcards, arrays, type variables, or primitives.");
return false;
}
}
Name delegateFieldName = annotationNode.toName(annotationNode.up().getName());
for (MethodSig sig : signatures) generateAndAdd(sig, annotationNode, delegateFieldName);
return false;
}
private void generateAndAdd(MethodSig sig, JavacNode annotation, Name delegateFieldName) {
try {
JCMethodDecl method = createDelegateMethod(sig, annotation, delegateFieldName);
JavacHandlerUtil.injectMethod(annotation.up().up(), method);
} catch (TypeNotConvertibleException e) {
annotation.addError("Can't create delegate method for " + sig.name + ": " + e.getMessage());
}
}
private JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Name delegateFieldName) throws TypeNotConvertibleException {
/** public <P1, P2, ...> ReturnType methodName(ParamType1 $p1, ParamType2 $p2, ...) throws T1, T2, ... {
* (return) delegate.<P1, P2>methodName($p1, $p2);
* }
*/
TreeMaker maker = annotation.getTreeMaker();
com.sun.tools.javac.util.List<JCAnnotation> annotations;
if (sig.isDeprecated) {
annotations = com.sun.tools.javac.util.List.of(maker.Annotation(
JavacHandlerUtil.chainDots(maker, annotation, "java", "lang", "Deprecated"),
com.sun.tools.javac.util.List.<JCExpression>nil()));
} else {
annotations = com.sun.tools.javac.util.List.nil();
}
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annotations);
JCExpression returnType = JavacResolution.typeToJCTree((Type) sig.type.getReturnType(), maker, annotation.getAst(), true);
boolean useReturn = sig.type.getReturnType().getKind() != TypeKind.VOID;
ListBuffer<JCVariableDecl> params = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCVariableDecl>();
ListBuffer<JCExpression> args = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCExpression>();
ListBuffer<JCExpression> thrown = sig.type.getThrownTypes().isEmpty() ? null : new ListBuffer<JCExpression>();
ListBuffer<JCTypeParameter> typeParams = sig.type.getTypeVariables().isEmpty() ? null : new ListBuffer<JCTypeParameter>();
ListBuffer<JCExpression> typeArgs = sig.type.getTypeVariables().isEmpty() ? null : new ListBuffer<JCExpression>();
Types types = Types.instance(annotation.getContext());
int nameCounter = 0;
for (TypeMirror param : sig.type.getTypeVariables()) {
Name name = annotation.toName("P" + (nameCounter++));
typeParams.append(maker.TypeParameter(name, maker.Types(types.getBounds((TypeVar) param))));
typeArgs.append(maker.Ident(name));
}
for (TypeMirror ex : sig.type.getThrownTypes()) {
thrown.append(JavacResolution.typeToJCTree((Type) ex, maker, annotation.getAst(), true));
}
nameCounter = 0;
for (TypeMirror param : sig.type.getParameterTypes()) {
Name name = annotation.toName("$a" + (nameCounter++));
JCModifiers paramMods = maker.Modifiers(Flags.FINAL);
params.append(maker.VarDef(paramMods, name, JavacResolution.typeToJCTree((Type) param, maker, annotation.getAst(), true), null));
args.append(maker.Ident(name));
}
JCExpression delegateFieldRef = maker.Select(maker.Ident(annotation.toName("this")), delegateFieldName);
JCExpression delegateCall = maker.Apply(toList(typeArgs), maker.Select(delegateFieldRef, sig.name), toList(args));
JCStatement body = useReturn ? maker.Return(delegateCall) : maker.Exec(delegateCall);
JCBlock bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(body));
return maker.MethodDef(mods, sig.name, returnType, toList(typeParams), toList(params), toList(thrown), bodyBlock, null);
}
private static <T> com.sun.tools.javac.util.List<T> toList(ListBuffer<T> collection) {
return collection == null ? com.sun.tools.javac.util.List.<T>nil() : collection.toList();
}
private void addMethodBindings(List<MethodSig> signatures, ClassType ct, JavacNode node, Set<String> banList) {
TypeSymbol tsym = ct.asElement();
if (tsym == null) return;
for (Symbol member : tsym.getEnclosedElements()) {
if (member.getKind() != ElementKind.METHOD) continue;
if (member.isStatic()) continue;
if (member.isConstructor()) continue;
ExecutableElement exElem = (ExecutableElement)member;
if (!exElem.getModifiers().contains(Modifier.PUBLIC)) continue;
ExecutableType methodType = (ExecutableType) node.getTypesUtil().asMemberOf(ct, member);
String sig = printSig(methodType, member.name, node.getTypesUtil());
if (!banList.add(sig)) continue; //If add returns false, it was already in there
boolean isDeprecated = exElem.getAnnotation(Deprecated.class) != null;
signatures.add(new MethodSig(member.name, methodType, isDeprecated));
}
if (ct.supertype_field instanceof ClassType) addMethodBindings(signatures, (ClassType) ct.supertype_field, node, banList);
if (ct.interfaces_field != null) for (Type iface : ct.interfaces_field) {
if (iface instanceof ClassType) addMethodBindings(signatures, (ClassType) iface, node, banList);
}
}
private static class MethodSig {
final Name name;
final ExecutableType type;
final boolean isDeprecated;
MethodSig(Name name, ExecutableType type, boolean isDeprecated) {
this.name = name;
this.type = type;
this.isDeprecated = isDeprecated;
}
@Override public String toString() {
return (isDeprecated ? "@Deprecated " : "") + name + " " + type;
}
}
private static String printSig(ExecutableType method, Name name, JavacTypes types) {
StringBuilder sb = new StringBuilder();
sb.append(name.toString()).append("(");
boolean first = true;
for (TypeMirror param : method.getParameterTypes()) {
if (!first) sb.append(", ");
first = false;
sb.append(typeBindingToSignature(param, types));
}
return sb.append(")").toString();
}
private static String typeBindingToSignature(TypeMirror binding, JavacTypes types) {
binding = types.erasure(binding);
return binding.toString();
}
}
@@ -90,9 +90,9 @@ public class HandleVal extends JavacASTAdapter {
if (rhsOfEnhancedForLoop != null) {
Type componentType = JavacResolution.ifTypeIsIterableToComponent(type, localNode.getAst());
if (componentType == null) replacement = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst());
else replacement = JavacResolution.typeToJCTree(componentType, localNode.getTreeMaker(), localNode.getAst());
else replacement = JavacResolution.typeToJCTree(componentType, localNode.getTreeMaker(), localNode.getAst(), false);
} else {
replacement = JavacResolution.typeToJCTree(type, localNode.getTreeMaker(), localNode.getAst());
replacement = JavacResolution.typeToJCTree(type, localNode.getTreeMaker(), localNode.getAst(), false);
}
if (replacement != null) {
@@ -72,6 +72,7 @@ import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
public class PatchDelegate {
static void addPatches(ScriptManager sm, boolean ecj) {
@@ -287,7 +288,7 @@ public class PatchDelegate {
addAllMethodBindings(list, binding, banList);
}
private static void addAllMethodBindings(List<MethodBinding> list, TypeBinding binding, Collection<String> banList) {
private static void addAllMethodBindings(List<MethodBinding> list, TypeBinding binding, Set<String> banList) {
if (binding == null) return;
if (binding instanceof MemberTypeBinding) {
ClassScope cs = ((SourceTypeBinding)binding).scope;
@@ -310,9 +311,8 @@ public class PatchDelegate {
if (mb.isDefaultAbstract()) continue;
if (!mb.isPublic()) continue;
if (mb.isSynthetic()) continue;
if (banList.contains(sig)) continue;
if (!banList.add(sig)) continue; // If add returns false, it was already in there.
list.add(mb);
banList.add(sig);
}
addAllMethodBindings(list, rb.superclass(), banList);
ReferenceBinding[] interfaces = rb.superInterfaces();
@@ -344,14 +344,14 @@ public class PatchDelegate {
if (binding.parameters != null) for (TypeBinding param : binding.parameters) {
if (!first) signature.append(", ");
first = false;
signature.append(simpleTypeBindingToString(param));
signature.append(typeBindingToSignature(param));
}
signature.append(")");
return signature.toString();
}
private static String simpleTypeBindingToString(TypeBinding binding) {
private static String typeBindingToSignature(TypeBinding binding) {
binding = binding.erasure();
if (binding != null && binding.isBaseType()) {
return new String (binding.sourceName());
@@ -361,7 +361,7 @@ public class PatchDelegate {
return pkg.isEmpty() ? qsn : (pkg + "." + qsn);
} else if (binding instanceof ArrayBinding) {
StringBuilder out = new StringBuilder();
out.append(simpleTypeBindingToString(binding.leafComponentType()));
out.append(typeBindingToSignature(binding.leafComponentType()));
for (int i = 0; i < binding.dimensions(); i++) out.append("[]");
return out.toString();
}