From a82ecb60d6ee7ee46e5800088f1769f86872077b Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Mon, 29 Aug 2016 17:13:03 +0100 Subject: Initial support for static methods/fields on interfaces --- .../org/zwobble/couscous/ast/ClassNodeBuilder.java | 5 +- .../org/zwobble/couscous/ast/InterfaceNode.java | 50 +++++++++++----- .../couscous/ast/visitors/NodeTransformer.java | 16 ++++- .../backends/csharp/CsharpCodeGenerator.java | 19 +++--- .../couscous/frontends/java/JavaReader.java | 25 +++++--- .../transforms/SplitStaticsFromInterface.java | 68 ++++++++++++++++++++++ 6 files changed, 149 insertions(+), 34 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java (limited to 'src/main/java/org') diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java index a567254..4735a2e 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java @@ -127,7 +127,10 @@ public class ClassNodeBuilder { name, typeParameters.build(), superTypes.build(), - methods.build()); + fields.build(), + staticConstructor, + methods.build() + ); } public class MethodBuilder { diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java index de151e4..c0444a4 100644 --- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java @@ -12,26 +12,34 @@ public class InterfaceNode implements TypeNode { ScalarType name, List typeParameters, Set superTypes, + List fields, + List staticConstructor, List methods) { - return new InterfaceNode(name, typeParameters, superTypes, methods); + return new InterfaceNode(name, typeParameters, superTypes, fields, staticConstructor, methods); } private final ScalarType name; private final List typeParameters; private final Set superTypes; + private final List fields; + private final List staticConstructor; private final List methods; private InterfaceNode( ScalarType name, List typeParameters, Set superTypes, - List methodNodes) + List fields, + List staticConstructor, + List methods) { this.name = name; this.typeParameters = typeParameters; this.superTypes = superTypes; - methods = methodNodes; + this.fields = fields; + this.staticConstructor = staticConstructor; + this.methods = methods; } @Override @@ -49,19 +57,17 @@ public class InterfaceNode implements TypeNode { return superTypes; } - @Override - public List getMethods() { - return methods; + public List getFields() { + return fields; + } + + public List getStaticConstructor() { + return staticConstructor; } @Override - public String toString() { - return "InterfaceNode(" + - "name=" + name + - ", typeParameters=" + typeParameters + - ", superTypes=" + superTypes + - ", methods=" + methods + - ')'; + public List getMethods() { + return methods; } @Override @@ -80,9 +86,23 @@ public class InterfaceNode implements TypeNode { transformer.transform(name), transformer.transformFormalTypeParameters(typeParameters), transformer.transformTypes(superTypes), + transformer.transformFields(fields), + transformer.transformStatements(staticConstructor), transformer.transformMethods(methods)); } + @Override + public String toString() { + return "InterfaceNode(" + + "name=" + name + + ", typeParameters=" + typeParameters + + ", superTypes=" + superTypes + + ", fields=" + fields + + ", staticConstructor=" + staticConstructor + + ", methods=" + methods + + ')'; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -93,6 +113,8 @@ public class InterfaceNode implements TypeNode { if (!name.equals(that.name)) return false; if (!typeParameters.equals(that.typeParameters)) return false; if (!superTypes.equals(that.superTypes)) return false; + if (!fields.equals(that.fields)) return false; + if (!staticConstructor.equals(that.staticConstructor)) return false; return methods.equals(that.methods); } @@ -102,6 +124,8 @@ public class InterfaceNode implements TypeNode { int result = name.hashCode(); result = 31 * result + typeParameters.hashCode(); result = 31 * result + superTypes.hashCode(); + result = 31 * result + fields.hashCode(); + result = 31 * result + staticConstructor.hashCode(); result = 31 * result + methods.hashCode(); return result; } diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java index b2255d2..bcc920e 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java @@ -45,6 +45,7 @@ public class NodeTransformer { private Function transformType = type -> type; private Function transformMethodName = MethodSignature::getName; private Function transformFieldName = name -> name; + private Function transformReceiver = receiver -> receiver; public Builder transformExpression( Function> transformExpression) @@ -77,13 +78,19 @@ public class NodeTransformer { return this; } + public Builder transformReceiver(Function receiver) { + this.transformReceiver = receiver; + return this; + } + public NodeTransformer build() { return new NodeTransformer( transformExpression, transformStatement, transformType, transformMethodName, - transformFieldName + transformFieldName, + transformReceiver ); } } @@ -93,19 +100,22 @@ public class NodeTransformer { private final Function transformType; private final Function transformMethodName; private final Function transformFieldName; + private final Function transformReceiver; private NodeTransformer( Function> transformExpression, Function>> transformStatement, Function transformType, Function transformMethodName, - Function transformFieldName) + Function transformFieldName, + Function transformReceiver) { this.transformExpression = transformExpression; this.transformStatement = transformStatement; this.transformType = transformType; this.transformMethodName = transformMethodName; this.transformFieldName = transformFieldName; + this.transformReceiver = transformReceiver; } public MethodSignature transform(MethodSignature signature) { @@ -142,7 +152,7 @@ public class NodeTransformer { } public Receiver transformReceiver(Receiver receiver) { - return receiver.transformSubtree(this); + return transformReceiver.apply(receiver.transformSubtree(this)); } public VariableDeclaration transform(VariableDeclaration declaration) { 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 e576102..47a7635 100644 --- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java @@ -7,6 +7,7 @@ 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.SplitStaticsFromInterface; import org.zwobble.couscous.types.*; import java.util.List; @@ -47,14 +48,16 @@ public class CsharpCodeGenerator { } private List generateCode(List types) { - return NodeTransformer.applyAll( - list( - DesugarForEachToFor.transformer(), - DesugarForToWhile.transformer(), - CsharpPrimitiveMethods.TRANSFORMER, - nodeTransformer - ), - types + return SplitStaticsFromInterface.transform( + NodeTransformer.applyAll( + list( + DesugarForEachToFor.transformer(), + DesugarForToWhile.transformer(), + CsharpPrimitiveMethods.TRANSFORMER, + nodeTransformer + ), + types + ) ); } 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 1c4ac81..5202127 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java @@ -115,11 +115,18 @@ public class JavaReader { ScalarType name = erasure(typeOf(type.resolveBinding())); Scope scope = topScope.enterClass(name); List typeParameters = readTypeParameters(scope.getIdentifier(), type); - TypeDeclarationBody body = readTypeDeclarationBody(scope, name, type.bodyDeclarations()); + TypeDeclarationBody body = readTypeDeclarationBody(scope, name, type.bodyDeclarations(), type.isInterface()); Set superTypes = superTypes(type); if (type.isInterface()) { // TODO: throw exception if other parts of body are declared - return InterfaceNode.declareInterface(name, typeParameters, superTypes, body.getMethods()); + return InterfaceNode.declareInterface( + name, + typeParameters, + superTypes, + body.getFields(), + body.getStaticConstructor(), + body.getMethods() + ); } else { return ClassNode.declareClass( name, @@ -233,7 +240,7 @@ public class JavaReader { GeneratedClosure readAnonymousClass(Scope outerScope, AnonymousClassDeclaration declaration) { ScalarType className = generateAnonymousName(declaration); Scope scope = outerScope.enterClass(className); - TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(scope, className, declaration.bodyDeclarations()); + TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(scope, className, declaration.bodyDeclarations(), false); AnonymousClass anonymousClass = new AnonymousClass( Optional.of((AnonymousType) typeOf(declaration.resolveBinding())), superTypes(declaration), @@ -357,7 +364,7 @@ public class JavaReader { return ((AbstractTypeDeclaration)node).resolveBinding(); } - private TypeDeclarationBody readTypeDeclarationBody(Scope scope, ScalarType type, List bodyDeclarations) { + private TypeDeclarationBody readTypeDeclarationBody(Scope scope, ScalarType type, List bodyDeclarations, boolean isInterface) { TypeDeclarationBody.Builder body = TypeDeclarationBody.builder(); for (Object declaration : bodyDeclarations) { @@ -370,7 +377,7 @@ public class JavaReader { readStatementBody(scope, initializer.getBody()))); tryCast(FieldDeclaration.class, declaration) - .ifPresent(field -> readField(body, scope, type, field)); + .ifPresent(field -> readField(body, scope, type, field, isInterface)); tryCast(TypeDeclaration.class, declaration) .ifPresent(typeDeclaration -> classes.add(readNestedTypeDeclaration(body, typeDeclaration))); @@ -406,22 +413,22 @@ public class JavaReader { .orElse(typeNode); } - private void readField(TypeDeclarationBody.Builder builder, Scope scope, ScalarType declaringType, FieldDeclaration field) { + private void readField(TypeDeclarationBody.Builder builder, Scope scope, ScalarType declaringType, FieldDeclaration field, boolean isInterface) { @SuppressWarnings("unchecked") List fragments = field.fragments(); + boolean isStatic = isInterface || Modifier.isStatic(field.getModifiers()); Type type = typeOf(field.getType()); fragments.forEach(fragment -> { - builder.field(field(Modifier.isStatic(field.getModifiers()), fragment.getName().getIdentifier(), type)); + builder.field(field(isStatic, fragment.getName().getIdentifier(), type)); if (fragment.getInitializer() != null) { ExpressionNode value = readExpression(scope, type, fragment.getInitializer()); String name = fragment.getName().getIdentifier(); - boolean isStatic = Modifier.isStatic(field.getModifiers()); Receiver receiver = isStatic ? staticReceiver(declaringType) : instanceReceiver(thisReference(declaringType)); StatementNode assignment = assignStatement(fieldAccess(receiver, name, type), value); - builder.addInitializer(isStatic,assignment); + builder.addInitializer(isStatic, assignment); } }); diff --git a/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java b/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java new file mode 100644 index 0000000..b2c88ea --- /dev/null +++ b/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java @@ -0,0 +1,68 @@ +package org.zwobble.couscous.transforms; + +import com.google.common.collect.Iterables; +import org.zwobble.couscous.ast.*; +import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.types.ScalarType; + +import java.util.*; + +import static org.zwobble.couscous.util.ExtraLists.eagerFilter; +import static org.zwobble.couscous.util.ExtraLists.list; +import static org.zwobble.couscous.util.ExtraSets.set; + +public class SplitStaticsFromInterface { + public static List transform(List typeNodes) { + List result = new ArrayList<>(); + Set splitInterfaces = new HashSet<>(); + + for (TypeNode typeNode : typeNodes) { + if (isSplittable(typeNode)) { + splitInterfaces.add(typeNode.getName()); + InterfaceNode interfaceNode = (InterfaceNode) typeNode; + result.add(ClassNode.declareClass( + staticTypeFor(interfaceNode.getName()), + list(), + set(), + interfaceNode.getFields(), + interfaceNode.getStaticConstructor(), + ConstructorNode.DEFAULT, + eagerFilter(interfaceNode.getMethods(), method -> method.isStatic()) + )); + result.add(InterfaceNode.declareInterface( + interfaceNode.getName(), + interfaceNode.getTypeParameters(), + interfaceNode.getSuperTypes(), + list(), + list(), + eagerFilter(interfaceNode.getMethods(), method -> !method.isStatic()) + )); + } else { + result.add(typeNode); + } + } + + NodeTransformer transformer = NodeTransformer.builder() + .transformReceiver((Receiver receiver) -> { + if (receiver instanceof StaticReceiver && splitInterfaces.contains(((StaticReceiver) receiver).getType())) { + return StaticReceiver.staticReceiver(staticTypeFor(((StaticReceiver) receiver).getType())); + } else { + return receiver; + } + }) + .build(); + return NodeTransformer.apply(transformer, result); + } + + private static ScalarType staticTypeFor(ScalarType name) { + return ScalarType.of(name.getQualifiedName() + "_static"); + } + + private static boolean isSplittable(TypeNode typeNode) { + if (!(typeNode instanceof InterfaceNode)) { + return false; + } + InterfaceNode interfaceNode = (InterfaceNode) typeNode; + return !interfaceNode.getFields().isEmpty() || Iterables.any(interfaceNode.getMethods(), MethodNode::isStatic); + } +} -- cgit v1.2.3