aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2016-03-09 22:44:29 +0000
committerMichael Williamson <mike@zwobble.org>2016-03-10 16:43:05 +0000
commit2a182825a88b6ee957e205862f5cf3a505b3a173 (patch)
tree2054c8a0c4252bd4b3f161de0b34482158ee7728 /src/main/java
parent20dc1e124d606cf4298d5112b6d3c4f6c74e9b68 (diff)
Initial implementation of generics for C#
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/LiteralNode.java13
-rw-r--r--src/main/java/org/zwobble/couscous/ast/types/BoundTypeParameter.java55
-rw-r--r--src/main/java/org/zwobble/couscous/ast/types/ParameterizedType.java57
-rw-r--r--src/main/java/org/zwobble/couscous/ast/types/Type.java3
-rw-r--r--src/main/java/org/zwobble/couscous/ast/types/TypeParameter.java51
-rw-r--r--src/main/java/org/zwobble/couscous/ast/types/Types.java17
-rw-r--r--src/main/java/org/zwobble/couscous/backends/Names.java25
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java63
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java31
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java3
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java106
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java20
-rw-r--r--src/main/java/org/zwobble/couscous/util/ExtraLists.java29
13 files changed, 437 insertions, 36 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java
index bb38562..df2b87c 100644
--- a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java
@@ -1,10 +1,13 @@
package org.zwobble.couscous.ast;
+import org.zwobble.couscous.ast.types.ParameterizedType;
import org.zwobble.couscous.ast.types.ScalarType;
+import org.zwobble.couscous.ast.types.Type;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.values.*;
+import static org.zwobble.couscous.util.ExtraLists.list;
import static org.zwobble.couscous.values.PrimitiveValues.value;
public class LiteralNode implements ExpressionNode {
@@ -27,22 +30,22 @@ public class LiteralNode implements ExpressionNode {
}
public static LiteralNode literal(ScalarType type) {
- return new LiteralNode(value(type), ObjectValues.CLASS);
+ return new LiteralNode(value(type), new ParameterizedType(ObjectValues.CLASS, list(type)));
}
- public static LiteralNode of(PrimitiveValue value, ScalarType type) {
+ public static LiteralNode of(PrimitiveValue value, Type type) {
return new LiteralNode(value, type);
}
private final PrimitiveValue value;
- private final ScalarType type;
+ private final Type type;
- private LiteralNode(PrimitiveValue value, ScalarType type) {
+ private LiteralNode(PrimitiveValue value, Type type) {
this.value = value;
this.type = type;
}
- public ScalarType getType() {
+ public Type getType() {
return type;
}
diff --git a/src/main/java/org/zwobble/couscous/ast/types/BoundTypeParameter.java b/src/main/java/org/zwobble/couscous/ast/types/BoundTypeParameter.java
new file mode 100644
index 0000000..abb8906
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/types/BoundTypeParameter.java
@@ -0,0 +1,55 @@
+package org.zwobble.couscous.ast.types;
+
+public class BoundTypeParameter implements Type {
+ public static BoundTypeParameter boundTypeParameter(TypeParameter parameter, Type value) {
+ return new BoundTypeParameter(parameter, value);
+ }
+
+ private final TypeParameter parameter;
+ private final Type value;
+
+ public BoundTypeParameter(TypeParameter parameter, Type value) {
+ this.parameter = parameter;
+ this.value = value;
+ }
+
+ public TypeParameter getParameter() {
+ return parameter;
+ }
+
+ public Type getValue() {
+ return value;
+ }
+
+ @Override
+ public <T> T accept(Visitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ return "BoundTypeParameter(" +
+ "parameter=" + parameter +
+ ", value=" + value +
+ ')';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BoundTypeParameter that = (BoundTypeParameter) o;
+
+ if (!parameter.equals(that.parameter)) return false;
+ return value.equals(that.value);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = parameter.hashCode();
+ result = 31 * result + value.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/types/ParameterizedType.java b/src/main/java/org/zwobble/couscous/ast/types/ParameterizedType.java
new file mode 100644
index 0000000..a23fdc0
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/types/ParameterizedType.java
@@ -0,0 +1,57 @@
+package org.zwobble.couscous.ast.types;
+
+import java.util.List;
+
+public class ParameterizedType implements Type {
+ public static ParameterizedType parameterizedType(ScalarType rawType, List<Type> parameters) {
+ return new ParameterizedType(rawType, parameters);
+ }
+
+ private final ScalarType rawType;
+ private final List<Type> parameters;
+
+ public ParameterizedType(ScalarType rawType, List<Type> parameters) {
+ this.rawType = rawType;
+ this.parameters = parameters;
+ }
+
+ public ScalarType getRawType() {
+ return rawType;
+ }
+
+ public List<Type> getParameters() {
+ return parameters;
+ }
+
+ @Override
+ public <T> T accept(Visitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ return "ParameterizedType(" +
+ "rawType=" + rawType +
+ ", parameters=" + parameters +
+ ')';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ParameterizedType that = (ParameterizedType) o;
+
+ if (!rawType.equals(that.rawType)) return false;
+ return parameters.equals(that.parameters);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = rawType.hashCode();
+ result = 31 * result + parameters.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/types/Type.java b/src/main/java/org/zwobble/couscous/ast/types/Type.java
index e936279..2a64341 100644
--- a/src/main/java/org/zwobble/couscous/ast/types/Type.java
+++ b/src/main/java/org/zwobble/couscous/ast/types/Type.java
@@ -5,5 +5,8 @@ public interface Type {
interface Visitor<T> {
T visit(ScalarType type);
+ T visit(TypeParameter parameter);
+ T visit(ParameterizedType type);
+ T visit(BoundTypeParameter type);
}
}
diff --git a/src/main/java/org/zwobble/couscous/ast/types/TypeParameter.java b/src/main/java/org/zwobble/couscous/ast/types/TypeParameter.java
new file mode 100644
index 0000000..0dee75e
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/types/TypeParameter.java
@@ -0,0 +1,51 @@
+package org.zwobble.couscous.ast.types;
+
+public class TypeParameter implements Type {
+ public static TypeParameter typeParameter(ScalarType declaringType, String name) {
+ return new TypeParameter(declaringType, name);
+ }
+
+ private final ScalarType declaringType;
+ private final String name;
+
+ public TypeParameter(ScalarType declaringType, String name) {
+ this.declaringType = declaringType;
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public <T> T accept(Visitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public String toString() {
+ return "TypeParameter(" +
+ "declaringType=" + declaringType +
+ ", name=" + name +
+ ')';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeParameter that = (TypeParameter) o;
+
+ if (!declaringType.equals(that.declaringType)) return false;
+ return name.equals(that.name);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = declaringType.hashCode();
+ result = 31 * result + name.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/types/Types.java b/src/main/java/org/zwobble/couscous/ast/types/Types.java
index c222c54..5fb079b 100644
--- a/src/main/java/org/zwobble/couscous/ast/types/Types.java
+++ b/src/main/java/org/zwobble/couscous/ast/types/Types.java
@@ -1,5 +1,7 @@
package org.zwobble.couscous.ast.types;
+import org.zwobble.couscous.values.ObjectValues;
+
public class Types {
public static ScalarType erasure(Type type) {
return type.accept(new Type.Visitor<ScalarType>() {
@@ -7,6 +9,21 @@ public class Types {
public ScalarType visit(ScalarType type) {
return type;
}
+
+ @Override
+ public ScalarType visit(TypeParameter parameter) {
+ return ObjectValues.OBJECT;
+ }
+
+ @Override
+ public ScalarType visit(ParameterizedType type) {
+ return type.getRawType();
+ }
+
+ @Override
+ public ScalarType visit(BoundTypeParameter type) {
+ return erasure(type.getParameter());
+ }
});
}
}
diff --git a/src/main/java/org/zwobble/couscous/backends/Names.java b/src/main/java/org/zwobble/couscous/backends/Names.java
index 094de02..1e9600a 100644
--- a/src/main/java/org/zwobble/couscous/backends/Names.java
+++ b/src/main/java/org/zwobble/couscous/backends/Names.java
@@ -3,10 +3,9 @@ package org.zwobble.couscous.backends;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import org.zwobble.couscous.ast.MethodSignature;
-import org.zwobble.couscous.ast.types.Type;
+import org.zwobble.couscous.ast.types.*;
import static com.google.common.collect.Iterables.transform;
-import static org.zwobble.couscous.ast.types.Types.erasure;
import static org.zwobble.couscous.util.ExtraLists.list;
public class Names {
@@ -18,6 +17,26 @@ public class Names {
}
private static String typeToString(Type argument) {
- return erasure(argument).getQualifiedName().replace('.', '_');
+ return argument.accept(new Type.Visitor<String>() {
+ @Override
+ public String visit(ScalarType type) {
+ return type.getQualifiedName().replace('.', '_');
+ }
+
+ @Override
+ public String visit(TypeParameter parameter) {
+ return parameter.getName();
+ }
+
+ @Override
+ public String visit(ParameterizedType type) {
+ return typeToString(type.getRawType());
+ }
+
+ @Override
+ public String visit(BoundTypeParameter type) {
+ return typeToString(type.getParameter());
+ }
+ });
}
}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
index 8388421..469705b 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
@@ -2,8 +2,7 @@ package org.zwobble.couscous.backends.csharp;
import com.google.common.collect.ImmutableMap;
import org.zwobble.couscous.ast.*;
-import org.zwobble.couscous.ast.types.ScalarType;
-import org.zwobble.couscous.ast.types.Type;
+import org.zwobble.couscous.ast.types.*;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.backends.Names;
import org.zwobble.couscous.values.*;
@@ -13,6 +12,7 @@ import java.util.Optional;
import static org.zwobble.couscous.ast.types.Types.erasure;
import static org.zwobble.couscous.util.Casts.tryCast;
+import static org.zwobble.couscous.util.ExtraLists.eagerMap;
public class CsharpCodeGenerator {
private final static Map<ScalarType, ScalarType> PRIMITIVES = ImmutableMap.<ScalarType, ScalarType>builder()
@@ -44,11 +44,39 @@ public class CsharpCodeGenerator {
}
private Type transformType(Type type) {
- if (PRIMITIVES.containsKey(type)) {
- return PRIMITIVES.get(type);
- } else {
- return addPrefix(type, namespace);
- }
+ return type.accept(new Type.Visitor<Type>() {
+ @Override
+ public Type visit(ScalarType type) {
+ if (PRIMITIVES.containsKey(type)) {
+ return PRIMITIVES.get(type);
+ } else {
+ return addPrefix(type, namespace);
+ }
+ }
+
+ @Override
+ public TypeParameter visit(TypeParameter parameter) {
+ return parameter;
+ }
+
+ @Override
+ public Type visit(ParameterizedType type) {
+ if (type.getRawType().equals(ObjectValues.CLASS)) {
+ return ScalarType.of("System.Type");
+ } else {
+ return new ParameterizedType(
+ (ScalarType) transformType(type.getRawType()),
+ eagerMap(type.getParameters(), parameter -> transformType(parameter)));
+ }
+ }
+
+ @Override
+ public Type visit(BoundTypeParameter type) {
+ return new BoundTypeParameter(
+ visit(type.getParameter()),
+ transformType(type.getValue()));
+ }
+ });
}
private String transformMethodName(MethodSignature signature) {
@@ -81,9 +109,28 @@ public class CsharpCodeGenerator {
private Type addPrefix(Type type, String namespace) {
return type.accept(new Type.Visitor<Type>() {
@Override
- public Type visit(ScalarType type) {
+ public ScalarType visit(ScalarType type) {
return ScalarType.of(namespace + "." + type.getQualifiedName());
}
+
+ @Override
+ public TypeParameter visit(TypeParameter parameter) {
+ return parameter;
+ }
+
+ @Override
+ public Type visit(ParameterizedType type) {
+ return new ParameterizedType(
+ visit(type.getRawType()),
+ eagerMap(type.getParameters(), parameter -> addPrefix(parameter, namespace)));
+ }
+
+ @Override
+ public Type visit(BoundTypeParameter type) {
+ return new BoundTypeParameter(
+ visit(type.getParameter()),
+ addPrefix(type.getValue(), namespace));
+ }
});
}
}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java
index c898525..0d38a88 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java
@@ -1,8 +1,7 @@
package org.zwobble.couscous.backends.csharp;
import org.zwobble.couscous.ast.*;
-import org.zwobble.couscous.ast.types.ScalarType;
-import org.zwobble.couscous.ast.types.Type;
+import org.zwobble.couscous.ast.types.*;
import org.zwobble.couscous.ast.visitors.NodeVisitor;
import org.zwobble.couscous.backends.SourceCodeWriter;
import org.zwobble.couscous.util.Action;
@@ -422,6 +421,34 @@ public class CsharpSerializer implements NodeVisitor {
writer.writeIdentifier(typeReference(type));
return null;
}
+
+ @Override
+ public Void visit(TypeParameter parameter) {
+ writer.writeIdentifier(parameter.getName());
+ return null;
+ }
+
+ @Override
+ public Void visit(ParameterizedType type) {
+ writer.writeIdentifier(typeReference(type.getRawType()));
+ writer.writeSymbol("<");
+ writer.writeWithSeparator(
+ type.getParameters(),
+ parameter -> writeTypeReference(parameter),
+ () -> {
+ writer.writeSymbol(",");
+ writer.writeSpace();
+ }
+ );
+ writer.writeSymbol(">");
+ return null;
+ }
+
+ @Override
+ public Void visit(BoundTypeParameter type) {
+ writeTypeReference(type.getValue());
+ return null;
+ }
});
}
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 98f2686..72766b3 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java
@@ -184,7 +184,8 @@ public class JavaExpressionReader {
List<ExpressionNode> arguments = readArguments(
expression.resolveMethodBinding(),
expression.arguments());
- final Type type = typeOf(expression);
+ MethodSignature signature = JavaReader.signature(expression.resolveMethodBinding());
+ final Type type = signature.getReturnType();
IMethodBinding methodBinding = expression.resolveMethodBinding();
Type receiverType = typeOf(methodBinding.getDeclaringClass());
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
index 950e678..382c592 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
@@ -1,6 +1,7 @@
package org.zwobble.couscous.frontends.java;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.*;
@@ -8,8 +9,10 @@ import org.zwobble.couscous.ast.*;
import org.zwobble.couscous.ast.sugar.AnonymousClass;
import org.zwobble.couscous.ast.sugar.Lambda;
import org.zwobble.couscous.ast.sugar.TypeDeclarationBody;
+import org.zwobble.couscous.ast.types.BoundTypeParameter;
import org.zwobble.couscous.ast.types.ScalarType;
import org.zwobble.couscous.ast.types.Type;
+import org.zwobble.couscous.ast.types.TypeParameter;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.util.ExtraLists;
@@ -17,6 +20,7 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Function;
+import java.util.stream.Collectors;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.*;
@@ -29,8 +33,10 @@ import static org.zwobble.couscous.ast.FieldAccessNode.fieldAccess;
import static org.zwobble.couscous.ast.FieldDeclarationNode.field;
import static org.zwobble.couscous.ast.FormalTypeParameterNode.formalTypeParameter;
import static org.zwobble.couscous.ast.InstanceReceiver.instanceReceiver;
+import static org.zwobble.couscous.ast.ReturnNode.returns;
import static org.zwobble.couscous.ast.StaticReceiver.staticReceiver;
import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference;
+import static org.zwobble.couscous.ast.TypeCoercionNode.typeCoercion;
import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
import static org.zwobble.couscous.ast.types.Types.erasure;
import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables;
@@ -128,10 +134,14 @@ public class JavaReader {
ScalarType name = generateAnonymousName(expression);
Scope scope = outerScope.enterClass(name);
IMethodBinding functionalInterfaceMethod = expression.resolveTypeBinding().getFunctionalInterfaceMethod();
- return generateClosure(scope, name, toAnonymousClass(functionalInterfaceMethod, lambda.apply(scope)));
+ return generateClosure(scope, name, toAnonymousClass(scope, name, functionalInterfaceMethod, lambda.apply(scope)));
}
- private AnonymousClass toAnonymousClass(IMethodBinding functionalInterfaceMethod, Lambda lambda) {
+ private AnonymousClass toAnonymousClass(Scope scope, ScalarType name, IMethodBinding functionalInterfaceMethod, Lambda lambda) {
+ // TODO: does this need type coercion adding? Since the body is straight from the lambda, but the type signature is from the interface method
+ // TODO: can generate override methods by using a placeholder this reference for, well, this references.
+ // They can be replaced once the anonymous class is turned into a real class.
+ // Or perhaps we should just do the conversion directly? Since we (in practice) already know the real class name.
MethodNode method = MethodNode.method(
emptyList(),
false,
@@ -140,10 +150,12 @@ public class JavaReader {
typeOf(functionalInterfaceMethod.getReturnType()),
Optional.of(lambda.getBody()));
+ Iterable<MethodNode> overrides = generateOverrideMethods(name, functionalInterfaceMethod, scope, method);
+
return new AnonymousClass(
superTypesAndSelf(functionalInterfaceMethod.getDeclaringClass()),
list(),
- list(method));
+ ExtraLists.cons(method, overrides));
}
private List<StatementNode> replaceCaptureReferences(
@@ -305,7 +317,7 @@ public class JavaReader {
for (Object declaration : bodyDeclarations) {
tryCast(MethodDeclaration.class, declaration)
- .ifPresent(method -> readMethod(body, scope, method));
+ .ifPresent(method -> readMethod(body, scope, type, method));
tryCast(Initializer.class, declaration)
.ifPresent(initializer -> body.addInitializer(
@@ -356,8 +368,9 @@ public class JavaReader {
}
- private void readMethod(TypeDeclarationBody.Builder builder, Scope outerScope, MethodDeclaration method) {
- Scope scope = outerScope.enterMethod(method.getName().getIdentifier());
+ private void readMethod(TypeDeclarationBody.Builder builder, Scope outerScope, ScalarType type, MethodDeclaration method) {
+ String methodName = method.getName().getIdentifier();
+ Scope scope = outerScope.enterMethod(methodName);
List<FormalArgumentNode> formalArguments = readFormalArguments(scope, method);
List<AnnotationNode> annotations = readAnnotations(method);
@@ -370,16 +383,89 @@ public class JavaReader {
formalArguments,
body.get()));
} else {
- builder.addMethod(MethodNode.method(
+ boolean isStatic = Modifier.isStatic(method.getModifiers());
+ MethodNode methodNode = MethodNode.method(
annotations,
- Modifier.isStatic(method.getModifiers()),
- method.getName().getIdentifier(),
+ isStatic,
+ methodName,
formalArguments,
returnType.get(),
- body));
+ body);
+ builder.addMethod(methodNode);
+
+ Iterable<MethodNode> overrideMethods = generateOverrideMethods(type, method.resolveBinding(), scope, methodNode);
+
+ for (MethodNode overrideMethod : overrideMethods) {
+ builder.addMethod(overrideMethod);
+ }
}
}
+ private Iterable<MethodNode> generateOverrideMethods(ScalarType type, IMethodBinding method, Scope scope, MethodNode methodNode) {
+ return transform(
+ filter(
+ overrides(method),
+ override -> !override.equals(methodNode.signature())),
+ override -> {
+ List<FormalArgumentNode> arguments = eagerMap(override.getArguments(), argument -> scope.formalArgument("arg", argument));
+ return MethodNode.method(
+ methodNode.getAnnotations(),
+ methodNode.isStatic(),
+ methodNode.getName(),
+ arguments,
+ override.getReturnType(),
+ Optional.of(list(
+ returns(typeCoercion(
+ MethodCallNode.methodCall(
+ thisReference(type),
+ methodNode.getName(),
+ eagerMap(arguments, argument -> reference(argument)),
+ methodNode.getReturnType()),
+ override.getReturnType()))
+ )));
+ });
+ }
+
+ private Iterable<MethodSignature> overrides(IMethodBinding methodBinding) {
+ ITypeBinding declaringClass = methodBinding.getDeclaringClass();
+
+ // TODO: remove duplication with JavaTypes
+ // TODO: consider superclass
+ return ExtraLists.concat(list(declaringClass), asList(declaringClass.getInterfaces()))
+ .stream()
+ .flatMap(type -> asList(type.getDeclaredMethods()).stream().filter(superMethod -> methodBinding.overrides(superMethod)))
+ .map(JavaReader::signature)
+ .collect(Collectors.toList());
+ }
+
+ // TODO: move this elsewhere
+ static MethodSignature signature(IMethodBinding method) {
+ ITypeBinding declaringClass = method.getDeclaringClass();
+ ITypeBinding erasedDeclaringClass = declaringClass.getErasure();
+
+ List<Type> argumentTypes = eagerMap(asList(method.getParameterTypes()), JavaTypes::typeOf);
+ Type returnType = typeOf(method.getReturnType());
+
+ if (declaringClass.isEqualTo(erasedDeclaringClass)) {
+ return new MethodSignature(method.getName(), argumentTypes, returnType);
+ } else {
+ IMethodBinding[] erasedMethods = erasedDeclaringClass.getDeclaredMethods();
+ IMethodBinding erasedMethod = Iterables.getOnlyElement(Iterables.filter(
+ asList(erasedMethods),
+ m -> m.isSubsignature(method)));
+ return new MethodSignature(
+ method.getName(),
+ eagerMap(
+ argumentTypes,
+ asList(erasedMethod.getParameterTypes()),
+ (argumentType, erasedParameterType) -> new BoundTypeParameter((TypeParameter) typeOf(erasedParameterType), argumentType)),
+ new BoundTypeParameter(
+ (TypeParameter) typeOf(erasedMethod.getReturnType()),
+ returnType));
+ }
+ }
+
+
private List<AnnotationNode> readAnnotations(MethodDeclaration method) {
return eagerMap(asList(method.resolveBinding().getAnnotations()), this::readAnnotation);
}
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java
index fd59e8c..f0060a2 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java
@@ -2,16 +2,20 @@ package org.zwobble.couscous.frontends.java;
import com.google.common.collect.ImmutableSet;
import org.eclipse.jdt.core.dom.*;
+import org.zwobble.couscous.ast.types.ParameterizedType;
import org.zwobble.couscous.ast.types.ScalarType;
import org.zwobble.couscous.ast.types.Type;
-import org.zwobble.couscous.ast.types.Types;
+import org.zwobble.couscous.ast.types.TypeParameter;
import org.zwobble.couscous.values.ObjectValues;
+import java.util.List;
import java.util.Optional;
import java.util.Set;
import static com.google.common.collect.Iterables.transform;
import static java.util.Arrays.asList;
+import static org.zwobble.couscous.ast.types.Types.erasure;
+import static org.zwobble.couscous.util.ExtraLists.eagerMap;
class JavaTypes {
static Type typeOf(Expression expression) {
@@ -29,9 +33,19 @@ class JavaTypes {
static Type typeOf(ITypeBinding typeBinding) {
ITypeBinding outerClass = typeBinding.getDeclaringClass();
if (outerClass == null) {
- return ScalarType.of(typeBinding.getErasure().getQualifiedName());
+ ScalarType rawType = ScalarType.of(typeBinding.getErasure().getQualifiedName());
+ if (typeBinding.isParameterizedType()) {
+ List<Type> typeParameters = eagerMap(
+ asList(typeBinding.getTypeArguments()),
+ JavaTypes::typeOf);
+ return new ParameterizedType(rawType, typeParameters);
+ } else {
+ return rawType;
+ }
+ } else if (typeBinding.isTypeVariable()) {
+ return new TypeParameter(erasure(typeOf(outerClass)), typeBinding.getName());
} else {
- return ScalarType.of(Types.erasure(typeOf(outerClass)).getQualifiedName() + "__" + typeBinding.getName());
+ return ScalarType.of(erasure(typeOf(outerClass)).getQualifiedName() + "__" + typeBinding.getName());
}
}
diff --git a/src/main/java/org/zwobble/couscous/util/ExtraLists.java b/src/main/java/org/zwobble/couscous/util/ExtraLists.java
index ce9230a..8485b02 100644
--- a/src/main/java/org/zwobble/couscous/util/ExtraLists.java
+++ b/src/main/java/org/zwobble/couscous/util/ExtraLists.java
@@ -1,8 +1,11 @@
package org.zwobble.couscous.util;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Iterator;
import java.util.List;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -26,16 +29,16 @@ public class ExtraLists {
return ImmutableList.of(value1, value2, value3);
}
- public static <T> List<T> cons(T value, List<T> list) {
- return Stream.concat(Stream.of(value), list.stream()).collect(Collectors.toList());
+ public static <T> List<T> cons(T value, Iterable<T> list) {
+ return concat(list(value), list);
}
public static <T> List<T> append(List<T> list, T value) {
return Stream.concat(list.stream(), Stream.of(value)).collect(Collectors.toList());
}
- public static <T> List<T> concat(List<? extends T> first, List<? extends T> second) {
- return Stream.concat(first.stream(), second.stream()).collect(Collectors.toList());
+ public static <T> List<T> concat(Iterable<? extends T> first, Iterable<? extends T> second) {
+ return ImmutableList.copyOf(Iterables.concat(first, second));
}
public static <T> List<T> concat(List<? extends T> first, List<? extends T> second, List<? extends T> third) {
@@ -82,6 +85,24 @@ public class ExtraLists {
.collect(Collectors.toList());
}
+ public static <T1, T2, R> List<R> eagerMap(
+ Iterable<T1> iterable1,
+ Iterable<T2> iterable2,
+ BiFunction<T1, T2, R> function)
+ {
+ // TODO: error handling when iterables are different lengths
+ Iterator<T1> iterator1 = iterable1.iterator();
+ Iterator<T2> iterator2 = iterable2.iterator();
+
+ ImmutableList.Builder<R> result = ImmutableList.builder();
+
+ while (iterator1.hasNext()) {
+ result.add(function.apply(iterator1.next(), iterator2.next()));
+ }
+
+ return result.build();
+ }
+
public static <T, R> List<R> eagerFlatMap(
Iterable<T> iterable,
Function<T, Iterable<R>> function) {