diff options
| author | Michael Williamson <mike@zwobble.org> | 2016-04-09 14:59:32 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2016-04-09 14:59:32 +0100 |
| commit | 2bc236438ce5d5120c9c34f213b4b49b475dcee2 (patch) | |
| tree | be5008e70cf11ebece0c07165c86c034ddef3920 /src/main/java/org/zwobble | |
| parent | 0a550c3bb4e624d76b3876e92a2bd0e235301fba (diff) | |
Read generic signature for method calls
Diffstat (limited to 'src/main/java/org/zwobble')
7 files changed, 129 insertions, 64 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java b/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java index adc3b05..3f9b06b 100644 --- a/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java +++ b/src/main/java/org/zwobble/couscous/ast/MethodCallNode.java @@ -87,23 +87,36 @@ public class MethodCallNode implements ExpressionNode { List<ExpressionNode> arguments, MethodSignature signature) { - return new MethodCallNode(receiver, methodName, arguments, signature); + return new MethodCallNode(receiver, methodName, arguments, signature.getReturnType(), signature); + } + + public static MethodCallNode methodCall( + Receiver receiver, + String methodName, + List<ExpressionNode> arguments, + Type returnType, + MethodSignature signature) + { + return new MethodCallNode(receiver, methodName, arguments, returnType, signature); } private final Receiver receiver; private final String methodName; private final List<ExpressionNode> arguments; + private final Type returnType; private final MethodSignature signature; private MethodCallNode( Receiver receiver, String methodName, List<ExpressionNode> arguments, + Type returnType, MethodSignature signature) { this.receiver = receiver; this.methodName = methodName; this.arguments = arguments; + this.returnType = returnType; this.signature = signature; } @@ -121,7 +134,7 @@ public class MethodCallNode implements ExpressionNode { @Override public Type getType() { - return signature.getReturnType(); + return returnType; } public MethodSignature signature() { @@ -139,59 +152,43 @@ public class MethodCallNode implements ExpressionNode { transformer.transformReceiver(receiver), transformer.transformMethodName(signature()), transformer.transformExpressions(arguments), + transformer.transform(returnType), transformer.transform(signature)); } @Override public String toString() { - return "MethodCallNode(receiver=" + receiver + ", methodName=" - + methodName + ", arguments=" + arguments + ", signature=" + signature - + ")"; + return "MethodCallNode(" + + "receiver=" + receiver + + ", methodName=" + methodName + + ", arguments=" + arguments + + ", returnType=" + returnType + + ", signature=" + signature + + ')'; } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((arguments == null) ? 0 : arguments.hashCode()); - result = prime * result - + ((methodName == null) ? 0 : methodName.hashCode()); - result = prime * result - + ((receiver == null) ? 0 : receiver.hashCode()); - result = prime * result + ((signature == null) ? 0 : signature.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MethodCallNode that = (MethodCallNode) o; + + if (!receiver.equals(that.receiver)) return false; + if (!methodName.equals(that.methodName)) return false; + if (!arguments.equals(that.arguments)) return false; + if (!returnType.equals(that.returnType)) return false; + return signature.equals(that.signature); + } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - MethodCallNode other = (MethodCallNode) obj; - if (arguments == null) { - if (other.arguments != null) - return false; - } else if (!arguments.equals(other.arguments)) - return false; - if (methodName == null) { - if (other.methodName != null) - return false; - } else if (!methodName.equals(other.methodName)) - return false; - if (receiver == null) { - if (other.receiver != null) - return false; - } else if (!receiver.equals(other.receiver)) - return false; - if (signature == null) { - if (other.signature != null) - return false; - } else if (!signature.equals(other.signature)) - return false; - return true; + public int hashCode() { + int result = receiver.hashCode(); + result = 31 * result + methodName.hashCode(); + result = 31 * result + arguments.hashCode(); + result = 31 * result + returnType.hashCode(); + result = 31 * result + signature.hashCode(); + return result; } } diff --git a/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java b/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java index 4a90ee7..bd59261 100644 --- a/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java +++ b/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java @@ -5,6 +5,10 @@ import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; public class StaticReceiver implements Receiver { + public static Receiver staticReceiver(String type) { + return new StaticReceiver(ScalarType.of(type)); + } + public static Receiver staticReceiver(ScalarType type) { return new StaticReceiver(type); } diff --git a/src/main/java/org/zwobble/couscous/ast/TypeCoercionNode.java b/src/main/java/org/zwobble/couscous/ast/TypeCoercionNode.java index 721027f..503aa7e 100644 --- a/src/main/java/org/zwobble/couscous/ast/TypeCoercionNode.java +++ b/src/main/java/org/zwobble/couscous/ast/TypeCoercionNode.java @@ -4,9 +4,11 @@ import org.zwobble.couscous.types.Type; import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; +import static org.zwobble.couscous.types.Types.concrete; + public class TypeCoercionNode implements ExpressionNode { public static ExpressionNode coerce(ExpressionNode expression, Type type) { - if (type.equals(expression.getType())) { + if (concrete(type).equals(concrete(expression.getType()))) { return expression; } else { return new TypeCoercionNode(expression, type); diff --git a/src/main/java/org/zwobble/couscous/ast/identifiers/Identifiers.java b/src/main/java/org/zwobble/couscous/ast/identifiers/Identifiers.java index 9beac27..f413ec0 100644 --- a/src/main/java/org/zwobble/couscous/ast/identifiers/Identifiers.java +++ b/src/main/java/org/zwobble/couscous/ast/identifiers/Identifiers.java @@ -1,5 +1,7 @@ package org.zwobble.couscous.ast.identifiers; +import org.zwobble.couscous.types.ScalarType; + public class Identifiers { public static final Identifier TOP = new TopIdentifier(); @@ -26,4 +28,8 @@ public class Identifiers { public static Identifier forType(String name) { return type(TOP, name); } + + public static Identifier forType(ScalarType type) { + return forType(type.getQualifiedName()); + } } diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java index 4c27a75..0d19cd1 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java @@ -18,7 +18,6 @@ import static org.zwobble.couscous.ast.ConstructorCallNode.constructorCall; import static org.zwobble.couscous.ast.InstanceReceiver.instanceReceiver; import static org.zwobble.couscous.ast.LiteralNode.literal; import static org.zwobble.couscous.ast.MethodCallNode.methodCall; -import static org.zwobble.couscous.ast.MethodCallNode.staticMethodCall; import static org.zwobble.couscous.ast.OperationNode.operation; import static org.zwobble.couscous.ast.Operations.not; import static org.zwobble.couscous.ast.StaticReceiver.staticReceiver; @@ -210,24 +209,27 @@ public class JavaExpressionReader { List<ExpressionNode> arguments = readArguments( expression.resolveMethodBinding(), expression.arguments()); - MethodSignature signature = JavaMethods.signature(expression.resolveMethodBinding()); + Type returnType = typeOf(expression); + MethodSignature signature = JavaMethods.signature(expression.resolveMethodBinding().getMethodDeclaration()); IMethodBinding methodBinding = expression.resolveMethodBinding(); Type receiverType = typeOf(methodBinding.getDeclaringClass()); if ((Modifier.isStatic(methodBinding.getModifiers()))) { - return staticMethodCall( - erasure(receiverType), + return methodCall( + staticReceiver(erasure(receiverType)), methodName, arguments, + returnType, signature); } else { ExpressionNode receiver = expression.getExpression() == null ? thisReference(receiverType) : readExpressionWithoutBoxing(expression.getExpression()); return methodCall( - receiver, + instanceReceiver(receiver), methodName, arguments, + returnType, signature); } } diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaMethods.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaMethods.java index b52110f..014e4ae 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaMethods.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaMethods.java @@ -4,12 +4,16 @@ import com.google.common.collect.Iterables; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.zwobble.couscous.ast.MethodSignature; +import org.zwobble.couscous.ast.identifiers.Identifier; +import org.zwobble.couscous.ast.identifiers.Identifiers; import org.zwobble.couscous.types.Type; +import org.zwobble.couscous.types.TypeParameter; import java.util.List; import static java.util.Arrays.asList; import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf; +import static org.zwobble.couscous.types.Types.erasure; import static org.zwobble.couscous.util.ExtraLists.eagerMap; import static org.zwobble.couscous.util.ExtraLists.list; @@ -17,26 +21,50 @@ class JavaMethods { static MethodSignature signature(IMethodBinding method) { ITypeBinding declaringClass = method.getDeclaringClass(); ITypeBinding erasedDeclaringClass = declaringClass.getErasure(); + IMethodBinding declaringMethod = method.getMethodDeclaration(); - List<Type> argumentTypes = eagerMap(asList(method.getParameterTypes()), JavaTypes::typeOf); - Type returnType = typeOf(method.getReturnType()); + MethodSignature unboundSignature = unboundSignature(method); - if (declaringClass.isEqualTo(erasedDeclaringClass)) { - return new MethodSignature(method.getName(), list(), argumentTypes, returnType); - } else { + if (!declaringMethod.isEqualTo(method)) { + return bind(unboundSignature(declaringMethod), unboundSignature); + } else if (!declaringClass.isEqualTo(erasedDeclaringClass)) { IMethodBinding[] erasedMethods = erasedDeclaringClass.getDeclaredMethods(); IMethodBinding erasedMethod = Iterables.getOnlyElement(Iterables.filter( asList(erasedMethods), m -> m.isSubsignature(method))); - return new MethodSignature( - method.getName(), - list(), - eagerMap( - argumentTypes, - asList(erasedMethod.getParameterTypes()), - (argumentType, erasedParameterType) -> JavaTypes.bind(typeOf(erasedParameterType), argumentType)), - JavaTypes.bind(typeOf(erasedMethod.getReturnType()), returnType)); + return bind(unboundSignature(erasedMethod), unboundSignature); + } else { + return unboundSignature; + } + } + + private static MethodSignature bind(MethodSignature genericSignature, MethodSignature concreteSignature) { + if (!genericSignature.getTypeParameters().isEmpty()) { + throw new UnsupportedOperationException(); } + return new MethodSignature( + concreteSignature.getName(), + list(), + eagerMap( + genericSignature.getArguments(), + concreteSignature.getArguments(), + JavaTypes::bind), + JavaTypes.bind(genericSignature.getReturnType(), concreteSignature.getReturnType())); + } + + private static MethodSignature unboundSignature(IMethodBinding method) { + List<TypeParameter> typeParameters = eagerMap( + asList(method.getTypeParameters()), + parameter -> new TypeParameter(identifierFor(method), parameter.getName())); + List<Type> argumentTypes = eagerMap(asList(method.getParameterTypes()), JavaTypes::typeOf); + Type returnType = typeOf(method.getReturnType()); + return new MethodSignature(method.getName(), typeParameters, argumentTypes, returnType); } + private static Identifier identifierFor(IMethodBinding method) { + // TODO: disambiguate overloads + return Identifiers.method( + Identifiers.forType(erasure(typeOf(method.getDeclaringClass()))), + method.getName()); + } } diff --git a/src/main/java/org/zwobble/couscous/types/Types.java b/src/main/java/org/zwobble/couscous/types/Types.java index 89e88a6..505d9a7 100644 --- a/src/main/java/org/zwobble/couscous/types/Types.java +++ b/src/main/java/org/zwobble/couscous/types/Types.java @@ -95,4 +95,30 @@ public class Types { } }); } + + public static Type concrete(Type type) { + return type.accept(new Type.Visitor<Type>() { + @Override + public Type visit(ScalarType type) { + return type; + } + + @Override + public Type visit(TypeParameter parameter) { + return parameter; + } + + @Override + public Type visit(ParameterizedType type) { + return new ParameterizedType( + type.getRawType(), + eagerMap(type.getParameters(), parameter -> parameter.accept(this))); + } + + @Override + public Type visit(BoundTypeParameter type) { + return type.getValue(); + } + }); + } } |
