buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

IDEA Rebuild项目错误:Information:java: java.lang.AssertionError: Value of x -1

问题:java.lang.AssertionError: Value of x -1

模仿lombok工具,我的enumgen工具基本上写完了。

 

发布后准备投产时翻车了!公司的项目emax-rpcapi-list依赖enumgen后,IDEA Rebuild Project时,或者maven package/install的时候,出现报错→Information:java: java.lang.AssertionError: Value of x -1

下面是关于这个错误的详细信息:

Information:java: 编译器 (1.8.0_241) 中出现异常错误。如果在 Bug Database (http://bugs.java.com) 中没有找到该错误, 请通
过 Java Bug 报告页 (http://bugreport.java.com) 建立该 Java 编译器 Bug。请在报告中附上您的程序和以下诊断信息。谢谢。 Information:java: java.lang.AssertionError: Value of x -1 Information:java: at com.sun.tools.javac.util.Assert.error(Assert.java:133) Information:java: at com.sun.tools.javac.util.Assert.check(Assert.java:94) Information:java: at com.sun.tools.javac.util.Bits.incl(Bits.java:186) Information:java: at com.sun.tools.javac.comp.Flow$AssignAnalyzer.initParam(Flow.java:1858) Information:java: at com.sun.tools.javac.comp.Flow$AssignAnalyzer.visitMethodDef(Flow.java:1807) Information:java: at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:778) Information:java: at com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49) Information:java: at com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:404) Information:java: at com.sun.tools.javac.comp.Flow$AssignAnalyzer.scan(Flow.java:1382) Information:java: at com.sun.tools.javac.comp.Flow$AssignAnalyzer.visitClassDef(Flow.java:1749) Information:java: at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:693) Information:java: at com.sun.tools.javac.comp.Flow$AssignAnalyzer.analyzeTree(Flow.java:2446) Information:java: at com.sun.tools.javac.comp.Flow$AssignAnalyzer.analyzeTree(Flow.java:2429) Information:java: at com.sun.tools.javac.comp.Flow.analyzeTree(Flow.java:211) Information:java: at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1327) Information:java: at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1296) Information:java: at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:901) Information:java: at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:860) Information:java: at com.sun.tools.javac.main.Main.compile(Main.java:523) Information:java: at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129) Information:java: at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138) Information:java: at org.jetbrains.jps.javac.JavacMain.compile(JavacMain.java:207) Information:java: at org.jetbrains.jps.incremental.java.JavaBuilder.compileJava(JavaBuilder.java:493) Information:java: at org.jetbrains.jps.incremental.java.JavaBuilder.compile(JavaBuilder.java:345) Information:java: at org.jetbrains.jps.incremental.java.JavaBuilder.doBuild(JavaBuilder.java:270) Information:java: at org.jetbrains.jps.incremental.java.JavaBuilder.build(JavaBuilder.java:223) Information:java: at org.jetbrains.jps.incremental.IncProjectBuilder.runModuleLevelBuilders(IncProjectBuilder.java:1414) Information:java: at org.jetbrains.jps.incremental.IncProjectBuilder.runBuildersForChunk(IncProjectBuilder.java:1092) Information:java: at org.jetbrains.jps.incremental.IncProjectBuilder.buildTargetsChunk(IncProjectBuilder.java:1159) Information:java: at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunkIfAffected(IncProjectBuilder.java:1053) Information:java: at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunks(IncProjectBuilder.java:882) Information:java: at org.jetbrains.jps.incremental.IncProjectBuilder.runBuild(IncProjectBuilder.java:449) Information:java: at org.jetbrains.jps.incremental.IncProjectBuilder.build(IncProjectBuilder.java:190) Information:java: at org.jetbrains.jps.cmdline.BuildRunner.runBuild(BuildRunner.java:138) Information:java: at org.jetbrains.jps.cmdline.BuildSession.runBuild(BuildSession.java:297) Information:java: at org.jetbrains.jps.cmdline.BuildSession.run(BuildSession.java:130) Information:java: at org.jetbrains.jps.cmdline.BuildMain$MyMessageHandler.lambda$channelRead0$0(BuildMain.java:218) Information:java: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) Information:java: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) Information:java: at java.lang.Thread.run(Thread.java:748) Information:java: Errors occurred while compiling module 'zhenghe-rpcapistyle' Information:javac 1.8.0_241 was used to compile java sources Information:2023/3/20 20:57 - Build completed with 1 error and 0 warnings in 5 s 993 ms Error:java: Compilation failed: internal java compiler error

 

此问题网上资料较少,在sun平台有网友提问,但是没有结果。

 

临时解决

这个错误让我有点摸不着头脑。因为我自己的demo项目在依赖了enumgen是可以正常使用的。

由于emax-rpcapi-list和我的demo项目同时也都依赖了lombok,直觉分析是enumgen与lombok产生冲突。我去修改我的注解处理器实现逻辑,去尝试解决,未能修复。中间排查过程比较曲折。最后在比对emax-rpcapi-list和我的demo工程两者的mvn:dependency:tree时,才有了下面的临时解决办法。

  • 第一个是调整maven dependencies依赖顺序。我的要在lombok的前面先行依赖enumgen。
  • 第二个是lombok版本兼容性。lombok版本1.18.20是有问题的,升级lombok的版本到1.18.24,就可以了。

 

问题又出现了

为什么叫临时解决办法呢? 因为我的enumgen后续又扩展了一些代码。结果,同样的错误又出现了→Information:java: java.lang.AssertionError: Value of x -1

我开始仔细分析错误堆栈,注意到 visitClassDef、visitMethodDef 这些关键字。

 

Flow:2493 ---
Flow$AssignAnalyzer#visitMethodDef

@Override
public void visitMethodDef(JCMethodDecl tree) {
    if (tree.body == null) {
        return;
    }

    /*  MemberEnter can generate synthetic methods ignore them
     */
    if ((tree.sym.flags() & SYNTHETIC) != 0) {
        return;
    }

    Lint lintPrev = lint;
    lint = lint.augment(tree.sym);
    try {
        super.visitMethodDef(tree); // 这里调用super---Flow$AbstractAssignAnalyzer
    } finally {
        lint = lintPrev;
    }
}

 Flow$AbstractAssignAnalyzer#visitMethodDef(JCMethodDecl)

for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
    JCVariableDecl def = l.head;
    scan(def);
    Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag");
    /*  If we are executing the code from Gen, then there can be
     *  synthetic or mandated variables, ignore them.
     */
    initParam(def);
}

Flow:1795

Flow$AbstractAssignAnalyzer#initParam(JCVariableDecl)

protected void initParam(JCVariableDecl def) {
    inits.incl(def.sym.adr); // inits是com.sun.tools.javac.util.Bits实例
    uninits.excl(def.sym.adr);
}

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

/** Include x in this set.
 */
public void incl(int x) {
    Assert.check(currentState != BitsState.UNKNOWN);
    Assert.check(x >= 0, "Value of x " + x); // 这里调用了Assert#check
    sizeTo((x >>> wordshift) + 1);
    bits[x >>> wordshift] = bits[x >>> wordshift] |
        (1 << (x & wordmask));
    currentState = BitsState.NORMAL;
}

JCVariableDecl.sym.adr见Symbol#adr

/** The variable's address. Used for different purposes during
 *  flow analysis, translation and code generation.
 *  Flow analysis:
 *    If this is a blank final or local variable, its sequence number.
 *  Translation:
 *    If this is a private field, its access number.
 *  Code generation:
 *    If this is a local variable, its logical slot number.
 */
public int adr = -1;

借助百度翻译来理解一下这段javadoc:

/**变量的地址。在Flow分析、翻译和代码生成过程中用于不同的目的。
*Flow分析:
*如果这是一个空白的最终变量或局部变量,那么它就是序列号。
*Translation:
*如果这是一个私人字段,那么它就是访问号码。
*代码生成:
*如果这是一个局部变量,那么它就是逻辑槽号。*/
public int adr = -1;

堆栈里是Flow$Analyzer,所以推断是我的变量定义的问题。然后,就是一顿改修改的类似下面的VarDef相关的代码,主要是关注VarDef的第一个参数modifier,发现也就只能用Flags.PARAMETER。依然无法解决问题。

JCTree.JCVariableDecl var_exception = maker.VarDef(maker.Modifiers(Flags.PARAMETER), names.fromString("e"), maker.Ident(names.fromString(exceptionClass.getSimpleName())), null);

最终的解决方案

先贴出来我的代码吧

 1 @Override
 2 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 3     final Set<? extends Element> dataAnnotations = roundEnv.getElementsAnnotatedWith(EnumGetByCode.class);
 4     dataAnnotations.stream()
 5             .filter(element -> element.getKind() == ElementKind.ENUM)//筛选枚举类
 6             .map(element -> trees.getTree(element))
 7             .forEach(tree -> {
 8                         tree.accept(new TreeTranslator() {
 9                             @Override
10                             public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
11                                 astUtil.setClass(jcClassDecl);
12 
13                                 prependGetBeanByName(jcClassDecl);
14 
15                                 JCTree.JCMethodDecl newMethodDecl = prependGetBeanByCode(jcClassDecl);
16                                 if (newMethodDecl != null) {
17                                     astUtil.addImport(jcClassDecl);
18 
19                                     prependGetDescriptionByCode(jcClassDecl, newMethodDecl);
20                                 }
21                                 super.visitClassDef(jcClassDecl);
22 
23                             }
24                         });
25                     }
26             );
27     return true;
28 }

 

终极解决办法是参考简书上的一篇帖子,设置上下文的treeMaker实例的pos。

当到达下个注解使用处时,需要首先设置一下pos。因为系统中有很多的类或者方法使用了目标注解,而在代码生成过程中(也就是accept里重写visitClassDef为当前class append或prepend的元素),没有对treeMarker.pos的修改,使得其一直保持不变即使操作已经进行到下个类文件,pos仍然是不变的。
的确如此,当我在visitClassDef把那些append或prepend注释掉时,是不会出现这个错误的。

 

修改后的代码,就是在上面第8行调用tree.accept前面,加上一行代码: maker.pos = tree.pos; 

终于松了一口气!

posted on 2023-03-21 13:52  buguge  阅读(187)  评论(0编辑  收藏  举报