Java code transform at compile time
Asked Answered
L

4

27

I would like to transform java source code at compile time just before passing the source code to the compiler. In other word I would like to create a preprocessor able to transform

"bla bla bla" 

into any other code such as:

new MyClass("bla", 3) 

My actual motivation is to do string encryption, as explained here

Some people suggest writing custom annotation processors but as far as I understand annotations:

  • they can be used to generate new class file, but not to transform existing code before being passed to compiler
  • they seem to work at package, class or method level, but not method body/implementation.

Some people suggest using frameworks like Spoon or ObjectsWeb ASM, but these frameworks seem complicated to learn and deploy on an existing code base.

I thrive to find a simple example of java code preprocessing for both approaches.

Does anybody see any smart way of doing code transform, without completely changing an existing large code base with multiple ivy module? Annotations seem to be the best way, but I don't understand how to do that.

Longeron answered 7/5, 2012 at 13:58 Comment(3)
Jet might be helpful (I do not know in which state it is, but worked well some years ago). JavaCC does follow the same approach.Layton
You can write a custom Ant/Mavin plugin which would first do the encryption and then compile your code.Dulciedulcify
I have the same question. Can you share what you ended up with?Irradiant
J
15

I think you could try the same technique used in Project Lombok

It's approximately explained by the authors in this interview:

What's going on under the hood? I.e., how does an annotation result in the boilerplate ending up in the bytecode?

Reinier: The annotation processor API only lets you create new files, it does not let you modify the file that has the annotation inside of it. Which is what Lombok does, so Lombok does not use the annotation processor API.

Instead, Lombok uses the annotation processor API only as a mechanism to inject itself into the compilation process. All annotation processors are initialized early in the compilation process, and Lombok modifies javac when it is initialized as an annotation processor. We change only one thing: The AST (the raw source code, parsed into a tree form) is first handed off to Lombok, which generates whatever needs to be generated, before javac continues.

and in How does lombok work?

It's also possible to extend Project Lombok to your needs

Jehoshaphat answered 8/5, 2012 at 9:25 Comment(0)
G
2

There is someone who already wrote a small C-like preprocessor ant plugin in python/jython. You can find it here. Note that I have never used it, but perhaps it can be a good starting point for your needs.

There is also a java-comment-preprocessor maven plugin (similar style) in google code that might also be a helpful starting point.

Good luck. Sounds like a fun challenge. Of course, keep in mind that obfuscation is just making it a little more challenging to extract the string, but still not impossible. If you really want to make it significantly more difficult, then you might want to look at encrypting your strings and/or using AOP.

Guttapercha answered 7/5, 2012 at 15:9 Comment(0)
U
0

If the C preprocessor would suffice, I did just manage to get it working with Eclipse in Windows. It only works on Juno though.

https://mcmap.net/q/411648/-using-m4-macros-with-eclipse-amp-java

Ulna answered 8/5, 2012 at 11:12 Comment(0)
N
0

Perhaps reconsider Spoon? It is intuitive once you know what an abstract syntax tree is. You basically navigate, filter and replace nodes on the AST representing your code until you have your desired transformation.

If we have the following class:

public class Test {
    Object method(){
        return "bla bla bla";
    }
}

a possible Spoon solution to your example would be:

Launcher spoon = new Launcher();
spoon.addInputResource("./src/main/java/Test.java");
spoon.buildModel();
CtClass ctClass = spoon.getModel().getRootPackage().getType("Test");
CtLiteral match = ctClass.filterChildren((CtLiteral l) -> l.getType().getSimpleName().equals("String") && l.getValue().equals("bla bla bla")).first();
Factory factory = spoon.getFactory();
match.replace(factory.createCodeSnippetExpression("new MyClass(\"bla\", 3)"));
System.out.println(ctClass);

The first four lines build the Spoon model of the input program and retrieve the class element of interest.

Next the literal to be modified is retrieved with a filter that returns the first and only literal of type name "String" and value "bla bla bla".

Last the matched literal is replaced in the AST to the desired code snippet. The code snippet is automatically parsed from a string to a Spoon model using the code construction class Factory.

When the class model is printed, the desired transformation is given:

public class Test {
    java.lang.Object method() {
        return new MyClass("bla", 3);
    }
}
Nagaland answered 27/11, 2020 at 7:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.