From 4b645195d711c7a306d6d08a6c271c303948af73 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 11 Apr 2016 21:23:30 +0100 Subject: Add hacktastic type parameter capturing --- .../couscous/frontends/java/FreeVariables.java | 38 ++++++++++++++++++++++ .../couscous/frontends/java/GeneratedClosure.java | 16 ++++++--- .../couscous/frontends/java/JavaReader.java | 11 +++++-- .../java/org/zwobble/couscous/types/Types.java | 10 ++++++ 4 files changed, 67 insertions(+), 8 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java b/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java index 00184b1..9e507dc 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java @@ -1,7 +1,10 @@ package org.zwobble.couscous.frontends.java; import org.zwobble.couscous.ast.*; +import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.types.*; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -12,6 +15,41 @@ import static org.zwobble.couscous.util.Casts.tryCast; import static org.zwobble.couscous.util.ExtraStreams.toStream; public class FreeVariables { + public static Set findFreeTypeParameters(Node node) { + // TODO: this assumes that *all* type parameters are free, which is often but not always true in inner types + Set freeTypeParameters = new HashSet<>(); + + NodeTransformer.builder().transformType(type -> { + type.accept(new Type.Visitor() { + @Override + public Void visit(ScalarType type) { + return null; + } + + @Override + public Void visit(TypeParameter parameter) { + freeTypeParameters.add(parameter); + return null; + } + + @Override + public Void visit(ParameterizedType type) { + type.getParameters().forEach(parameter -> parameter.accept(this)); + return null; + } + + @Override + public Void visit(BoundTypeParameter type) { + type.getValue().accept(this); + return null; + } + }); + return type; + }).build().transform(node); + + return freeTypeParameters; + } + public static List findFreeVariables(List body) { Set declarations = body.stream() .flatMap(FreeVariables::findDeclarations) diff --git a/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java b/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java index b7059e2..9bad1d2 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java @@ -1,23 +1,28 @@ package org.zwobble.couscous.frontends.java; +import com.google.common.collect.ImmutableList; import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ExpressionNode; import org.zwobble.couscous.ast.ReferenceNode; import org.zwobble.couscous.types.ScalarType; +import org.zwobble.couscous.types.TypeParameter; import org.zwobble.couscous.util.InsertionOrderSet; import static org.zwobble.couscous.ast.ConstructorCallNode.constructorCall; +import static org.zwobble.couscous.types.Types.addTypeParameters; import static org.zwobble.couscous.util.ExtraLists.concat; import static org.zwobble.couscous.util.ExtraLists.list; public class GeneratedClosure { private final ClassNode classNode; - private final InsertionOrderSet captures; + private final InsertionOrderSet capturedTypes; + private final InsertionOrderSet capturedVariables; - public GeneratedClosure(ClassNode classNode, InsertionOrderSet captures) { + public GeneratedClosure(ClassNode classNode, InsertionOrderSet capturedTypes, InsertionOrderSet capturedVariables) { this.classNode = classNode; - this.captures = captures; + this.capturedTypes = capturedTypes; + this.capturedVariables = capturedVariables; } public ClassNode getClassNode() { @@ -30,8 +35,9 @@ public class GeneratedClosure { public ConstructorCallNode generateConstructor(Iterable arguments) { return constructorCall( - getType(), - concat(captures, arguments)); + // TODO: tidy up type parameters to avoid copy + addTypeParameters(getType(), ImmutableList.copyOf(capturedTypes.asList())), + concat(capturedVariables, arguments)); } private ScalarType getType() { 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 8353dac..e096429 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java @@ -11,7 +11,7 @@ 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.visitors.NodeTransformer; -import org.zwobble.couscous.types.ScalarType; +import org.zwobble.couscous.types.*; import org.zwobble.couscous.types.Type; import org.zwobble.couscous.util.ExtraLists; import org.zwobble.couscous.util.InsertionOrderSet; @@ -40,6 +40,7 @@ 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.VariableReferenceNode.reference; +import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeTypeParameters; import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables; import static org.zwobble.couscous.frontends.java.JavaMethods.signature; import static org.zwobble.couscous.frontends.java.JavaTypes.*; @@ -257,6 +258,9 @@ public class JavaReader { Scope scope, ClassNode classNode ) { + InsertionOrderSet freeTypeParameters = InsertionOrderSet.copyOf( + findFreeTypeParameters(classNode)); + InsertionOrderSet freeVariables = InsertionOrderSet.copyOf(Iterables.filter( findFreeVariables(ExtraLists.concat( classNode.getFields(), @@ -278,14 +282,15 @@ public class JavaReader { ClassNode generatedClass = new ClassNode( classNode.getName(), - classNode.getTypeParameters(), + // TODO: generate fresh type parameter and replace in body + ExtraLists.concat(lazyMap(freeTypeParameters, parameter -> formalTypeParameter(parameter)), classNode.getTypeParameters()), classNode.getSuperTypes(), fields, list(), buildConstructor(scope, classNode.getName(), capturedVariables, classNode.getConstructor()), eagerMap(classNode.getMethods(), method -> method.mapBody(body -> replaceCaptureReferences(classNode.getName(), body, capturedVariables)))); - return new GeneratedClosure(generatedClass, freeVariables); + return new GeneratedClosure(generatedClass, freeTypeParameters, freeVariables); } private boolean isThisReference(ScalarType name, ReferenceNode reference) { diff --git a/src/main/java/org/zwobble/couscous/types/Types.java b/src/main/java/org/zwobble/couscous/types/Types.java index 505d9a7..5338eb3 100644 --- a/src/main/java/org/zwobble/couscous/types/Types.java +++ b/src/main/java/org/zwobble/couscous/types/Types.java @@ -1,7 +1,9 @@ package org.zwobble.couscous.types; +import java.util.List; import java.util.Map; +import static org.zwobble.couscous.types.ParameterizedType.parameterizedType; import static org.zwobble.couscous.util.ExtraLists.eagerMap; import static org.zwobble.couscous.util.ExtraLists.list; @@ -16,6 +18,14 @@ public class Types { public static final ScalarType BOXED_BOOLEAN = ScalarType.of("java.lang.Boolean"); public static final ScalarType ARRAY = ScalarType.of("array"); + public static Type addTypeParameters(ScalarType rawType, List typeParameters) { + if (typeParameters.isEmpty()) { + return rawType; + } else { + return parameterizedType(rawType, typeParameters); + } + } + public static Type array(Type elementType) { return new ParameterizedType(ARRAY, list(elementType)); } -- cgit v1.2.3