From 1ef4865a040ae94b69a5c4dabcb5dca837cae67c Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 30 Oct 2016 23:55:19 +0000 Subject: Defer generation of classes for lambdas --- .../java/org/zwobble/couscous/ast/ClassNode.java | 12 ++- .../org/zwobble/couscous/ast/ClassNodeBuilder.java | 8 +- .../java/org/zwobble/couscous/ast/EnumNode.java | 5 ++ .../org/zwobble/couscous/ast/InterfaceNode.java | 12 ++- .../java/org/zwobble/couscous/ast/NodeTypes.java | 2 + .../java/org/zwobble/couscous/ast/TypeNode.java | 3 +- .../zwobble/couscous/ast/sugar/AnonymousClass.java | 86 ++++++++++++++++---- .../backends/csharp/CsharpCodeGenerator.java | 7 +- .../couscous/backends/python/PythonBackend.java | 22 +++--- .../couscous/frontends/java/AnonymousType.java | 6 +- .../couscous/frontends/java/FreeVariables.java | 4 + .../frontends/java/JavaExpressionReader.java | 13 +--- .../couscous/frontends/java/JavaReader.java | 91 +++++----------------- .../org/zwobble/couscous/frontends/java/Scope.java | 4 + .../zwobble/couscous/interpreter/JavaProject.java | 8 +- .../transforms/AnonymousClassToInnerClass.java | 86 ++++++++++++++++++++ 16 files changed, 250 insertions(+), 119 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/transforms/AnonymousClassToInnerClass.java (limited to 'src/main/java/org') diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java index 0d37130..ce27694 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -9,6 +9,7 @@ import org.zwobble.couscous.util.ExtraIterables; import java.util.List; import java.util.Set; +import static org.zwobble.couscous.util.ExtraLists.concat; import static org.zwobble.couscous.util.ExtraLists.eagerFlatMap; import static org.zwobble.couscous.util.ExtraLists.list; @@ -138,6 +139,15 @@ public class ClassNode implements TypeNode { @Override public TypeNode stripInnerTypes() { + return withInnerTypes(list()); + } + + @Override + public TypeNode addInnerTypes(List types) { + return withInnerTypes(concat(innerTypes, types)); + } + + private TypeNode withInnerTypes(List types) { return new ClassNode( name, typeParameters, @@ -146,7 +156,7 @@ public class ClassNode implements TypeNode { staticConstructor, constructor, methods, - list() + types ); } diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java index 4d39b01..71c226a 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSet; import org.zwobble.couscous.ast.identifiers.Identifier; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; +import org.zwobble.couscous.types.TypeParameter; import org.zwobble.couscous.types.Types; import java.util.List; @@ -46,6 +47,11 @@ public class ClassNodeBuilder { return this; } + public ClassNodeBuilder addTypeParameter(TypeParameter typeParameter) { + this.typeParameters.add(formalTypeParameter(typeParameter)); + return this; + } + public ClassNodeBuilder addSuperType(String type) { this.superTypes.add(ScalarType.topLevel(type)); return this; @@ -55,7 +61,7 @@ public class ClassNodeBuilder { return field(FieldDeclarationNode.staticField(name, type)); } - public ClassNodeBuilder field(String name, ScalarType type) { + public ClassNodeBuilder field(String name, Type type) { return field(FieldDeclarationNode.field(name, type)); } diff --git a/src/main/java/org/zwobble/couscous/ast/EnumNode.java b/src/main/java/org/zwobble/couscous/ast/EnumNode.java index 4a41a03..1b5941f 100644 --- a/src/main/java/org/zwobble/couscous/ast/EnumNode.java +++ b/src/main/java/org/zwobble/couscous/ast/EnumNode.java @@ -76,6 +76,11 @@ public class EnumNode implements TypeNode { return this; } + @Override + public TypeNode addInnerTypes(List types) { + throw new UnsupportedOperationException(); + } + @Override public String toString() { return "EnumNode(" + diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java index 6a56a6b..1392aee 100644 --- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java @@ -8,6 +8,7 @@ import org.zwobble.couscous.types.Type; import java.util.List; import java.util.Set; +import static org.zwobble.couscous.util.ExtraLists.concat; import static org.zwobble.couscous.util.ExtraLists.eagerFlatMap; import static org.zwobble.couscous.util.ExtraLists.list; @@ -116,6 +117,15 @@ public class InterfaceNode implements TypeNode { @Override public TypeNode stripInnerTypes() { + return withInnerTypes(list()); + } + + @Override + public TypeNode addInnerTypes(List types) { + return withInnerTypes(concat(innerTypes, types)); + } + + private TypeNode withInnerTypes(List types) { return new InterfaceNode( name, typeParameters, @@ -123,7 +133,7 @@ public class InterfaceNode implements TypeNode { fields, staticConstructor, methods, - list() + types ); } diff --git a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java index ac37540..c3710bb 100644 --- a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java +++ b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java @@ -1,5 +1,6 @@ package org.zwobble.couscous.ast; +import org.zwobble.couscous.ast.sugar.AnonymousClass; import org.zwobble.couscous.ast.sugar.SwitchCaseNode; import org.zwobble.couscous.ast.sugar.SwitchNode; @@ -51,6 +52,7 @@ public class NodeTypes { public static final int FOR = register(ForNode.class); public static final int FOR_EACH = register(ForEachNode.class); public static final int STATEMENT_BLOCK = register(StatementBlockNode.class); + public static final int ANONYMOUS_CLASS = register(AnonymousClass.class); private NodeTypes() { } diff --git a/src/main/java/org/zwobble/couscous/ast/TypeNode.java b/src/main/java/org/zwobble/couscous/ast/TypeNode.java index 01b033b..07d3f1a 100644 --- a/src/main/java/org/zwobble/couscous/ast/TypeNode.java +++ b/src/main/java/org/zwobble/couscous/ast/TypeNode.java @@ -1,8 +1,8 @@ package org.zwobble.couscous.ast; +import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; -import org.zwobble.couscous.ast.visitors.NodeTransformer; import java.util.List; import java.util.Set; @@ -15,4 +15,5 @@ public interface TypeNode extends Node { List getInnerTypes(); TypeNode transformSubtree(NodeTransformer transformer); TypeNode stripInnerTypes(); + TypeNode addInnerTypes(List types); } diff --git a/src/main/java/org/zwobble/couscous/ast/sugar/AnonymousClass.java b/src/main/java/org/zwobble/couscous/ast/sugar/AnonymousClass.java index 08bb9a3..0722705 100644 --- a/src/main/java/org/zwobble/couscous/ast/sugar/AnonymousClass.java +++ b/src/main/java/org/zwobble/couscous/ast/sugar/AnonymousClass.java @@ -1,38 +1,63 @@ package org.zwobble.couscous.ast.sugar; -import org.zwobble.couscous.ast.FieldDeclarationNode; -import org.zwobble.couscous.ast.MethodNode; +import org.zwobble.couscous.ast.*; +import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.frontends.java.AnonymousType; import org.zwobble.couscous.types.Type; import java.util.List; import java.util.Optional; -import java.util.Set; -public class AnonymousClass { - private final Optional type; - private final Set superTypes; +import static com.google.common.collect.Iterables.concat; + +public class AnonymousClass implements ExpressionNode { + private final Optional anonymousType; + private final Type superType; private final List fields; private final List methods; public AnonymousClass( - Optional type, - Set superTypes, + Optional anonymousType, + Type superType, List fields, List methods) { - this.type = type; - this.superTypes = superTypes; + this.anonymousType = anonymousType; + this.superType = superType; this.fields = fields; this.methods = methods; } - public Optional getType() { - return type; + @Override + public Type getType() { + return superType; + } + + public Optional getAnonymousType() { + return anonymousType; + } + + @Override + public int nodeType() { + return NodeTypes.ANONYMOUS_CLASS; + } + + @Override + public Iterable childNodes() { + return concat( + fields, + methods + ); } - public Set getSuperTypes() { - return superTypes; + @Override + public ExpressionNode transformSubtree(NodeTransformer transformer) { + return new AnonymousClass( + anonymousType, + transformer.transform(superType), + transformer.transformFields(fields), + transformer.transformMethods(methods) + ); } public List getFields() { @@ -42,4 +67,37 @@ public class AnonymousClass { public List getMethods() { return methods; } + + @Override + public String toString() { + return "AnonymousClass(" + + "anonymousType=" + anonymousType + + ", superType=" + superType + + ", fields=" + fields + + ", methods=" + methods + + ')'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AnonymousClass that = (AnonymousClass) o; + + if (!anonymousType.equals(that.anonymousType)) return false; + if (!superType.equals(that.superType)) return false; + if (!fields.equals(that.fields)) return false; + return methods.equals(that.methods); + + } + + @Override + public int hashCode() { + int result = anonymousType.hashCode(); + result = 31 * result + superType.hashCode(); + result = 31 * result + fields.hashCode(); + result = 31 * result + methods.hashCode(); + return result; + } } 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 68eaa73..2936735 100644 --- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java @@ -5,10 +5,7 @@ import org.zwobble.couscous.ast.TypeNode; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.backends.csharp.primitives.CsharpPrimitiveMethods; import org.zwobble.couscous.backends.naming.Naming; -import org.zwobble.couscous.transforms.DesugarForEachToFor; -import org.zwobble.couscous.transforms.DesugarForToWhile; -import org.zwobble.couscous.transforms.HoistNestedTypes; -import org.zwobble.couscous.transforms.SplitStaticsFromInterface; +import org.zwobble.couscous.transforms.*; import org.zwobble.couscous.types.*; import java.util.List; @@ -57,7 +54,7 @@ public class CsharpCodeGenerator { CsharpPrimitiveMethods.TRANSFORMER, nodeTransformer ), - HoistNestedTypes.hoist(types) + HoistNestedTypes.hoist(eagerMap(types, AnonymousClassToInnerClass::transform)) ) ); } diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java b/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java index 6509fc4..31cb545 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java @@ -5,10 +5,7 @@ import com.google.common.io.Resources; import org.zwobble.couscous.Backend; import org.zwobble.couscous.ast.TypeNode; import org.zwobble.couscous.ast.visitors.NodeTransformer; -import org.zwobble.couscous.transforms.DesugarForEachToFor; -import org.zwobble.couscous.transforms.DesugarForToWhile; -import org.zwobble.couscous.transforms.DesugarSwitchToIfElse; -import org.zwobble.couscous.transforms.HoistNestedTypes; +import org.zwobble.couscous.transforms.*; import java.io.File; import java.io.IOException; @@ -19,6 +16,7 @@ import java.util.List; import static org.zwobble.couscous.backends.python.PythonCodeGenerator.generateCode; import static org.zwobble.couscous.backends.python.PythonSerializer.serialize; +import static org.zwobble.couscous.util.ExtraLists.eagerMap; import static org.zwobble.couscous.util.ExtraLists.list; public class PythonBackend implements Backend { @@ -51,14 +49,14 @@ public class PythonBackend implements Backend { } private List desugar(List classes) { - return HoistNestedTypes.hoist( - NodeTransformer.applyAll( - list( - DesugarSwitchToIfElse.transformer(), - DesugarForEachToFor.transformer(), - DesugarForToWhile.transformer() - ), - classes + return NodeTransformer.applyAll( + list( + DesugarSwitchToIfElse.transformer(), + DesugarForEachToFor.transformer(), + DesugarForToWhile.transformer() + ), + HoistNestedTypes.hoist( + eagerMap(classes, AnonymousClassToInnerClass::transform) ) ); } diff --git a/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java b/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java index 5d5915d..4adf7ab 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java @@ -15,6 +15,10 @@ public class AnonymousType implements Type { this.key = key; } + public String getKey() { + return key; + } + @Override public T accept(Visitor visitor) { throw new UnsupportedOperationException(); @@ -22,7 +26,7 @@ public class AnonymousType implements Type { @Override public Type transformSubTypes(Function transform) { - throw new UnsupportedOperationException(); + return this; } @Override 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 7679431..7917a49 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java @@ -59,6 +59,10 @@ public class FreeVariables { public Stream visit(MethodNode methodNode) { return methodNode.getTypeParameters().stream().map(parameter -> parameter.getType()); } + + public Stream visit(ClassNode classNode) { + return classNode.getTypeParameters().stream().map(parameter -> parameter.getType()); + } } public static Set findFreeTypeParameters(Node root) { 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 747bcac..caeb8aa 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java @@ -281,9 +281,7 @@ public class JavaExpressionReader { ITypeBinding declaringClass = constructor.getDeclaringClass(); if (declaringClass.isAnonymous()) { - GeneratedClosure closure = - javaReader.readAnonymousClass(scope, expression.getAnonymousClassDeclaration()); - return closure.generateConstructor(arguments); + return javaReader.readAnonymousClass(scope, expression.getAnonymousClassDeclaration()); } else { if (expression.getExpression() != null) { throw new UnsupportedOperationException(); @@ -293,18 +291,15 @@ public class JavaExpressionReader { } private ExpressionNode readLambdaExpression(LambdaExpression expression) { - GeneratedClosure closure = javaReader.readLambda(scope, expression); - return closure.generateConstructor(); + return javaReader.readLambda(scope, expression); } private ExpressionNode readExpressionMethodReference(ExpressionMethodReference expression) { - GeneratedClosure closure = javaReader.readExpressionMethodReference(scope, expression); - return closure.generateConstructor(); + return javaReader.readExpressionMethodReference(scope, expression); } private ExpressionNode readCreationReference(CreationReference expression) { - GeneratedClosure closure = javaReader.readCreationReference(scope, expression); - return closure.generateConstructor(); + return javaReader.readCreationReference(scope, expression); } private List readArguments(IMethodBinding method, List javaArguments) { 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 4acea74..6553b91 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java @@ -8,8 +8,6 @@ import org.zwobble.couscous.ast.identifiers.Identifier; 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.transforms.ClosureGenerator; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; import org.zwobble.couscous.util.ExtraLists; @@ -39,8 +37,8 @@ import static org.zwobble.couscous.frontends.java.JavaMethods.signature; import static org.zwobble.couscous.frontends.java.JavaTypes.*; import static org.zwobble.couscous.types.Types.erasure; import static org.zwobble.couscous.util.Casts.tryCast; +import static org.zwobble.couscous.util.ExtraIterables.only; import static org.zwobble.couscous.util.ExtraLists.*; -import static org.zwobble.couscous.util.ExtraMaps.map; public class JavaReader { public static List readClassesFromFiles(List sourcePaths, List sourceFiles) throws IOException { @@ -143,32 +141,30 @@ public class JavaReader { parameter -> formalTypeParameter(declaringScope, parameter.getName())); } - GeneratedClosure readExpressionMethodReference(Scope outerScope, ExpressionMethodReference expression) { + AnonymousClass readExpressionMethodReference(Scope outerScope, ExpressionMethodReference expression) { return readLambda( outerScope, expression, scope -> new JavaMethodReferenceReader(this).toLambda(scope, expression)); } - GeneratedClosure readCreationReference(Scope outerScope, CreationReference expression) { + AnonymousClass readCreationReference(Scope outerScope, CreationReference expression) { return readLambda( outerScope, expression, scope -> new JavaMethodReferenceReader(this).toLambda(scope, expression)); } - GeneratedClosure readLambda(Scope outerScope, LambdaExpression expression) { + AnonymousClass readLambda(Scope outerScope, LambdaExpression expression) { return readLambda( outerScope, expression, scope -> new JavaLambdaExpressionReader(this).toLambda(scope, expression)); } - private GeneratedClosure readLambda(Scope outerScope, Expression expression, Function lambda) { - ScalarType name = generateAnonymousName(expression); - Scope scope = outerScope.enterClass(name); + private AnonymousClass readLambda(Scope outerScope, Expression expression, Function lambda) { IMethodBinding functionalInterfaceMethod = expression.resolveTypeBinding().getFunctionalInterfaceMethod(); - return generateClosure(scope, name, toAnonymousClass(functionalInterfaceMethod, lambda.apply(scope))); + return toAnonymousClass(functionalInterfaceMethod, lambda.apply(outerScope)); } private AnonymousClass toAnonymousClass(IMethodBinding functionalInterfaceMethod, Lambda lambda) { @@ -186,74 +182,24 @@ public class JavaReader { return new AnonymousClass( Optional.empty(), - superTypesAndSelf(functionalInterfaceMethod.getDeclaringClass()), + only(superTypesAndSelf(functionalInterfaceMethod.getDeclaringClass())), list(), list(method)); } - GeneratedClosure readAnonymousClass(Scope outerScope, AnonymousClassDeclaration declaration) { - ScalarType className = generateAnonymousName(declaration); - Scope scope = outerScope.enterClass(className); - TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(scope, className, declaration.bodyDeclarations(), false); - AnonymousClass anonymousClass = new AnonymousClass( - Optional.of((AnonymousType) typeOf(declaration.resolveBinding())), - superTypes(declaration), + AnonymousClass readAnonymousClass(Scope outerScope, AnonymousClassDeclaration declaration) { + AnonymousType type = (AnonymousType) typeOf(declaration.resolveBinding()); + Scope scope = outerScope.enterType(type); + TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(scope, type, declaration.bodyDeclarations(), false); + return new AnonymousClass( + Optional.of(type), + only(superTypes(declaration)), bodyDeclarations.getFields(), - bodyDeclarations.getMethods()); - return generateClosure(scope, className, anonymousClass); - } - - GeneratedClosure generateClosure(Scope scope, ScalarType className, AnonymousClass anonymousClass) { - GeneratedClosure closure = classWithCapture(scope, className, anonymousClass); - classes.add(closure.getClassNode()); - return closure; - } - - private GeneratedClosure classWithCapture( - Scope scope, - ScalarType className, - AnonymousClass anonymousClass - ) { - ClassNode classNode = ClassNode.declareClass( - className, - list(), - anonymousClass.getSuperTypes(), - anonymousClass.getFields(), - list(), - ConstructorNode.DEFAULT, - anonymousClass.getMethods(), - list() + bodyDeclarations.getMethods() ); - if (anonymousClass.getType().isPresent()) { - NodeTransformer nodeTransformer = NodeTransformer.replaceExpressions(map( - thisReference(anonymousClass.getType().get()), - thisReference(className) - )); - classNode = nodeTransformer.transformClass(classNode); - } - return ClosureGenerator.classWithCapture(scope, classNode); - } - - private ScalarType generateAnonymousName(ASTNode node) { - ITypeBinding type = findDeclaringClass(node); - while (type.isAnonymous()) { - type = type.getDeclaringClass(); - } - ScalarType typeName = erasure(typeOf(type)); - while (typeName.outerType().isPresent()) { - typeName = typeName.outerType().get(); - } - return ScalarType.topLevel(typeName.getQualifiedName() + "_Anonymous_" + (anonymousClassCount++)); - } - - private ITypeBinding findDeclaringClass(ASTNode node) { - while (!(node instanceof AbstractTypeDeclaration)) { - node = node.getParent(); - } - return ((AbstractTypeDeclaration)node).resolveBinding(); } - private TypeDeclarationBody readTypeDeclarationBody(Scope scope, ScalarType type, List bodyDeclarations, boolean isInterface) { + private TypeDeclarationBody readTypeDeclarationBody(Scope scope, Type type, List bodyDeclarations, boolean isInterface) { TypeDeclarationBody.Builder body = TypeDeclarationBody.builder(); for (Object declaration : bodyDeclarations) { @@ -274,7 +220,7 @@ public class JavaReader { return body.build(); } - private void readField(TypeDeclarationBody.Builder builder, Scope scope, ScalarType declaringType, FieldDeclaration field, boolean isInterface) { + private void readField(TypeDeclarationBody.Builder builder, Scope scope, Type declaringType, FieldDeclaration field, boolean isInterface) { @SuppressWarnings("unchecked") List fragments = field.fragments(); boolean isStatic = isInterface || Modifier.isStatic(field.getModifiers()); @@ -284,8 +230,9 @@ public class JavaReader { if (fragment.getInitializer() != null) { ExpressionNode value = readExpression(scope, type, fragment.getInitializer()); String name = fragment.getName().getIdentifier(); + // TODO: support static values for anonymous classes Receiver receiver = isStatic - ? staticReceiver(declaringType) + ? staticReceiver((ScalarType)declaringType) : instanceReceiver(thisReference(declaringType)); StatementNode assignment = assignStatement(fieldAccess(receiver, name, type), value); diff --git a/src/main/java/org/zwobble/couscous/frontends/java/Scope.java b/src/main/java/org/zwobble/couscous/frontends/java/Scope.java index d4af1f5..0ee9148 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/Scope.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/Scope.java @@ -51,6 +51,10 @@ public class Scope { return enter(identifier.type(className.getQualifiedName())); } + public Scope enterType(AnonymousType type) { + return enter(identifier.type(type.getKey())); + } + public Scope enterConstructor() { return enter(identifier.constructor()); } diff --git a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java index a85d126..6629794 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java +++ b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java @@ -8,13 +8,14 @@ import org.zwobble.couscous.interpreter.types.IntrinsicInterpreterType; import org.zwobble.couscous.interpreter.types.UserDefinedInterpreterType; import org.zwobble.couscous.interpreter.values.BoxedIntegerInterpreterValue; import org.zwobble.couscous.interpreter.values.InternalCouscousInterpreterValue; +import org.zwobble.couscous.transforms.AnonymousClassToInnerClass; import org.zwobble.couscous.transforms.DesugarForToWhile; import org.zwobble.couscous.transforms.DesugarSwitchToIfElse; import org.zwobble.couscous.transforms.HoistNestedTypes; import java.util.List; -import static org.zwobble.couscous.util.ExtraLists.list; +import static org.zwobble.couscous.util.ExtraLists.*; public class JavaProject { private static final InterpreterType OBJECT_TYPE = IntrinsicInterpreterType.classBuilder("java.lang.Object") @@ -32,7 +33,10 @@ public class JavaProject { NodeTransformer forTransformer = DesugarForToWhile.transformer(); Iterable concreteTypes = Iterables.transform( HoistNestedTypes.hoist( - NodeTransformer.applyAll(list(switchTransformer, forTransformer), classNodes) + eagerMap( + NodeTransformer.applyAll(list(switchTransformer, forTransformer), classNodes), + AnonymousClassToInnerClass::transform + ) ), UserDefinedInterpreterType::new ); diff --git a/src/main/java/org/zwobble/couscous/transforms/AnonymousClassToInnerClass.java b/src/main/java/org/zwobble/couscous/transforms/AnonymousClassToInnerClass.java new file mode 100644 index 0000000..4a44a0c --- /dev/null +++ b/src/main/java/org/zwobble/couscous/transforms/AnonymousClassToInnerClass.java @@ -0,0 +1,86 @@ +package org.zwobble.couscous.transforms; + +import com.google.common.collect.ImmutableList; +import org.zwobble.couscous.ast.*; +import org.zwobble.couscous.ast.sugar.AnonymousClass; +import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.frontends.java.GeneratedClosure; +import org.zwobble.couscous.frontends.java.Scope; +import org.zwobble.couscous.types.ScalarType; + +import java.util.List; +import java.util.Optional; + +import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference; +import static org.zwobble.couscous.util.Casts.tryCast; +import static org.zwobble.couscous.util.ExtraIterables.only; +import static org.zwobble.couscous.util.ExtraLists.*; +import static org.zwobble.couscous.util.ExtraMaps.map; +import static org.zwobble.couscous.util.ExtraSets.set; + +public class AnonymousClassToInnerClass { + public static TypeNode transform(TypeNode type) { + Scope scope = Scope.create().temporaryPrefix("_couscous_anonymous_class_to_inner_class"); + AnonymousClassToInnerClass transformer = new AnonymousClassToInnerClass(scope, type); + return transformer.transformTypeDeclaration(type); + } + + private final Scope scope; + private final TypeNode type; + private final ImmutableList.Builder innerTypes = ImmutableList.builder(); + private int anonymousClassCount = 0; + + private AnonymousClassToInnerClass(Scope scope, TypeNode type) { + this.scope = scope; + this.type = type; + } + + private TypeNode transformTypeDeclaration(TypeNode type) { + NodeTransformer transformer = NodeTransformer.builder() + .transformExpression(this::transformExpression) + .build(); + return only(transformer.transformTypeDeclaration(type)).addInnerTypes(innerTypes.build()); + } + + private Optional transformExpression(ExpressionNode expression) { + return tryCast(AnonymousClass.class, expression) + .map(anonymous -> { + ScalarType className = ScalarType.innerType(type.getName(), "Anonymous_" + anonymousClassCount++); + GeneratedClosure closure = classWithCapture(scope, className, anonymous); + innerTypes.add(closure.getClassNode()); + return closure.generateConstructor(); + }); + } + + private GeneratedClosure classWithCapture( + Scope scope, + ScalarType className, + AnonymousClass anonymousClass + ) { + ClassNode classNode = ClassNode.declareClass( + className, + list(), + set(anonymousClass.getType()), + anonymousClass.getFields(), + list(), + ConstructorNode.DEFAULT, + anonymousClass.getMethods(), + list() + ); + if (anonymousClass.getAnonymousType().isPresent()) { + NodeTransformer nodeTransformer = NodeTransformer.replaceExpressions(map( + thisReference(anonymousClass.getAnonymousType().get()), + thisReference(className) + )); + classNode = nodeTransformer.transformClass(classNode); + } + return ClosureGenerator.classWithCapture(scope, classNode); + } + + private List relevantDescendants(Node root) { + return eagerFlatMap( + eagerFilter(root.childNodes(), node -> !(node instanceof TypeNode)), + node -> cons(node, relevantDescendants(node)) + ); + } +} -- cgit v1.2.3