From 76f3ac478316f401a6e55208198201c9a12713cb Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 30 Oct 2016 20:31:41 +0000 Subject: Support inner-inner-classes --- .../java/org/zwobble/couscous/ast/ClassNode.java | 5 ++- .../org/zwobble/couscous/ast/InterfaceNode.java | 9 +++++- .../couscous/transforms/ClosureGenerator.java | 18 +++++------ .../couscous/transforms/HoistNestedTypes.java | 13 ++++++-- .../org/zwobble/couscous/tests/CompilerTests.java | 12 ++++++++ .../inner-inner-class/com/example/InnerClass.java | 36 ++++++++++++++++++++++ 6 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 src/test/resources/java/inner-inner-class/com/example/InnerClass.java (limited to 'src') diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java index 322a32a..0d37130 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -113,9 +113,12 @@ public class ClassNode implements TypeNode { @Override public Iterable childNodes() { return Iterables.concat( + typeParameters, + staticConstructor, ExtraIterables.of(constructor), fields, - methods + methods, + innerTypes ); } diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java index 3159d03..6a56a6b 100644 --- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java @@ -1,5 +1,6 @@ package org.zwobble.couscous.ast; +import com.google.common.collect.Iterables; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; @@ -91,7 +92,13 @@ public class InterfaceNode implements TypeNode { @Override public Iterable childNodes() { - return methods; + return Iterables.concat( + typeParameters, + staticConstructor, + fields, + methods, + innerTypes + ); } @Override diff --git a/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java b/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java index 68c0aa6..8c454d4 100644 --- a/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java +++ b/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java @@ -29,6 +29,7 @@ import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariable import static org.zwobble.couscous.types.Types.erasure; import static org.zwobble.couscous.util.Casts.tryCast; import static org.zwobble.couscous.util.ExtraIterables.lazyMap; +import static org.zwobble.couscous.util.ExtraIterables.only; import static org.zwobble.couscous.util.ExtraLists.*; public class ClosureGenerator { @@ -40,9 +41,7 @@ public class ClosureGenerator { findFreeTypeParameters(classNode)); InsertionOrderSet freeVariables = InsertionOrderSet.copyOf(Iterables.filter( - findFreeVariables(ExtraLists.concat( - classNode.getFields(), - classNode.getMethods())), + findFreeVariables(ExtraLists.copyOf(classNode.childNodes())), // TODO: check this references work correctly in anonymous classes variable -> !isThisReference(classNode.getName(), variable))); InsertionOrderSet capturedVariables = InsertionOrderSet.copyOf(transform( @@ -58,6 +57,8 @@ public class ClosureGenerator { throw new RuntimeException("Class has unexpected static constructor"); } + NodeTransformer transformer = replaceCaptureReferencesTransformer(classNode.getName(), capturedVariables); + ClassNode generatedClass = new ClassNode( classNode.getName(), // TODO: generate fresh type parameter and replace in body @@ -67,8 +68,9 @@ public class ClosureGenerator { list(), buildConstructor(scope, classNode.getName(), capturedVariables, classNode.getConstructor()), eagerMap(classNode.getMethods(), method -> - method.mapBody(body -> replaceCaptureReferences(classNode.getName(), body, capturedVariables))), - list() + method.mapBody(body -> transformer.transformStatements(body))), + // TODO: should just call replaceCaptureReferences over entire class? + eagerMap(classNode.getInnerTypes(), type -> only(transformer.transformTypeDeclaration(type))) ); return new GeneratedClosure(generatedClass, freeTypeParameters, freeVariables); } @@ -95,16 +97,14 @@ public class ClosureGenerator { }); } - private static List replaceCaptureReferences( + private static NodeTransformer replaceCaptureReferencesTransformer( ScalarType className, - List body, InsertionOrderSet freeVariables) { Map replacements = Maps.transformValues( Maps.uniqueIndex(freeVariables, variable -> variable.freeVariable), freeVariable -> captureAccess(className, freeVariable)); - NodeTransformer transformer = NodeTransformer.replaceExpressions(replacements); - return eagerFlatMap(body, transformer::transformStatement); + return NodeTransformer.replaceExpressions(replacements); } private static ConstructorNode buildConstructor( diff --git a/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java b/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java index 6c40af9..1cb74c8 100644 --- a/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java +++ b/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java @@ -29,6 +29,17 @@ public class HoistNestedTypes { private final Scope topScope = Scope.create().temporaryPrefix("_couscous_hoist_nested_types"); private List hoistTypes(List declarations) { + while (true) { + List hoisted = hoistInnerTypes(declarations); + if (hoisted.size() == declarations.size()) { + return hoisted; + } else { + declarations = hoisted; + } + } + } + + private List hoistInnerTypes(List declarations) { List hoistResults = eagerMap(declarations, declaration -> hoistInnerTypes(declaration)); Iterable hoistedTypes = lazyFlatMap(hoistResults, hoisted -> hoisted.hoistedTypes); @@ -126,8 +137,6 @@ public class HoistNestedTypes { } private HoistedType hoistType(TypeNode typeNode) { - // TODO: we need to hoist inner types of the inner types, specifically *after* the initial hoist (to make closures easier to generate) - // TODO: can we remove duplication of scope creation with readTypeDeclaration()? Scope scope = topScope.enterClass(typeNode.getName()); return tryCast(ClassNode.class, typeNode) .flatMap(node -> { diff --git a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java index 495f7ed..838029a 100644 --- a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java +++ b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java @@ -132,6 +132,18 @@ public abstract class CompilerTests { Types.INT)); } + @Test + public void innerInnerClasses() throws Exception { + assertEquals( + value(42), + execTestProgram( + "inner-inner-class", + ScalarType.topLevel("com.example.InnerClass"), + "run", + list(), + Types.INT)); + } + @Test public void canImplementUserDefinedInterface() throws Exception { assertEquals( diff --git a/src/test/resources/java/inner-inner-class/com/example/InnerClass.java b/src/test/resources/java/inner-inner-class/com/example/InnerClass.java new file mode 100644 index 0000000..ec93f19 --- /dev/null +++ b/src/test/resources/java/inner-inner-class/com/example/InnerClass.java @@ -0,0 +1,36 @@ +package com.example; + +public class InnerClass { + public static int run() { + return new InnerClass(21).value(); + } + + private final int stash; + + public InnerClass(int stash) { + this.stash = stash; + } + + public int value() { + IntSupplier supplier = new IntSupplier(2); + return supplier.get(); + } + + private class IntSupplier { + private final int factor; + + public IntSupplier(int factor) { + this.factor = factor; + } + + public int get() { + return new Multiplier().calculate(); + } + + private class Multiplier { + public int calculate() { + return stash * factor; + } + } + } +} -- cgit v1.2.3