aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/MethodCallNode.java87
-rw-r--r--src/main/java/org/zwobble/couscous/ast/StaticReceiver.java4
-rw-r--r--src/main/java/org/zwobble/couscous/ast/TypeCoercionNode.java4
-rw-r--r--src/main/java/org/zwobble/couscous/ast/identifiers/Identifiers.java6
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java12
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaMethods.java54
-rw-r--r--src/main/java/org/zwobble/couscous/types/Types.java26
-rw-r--r--src/test/java/org/zwobble/couscous/tests/CompilerTests.java2
-rw-r--r--src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java7
-rw-r--r--src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java46
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("_"))),