Groovy web console

subscribe to the feed Subscribe
to this
site
Finding a closure's nested closures and AICs and their bytecode (via #groovywebconsole)
tweet this snippet Tweet
this
script

Finding a closure's nested closures and AICs and their bytecode

Published 6 years ago by glaforge with tags closure aic ast bytecode
Actions  ➤ Edit in console Back to console Show/hide line numbers View recent scripts
import groovy.transform.TupleConstructor
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.expr.ClosureExpression
import org.codehaus.groovy.classgen.GeneratorContext
import org.codehaus.groovy.control.*
import org.codehaus.groovy.control.CompilationUnit.PrimaryClassNodeOperation
import org.codehaus.groovy.control.CompilationUnit.GroovyClassOperation
import org.codehaus.groovy.tools.GroovyClass

@TupleConstructor
class Position implements Comparable {
    int sc, sl, ec, el
    
    static from(ASTNode node) { 
        new Position(node.columnNumber, node.lineNumber, node.lastColumnNumber, node.lastLineNumber)
    }
    
    int compareTo(otherPos) {
        if (sl < otherPos.sl) return -1
        if (sl > otherPos.sl) return 1
        if (sc < otherPos.sc) return -1
        if (sc > otherPos.sc) return 1
        return 0
    }
    
    boolean before(otherPos) { compareTo(otherPos) == -1 }
    
    boolean after(otherPos) {
        if (el < otherPos.el) return false
        if (el > otherPos.el) return true
        if (ec < otherPos.ec) return false
        if (ec > otherPos.ec) return true
        return true
    }
    
    String toString() { "[$sc:$sl/$ec:$el]" }
}

def cu = new CompilationUnit()
cu.addSource("t.groovy", '''
    exec {                               //  2
        def d = {                        //  3
            def o = new Object() {       //  4
                void run() {             //  5
                    def f = {}           //  6
                }                        //  7
            }                            //  8
            def e = {}                   //  9
        }                                // 10
    }                                    // 11
    def g = {}                           // 12
''')

def positions = new TreeMap()

cu.addPhaseOperation(new PrimaryClassNodeOperation() {
    void call(SourceUnit source, GeneratorContext context, ClassNode cn) {
        def pos = Position.from(cn)
        positions[cn.name] = pos
        
        cn.innerClasses.each { InnerClassNode icn ->
            def innerPos = icn.superClass == ClassHelper.CLOSURE_TYPE ?
                Position.from(icn.getMethods('doCall')[0]) :
                Position.from(icn)
            positions[icn.name] = innerPos
        }
    }
}, Phases.CLASS_GENERATION)

final Map bytecodes = new HashMap();        
         
cu.addPhaseOperation(new GroovyClassOperation() {
    void call(GroovyClass gclass) {
        bytecodes[gclass.name] = gclass.bytes
    }
})
         
cu.compile()


// print all positions of AICs and Closures
positions.each { String cName, Position pos ->
    //println "$cName $pos"
}


def findClosureStartingAt = { int column, int line ->
    positions.find { cName, pos -> pos.sl == line && pos.sc == column }
}

def findNested = { clos ->
    positions.findAll { cName, pos -> 
        clos.value < pos && clos.value.after(pos)
    }
}

def execClosure = findClosureStartingAt(10, 2)
println "Finding nested closures and AICs for: $execClosure"


def results = findNested(execClosure)
results.each { println " - $it (bytecode: ${bytecodes[it.key][0..10]}...)" }

println "Done"