alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Groovy example source code file (AbstractBytecodeTestCase.groovy)

This example Groovy source code file (AbstractBytecodeTestCase.groovy) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

compilationunit, emptyvisitor, emptyvisitor, groovytestcase, instructionsequence, instructionsequence, list, object, printwriter, printwriter, string, string, stringwriter, traceclassvisitor

The Groovy AbstractBytecodeTestCase.groovy source code

package org.codehaus.groovy.classgen.asm

import org.codehaus.groovy.control.CompilationUnit
import org.objectweb.asm.util.TraceClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.ClassReader
import org.codehaus.groovy.control.Phases
import org.objectweb.asm.commons.EmptyVisitor

/**
 * Abstract test case to extend to check the instructions we generate in the bytecode of groovy programs.
 *
 * @author Guillaume Laforge
 */
abstract class AbstractBytecodeTestCase extends GroovyTestCase {

    /**
     * Compiles a script into bytecode and returns the decompiled string equivalent using ASM.
     *
     * @param scriptText the script to compile
     * @return the decompiled <code>InstructionSequence
     */
    InstructionSequence compile(Map options=[method:"run"], String scriptText) {
        def cu = new CompilationUnit()
        def su = cu.addSource("script", scriptText)
        cu.compile(Phases.CONVERSION)
        if (options.conversionAction!=null) {
            options.conversionAction(su)
        }
        cu.compile(Phases.CLASS_GENERATION)
        
        
        def output = new StringWriter()
        def tcf = new TraceClassVisitor(new PrintWriter(output)) {
            MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                if (options.method == name) {
                    super.visitMethod(access, name, desc, signature, exceptions)
                } else {
                    new EmptyVisitor()
                }
            }
            FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
                if (options.field == name) {
                    super.visitField(access, name, desc, signature, value)
                } else {
                    new EmptyVisitor()
                }
            }
            
        }
        def cr = new ClassReader(cu.classes[0].bytes)
        cr.accept(tcf, 0)

        def code = output.toString()

        return new InstructionSequence(instructions: code.split('\n')*.trim())
    }
}

/**
 * A sequence of instruction with matching and strict matching capabilities
 * to find subsequences of bytecode instructions.
 *
 * @author Guillaume Laforge
 */
class InstructionSequence {
    List<String> instructions

    /**
     * Find a sub-sequence of instructions of the list of instructions.
     *
     * @param pattern the list of instructions to find in the bytecode
     * @param offset at which to find the sub-sequence or remaining sub-sequence (start at offset 0)
     * @param strict whether the search should be strict with contiguous instructions (false by default)
     * @return true if a match is found
     */
    boolean hasSequence(List<String> pattern, int offset = 0, boolean strict = false) {
        if (pattern.size() == 0) return true

        def idx = indexOf(pattern[0], offset)
        if (idx > -1) {
            // not the first call with offset 0 and check that the next instruction match
            // is the exact following instruction in the pattern and in the bytecode instructions
            if (strict && offset > 0 && idx - offset > 1) {
                return false
            } else {
                return hasSequence(pattern.tail(), idx, strict)
            }
        } else {
            return false
        }
    }

    /**
     * Find a strict sub-sequence of instructions of the list of instructions.
     *
     * @param pattern the list of instructions to find in the bytecode
     * @param offset at which to find the sub-sequence or remaining sub-sequence (start at offset 0)
     * @param strict whether the search should be strict with contiguous instructions (true by default)
     * @return true if a match is found
     */
    boolean hasStrictSequence(List<String> pattern, int offset = 0, boolean strict = true) {
        hasSequence(pattern, offset, strict)
    }

    /**
     * Finds the index of a single instruction in a list of instructions
     * @param singleInst single instruction to find
     * @param offset the offset from which to start the search
     * @return the index of that single instruction if found, -1 otherwise
     */
    private int indexOf(String singleInst, int offset = 0) {
        for (i in offset..<instructions.size()) {
            if (instructions[i].startsWith(singleInst))
                return i
        }
        return -1
    }

    String toString() {
        instructions.join('\n')
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy AbstractBytecodeTestCase.groovy source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.