From 284d439f4db00be49de12862b2610a40d84cfc81 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 14 Jul 2016 16:35:31 +0100 Subject: Remove NodeMapper in favour of a DynamicNodeMapper --- .../org/zwobble/couscous/ast/AnnotationNode.java | 8 +- .../java/org/zwobble/couscous/ast/ClassNode.java | 6 - .../org/zwobble/couscous/ast/ConstructorNode.java | 6 - .../java/org/zwobble/couscous/ast/EnumNode.java | 6 - .../org/zwobble/couscous/ast/ExpressionNode.java | 72 +--------- .../zwobble/couscous/ast/FieldDeclarationNode.java | 8 +- .../zwobble/couscous/ast/FormalArgumentNode.java | 8 +- .../couscous/ast/FormalTypeParameterNode.java | 6 - .../org/zwobble/couscous/ast/InstanceReceiver.java | 6 - .../org/zwobble/couscous/ast/InterfaceNode.java | 8 +- .../java/org/zwobble/couscous/ast/MethodNode.java | 6 - src/main/java/org/zwobble/couscous/ast/Node.java | 2 - .../org/zwobble/couscous/ast/StatementNode.java | 42 ------ .../org/zwobble/couscous/ast/StaticReceiver.java | 8 +- .../couscous/ast/visitors/DynamicNodeMapper.java | 145 +++++++++++++++++++++ 15 files changed, 151 insertions(+), 186 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/ast/visitors/DynamicNodeMapper.java (limited to 'src/main/java/org/zwobble/couscous/ast') diff --git a/src/main/java/org/zwobble/couscous/ast/AnnotationNode.java b/src/main/java/org/zwobble/couscous/ast/AnnotationNode.java index 1d4b8ce..7cba234 100644 --- a/src/main/java/org/zwobble/couscous/ast/AnnotationNode.java +++ b/src/main/java/org/zwobble/couscous/ast/AnnotationNode.java @@ -1,8 +1,7 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.types.Type; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.types.Type; import org.zwobble.couscous.util.ExtraIterables; public class AnnotationNode implements Node { @@ -20,11 +19,6 @@ public class AnnotationNode implements Node { return type; } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public int type() { return NodeTypes.ANNOTATION; diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java index 4664bc0..80f3d38 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -1,7 +1,6 @@ package org.zwobble.couscous.ast; import com.google.common.collect.Iterables; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; @@ -92,11 +91,6 @@ public class ClassNode implements TypeNode { return name.getSimpleName(); } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public int type() { return NodeTypes.CLASS; diff --git a/src/main/java/org/zwobble/couscous/ast/ConstructorNode.java b/src/main/java/org/zwobble/couscous/ast/ConstructorNode.java index cca7a31..5b49659 100644 --- a/src/main/java/org/zwobble/couscous/ast/ConstructorNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ConstructorNode.java @@ -1,7 +1,6 @@ package org.zwobble.couscous.ast; import com.google.common.collect.Iterables; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import java.util.Collections; @@ -34,11 +33,6 @@ public class ConstructorNode implements Node { return body; } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public int type() { return NodeTypes.CONSTRUCTOR; diff --git a/src/main/java/org/zwobble/couscous/ast/EnumNode.java b/src/main/java/org/zwobble/couscous/ast/EnumNode.java index c1db294..f207618 100644 --- a/src/main/java/org/zwobble/couscous/ast/EnumNode.java +++ b/src/main/java/org/zwobble/couscous/ast/EnumNode.java @@ -1,6 +1,5 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Type; @@ -67,11 +66,6 @@ public class EnumNode implements TypeNode { eagerMap(values, transformer::transformFieldName)); } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public String toString() { return "EnumNode(" + diff --git a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java index 4c104f3..16a47e8 100644 --- a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java @@ -1,83 +1,13 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.types.Type; import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.types.Type; public interface ExpressionNode extends Node { Type getType(); T accept(ExpressionNodeMapper visitor); - - @Override - default T accept(NodeMapper visitor) { - return accept(new ExpressionNodeMapper() { - @Override - public T visit(LiteralNode literal) { - return visitor.visit(literal); - } - - @Override - public T visit(VariableReferenceNode variableReference) { - return visitor.visit(variableReference); - } - - @Override - public T visit(ThisReferenceNode reference) { - return visitor.visit(reference); - } - - @Override - public T visit(ArrayNode array) { - return visitor.visit(array); - } - - @Override - public T visit(AssignmentNode assignment) { - return visitor.visit(assignment); - } - - @Override - public T visit(TernaryConditionalNode ternaryConditional) { - return visitor.visit(ternaryConditional); - } - - @Override - public T visit(MethodCallNode methodCall) { - return visitor.visit(methodCall); - } - - @Override - public T visit(ConstructorCallNode call) { - return visitor.visit(call); - } - - @Override - public T visit(OperationNode operation) { - return visitor.visit(operation); - } - - @Override - public T visit(InstanceOfNode instanceOf) { - return visitor.visit(instanceOf); - } - - @Override - public T visit(FieldAccessNode fieldAccess) { - return visitor.visit(fieldAccess); - } - - @Override - public T visit(TypeCoercionNode typeCoercion) { - return visitor.visit(typeCoercion); - } - @Override - public T visit(CastNode cast) { - return visitor.visit(cast); - } - }); - } ExpressionNode transform(NodeTransformer transformer); } diff --git a/src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java b/src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java index 59fcab8..3e23ec9 100644 --- a/src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java +++ b/src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java @@ -1,8 +1,7 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.types.Type; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.types.Type; import org.zwobble.couscous.util.ExtraIterables; public class FieldDeclarationNode implements Node { @@ -40,11 +39,6 @@ public class FieldDeclarationNode implements Node { return type; } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public int type() { return NodeTypes.FIELD_DECLARATION; diff --git a/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java b/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java index 9c20aff..f1d28c1 100644 --- a/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java +++ b/src/main/java/org/zwobble/couscous/ast/FormalArgumentNode.java @@ -1,8 +1,7 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.types.Type; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.types.Type; import org.zwobble.couscous.util.ExtraIterables; public class FormalArgumentNode implements VariableNode, Node { @@ -28,11 +27,6 @@ public class FormalArgumentNode implements VariableNode, Node { return declaration.getName(); } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public int type() { return NodeTypes.FORMAL_ARGUMENT; diff --git a/src/main/java/org/zwobble/couscous/ast/FormalTypeParameterNode.java b/src/main/java/org/zwobble/couscous/ast/FormalTypeParameterNode.java index d51a4a4..f7d1609 100644 --- a/src/main/java/org/zwobble/couscous/ast/FormalTypeParameterNode.java +++ b/src/main/java/org/zwobble/couscous/ast/FormalTypeParameterNode.java @@ -1,7 +1,6 @@ package org.zwobble.couscous.ast; import org.zwobble.couscous.ast.identifiers.Identifier; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.types.TypeParameter; import org.zwobble.couscous.util.ExtraIterables; @@ -43,11 +42,6 @@ public class FormalTypeParameterNode implements Node { return new FormalTypeParameterNode(transformer.transform(type)); } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public String toString() { return "FormalTypeParameterNode(" + diff --git a/src/main/java/org/zwobble/couscous/ast/InstanceReceiver.java b/src/main/java/org/zwobble/couscous/ast/InstanceReceiver.java index d86d5d1..413c06c 100644 --- a/src/main/java/org/zwobble/couscous/ast/InstanceReceiver.java +++ b/src/main/java/org/zwobble/couscous/ast/InstanceReceiver.java @@ -1,6 +1,5 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.util.ExtraIterables; @@ -39,11 +38,6 @@ public class InstanceReceiver implements Receiver { return new InstanceReceiver(transformer.transformExpression(expression)); } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public String toString() { return "InstanceReceiver(" + diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java index c01df04..cba6d38 100644 --- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java @@ -1,9 +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.NodeMapper; -import org.zwobble.couscous.ast.visitors.NodeTransformer; import java.util.List; import java.util.Set; @@ -55,11 +54,6 @@ public class InterfaceNode implements TypeNode { return methods; } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public String toString() { return "InterfaceNode(" + diff --git a/src/main/java/org/zwobble/couscous/ast/MethodNode.java b/src/main/java/org/zwobble/couscous/ast/MethodNode.java index 143158a..5497995 100644 --- a/src/main/java/org/zwobble/couscous/ast/MethodNode.java +++ b/src/main/java/org/zwobble/couscous/ast/MethodNode.java @@ -2,7 +2,6 @@ package org.zwobble.couscous.ast; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.types.Type; import org.zwobble.couscous.types.Types; @@ -189,11 +188,6 @@ public class MethodNode implements Node { return new MethodNode(annotations, isStatic, name, typeParameters, arguments, returnType, body.map(function), overrides); } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public int type() { return NodeTypes.METHOD; diff --git a/src/main/java/org/zwobble/couscous/ast/Node.java b/src/main/java/org/zwobble/couscous/ast/Node.java index 32cd4d8..4039e31 100644 --- a/src/main/java/org/zwobble/couscous/ast/Node.java +++ b/src/main/java/org/zwobble/couscous/ast/Node.java @@ -1,11 +1,9 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; public interface Node { int type(); Iterable childNodes(); - T accept(NodeMapper visitor); Node transform(NodeTransformer transformer); } diff --git a/src/main/java/org/zwobble/couscous/ast/StatementNode.java b/src/main/java/org/zwobble/couscous/ast/StatementNode.java index 4755de7..cdcd636 100644 --- a/src/main/java/org/zwobble/couscous/ast/StatementNode.java +++ b/src/main/java/org/zwobble/couscous/ast/StatementNode.java @@ -1,51 +1,9 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.ast.visitors.StatementNodeMapper; public interface StatementNode extends Node { T accept(StatementNodeMapper visitor); - - @Override - default T accept(NodeMapper visitor) { - return accept(new StatementNodeMapper() { - @Override - public T visit(ReturnNode returnNode) { - return visitor.visit(returnNode); - } - - @Override - public T visit(ThrowNode throwNode) { - return visitor.visit(throwNode); - } - - @Override - public T visit(ExpressionStatementNode expressionStatement) { - return visitor.visit(expressionStatement); - } - - @Override - public T visit(LocalVariableDeclarationNode localVariableDeclaration) { - return visitor.visit(localVariableDeclaration); - } - - @Override - public T visit(IfStatementNode ifStatement) { - return visitor.visit(ifStatement); - } - - @Override - public T visit(WhileNode whileLoop) { - return visitor.visit(whileLoop); - } - - @Override - public T visit(TryNode tryStatement) { - return visitor.visit(tryStatement); - } - }); - } - StatementNode transform(NodeTransformer transformer); } diff --git a/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java b/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java index 34f9e13..cbef8c5 100644 --- a/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java +++ b/src/main/java/org/zwobble/couscous/ast/StaticReceiver.java @@ -1,8 +1,7 @@ package org.zwobble.couscous.ast; -import org.zwobble.couscous.types.ScalarType; -import org.zwobble.couscous.ast.visitors.NodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; +import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.util.ExtraIterables; public class StaticReceiver implements Receiver { @@ -29,11 +28,6 @@ public class StaticReceiver implements Receiver { return mapper.visit(type); } - @Override - public T accept(NodeMapper visitor) { - return visitor.visit(this); - } - @Override public int type() { return NodeTypes.STATIC_RECEIVER; diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/DynamicNodeMapper.java b/src/main/java/org/zwobble/couscous/ast/visitors/DynamicNodeMapper.java new file mode 100644 index 0000000..29fb4dd --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/visitors/DynamicNodeMapper.java @@ -0,0 +1,145 @@ +package org.zwobble.couscous.ast.visitors; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.modifier.FieldManifestation; +import net.bytebuddy.description.modifier.Visibility; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.Duplication; +import net.bytebuddy.implementation.bytecode.StackManipulation; +import net.bytebuddy.implementation.bytecode.Throw; +import net.bytebuddy.implementation.bytecode.TypeCreation; +import net.bytebuddy.implementation.bytecode.assign.TypeCasting; +import net.bytebuddy.implementation.bytecode.member.FieldAccess; +import net.bytebuddy.implementation.bytecode.member.MethodInvocation; +import net.bytebuddy.implementation.bytecode.member.MethodReturn; +import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess; +import net.bytebuddy.matcher.ElementMatchers; +import org.zwobble.couscous.ast.Node; +import org.zwobble.couscous.ast.NodeTypes; +import org.zwobble.couscous.util.ExtraIterables; +import org.zwobble.couscous.util.asm.Implementations; +import org.zwobble.couscous.util.asm.StackManipulationSwitch; +import org.zwobble.couscous.util.asm.TypeDescriptions; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import static java.util.Arrays.asList; +import static org.zwobble.couscous.util.ExtraLists.eagerFilter; +import static org.zwobble.couscous.util.ExtraLists.eagerMap; + +public class DynamicNodeMapper { + public static DynamicNodeMapper build(Class clazz, Class returnType, String methodName) { + return build(clazz, new TypeDescription.ForLoadedType(returnType), methodName); + } + + public static DynamicNodeMapper build(Class clazz, TypeDescription returnType, String methodName) { + List visitMethods = eagerFilter( + asList(clazz.getMethods()), + method -> isVisitMethod(methodName, method) + ); + + Optional defaultMethod = ExtraIterables.find( + asList(clazz.getMethods()), + method -> + method.getName().equals(methodName) && + method.getParameterCount() == 1 && + method.getParameterTypes()[0].equals(Node.class) + ); + + Class visitor = new ByteBuddy() + .subclass(Function.class) + + .defineField("visitor", clazz, Visibility.PRIVATE, FieldManifestation.FINAL) + + .defineConstructor(Visibility.PUBLIC) + .withParameters(clazz) + .intercept(Implementations.stackManipulation(target -> new StackManipulation.Compound( + // this() + MethodVariableAccess.REFERENCE.loadOffset(0), + MethodInvocation.invoke(TypeDescriptions.findConstructor(Object.class)), + + // this.visitor = visitor + MethodVariableAccess.REFERENCE.loadOffset(0), + MethodVariableAccess.REFERENCE.loadOffset(1), + FieldAccess.forField(TypeDescriptions.findField(target, "visitor")).putter(), + + // return + MethodReturn.VOID + ))) + + .method(ElementMatchers.named("apply")) + .intercept(Implementations.stackManipulation(target -> { + MethodDescription typeMethod = TypeDescriptions.findMethod(Node.class, "type"); + return new StackManipulation.Compound( + MethodVariableAccess.REFERENCE.loadOffset(1), + MethodInvocation.invoke(typeMethod), + new StackManipulationSwitch( + defaultMethod + .map(defaultMethodValue -> + new StackManipulation.Compound( + MethodVariableAccess.REFERENCE.loadOffset(0), + FieldAccess.forField(TypeDescriptions.findField(target, "visitor")).getter(), + MethodVariableAccess.REFERENCE.loadOffset(1), + TypeCasting.to(new TypeDescription.ForLoadedType(Node.class)), + MethodInvocation.invoke(new MethodDescription.ForLoadedMethod(defaultMethodValue)), + MethodReturn.REFERENCE + )) + .orElseGet(() -> new StackManipulation.Compound( + TypeCreation.of(new TypeDescription.ForLoadedType(UnsupportedOperationException.class)), + Duplication.SINGLE, + MethodInvocation.invoke(TypeDescriptions.findConstructor(UnsupportedOperationException.class)), + Throw.INSTANCE + )), + eagerMap(visitMethods, method -> { + Class nodeClass = method.getParameterTypes()[0]; + return StackManipulationSwitch.switchCase( + NodeTypes.forClass(nodeClass), + new StackManipulation.Compound( + MethodVariableAccess.REFERENCE.loadOffset(0), + FieldAccess.forField(TypeDescriptions.findField(target, "visitor")).getter(), + MethodVariableAccess.REFERENCE.loadOffset(1), + TypeCasting.to(new TypeDescription.ForLoadedType(nodeClass)), + MethodInvocation.invoke(new MethodDescription.ForLoadedMethod(method)), + MethodReturn.REFERENCE + ) + ); + }) + ) + ); + })) + .make() + .load(DynamicNodeMapper.class.getClassLoader()) + .getLoaded(); + + return new DynamicNodeMapper(clazz, visitor); + } + + private static boolean isVisitMethod(String methodName, Method method) { + return + method.getName().equals(methodName) && + method.getParameterCount() == 1 && + Node.class.isAssignableFrom(method.getParameterTypes()[0]) && + !method.getParameterTypes()[0].equals(Node.class); + } + + private final Class clazz; + private final Class visitor; + + public DynamicNodeMapper(Class clazz, Class visitor) { + this.clazz = clazz; + this.visitor = visitor; + } + + public Function instantiate(T instance) { + try { + return visitor.getConstructor(clazz).newInstance(instance); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) { + throw new RuntimeException(exception); + } + } +} -- cgit v1.2.3