Lombok principle and Implementation

Posted by icyfire624 on Wed, 05 Jan 2022 00:42:21 +0100

This paper mainly includes the following contents:

  1. Analysis of the implementation mechanism of Lombok.
  2. Description and use of plug-in annotation processor.
  3. Implement the @ Getter and @ Setter annotations of lombok.
  4. Configure the IDEA to debug the Java compilation process.

1. Lombok

Introduction to the official website:

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

Add Lombok dependency in Maven:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

A simple example is as follows. getter/setter methods are automatically generated by adding annotations.

package com.sumkor;

import lombok.Getter;
import lombok.Setter;

/**
 * @author Sumkor
 * @since 2021/12/27
 */
@Setter
@Getter
public class MyTest {

    private String value;

    public static void main(String[] args) {
        MyTest myTest = new MyTest();
        myTest.setValue("hello");
        System.out.println(myTest.getValue());
    }
}

The compiled code is as follows:

package com.sumkor;

public class MyTest {
    private String value;

    public MyTest() {
    }

    public static void main(String[] args) {
        MyTest myTest = new MyTest();
        myTest.setValue("hello");
        System.out.println(myTest.getValue());
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }
}

2. Annotation Processor

2.1 Javac compiler

The Javac compiler is introduced in Chapter 10, front-end compilation and optimization, in "understanding the Java virtual machine: JVM advanced features and best practices".

Javac compilation process is as follows:

From the overall structure of Javac code, the compilation process can be roughly divided into one preparation process and three processing processes, as shown below.

  1. Preparation process: initialize the plug-in annotation processor.
  2. The process of parsing and filling symbol table, including lexical and grammatical analysis; Populate the symbol table.
  3. Annotation processing of plug-in annotation processor.
  4. Analysis and bytecode generation process.

Focus on the description of the plug-in annotation processor:

After JDK 5, The Java language provides support for annotations. Annotations, like ordinary java code, are designed to work only during program operation. However, JSR-269 proposal is proposed and adopted in JDK 6, which designs a group of annotations called "plug-in annotation processors" The standard API can be advanced to the compilation time to process specific annotations in the code, which affects the working process of the front-end compiler. We can regard the plug-in annotation processor as a set of compiler plug-ins. When these plug-ins work, they can read, modify and add any element in the abstract syntax tree. If these plug-ins modify the syntax tree during annotation processing, the compiler will return to the process of parsing and filling the symbol table for reprocessing until all plug-in annotation processors have not modified the syntax tree.

You can see that Lombok is implemented based on the plug-in annotation processor:

With the standard API for compiler annotation processing, the programmer's code may interfere with the behavior of the compiler. Since any element in the syntax tree, even code annotations, can be accessed in the plug-in, the plug-in implemented by the plug-in annotation processor has a lot of room for function. With enough creativity, programmers can use plug-in annotation processors to achieve many things that can only be done manually in coding. For example, Lombok, a famous java coding efficiency tool, can automatically generate getter/setter methods, perform vacancy check, generate checked exception table, generate equals() and hashCode() methods through annotations to help developers eliminate Java's lengthy code. These are implemented by relying on plug-in annotation processor.

2.2 Java annotations

Annotations in Java are divided into runtime annotations and compile time annotations. Specify the Retention policy of annotations by setting the RetentionPolicy in meta annotation @ Retention:

/**
 * Annotation retention policy.  The constants of this enumerated type
 * describe the various policies for retaining annotations.  They are used
 * in conjunction with the {@link Retention} meta-annotation type to specify
 * how long annotations are to be retained.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

explain:

  • SOURCE: the annotation information will be discarded by the compiler and will not be left in the class file. The annotation information will only be left in the SOURCE file.
  • class: information indicating annotations is retained in the class file. When the program is compiled, but will not be read by the virtual machine at run time.
  • RUNTIME: the information indicating the annotation is retained in the class file. When the program is compiled, it is retained by the virtual machine at run time.

The annotations used in daily development are of RUNTIME type and can be read by reflection calls at run time.

javax.annotation.Resource

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource

The annotations in Lombok are of type SOURCE and will only be read by the plug-in annotation processor during compilation.

lombok.Getter

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter

2.3 plug in annotation processor

The custom annotation processor needs to inherit the AbstractProcessor class. The basic framework is as follows:

package com.sumkor.processor;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.util.Set;

@SupportedAnnotationTypes("com.sumkor.annotation.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return true;
    }
}

Of which:

  • @Supported annotation types represents which annotations the annotation processor is interested in. You can use an asterisk * as a wildcard to indicate that you are interested in all annotations.
  • @The supported sourceversion indicates which versions of Java code this annotation processor can handle.
  • init() is used to obtain some environment information during the compilation phase.
  • process() can write specific logic to process the syntax tree. If you do not need to change or add the contents of the abstract syntax tree, the process() method can return a Boolean value with a value of false to inform the compiler that the code in this round has not changed.

Later in this paper, we will implement a specific custom plug-in annotation processor and debug the break point.

2.4 Javac APT

Using the plug-in annotation processor to modify the syntax tree in the compilation stage requires the annotation processing tool APT(Annotation Processing Tool) in Javac, which is provided by Sun to help the annotation processing process. APT is designed to operate Java source files rather than compiled classes.

JDK 8 is used in this paper. The source code related to Javac is stored in tools Jar, if you want to use it in your program, you must put this library on the classpath. Note that when JDK 9 is noticed, all Java class libraries in the whole JDK are reconstructed and divided by modularization, and the Javac compiler is moved to JDK Compiler module, and the access to this module is strictly limited.

2.5 JCTree syntax tree

com.sun.tools.javac.tree.JCTree is the base class of syntax tree elements and contains the following important subclasses:

  • JCStatement: declare syntax tree nodes. Common subclasses are as follows

    • JCBlock: statement block syntax tree node
    • JCReturn: return statement syntax tree node
    • JCClassDecl: class definition syntax tree node
    • JCVariableDecl: field / variable definition syntax tree node
  • JCMethodDecl: method definition syntax tree node
  • JCModifiers: access flag syntax tree node
  • JCExpression: expression syntax tree node. Common subclasses are as follows

    • JCAssign: assignment statement syntax tree node
    • JCIdent: identifier syntax tree node, which can be variable, type, keyword, etc

JCTree uses the visitor pattern to decouple data from data processing. Part of the source code is as follows:

public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {

    public int pos = -1;

    public abstract void accept(JCTree.Visitor visitor);

}

Using the visitor TreeTranslator, you can access the class definition node JCClassDecl on JCTree, and then you can obtain and modify the member variables, methods and other nodes in the class.

In the coding process, you can use javax annotation. processing. Messager to print information about the compilation process.
Note that the printMessage method of Messager will automatically filter duplicate log information when printing log.

Compared with printing logs, using the IDEA tool to debug the compilation process will have a more intuitive understanding of the JCTree syntax tree.
The configuration of the plug-in annotation processor is debugged in IDEA.

3. Hands on Implementation

Create two projects to implement and validate the @ Getter and @ Setter annotations.

Create a project Lombok processor that contains custom annotations and plug-in annotation processors.
Create the project Lombok app, which relies on the Lombok processor project and tests it with custom annotations.

3.1 processor project

The overall structure of the project is as follows:

Maven configuration

Since the Java syntax tree needs to be modified at the compilation stage, it is necessary to call the API related to the syntax tree, so the tools. XML in the JDK directory will be used Jar to import the current project.

<dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

Lombok processor project adopts Java SPI mechanism to make its custom plug-in annotation processor take effect on Lombok app project. Since the Lombok processor project needs to exclude its own plug-in annotation processor during compilation, configure maven resource to filter out SPI files. When packaging, add SPI files to the jar package of Lombok processor project.
In addition, in order to facilitate debugging, the source code of Lombok processor project is also released to the local warehouse.

The complete maven build configuration is as follows:

<build>
    <!-- Configure it resources Label, filter out META-INF Folder so that it will not be found when compiling services Configuration of -->
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <excludes>
                <exclude>META-INF/**/*</exclude>
            </excludes>
        </resource>
    </resources>

    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>${maven.compiler.target}</source>
                <target>${maven.compiler.target}</target>
            </configuration>
        </plugin>
        <!-- Re copy the services folder before packaging (prepare package life cycle) - >
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.6</version>
            <executions>
                <execution>
                    <id>process-META</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-resources</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/classes</outputDirectory>
                        <resources>
                            <resource>
                                <directory>${basedir}/src/main/resources/</directory>
                                <includes>
                                    <include>**/*</include>
                                </includes>
                            </resource>
                        </resources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!-- Source attach plugin -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
            <version>3.0.1</version>
            <configuration>
                <attach>true</attach>
            </configuration>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Custom annotation

The user-defined annotation mainly uses two meta annotations:

  • @Target({ElementType.TYPE}) indicates that it is an annotation to a class.
  • @Retention(RetentionPolicy.SOURCE) means that this annotation only works at compile time and will not exist at run time.

Custom @ Getter annotation:

package com.sumkor.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE}) 
@Retention(RetentionPolicy.SOURCE) 
public @interface Getter {
}

Custom @ Setter annotation:

package com.sumkor.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
}

Custom annotation processor

BaseProcessor

Define the abstract base class BaseProcessor, which is used to uniformly obtain the tool classes in the compilation stage, such as JavacTrees, TreeMaker, etc.
Because the project needs to be debugged and run in the IDEA, the processing environment of the IDEA environment is introduced.

package com.sumkor.processor;

import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import java.lang.reflect.Method;

/**
 * @author Sumkor
 * @since 2021/12/27
 */
public abstract class BaseProcessor extends AbstractProcessor {

    protected Messager messager;   // Used to log during compilation
    protected JavacTrees trees;    // Provides an abstract syntax tree to be processed
    protected TreeMaker treeMaker; // Encapsulates some methods of creating AST nodes
    protected Names names;         // Provides methods for creating identifiers

    /**
     * Get some environment information during the compilation phase
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        processingEnv = jbUnwrap(ProcessingEnvironment.class, processingEnv);
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    /**
     * Get the ProcessingEnvironment in the IDEA environment
     */
    private static <T> T jbUnwrap(Class<? extends T> iface, T wrapper) {
        T unwrapped = null;
        try {
            final Class<?> apiWrappers = wrapper.getClass().getClassLoader().loadClass("org.jetbrains.jps.javac.APIWrappers");
            final Method unwrapMethod = apiWrappers.getDeclaredMethod("unwrap", Class.class, Object.class);
            unwrapped = iface.cast(unwrapMethod.invoke(null, iface, wrapper));
        }
        catch (Throwable ignored) {}
        return unwrapped != null? unwrapped : wrapper;
    }

}

GetterProcessor

The annotation processor corresponding to custom annotation @ Getter is as follows, and the code flow is as follows:

  1. Gets the class modified by the @ Getter annotation.
  2. Find all member variables on the class.
  3. Construct getter methods for member variables.

The difficulty lies in the use of JavacTrees and TreeMaker related API s. The key codes in this paper are annotated for easy understanding.

package com.sumkor.processor;

import com.sumkor.annotation.Getter;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;

import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

/**
 * @author Sumkor
 * @since 2021/12/24
 */
@SupportedAnnotationTypes("com.sumkor.annotation.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends BaseProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager.printMessage(Diagnostic.Kind.NOTE, "========= GetterProcessor init =========");
    }

    /**
     * Process AST
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // Gets the element modified by the custom Getter annotation
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);
        set.forEach(element -> {
            // Get the corresponding syntax tree JCTree according to the element
            JCTree jcTree = trees.getTree(element);
            jcTree.accept(new TreeTranslator() {
                // Handle the class definition part of the syntax tree JCClassDecl
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
                    for (JCTree tree : jcClassDecl.defs) {
                        // Find the member variable node on the syntax tree and store it in the jcVariableDeclList collection
                        if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                            JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                            jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                        }
                    }
                    // Construct getter methods for member variables and add them to JCClassDecl
                    jcVariableDeclList.forEach(jcVariableDecl -> {
                        messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
                        jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                    });
                    super.visitClassDef(jcClassDecl);
                }

            });
        });
        return true;
    }

    /**
     * Construct getter methods for member traversal
     */
    private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        // Generate the expression return this value;
        statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
        // Add braces {return this.value;}
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        // Assembly method
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null);
    }

    /**
     * Hump nomenclature
     */
    private Name getNewMethodName(Name name) {
        String s = name.toString();
        return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
    }

}

SetterProcessor

The annotation processor corresponding to the custom annotation @ Setter is SetterProcessor, and the overall process is similar to that of GetterProcessor:

  1. Gets the class modified by the @ Setter annotation.
  2. Find all member variables on the class.
  3. Construct setter methods for member variables.

Focus on the logic of constructing setter methods:

/**
 * Construct setter methods for members
 */
private JCTree.JCMethodDecl makeSetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
    ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
    // Generate the expression this value = value;
    JCTree.JCExpressionStatement aThis = makeAssignment(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName()), treeMaker.Ident(jcVariableDecl.getName()));
    statements.append(aThis);
    // Add braces {this.value = value;}
    JCTree.JCBlock block = treeMaker.Block(0, statements.toList());

    // Before generating method parameters, indicate the position of the current syntax node in the syntax tree to avoid exception Java lang.AssertionError: Value of x -1
    treeMaker.pos = jcVariableDecl.pos;

    // Generate method parameter String value
    JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), jcVariableDecl.getName(), jcVariableDecl.vartype, null);
    List<JCTree.JCVariableDecl> parameters = List.of(param);
    // Generate return object void
    JCTree.JCExpression methodType = treeMaker.Type(new Type.JCVoidType());

    // Assembly method
    return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), methodType, List.nil(), parameters, List.nil(), block, null);
}

/**
 * Assignment operation lhs = rhs
 */
private JCTree.JCExpressionStatement makeAssignment(JCTree.JCExpression lhs, JCTree.JCExpression rhs) {
    return treeMaker.Exec(
            treeMaker.Assign(lhs, rhs)
    );
}

/**
 * Hump nomenclature
 */
private Name getNewMethodName(Name name) {
    String s = name.toString();
    return names.fromString("set" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
}

3.2 app project

The overall structure of the project is as follows. The red prompt of IDEA here is caused by the inability to recognize user-defined annotations, which will not affect the operation of the project.

Introduce the Lombok processor project in maven:

<dependency>
    <groupId>com.sumkor</groupId>
    <artifactId>lombok-processor</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

Write the test class and introduce the custom annotations @ Getter and @ Setter. You can see that the IDEA can be compiled and run normally although it is marked in red to indicate that no method can be found.

4. Commissioning

4.1 IDEA configuration

Use Attach Remote JVM to debug the compilation process of Lombok app project.

  1. Create a remote debugging and specify the port as 5005.
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

  1. Select Help - > Edit custom VM options from the menu, add the following content and restart the IDEA. The port here is the one you selected in the previous step to attach.
-Dcompiler.process.debug.port=5005
  1. Open the Debug Build Process in IDEA. Note that this option is turned off by default after each IDEA restart.

4.2 start commissioning

  1. Add a breakpoint in the annotation processor. It is recommended that the breakpoint be set in AbstractProcessor#init.
  2. Execute the mvn clean operation to clear the contents of the last build.
  3. Execute ctrl + F9 to build the Lombok app project.

    In the status bar, you can see that the build process is waiting for the debugger connection:
  4. Run the remote debugging you just created.

    You can see that the breakpoint in AbstractProcessor#init has been entered.
    Debug the custom annotation processor and observe the modification results of TreeMaker on the syntax tree.

    4.3 problem solving

When writing the SetterProcessor#process method, if treemaker is missing pos = jcVariableDecl. pos; This line of code will give an error prompt during compilation: Java Lang. assertionerror: value of X - 1. The specific compilation information is as follows:

Executing pre-compile tasks...
Loading Ant configuration...
Running Ant tasks...
Running 'before' tasks
Checking sources
Copying resources... [lombok-app]
Parsing java... [lombok-app]
java: ========= GetterProcessor init =========
java: value has been processed
java: ========= SetterProcessor init =========
java: compiler (1.8.0_91) An unexpected error occurred in. If in Bug Database (http://bugs. java. The error is not found in. Com), please report through the Java Bug page( http://bugreport.java.com )Create the java compiler Bug. Please attach your procedure and the following diagnostic information to the report. thank you.
java: java.lang.AssertionError: Value of x -1
java:     at com.sun.tools.javac.util.Assert.error(Assert.java:133)
java:     at com.sun.tools.javac.util.Assert.check(Assert.java:94)
java:     at com.sun.tools.javac.util.Bits.incl(Bits.java:186)
java:     at com.sun.tools.javac.comp.Flow$AssignAnalyzer.initParam(Flow.java:1858)
java:     at com.sun.tools.javac.comp.Flow$AssignAnalyzer.visitMethodDef(Flow.java:1807)
java:     at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:778)
java:     at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
java:     at com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:404)
java:     at com.sun.tools.javac.comp.Flow$AssignAnalyzer.scan(Flow.java:1382)
java:     at com.sun.tools.javac.comp.Flow$AssignAnalyzer.visitClassDef(Flow.java:1749)
java:     at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:693)
java:     at com.sun.tools.javac.comp.Flow$AssignAnalyzer.analyzeTree(Flow.java:2446)
java:     at com.sun.tools.javac.comp.Flow$AssignAnalyzer.analyzeTree(Flow.java:2429)
java:     at com.sun.tools.javac.comp.Flow.analyzeTree(Flow.java:211)
java:     at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1327)
java:     at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1296)
java:     at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:901)
java:     at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:860)
java:     at com.sun.tools.javac.main.Main.compile(Main.java:523)
java:     at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
java:     at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
java:     at org.jetbrains.jps.javac.JavacMain.compile(JavacMain.java:238)
java:     at org.jetbrains.jps.incremental.java.JavaBuilder.lambda$compileJava$2(JavaBuilder.java:514)
java:     at org.jetbrains.jps.incremental.java.JavaBuilder.invokeJavac(JavaBuilder.java:560)
java:     at org.jetbrains.jps.incremental.java.JavaBuilder.compileJava(JavaBuilder.java:512)
java:     at org.jetbrains.jps.incremental.java.JavaBuilder.compile(JavaBuilder.java:355)
java:     at org.jetbrains.jps.incremental.java.JavaBuilder.doBuild(JavaBuilder.java:280)
java:     at org.jetbrains.jps.incremental.java.JavaBuilder.build(JavaBuilder.java:234)
java:     at org.jetbrains.jps.incremental.IncProjectBuilder.runModuleLevelBuilders(IncProjectBuilder.java:1485)
java:     at org.jetbrains.jps.incremental.IncProjectBuilder.runBuildersForChunk(IncProjectBuilder.java:1123)
java:     at org.jetbrains.jps.incremental.IncProjectBuilder.buildTargetsChunk(IncProjectBuilder.java:1268)
java:     at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunkIfAffected(IncProjectBuilder.java:1088)
java:     at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunks(IncProjectBuilder.java:854)
java:     at org.jetbrains.jps.incremental.IncProjectBuilder.runBuild(IncProjectBuilder.java:441)
java:     at org.jetbrains.jps.incremental.IncProjectBuilder.build(IncProjectBuilder.java:190)
java:     at org.jetbrains.jps.cmdline.BuildRunner.runBuild(BuildRunner.java:132)
java:     at org.jetbrains.jps.cmdline.BuildSession.runBuild(BuildSession.java:318)
java:     at org.jetbrains.jps.cmdline.BuildSession.run(BuildSession.java:146)
java:     at org.jetbrains.jps.cmdline.BuildMain$MyMessageHandler.lambda$channelRead0$0(BuildMain.java:218)
java:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java:     at java.lang.Thread.run(Thread.java:745)
java: Compilation failed: internal java compiler error
Checking dependencies... [lombok-app]
Dependency analysis found 0 affected files
Errors occurred while compiling module 'lombok-app'
javac 1.8.0_91 was used to compile java sources
Finished, saving caches...
Compilation failed: errors: 1; warnings: 0
Executing post-compile tasks...
Loading Ant configuration...
Running Ant tasks...
Synchronizing output directories...

From the exception stack information, we can see that it is com sun. tools. javac. util. Bits. Incl (bits. Java: 186) reports an error and locates the abnormal position:

com.sun.tools.javac.util.Bits#incl

public void incl(int var1) {
    Assert.check(this.currentState != Bits.BitsState.UNKNOWN);
    Assert.check(var1 >= 0, "Value of x " + var1); // This line reports an error
    this.sizeTo((var1 >>> 5) + 1);
    this.bits[var1 >>> 5] |= 1 << (var1 & 31);
    this.currentState = Bits.BitsState.NORMAL;
}

The break point analyzes the abnormal link, and it can be located that the error is caused by the SetterProcessor#process method. The core value causing the problem is the pos field of JCTree, which is used to indicate the position of the current syntax tree node in the syntax tree. The pos generated by TreeMaker is a fixed value. You need to set this field to the pos of the parsed element (see the previous section for the modified SetterProcessor#process method).

5. Reference

Author: Sumkor
Link: https://segmentfault.com/a/11...

Topics: Java Lombok