diff options
Diffstat (limited to 'src')
10 files changed, 176 insertions, 72 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(); + } + }); + } } diff --git a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java index cbf4fb5..ea8865a 100644 --- a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java +++ b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java @@ -1,6 +1,5 @@ package org.zwobble.couscous.tests; -import org.junit.Ignore; import org.junit.Test; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Types; @@ -218,7 +217,6 @@ public abstract class CompilerTests { } @Test - @Ignore("WIP") public void genericMethods() throws Exception { assertEquals( value("two"), diff --git a/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java b/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java index 5d44dc7..b6a0cb7 100644 --- a/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java +++ b/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java @@ -1,5 +1,7 @@ package org.zwobble.couscous.tests.backends.csharp; +import org.junit.Ignore; +import org.junit.Test; import org.zwobble.couscous.CouscousCompiler; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.backends.csharp.CsharpBackend; @@ -16,6 +18,11 @@ import static org.zwobble.couscous.tests.util.ExtraFiles.deleteRecursively; import static org.zwobble.couscous.util.ExtraLists.list; public class JavaToCsharpTests extends CompilerTests { + @Test + @Ignore("WIP") + public void genericMethods() throws Exception { + } + protected PrimitiveValue execProgram( Path directory, ScalarType type, diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java index 72646bc..91136b6 100644 --- a/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java +++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java @@ -2,7 +2,9 @@ package org.zwobble.couscous.tests.frontends.java; import org.junit.Test; import org.zwobble.couscous.ast.*; +import org.zwobble.couscous.ast.identifiers.Identifiers; import org.zwobble.couscous.types.ScalarType; +import org.zwobble.couscous.types.TypeParameter; import org.zwobble.couscous.types.Types; import java.util.List; @@ -20,7 +22,9 @@ import static org.zwobble.couscous.ast.FieldDeclarationNode.staticField; 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.MethodSignature.signature; import static org.zwobble.couscous.ast.Operations.*; +import static org.zwobble.couscous.ast.StaticReceiver.staticReceiver; import static org.zwobble.couscous.ast.TernaryConditionalNode.ternaryConditional; import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference; import static org.zwobble.couscous.ast.TypeCoercionNode.typeCoercion; @@ -28,6 +32,7 @@ import static org.zwobble.couscous.ast.VariableReferenceNode.reference; import static org.zwobble.couscous.tests.frontends.java.JavaReading.*; import static org.zwobble.couscous.tests.frontends.java.NodeMatchers.*; import static org.zwobble.couscous.types.ParameterizedType.parameterizedType; +import static org.zwobble.couscous.types.TypeParameter.typeParameter; import static org.zwobble.couscous.util.ExtraLists.list; public class ExpressionReadingTests { @@ -213,27 +218,56 @@ public class ExpressionReadingTests { @Test public void canReadMethodCallsWithZeroExpressionsAsVarargs() { + TypeParameter typeParameter = typeParameter(Identifiers.method(Identifiers.forType("java.util.Arrays"), "asList"), "T"); assertEquals( - staticMethodCall( - ScalarType.of("java.util.Arrays"), + methodCall( + staticReceiver("java.util.Arrays"), "asList", list(array(Types.STRING, list())), - parameterizedType(ScalarType.of("java.util.List"), list(Types.STRING))), + parameterizedType(ScalarType.of("java.util.List"), list(Types.STRING)), + signature( + "asList", + list(typeParameter), + list(Types.array(typeParameter)), + parameterizedType(ScalarType.of("java.util.List"), list(typeParameter)))), readExpression("java.util.List<String>", "java.util.Arrays.asList()")); } @Test public void canReadMethodCallsWithMultipleExpressionsAsVarargs() { + TypeParameter typeParameter = typeParameter(Identifiers.method(Identifiers.forType("java.util.Arrays"), "asList"), "T"); assertEquals( - staticMethodCall( - ScalarType.of("java.util.Arrays"), + methodCall( + staticReceiver("java.util.Arrays"), "asList", list(array(Types.STRING, list(literal("one"), literal("two")))), - parameterizedType(ScalarType.of("java.util.List"), list(Types.STRING))), + parameterizedType(ScalarType.of("java.util.List"), list(Types.STRING)), + signature( + "asList", + list(typeParameter), + list(Types.array(typeParameter)), + parameterizedType(ScalarType.of("java.util.List"), list(typeParameter)))), readExpression("java.util.List<String>", "java.util.Arrays.asList(\"one\", \"two\")")); } @Test + public void canReadGenericStaticMethodCalls() { + TypeParameter typeParameter = typeParameter(Identifiers.method(Identifiers.forType("java.util.Collections"), "emptyList"), "T"); + assertEquals( + methodCall( + staticReceiver("java.util.Collections"), + "emptyList", + list(), + parameterizedType(ScalarType.of("java.util.List"), list(Types.STRING)), + signature( + "emptyList", + list(typeParameter), + list(), + parameterizedType(ScalarType.of("java.util.List"), list(typeParameter)))), + readExpression("java.util.List<String>", "java.util.Collections.<String>emptyList()")); + } + + @Test public void canReadConstructorCalls() { assertEquals( constructorCall(ScalarType.of("java.lang.String"), list(literal("_"))), |
