Search code examples
javaparsingbeanshelljavacc

Parsing Beanshell code


I am trying to write a basic static code analysis tool for code written in beanshell that would perform some basic checks like unused variables, methods and possibly conditions that would never evaluate to true.

I have tried using the parser that comes with the beanshell source distribution in the way shown in a few examples like following:

import java.io.FileInputStream;
import java.io.IOException;

import bsh.ParseException;
import bsh.Parser;
import bsh.SimpleNode;

public class FindUnusedVariablesTask {

    String sourseFilePath;

    public FindUnusedVariablesTask(String sourseFilePath) {            
        this.sourseFilePath = sourseFilePath;
    }


    public String perform() throws ParseException, IOException {
        FileInputStream sourceStream = new FileInputStream(sourseFilePath);
        Parser p = new Parser(sourceStream);

        while (!p.Line()) {
            SimpleNode node = p.popNode();
            System.out.println(node.getText());
            for (int i=0; i<node.jjtGetNumChildren(); i++)
                System.out.println(node.getChild(i).getText());
        }
        sourceStream.close();
        return "";
    }
}

For the following beanshell code:

f1 () {
  return 1;
}

String f2(String x) {
    return x + f1() + " OK";
}

The output is as follows:

f1 ( ) { 
( ) 
{ 

String f2 ( String x ) { 
String 
( String x ) 
{ 

Basically I'm only getting the parsed method declarations. I cannot find a way access the parsed statements within. How can that be done?


Solution

  • The BeanShell parser produces an AST. Generally speaking, ASTs can be fairly deep in their structure. The code you give above only looks 1 layer deep into the AST.

    Try a recursive traversal (I don't have the devkit, so consider this as pseudocode):

    import bsh.Node; //you need this as well
    
    public String perform() throws ParseException, IOException {
        FileInputStream sourceStream = new FileInputStream(sourseFilePath);
        Parser p = new Parser(sourceStream);
    
        while (!p.Line()) {
            recursive_print(p.popNode(), "");
        }
    
        sourceStream.close();
        return "";
    }
    
    public void recursive_print(Node node, String prefix)
    {
        System.out.println(prefix + node.getText());
        for (int i=0; i<node.jjtGetNumChildren(); i++)
            recursive_print(node.getChild(i), prefix+"  ");
    }