diff options
| author | Michael Williamson <mike@zwobble.org> | 2016-07-14 16:35:31 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2016-07-14 16:35:31 +0100 |
| commit | 284d439f4db00be49de12862b2610a40d84cfc81 (patch) | |
| tree | a6a3161536b58ff9e5a4491d9820046096e9907c /src/main/java/org/zwobble/couscous/ast | |
| parent | 6d5608b5061bc7b5e40db9c37f186cfd0d48a3e6 (diff) | |
Remove NodeMapper in favour of a DynamicNodeMapper
Diffstat (limited to 'src/main/java/org/zwobble/couscous/ast')
15 files changed, 151 insertions, 186 deletions
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 { @@ -21,11 +20,6 @@ public class AnnotationNode implements Node { } @Override - public <T> T accept(NodeMapper<T> 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; @@ -93,11 +92,6 @@ public class ClassNode implements TypeNode { } @Override - public <T> T accept(NodeMapper<T> 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; @@ -35,11 +34,6 @@ public class ConstructorNode implements Node { } @Override - public <T> T accept(NodeMapper<T> 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; @@ -68,11 +67,6 @@ public class EnumNode implements TypeNode { } @Override - public <T> T accept(NodeMapper<T> visitor) { - return visitor.visit(this); - } - - @Override public String toString() { return "EnumNode(" + "name=" + name + 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> T accept(ExpressionNodeMapper<T> visitor); - - @Override - default <T> T accept(NodeMapper<T> visitor) { - return accept(new ExpressionNodeMapper<T>() { - @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 { @@ -41,11 +40,6 @@ public class FieldDeclarationNode implements Node { } @Override - public <T> T accept(NodeMapper<T> 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 { @@ -29,11 +28,6 @@ public class FormalArgumentNode implements VariableNode, Node { } @Override - public <T> T accept(NodeMapper<T> 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; @@ -44,11 +43,6 @@ public class FormalTypeParameterNode implements Node { } @Override - public <T> T accept(NodeMapper<T> visitor) { - return visitor.visit(this); - } - - @Override public String toString() { return "FormalTypeParameterNode(" + "type=" + type + 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; @@ -40,11 +39,6 @@ public class InstanceReceiver implements Receiver { } @Override - public <T> T accept(NodeMapper<T> visitor) { - return visitor.visit(this); - } - - @Override public String toString() { return "InstanceReceiver(" + "expression=" + expression + 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; @@ -56,11 +55,6 @@ public class InterfaceNode implements TypeNode { } @Override - public <T> T accept(NodeMapper<T> visitor) { - return visitor.visit(this); - } - - @Override public String toString() { return "InterfaceNode(" + "name=" + name + 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; @@ -190,11 +189,6 @@ public class MethodNode implements Node { } @Override - public <T> T accept(NodeMapper<T> 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<? extends Node> childNodes(); - <T> T accept(NodeMapper<T> 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> T accept(StatementNodeMapper<T> visitor); - - @Override - default <T> T accept(NodeMapper<T> visitor) { - return accept(new StatementNodeMapper<T>() { - @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 { @@ -30,11 +29,6 @@ public class StaticReceiver implements Receiver { } @Override - public <T> T accept(NodeMapper<T> 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<T, R> { + public static <T, R> DynamicNodeMapper<T, R> build(Class<T> clazz, Class<R> returnType, String methodName) { + return build(clazz, new TypeDescription.ForLoadedType(returnType), methodName); + } + + public static <T, R> DynamicNodeMapper<T, R> build(Class<T> clazz, TypeDescription returnType, String methodName) { + List<Method> visitMethods = eagerFilter( + asList(clazz.getMethods()), + method -> isVisitMethod(methodName, method) + ); + + Optional<Method> defaultMethod = ExtraIterables.find( + asList(clazz.getMethods()), + method -> + method.getName().equals(methodName) && + method.getParameterCount() == 1 && + method.getParameterTypes()[0].equals(Node.class) + ); + + Class<? extends Function> 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<T, R>(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<T> clazz; + private final Class<? extends Function> visitor; + + public DynamicNodeMapper(Class<T> clazz, Class<? extends Function> visitor) { + this.clazz = clazz; + this.visitor = visitor; + } + + public Function<Node, R> instantiate(T instance) { + try { + return visitor.getConstructor(clazz).newInstance(instance); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) { + throw new RuntimeException(exception); + } + } +} |
