blob: c184a5d030f0e1ba0515a88b7237f480d1422a45 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 groovy.console.ui
import groovy.text.GStringTemplateEngine
import groovy.text.Template
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.apache.groovy.io.StringBuilderWriter
import org.codehaus.groovy.GroovyBugError
import org.codehaus.groovy.ast.AnnotatedNode
import org.codehaus.groovy.ast.AnnotationNode
import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.CodeVisitorSupport
import org.codehaus.groovy.ast.ConstructorNode
import org.codehaus.groovy.ast.DynamicVariable
import org.codehaus.groovy.ast.FieldNode
import org.codehaus.groovy.ast.InnerClassNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.ModuleNode
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.ast.PropertyNode
import org.codehaus.groovy.ast.expr.ArgumentListExpression
import org.codehaus.groovy.ast.expr.ArrayExpression
import org.codehaus.groovy.ast.expr.AttributeExpression
import org.codehaus.groovy.ast.expr.BinaryExpression
import org.codehaus.groovy.ast.expr.BitwiseNegationExpression
import org.codehaus.groovy.ast.expr.BooleanExpression
import org.codehaus.groovy.ast.expr.CastExpression
import org.codehaus.groovy.ast.expr.ClassExpression
import org.codehaus.groovy.ast.expr.ClosureExpression
import org.codehaus.groovy.ast.expr.ClosureListExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.expr.ConstructorCallExpression
import org.codehaus.groovy.ast.expr.DeclarationExpression
import org.codehaus.groovy.ast.expr.ElvisOperatorExpression
import org.codehaus.groovy.ast.expr.Expression
import org.codehaus.groovy.ast.expr.FieldExpression
import org.codehaus.groovy.ast.expr.GStringExpression
import org.codehaus.groovy.ast.expr.LambdaExpression
import org.codehaus.groovy.ast.expr.ListExpression
import org.codehaus.groovy.ast.expr.MapEntryExpression
import org.codehaus.groovy.ast.expr.MapExpression
import org.codehaus.groovy.ast.expr.MethodCallExpression
import org.codehaus.groovy.ast.expr.MethodPointerExpression
import org.codehaus.groovy.ast.expr.MethodReferenceExpression
import org.codehaus.groovy.ast.expr.NamedArgumentListExpression
import org.codehaus.groovy.ast.expr.NotExpression
import org.codehaus.groovy.ast.expr.PostfixExpression
import org.codehaus.groovy.ast.expr.PrefixExpression
import org.codehaus.groovy.ast.expr.PropertyExpression
import org.codehaus.groovy.ast.expr.RangeExpression
import org.codehaus.groovy.ast.expr.SpreadExpression
import org.codehaus.groovy.ast.expr.SpreadMapExpression
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
import org.codehaus.groovy.ast.expr.TernaryExpression
import org.codehaus.groovy.ast.expr.TupleExpression
import org.codehaus.groovy.ast.expr.UnaryMinusExpression
import org.codehaus.groovy.ast.expr.UnaryPlusExpression
import org.codehaus.groovy.ast.expr.VariableExpression
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.Statement
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 org.codehaus.groovy.classgen.GeneratorContext
import org.codehaus.groovy.classgen.asm.BytecodeHelper
import org.codehaus.groovy.control.CompilationFailedException
import org.codehaus.groovy.control.CompilationUnit
import org.codehaus.groovy.control.CompilationUnit.PrimaryClassNodeOperation
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.SourceUnit
import java.util.concurrent.atomic.AtomicBoolean
/**
* This class controls the conversion from a Groovy script as a String into
* a tree representation of the AST of that script. The script itself
* will be a tree node, and each class in the script will be a tree node. The
* conversion creates tree nodes for any concrete class found within an AST
* visitor. So, if a tree node should be shown once for each ASTNode and the parent
* types will not appear as nodes. Custom subclasses of expression types will
* not appear in the tree.
*
* The String label of a tree node is defined by classname in AstBrowserProperties.properties.
*/
class ScriptToTreeNodeAdapter {
static Properties classNameToStringForm
boolean showScriptFreeForm, showScriptClass, showClosureClasses
final GroovyClassLoader classLoader
final AstBrowserNodeMaker nodeMaker
private final CompilerConfiguration config
static {
try {
URL url = ClassLoader.getSystemResource('groovy/console/ui/AstBrowserProperties.groovy')
if (!url) {
url = ScriptToTreeNodeAdapter.class.classLoader.getResource('groovy/console/ui/AstBrowserProperties.groovy')
}
def config = new ConfigSlurper().parse(url)
classNameToStringForm = config.toProperties()
String home = System.getProperty('user.home')
if (home) {
File userFile = new File(home + File.separator + '.groovy/AstBrowserProperties.groovy')
if (userFile.exists()) {
def customConfig = new ConfigSlurper().parse(userFile.toURL())
// layer custom string forms onto defaults with putAll, do not replace them
classNameToStringForm.putAll(customConfig.toProperties())
}
}
} catch(ex) {
// on restricted environments like, such calls may fail, but that should not prevent the class
// from being loaded. Tree nodes can still get rendered with their simple names.
classNameToStringForm = new Properties()
}
}
ScriptToTreeNodeAdapter(classLoader, showScriptFreeForm, showScriptClass, showClosureClasses, nodeMaker, config = null) {
this.classLoader = classLoader ?: new GroovyClassLoader(getClass().classLoader)
this.showScriptFreeForm = showScriptFreeForm
this.showScriptClass = showScriptClass
this.showClosureClasses = showClosureClasses
this.nodeMaker = nodeMaker
this.config = config
}
/**
* Performs the conversion from script to TreeNode.
*
* @param script
* a Groovy script in String form
* @param compilePhase
* the int based CompilePhase to compile it to.
* @param indy
* if {@code true} InvokeDynamic (Indy) bytecode is generated
*/
def compile(String script, int compilePhase, boolean indy=false) {
def scriptName = 'script' + System.currentTimeMillis() + '.groovy'
GroovyCodeSource codeSource = new GroovyCodeSource(script, scriptName, '/groovy/script')
CompilerConfiguration cc = new CompilerConfiguration(config ?: CompilerConfiguration.DEFAULT)
if (config) {
cc.addCompilationCustomizers(*config.compilationCustomizers)
}
if (indy) {
cc.optimizationOptions.put(CompilerConfiguration.INVOKEDYNAMIC, true)
}
CompilationUnit cu = new CompilationUnit(cc, codeSource.codeSource, classLoader)
cu.setClassgenCallback(classLoader.createCollector(cu, null))
TreeNodeBuildingNodeOperation operation = new TreeNodeBuildingNodeOperation(this, showScriptFreeForm, showScriptClass, showClosureClasses)
cu.addPhaseOperation(operation, compilePhase)
cu.addSource(codeSource.getName(), script)
try {
cu.compile(compilePhase)
} catch (CompilationFailedException cfe) {
operation.root.add(nodeMaker.makeNode('Unable to produce AST for this phase due to earlier compilation error:'))
cfe.message.eachLine {
operation.root.add(nodeMaker.makeNode(it))
}
operation.root.add(nodeMaker.makeNode('Fix the above error(s) and then press Refresh'))
} catch (Throwable t) {
operation.root.add(nodeMaker.makeNode('Unable to produce AST for this phase due to an error:'))
operation.root.add(nodeMaker.makeNode(t))
operation.root.add(nodeMaker.makeNode('Fix the above error(s) and then press Refresh'))
}
return operation.root
}
def make(node) {
nodeMaker.makeNodeWithProperties(getStringForm(node), getPropertyTable(node))
}
def make(MethodNode node) {
def table = getPropertyTable(node)
extendMethodNodePropertyTable(table, node)
nodeMaker.makeNodeWithProperties(getStringForm(node), table)
}
/**
* Extends the method node property table by adding custom properties.
*/
void extendMethodNodePropertyTable(List<List<String>> table, MethodNode node) {
table << ['descriptor', BytecodeHelper.getMethodDescriptor(node), 'String']
}
/**
* Creates the property table for the node so that the properties view can display nicely.
*/
private List<List<String>> getPropertyTable(node) {
node.metaClass.properties?.
findAll { it.getter }?.
collect {
def name = it.name.toString()
def value
try {
// multiple assignment statements cannot be cast to VariableExpression so
// instead reference the value through the leftExpression property, which is the same
if (node instanceof DeclarationExpression &&
(name == 'variableExpression' || name == 'tupleExpression')) {
value = toString(node.leftExpression)
} else {
value = toString(it.getProperty(node))
}
} catch (GroovyBugError reflectionArtefact) {
// compiler throws error if it thinks a field is being accessed
// before it is set under certain conditions. It wasn't designed
// to be walked reflectively like this.
value = null
}
def type = it.type.simpleName.toString()
[name, value, type]
}?.
sort { it[0] }
}
// GROOVY-8339: to avoid illegal access to a non-visible implementation class - can be removed if a more general solution is found
@CompileStatic
String toString(Object o) {
o.toString()
}
/**
* Handles the property file templating for node types.
*/
private String getStringForm(node) {
String templateTextForNode = classNameToStringForm[node.class.name]
if (templateTextForNode) {
GStringTemplateEngine engine = new GStringTemplateEngine()
Template template = engine.createTemplate(templateTextForNode)
Writable writable = template.make([expression: node])
Writer result = new StringBuilderWriter()
writable.writeTo(result)
result.toString()
} else {
node.class.simpleName
}
}
}
/**
* This Node Operation builds up a root tree node for the viewer.
*/
class TreeNodeBuildingNodeOperation extends PrimaryClassNodeOperation {
final root
final sourceCollected = new AtomicBoolean(false)
final ScriptToTreeNodeAdapter adapter
final showScriptFreeForm
final showScriptClass
final showClosureClasses
final nodeMaker
TreeNodeBuildingNodeOperation(ScriptToTreeNodeAdapter adapter, showScriptFreeForm, showScriptClass) {
this(adapter, showScriptFreeForm, showScriptClass, false)
}
TreeNodeBuildingNodeOperation(ScriptToTreeNodeAdapter adapter, showScriptFreeForm, showScriptClass, showClosureClasses) {
if (!adapter) throw new IllegalArgumentException('Null: adapter')
this.adapter = adapter
this.showScriptFreeForm = showScriptFreeForm
this.showScriptClass = showScriptClass
this.showClosureClasses = showClosureClasses
nodeMaker = adapter.nodeMaker
root = nodeMaker.makeNode('root')
}
@Override
void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
// module node
if (!sourceCollected.getAndSet(true) && showScriptFreeForm) {
// display the source unit AST
ModuleNode ast = source.getAST()
TreeNodeBuildingVisitor visitor = new TreeNodeBuildingVisitor(adapter)
ast.getStatementBlock().visit(visitor)
if (visitor.currentNode) root.add(visitor.currentNode)
collectModuleNodeMethodData('Methods', ast.getMethods())
}
if(classNode.isScript() && !showScriptClass) return
def child = adapter.make(classNode)
root.add(child)
collectConstructorData(child, 'Constructors', classNode)
collectObjectInitializers(child, 'Object Initializers', classNode)
collectMethodData(child, 'Methods', classNode)
collectFieldData(child, 'Fields', classNode)
collectPropertyData(child, 'Properties', classNode)
collectAnnotationData(child, 'Annotations', classNode)
if (showClosureClasses) {
makeClosureClassTreeNodes(classNode)
}
}
protected void makeClosureClassTreeNodes(ClassNode classNode) {
def compileUnit = classNode.compileUnit
if (!compileUnit.generatedInnerClasses) return
def innerClassNodes = compileUnit.generatedInnerClasses.values().sort { it.name }
innerClassNodes.each { InnerClassNode innerClassNode ->
if (!innerClassNode.implementsInterface(ClassHelper.GENERATED_CLOSURE_Type) && !innerClassNode.implementsInterface(ClassHelper.GENERATED_LAMBDA_TYPE)) return
if (innerClassNode.outerMostClass != classNode) return
def child = adapter.make(innerClassNode)
root.add(child)
collectConstructorData(child, 'Constructors', innerClassNode)
collectObjectInitializers(child, 'Object Initializers', innerClassNode)
collectMethodData(child, 'Methods', innerClassNode)
collectFieldData(child, 'Fields', innerClassNode)
collectPropertyData(child, 'Properties', innerClassNode)
collectAnnotationData(child, 'Annotations', innerClassNode)
}
}
private void collectAnnotationData(parent, String name, AnnotatedNode node) {
def allAnnotations = nodeMaker.makeNode(name)
if (node.annotations) {
parent.add(allAnnotations)
node.annotations?.each {AnnotationNode annotationNode ->
def ggrandchild = adapter.make(annotationNode)
allAnnotations.add(ggrandchild)
}
}
}
private void collectPropertyData(parent, String name, ClassNode classNode) {
def allProperties = nodeMaker.makeNode(name)
if (classNode.properties) parent.add(allProperties)
classNode.properties?.each {PropertyNode propertyNode ->
def ggrandchild = adapter.make(propertyNode)
allProperties.add(ggrandchild)
TreeNodeBuildingVisitor visitor = new TreeNodeBuildingVisitor(adapter)
if (propertyNode.field?.initialValueExpression) {
propertyNode.field.initialValueExpression.visit(visitor)
ggrandchild.add(visitor.currentNode)
}
}
}
private void collectFieldData(parent, String name, ClassNode classNode) {
def allFields = nodeMaker.makeNode(name)
if (classNode.fields) parent.add(allFields)
classNode.fields?.each {FieldNode fieldNode ->
def ggrandchild = adapter.make(fieldNode)
allFields.add(ggrandchild)
TreeNodeBuildingVisitor visitor = new TreeNodeBuildingVisitor(adapter)
if (fieldNode.initialValueExpression) {
fieldNode.initialValueExpression.visit(visitor)
if (visitor.currentNode) ggrandchild.add(visitor.currentNode)
}
}
}
private void collectMethodData(parent, String name, ClassNode classNode) {
def allMethods = nodeMaker.makeNode(name)
if (classNode.methods) parent.add(allMethods)
doCollectMethodData(allMethods, classNode.methods)
}
private void collectModuleNodeMethodData(String name, List methods) {
if(!methods) return
def allMethods = nodeMaker.makeNode(name)
root.add(allMethods)
doCollectMethodData(allMethods, methods)
}
private void doCollectMethodData(allMethods, List methods) {
methods?.each {MethodNode methodNode ->
def ggrandchild = adapter.make(methodNode)
allMethods.add(ggrandchild)
// print out parameters of method
methodNode.parameters?.each {Parameter parameter ->
def gggrandchild = adapter.make(parameter)
ggrandchild.add(gggrandchild)
if (parameter.initialExpression) {
TreeNodeBuildingVisitor visitor = new TreeNodeBuildingVisitor(adapter)
parameter.initialExpression.visit(visitor)
if (visitor.currentNode) gggrandchild.add(visitor.currentNode)
}
collectAnnotationData(gggrandchild, 'Annotations', parameter)
}
// print out code of method
TreeNodeBuildingVisitor visitor = new TreeNodeBuildingVisitor(adapter)
if (methodNode.code) {
methodNode.code.visit(visitor)
if (visitor.currentNode) ggrandchild.add(visitor.currentNode)
}
collectAnnotationData(ggrandchild, 'Annotations', methodNode)
}
}
private void collectConstructorData(parent, String name, ClassNode classNode) {
def allCtors = nodeMaker.makeNode(name)
if (classNode.declaredConstructors) parent.add(allCtors)
classNode.declaredConstructors?.each {ConstructorNode ctorNode ->
def ggrandchild = adapter.make(ctorNode)
allCtors.add(ggrandchild)
TreeNodeBuildingVisitor visitor = new TreeNodeBuildingVisitor(adapter)
if (ctorNode.code) {
ctorNode.code.visit(visitor)
if (visitor.currentNode) ggrandchild.add(visitor.currentNode)
}
collectAnnotationData(ggrandchild, 'Annotations', ctorNode)
}
}
private void collectObjectInitializers(parent, String name, ClassNode node) {
List<Statement> initStatements = node.getObjectInitializerStatements()
if (!initStatements) {
return
}
def allInitializers = nodeMaker.makeNode(name)
parent.add(allInitializers)
for (Statement stmt : initStatements) {
TreeNodeBuildingVisitor visitor = new TreeNodeBuildingVisitor(adapter)
stmt.visit(visitor)
if (visitor.currentNode) {
allInitializers.add(visitor.currentNode)
}
}
}
}
/**
* This AST visitor builds up a TreeNode.
*/
@PackageScope
class TreeNodeBuildingVisitor extends CodeVisitorSupport {
def currentNode
private final adapter
/**
* Creates the visitor. A file named AstBrowserProperties.groovy is located which is
* a property files the describes how to represent ASTNode types as Strings.
*/
TreeNodeBuildingVisitor(adapter) {
if (!adapter) throw new IllegalArgumentException('Null: adapter')
this.adapter = adapter
}
/**
* This method looks at the AST node and decides how to represent it in a TreeNode, then it
* continues walking the tree. If the node and the expectedSubclass are not exactly the same
* Class object then the node is not added to the tree. This is to eliminate seeing duplicate
* nodes, for instance seeing an ArgumentListExpression and a TupleExpression in the tree, when
* an ArgumentList is-a Tuple.
*/
private void addNode(node, Class expectedSubclass, Closure superMethod) {
if (expectedSubclass.getName() == node.getClass().getName()) {
if (currentNode == null) {
currentNode = adapter.make(node)
superMethod.call(node)
} else {
// visitor works off void methods... so we have to
// perform a swap to get accumulation like behavior.
def temp = currentNode
currentNode = adapter.make(node)
temp.add(currentNode)
currentNode.parent = temp
superMethod.call(node)
currentNode = temp
}
} else {
superMethod.call(node)
}
}
@Override
void visitBlockStatement(BlockStatement node) {
addNode(node, BlockStatement, { super.visitBlockStatement(it) })
}
@Override
void visitForLoop(ForStatement node) {
addNode(node, ForStatement, { super.visitForLoop(it) })
}
@Override
void visitWhileLoop(WhileStatement node) {
addNode(node, WhileStatement, { super.visitWhileLoop(it) })
}
@Override
void visitDoWhileLoop(DoWhileStatement node) {
addNode(node, DoWhileStatement, { super.visitDoWhileLoop(it) })
}
@Override
void visitIfElse(IfStatement node) {
addNode(node, IfStatement, { super.visitIfElse(it) })
}
@Override
void visitExpressionStatement(ExpressionStatement node) {
addNode(node, ExpressionStatement, { super.visitExpressionStatement(it) })
}
@Override
void visitReturnStatement(ReturnStatement node) {
addNode(node, ReturnStatement, { super.visitReturnStatement(it) })
}
@Override
void visitAssertStatement(AssertStatement node) {
addNode(node, AssertStatement, { super.visitAssertStatement(it) })
}
@Override
void visitTryCatchFinally(TryCatchStatement node) {
addNode(node, TryCatchStatement, { super.visitTryCatchFinally(it) })
}
@Override
void visitEmptyStatement(EmptyStatement node) {
addNode(node, EmptyStatement, { super.visitEmptyStatement(it) })
}
@Override
void visitSwitch(SwitchStatement node) {
addNode(node, SwitchStatement, { super.visitSwitch(it) })
}
@Override
void visitCaseStatement(CaseStatement node) {
addNode(node, CaseStatement, { super.visitCaseStatement(it) })
}
@Override
void visitBreakStatement(BreakStatement node) {
addNode(node, BreakStatement, { super.visitBreakStatement(it) })
}
@Override
void visitContinueStatement(ContinueStatement node) {
addNode(node, ContinueStatement, { super.visitContinueStatement(it) })
}
@Override
void visitSynchronizedStatement(SynchronizedStatement node) {
addNode(node, SynchronizedStatement, { super.visitSynchronizedStatement(it) })
}
@Override
void visitThrowStatement(ThrowStatement node) {
addNode(node, ThrowStatement, { super.visitThrowStatement(it) })
}
@Override
void visitMethodCallExpression(MethodCallExpression node) {
addNode(node, MethodCallExpression, { super.visitMethodCallExpression(it) })
}
@Override
void visitStaticMethodCallExpression(StaticMethodCallExpression node) {
addNode(node, StaticMethodCallExpression, { super.visitStaticMethodCallExpression(it) })
}
@Override
void visitConstructorCallExpression(ConstructorCallExpression node) {
addNode(node, ConstructorCallExpression, { super.visitConstructorCallExpression(it) })
}
@Override
void visitBinaryExpression(BinaryExpression node) {
addNode(node, BinaryExpression, { super.visitBinaryExpression(it) })
}
@Override
void visitTernaryExpression(TernaryExpression node) {
addNode(node, TernaryExpression, { super.visitTernaryExpression(it) })
}
@Override
void visitShortTernaryExpression(ElvisOperatorExpression node) {
addNode(node, ElvisOperatorExpression, { super.visitShortTernaryExpression(it) })
}
@Override
void visitPostfixExpression(PostfixExpression node) {
addNode(node, PostfixExpression, { super.visitPostfixExpression(it) })
}
@Override
void visitPrefixExpression(PrefixExpression node) {
addNode(node, PrefixExpression, { super.visitPrefixExpression(it) })
}
@Override
void visitBooleanExpression(BooleanExpression node) {
addNode(node, BooleanExpression, { super.visitBooleanExpression(it) })
}
@Override
void visitNotExpression(NotExpression node) {
addNode(node, NotExpression, { super.visitNotExpression(it) })
}
@Override
void visitClosureExpression(ClosureExpression node) {
addNode(node, ClosureExpression, {
it.parameters?.each { parameter -> visitParameter(parameter) }
super.visitClosureExpression(it)
})
}
@Override
void visitLambdaExpression(LambdaExpression node) {
addNode(node, LambdaExpression, {
// params will be catered for by super call
//it.parameters?.each { parameter -> visitParameter(parameter) }
super.visitLambdaExpression(it)
})
}
/**
* Makes walking parameters look like others in the visitor.
*/
void visitParameter(Parameter node) {
addNode(node, Parameter, {
if (node.initialExpression) {
node.initialExpression?.visit(this)
}
})
}
@Override
void visitTupleExpression(TupleExpression node) {
addNode(node, TupleExpression, { super.visitTupleExpression(it) })
}
@Override
void visitListExpression(ListExpression node) {
addNode(node, ListExpression, { super.visitListExpression(it) })
}
@Override
void visitArrayExpression(ArrayExpression node) {
addNode(node, ArrayExpression, { super.visitArrayExpression(it) })
}
@Override
void visitMapExpression(MapExpression node) {
addNode(node, MapExpression, { super.visitMapExpression(it) })
}
@Override
void visitMapEntryExpression(MapEntryExpression node) {
addNode(node, MapEntryExpression, { super.visitMapEntryExpression(it) })
}
@Override
void visitRangeExpression(RangeExpression node) {
addNode(node, RangeExpression, { super.visitRangeExpression(it) })
}
@Override
void visitSpreadExpression(SpreadExpression node) {
addNode(node, SpreadExpression, { super.visitSpreadExpression(it) })
}
@Override
void visitSpreadMapExpression(SpreadMapExpression node) {
addNode(node, SpreadMapExpression, { super.visitSpreadMapExpression(it) })
}
@Override
void visitMethodPointerExpression(MethodPointerExpression node) {
addNode(node, MethodPointerExpression, { super.visitMethodPointerExpression(it) })
}
@Override
void visitMethodReferenceExpression(MethodReferenceExpression node) {
addNode(node, MethodReferenceExpression, { super.visitMethodReferenceExpression(it) })
}
@Override
void visitUnaryMinusExpression(UnaryMinusExpression node) {
addNode(node, UnaryMinusExpression, { super.visitUnaryMinusExpression(it) })
}
@Override
void visitUnaryPlusExpression(UnaryPlusExpression node) {
addNode(node, UnaryPlusExpression, { super.visitUnaryPlusExpression(it) })
}
@Override
void visitBitwiseNegationExpression(BitwiseNegationExpression node) {
addNode(node, BitwiseNegationExpression, { super.visitBitwiseNegationExpression(it) })
}
@Override
void visitCastExpression(CastExpression node) {
addNode(node, CastExpression, { super.visitCastExpression(it) })
}
@Override
void visitConstantExpression(ConstantExpression node) {
addNode(node, ConstantExpression, { super.visitConstantExpression(it) })
}
@Override
void visitClassExpression(ClassExpression node) {
addNode(node, ClassExpression, { super.visitClassExpression(it) })
}
@Override
void visitVariableExpression(VariableExpression node) {
addNode(node, VariableExpression, { VariableExpression it ->
if (it.accessedVariable) {
if (it.accessedVariable instanceof Parameter) {
visitParameter((Parameter)it.accessedVariable)
} else if (it.accessedVariable instanceof DynamicVariable) {
addNode(it.accessedVariable, DynamicVariable,{ it.initialExpression?.visit(this)})
}
}
})
}
@Override
void visitDeclarationExpression(DeclarationExpression node) {
addNode(node, DeclarationExpression, { super.visitDeclarationExpression(it) })
}
@Override
void visitPropertyExpression(PropertyExpression node) {
addNode(node, PropertyExpression, { super.visitPropertyExpression(it) })
}
@Override
void visitAttributeExpression(AttributeExpression node) {
addNode(node, AttributeExpression, { super.visitAttributeExpression(it) })
}
@Override
void visitFieldExpression(FieldExpression node) {
addNode(node, FieldExpression, { super.visitFieldExpression(it) })
}
@Override
void visitGStringExpression(GStringExpression node) {
addNode(node, GStringExpression, { super.visitGStringExpression(it) })
}
@Override
void visitCatchStatement(CatchStatement node) {
addNode(node, CatchStatement, {
if (it.variable) visitParameter(it.variable)
super.visitCatchStatement(it)
})
}
@Override
void visitArgumentlistExpression(ArgumentListExpression node) {
addNode(node, ArgumentListExpression, { super.visitArgumentlistExpression(it) })
}
@Override
void visitClosureListExpression(ClosureListExpression node) {
addNode(node, ClosureListExpression, { super.visitClosureListExpression(it) })
}
@Override
void visitBytecodeExpression(BytecodeExpression node) {
addNode(node, BytecodeExpression, { super.visitBytecodeExpression(it) })
}
@Override
void visitListOfExpressions(List<? extends Expression> list) {
list.each { Expression node ->
if (node instanceof NamedArgumentListExpression ) {
addNode(node, NamedArgumentListExpression, { it.visit(this) })
} else {
node.visit(this)
}
}
}
}