You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2015/10/07 21:26:29 UTC
[09/37] incubator-groovy git commit: First bits of work on a
contextual class code visitor
First bits of work on a contextual class code visitor
Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/a522e8ff
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/a522e8ff
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/a522e8ff
Branch: refs/heads/master
Commit: a522e8ff44f1c2e11154376ec600944f9787bd57
Parents: f080510
Author: Cedric Champeau <ce...@gmail.com>
Authored: Tue Oct 14 20:29:26 2014 +0200
Committer: Sergei Egorov <bs...@gmail.com>
Committed: Mon Sep 28 14:32:05 2015 +0300
----------------------------------------------------------------------
.../groovy/ast/ContextualClassCodeVisitor.java | 638 +++++++++++++++++++
1 file changed, 638 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/a522e8ff/src/main/org/codehaus/groovy/ast/ContextualClassCodeVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/ContextualClassCodeVisitor.java b/src/main/org/codehaus/groovy/ast/ContextualClassCodeVisitor.java
new file mode 100644
index 0000000..742cb72
--- /dev/null
+++ b/src/main/org/codehaus/groovy/ast/ContextualClassCodeVisitor.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright 2003-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import groovy.lang.Closure;
+import groovy.lang.MapWithDefault;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A class code visitor which is capable of remembering the context of the current
+ * visit. This makes it easier for subclasses to perform context-dependent transformations,
+ * where for example it is necessary to check the parent nodes of an AST node before
+ * performing some operations.
+ *
+ * @author Cedric Champeau
+ * @since 2.4.0
+ */
+public abstract class ContextualClassCodeVisitor extends ClassCodeVisitorSupport {
+ private final Deque<TreeContext> treeContextStack = new ArrayDeque<TreeContext>();
+ private TreeContext lastContext;
+
+ public ContextualClassCodeVisitor() {
+ pushContext(new TreeContext(null, null));
+ }
+
+ public TreeContext getTreeContext() {
+ return treeContextStack.isEmpty()?null:treeContextStack.peek();
+ }
+
+ protected void pushContext(TreeContext ctx) {
+ treeContextStack.push(ctx);
+ }
+
+ protected TreeContext popContext() {
+ TreeContext treeContext = treeContextStack.pop();
+ List<TreeContextAction> actions = treeContext.getOnPopHandlers();
+ for (TreeContextAction contextAction : actions) {
+ contextAction.call(treeContext);
+ }
+ lastContext = treeContext;
+ return treeContext;
+ }
+
+ protected void pushContext(ASTNode node) {
+ pushContext(getTreeContext().fork(node));
+ }
+
+
+ // ----------------------- override visit methods to provide contextual information ---------------------------
+
+
+ @Override
+ public void visitClass(final ClassNode node) {
+ pushContext(node);
+ super.visitClass(node);
+ popContext();
+ }
+
+ @Override
+ public void visitPackage(final PackageNode node) {
+ if (node!=null) {
+ pushContext(node);
+ }
+ super.visitPackage(node);
+ if (node!=null) {
+ popContext();
+ }
+ }
+
+ @Override
+ public void visitImports(final ModuleNode node) {
+ pushContext(node);
+ super.visitImports(node);
+ popContext();
+ }
+
+ @Override
+ protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
+ pushContext(node);
+ super.visitConstructorOrMethod(node, isConstructor);
+ popContext();
+ }
+
+ @Override
+ public void visitField(final FieldNode node) {
+ pushContext(node);
+ super.visitField(node);
+ popContext();
+ }
+
+ @Override
+ public void visitProperty(final PropertyNode node) {
+ pushContext(node);
+ super.visitProperty(node);
+ popContext();
+ }
+
+ @Override
+ public void visitMethodCallExpression(final MethodCallExpression call) {
+ pushContext(call);
+ super.visitMethodCallExpression(call);
+ popContext();
+ }
+
+ @Override
+ public void visitStaticMethodCallExpression(final StaticMethodCallExpression call) {
+ pushContext(call);
+ super.visitStaticMethodCallExpression(call);
+ popContext();
+ }
+
+ @Override
+ public void visitConstructorCallExpression(final ConstructorCallExpression call) {
+ pushContext(call);
+ super.visitConstructorCallExpression(call);
+ popContext();
+ }
+
+ @Override
+ public void visitBinaryExpression(final BinaryExpression expression) {
+ pushContext(expression);
+ super.visitBinaryExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitTernaryExpression(final TernaryExpression expression) {
+ pushContext(expression);
+ super.visitTernaryExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitShortTernaryExpression(final ElvisOperatorExpression expression) {
+ pushContext(expression);
+ super.visitShortTernaryExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitPostfixExpression(final PostfixExpression expression) {
+ pushContext(expression);
+ super.visitPostfixExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitPrefixExpression(final PrefixExpression expression) {
+ pushContext(expression);
+ super.visitPrefixExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitBooleanExpression(final BooleanExpression expression) {
+ pushContext(expression);
+ super.visitBooleanExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitNotExpression(final NotExpression expression) {
+ pushContext(expression);
+ super.visitNotExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitClosureExpression(final ClosureExpression expression) {
+ pushContext(expression);
+ super.visitClosureExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitTupleExpression(final TupleExpression expression) {
+ pushContext(expression);
+ super.visitTupleExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitListExpression(final ListExpression expression) {
+ pushContext(expression);
+ super.visitListExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitArrayExpression(final ArrayExpression expression) {
+ pushContext(expression);
+ super.visitArrayExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitMapExpression(final MapExpression expression) {
+ pushContext(expression);
+ super.visitMapExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitMapEntryExpression(final MapEntryExpression expression) {
+ pushContext(expression);
+ super.visitMapEntryExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitRangeExpression(final RangeExpression expression) {
+ pushContext(expression);
+ super.visitRangeExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitSpreadExpression(final SpreadExpression expression) {
+ pushContext(expression);
+ super.visitSpreadExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitSpreadMapExpression(final SpreadMapExpression expression) {
+ pushContext(expression);
+ super.visitSpreadMapExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitMethodPointerExpression(final MethodPointerExpression expression) {
+ pushContext(expression);
+ super.visitMethodPointerExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitUnaryMinusExpression(final UnaryMinusExpression expression) {
+ pushContext(expression);
+ super.visitUnaryMinusExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitUnaryPlusExpression(final UnaryPlusExpression expression) {
+ pushContext(expression);
+ super.visitUnaryPlusExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitBitwiseNegationExpression(final BitwiseNegationExpression expression) {
+ pushContext(expression);
+ super.visitBitwiseNegationExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitCastExpression(final CastExpression expression) {
+ pushContext(expression);
+ super.visitCastExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitConstantExpression(final ConstantExpression expression) {
+ pushContext(expression);
+ super.visitConstantExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitClassExpression(final ClassExpression expression) {
+ pushContext(expression);
+ super.visitClassExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitVariableExpression(final VariableExpression expression) {
+ pushContext(expression);
+ super.visitVariableExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitPropertyExpression(final PropertyExpression expression) {
+ pushContext(expression);
+ super.visitPropertyExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitAttributeExpression(final AttributeExpression expression) {
+ pushContext(expression);
+ super.visitAttributeExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitFieldExpression(final FieldExpression expression) {
+ pushContext(expression);
+ super.visitFieldExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitGStringExpression(final GStringExpression expression) {
+ pushContext(expression);
+ super.visitGStringExpression(expression);
+ popContext();
+ }
+
+ @Override
+ public void visitClosureListExpression(final ClosureListExpression cle) {
+ pushContext(cle);
+ super.visitClosureListExpression(cle);
+ popContext();
+ }
+
+ @Override
+ public void visitBytecodeExpression(final BytecodeExpression cle) {
+ pushContext(cle);
+ super.visitBytecodeExpression(cle);
+ popContext();
+ }
+
+ @Override
+ public void visitAssertStatement(final AssertStatement statement) {
+ pushContext(statement);
+ super.visitAssertStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitBlockStatement(final BlockStatement block) {
+ pushContext(block);
+ super.visitBlockStatement(block);
+ popContext();
+ }
+
+ @Override
+ public void visitBreakStatement(final BreakStatement statement) {
+ pushContext(statement);
+ super.visitBreakStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitCaseStatement(final CaseStatement statement) {
+ pushContext(statement);
+ super.visitCaseStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitCatchStatement(final CatchStatement statement) {
+ pushContext(statement);
+ super.visitCatchStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitContinueStatement(final ContinueStatement statement) {
+ pushContext(statement);
+ super.visitContinueStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitDoWhileLoop(final DoWhileStatement loop) {
+ pushContext(loop);
+ super.visitDoWhileLoop(loop);
+ popContext();
+ }
+
+ @Override
+ public void visitExpressionStatement(final ExpressionStatement statement) {
+ pushContext(statement);
+ super.visitExpressionStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitForLoop(final ForStatement forLoop) {
+ pushContext(forLoop);
+ super.visitForLoop(forLoop);
+ popContext();
+ }
+
+ @Override
+ public void visitIfElse(final IfStatement ifElse) {
+ pushContext(ifElse);
+ super.visitIfElse(ifElse);
+ popContext();
+ }
+
+ @Override
+ public void visitReturnStatement(final ReturnStatement statement) {
+ pushContext(statement);
+ super.visitReturnStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitSwitch(final SwitchStatement statement) {
+ pushContext(statement);
+ super.visitSwitch(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitSynchronizedStatement(final SynchronizedStatement statement) {
+ pushContext(statement);
+ super.visitSynchronizedStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitThrowStatement(final ThrowStatement statement) {
+ pushContext(statement);
+ super.visitThrowStatement(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitTryCatchFinally(final TryCatchStatement statement) {
+ pushContext(statement);
+ super.visitTryCatchFinally(statement);
+ popContext();
+ }
+
+ @Override
+ public void visitWhileLoop(final WhileStatement loop) {
+ pushContext(loop);
+ super.visitWhileLoop(loop);
+ popContext();
+ }
+
+ @Override
+ protected void visitEmptyStatement(final EmptyStatement statement) {
+ pushContext(statement);
+ super.visitEmptyStatement(statement);
+ popContext();
+ }
+
+ public List<TreeContext> getTreePath() {
+ List<TreeContext> path = new LinkedList<TreeContext>();
+ path.add(lastContext);
+ for (TreeContext treeContext : treeContextStack) {
+ path.add(treeContext);
+ }
+ return path;
+ }
+ public List<TreeContext> pathMatches(List<ASTNodePredicate> predicates) {
+ List<TreeContext> path = new LinkedList<TreeContext>();
+ TreeContext current = lastContext.parent;
+ for (ASTNodePredicate predicate : predicates) {
+ path.add(current);
+ if (current==null || !predicate.matches(current.node)) {
+ return Collections.emptyList();
+ }
+ current = current.parent;
+ }
+ if (!path.isEmpty()) {
+ path.add(0, lastContext);
+ }
+ return path;
+ }
+
+ public List<TreeContext> pathUpTo(ASTNodePredicate predicate) {
+ return pathUpTo(null, predicate);
+ }
+
+ public List<TreeContext> pathUpTo(Class<ASTNode> node) {
+ return pathUpTo(node, null);
+ }
+
+ public List<TreeContext> pathUpTo(Class<ASTNode> node, ASTNodePredicate predicate) {
+ List<TreeContext> path = new LinkedList<TreeContext>();
+ TreeContext current = lastContext;
+ boolean found = false;
+ while (current!=null && !found) {
+ path.add(current);
+ ASTNode currentNode = current.node;
+ if (node==null) {
+ if (predicate.matches(currentNode)) {
+ found = true;
+ }
+ } else {
+ if (predicate==null) {
+ if (currentNode==null || node==currentNode.getClass()) {
+ found = true;
+ }
+ } else {
+ found = currentNode!=null && node==currentNode.getClass() && predicate.matches(currentNode);
+ }
+ }
+
+ current = current.parent;
+ }
+ if (found) {
+ return path;
+ }
+ return Collections.emptyList();
+ }
+
+ // ----------------------------- inner classes --------------------------------------
+
+ public static class TreeContext {
+ final TreeContext parent;
+ final ASTNode node;
+ final List<TreeContext> siblings = new LinkedList<TreeContext>();
+ final List<TreeContextAction> onPopHandlers = new LinkedList<TreeContextAction>();
+ final Map<?, List<?>> userdata = MapWithDefault.newInstance(
+ new HashMap<Object, List<?>>(),
+ new Closure(this) {
+ public Object doCall(String key) {
+ return new LinkedList<Object>();
+ }
+ }
+ );
+
+ private TreeContext(final TreeContext parent, final ASTNode node) {
+ this.parent = parent;
+ this.node = node;
+ if (parent!=null) {
+ parent.siblings.add(this);
+ }
+ }
+
+ public Map<?, ?> getUserdata() {
+ return userdata;
+ }
+
+ public TreeContext getParent() { return parent; }
+ public ASTNode getNode() { return node; }
+
+ public TreeContext fork(ASTNode node) {
+ return new TreeContext(this, node);
+ }
+
+ public boolean matches(ASTNodePredicate predicate) {
+ return predicate.matches(node);
+ }
+
+ public List<TreeContext> getSiblings() {
+ return Collections.unmodifiableList(siblings);
+ }
+
+ public List<TreeContextAction> getOnPopHandlers() {
+ return Collections.unmodifiableList(onPopHandlers);
+ }
+
+ public void afterVisit(TreeContextAction action) {
+ onPopHandlers.add(action);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("TreeContext{");
+ sb.append("node=").append(node!=null?node.getClass().getSimpleName():"undefined");
+ //sb.append(", siblings=").append(siblings);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ public static interface ASTNodePredicate {
+ boolean matches(ASTNode node);
+ }
+
+ public static interface TreeContextAction {
+ void call(TreeContext context);
+ }
+
+ public static List<ASTNodePredicate> matchByClass(Class<ASTNode>... classes) {
+ ArrayList<ASTNodePredicate> result = new ArrayList<ASTNodePredicate>(classes.length);
+ for (final Class<ASTNode> astNodeClass : classes) {
+ result.add(new MatchByClass(astNodeClass));
+ }
+ return result;
+ }
+
+ private static class MatchByClass implements ASTNodePredicate {
+ private final Class<ASTNode> astNodeClass;
+
+ public MatchByClass(final Class<ASTNode> astNodeClass) {
+ this.astNodeClass = astNodeClass;
+ }
+
+ @Override
+ public boolean matches(final ASTNode node) {
+ return astNodeClass ==node.getClass();
+ }
+ }
+}