Wednesday, 25 September 2013

Build your own compiler


Let’s Build a Compiler Plug-In
The first step is to download and build the current release of the Java 8 compiler, which supports compiler plug-ins.Toward this end, download
the latest version and follow the build instructions in theREADME file.
Once the compiler is built, include the generated dist/lib/ classes.jar file in your project. Alternatively, you can download a ready-made binary from
jdk8.java.net. Next, there are several steps we need to follow to build our plug-in.
Here is a summary of the steps:
1. Implement the com.sun.source.util.Plugin interface .
2. Add a TaskListener to perform additional behavior after the type-checking phase.
3. Create an abstract syntax tree (AST) visitor to locate binary expressions:
a. Evaluate whether the left side is a method call expression with a receiver’s type that is a subtype of java.util.Map.
b. Evaluate whether the right side is a null expression.

Step 1: Implement the com.sun.source.util .Plugin interface.
The first step is to create the main class, which implements com.sun.source
.util.Plugin. We return the name of our plug-in via the getName() method,

Step 2: Add a TaskListener.
We are looking for a code pattern that checks whether the receiver of
the method call get() is a subtype of java.util.Map. To get the type of the receiver, we need information about the types of expressions that are resolved during the type-checking phase. We therefore need to insert a new phase after type checking. Toward this end, we first create a TaskListener and add it to the current JavacTask. We now need to create the class  CodePatternTaskListener, which implements a TaskListener. A
TaskListener has two methods, started() and finished(), which are called, respectively, before and after certain events. These events are  encapsulated in a TaskEvent object. In our case, all we need to do is implement the finished() method and check for an event mentioning the Analyze phase (type checking.

Step 3: Create the AST visitor.
Next, we need to write the logic to locate the code pattern and report it. How do we do that? Thankfully, a task event provides us with a CompilationUnitTree,which represents the current source file analyzed in a tree structure. It can be accessed with the getCompilationUnit() method.
Our code will need to traverse this tree, locate a binary node, and evaluate the node’s left and right children. This sounds like a visitor pattern job. The Compiler Tree API provides us with a readymade visitor class designed for
such tasks: com.sun.source.util.TreeScanner<R, P>. It visits all the nodes in the AST.First, we initialize our visitor object and then visit the CompilationUnitTree. All we have left to do is to override visitBinary(BinaryTree node,P p) and write the logic to verify the code pattern.

Let’s Run Our Compiler Plug-In
We are almost finished. The final step is to set up a file called com.sun.source.util.Plugin located in META-INF/services/. This file must contain the name of our plug-in, CodePatternPlugin, which allows javac to load the appropriate plug-in.
Next, using your favorite IDE or the command line, create a Java archive (JAR) file from your project containing the META-INF directory and compiled class files:
Finally, you can run the plug-in
-processorpath indicates the path where the plug-in JAR file is located
-Xplugin indicates the name of the plug-in to run, which is CodePatternPlugin, in this case

The new plug-in mechanism provides a simple hook to javac. You can use it to extend javac with new behavior. In addition, you can distribute a plug-in without making modifications to the javac code base. In this article,
we showed how you can use this mechanism to easily write a customized

source code analysis tool for your applications

1 comment:

  1. Great post. One question: Is it possible to register the plugin using Maven? I am not sure how to transfer the command line options described above to the pom file.

    ReplyDelete