Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8047347

com.sun.source.util.Trees breaks the compiler.

XMLWordPrintable

    • b138
    • x86_64
    • windows_7
    • Verified

      FULL PRODUCT VERSION :
      java version "1.8.0_05"
      Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      Windows 7 64-bit

      A DESCRIPTION OF THE PROBLEM :
      This worked for sure in java 1.6, however in 1.8_u5:

      If you try to compile an enum using:
      JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

      Then add your own AbstractProcessor which runs a Trees.instance(pe) scanner on each file to the compile task.

      If the file you are compiling contains the following:

      public enum SampleEnum {
          Zero(1);

          SampleEnum(int value) { this.value = (byte) value; }
          
          private byte value;
          
          public long getValue() {
              long result = value;
              return value;
          }
      }

      Then the code will generate the following error:

      SampleEnum.java:12: error: call to super not allowed in enum constructor
          SampleEnum(int value) {
                                ^

      NOTE: This ONLY happens IF and only IF you have a variable declared in a method. Literally any variable. But if you take the declaration out, it will work fine.

      After hours of debugging, It seems that IF your visitor performs:

      public Object visitVariable(VariableTree vt, Trees trees) {
                      TreePath path = getCurrentPath();
                      Element variableElement = trees.getElement(path);
      ...

      Trees. getElement seemingly will go back into the code, and it will somehow add an implicit super(); call to the enum constructor. This of course is illegal as enums cannot call super.

      If you take out the getElement call, then everything works again. But it seems that any call to getEelement will eventually break something. Its almost like its incorrectly applying some kind of DeSugar() call to an enum its not supposed to.






      REGRESSION. Last worked in version 6u45

      ADDITIONAL REGRESSION INFORMATION:
      java version "1.6.0_45"
      Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
      Java HotSpot(TM) Client VM (build 20.45-b01, mixed mode)


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Create an enum as described above.
      2. Create a project that asks for a compiler using JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
      3. Create a compile task passing in the enum file as source code.
      4. Create a custom AbstractProcessor and add it to the compile task.
      5. Have the processor create a Trees using: trees = Trees.instance(pe);
      6. For the process command, iterate all the Root Type Elements
      7. Create a custom TreePathScanner<Object, Trees>
      8. Override the Object visitVariable(VariableTree vt, Trees trees) method.
      9. Call the following code there;

          TreePath path = getCurrentPath();
          //Crashes later when you try to compile:
           Element variableElement = trees.getElement(path);
           return super.visitVariable(vt, trees);

      10. Call the task:
           task.setProcessors(processors);
           boolean res = task.call();


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      I should have a compiled file from the source code with no errors.
      ACTUAL -
      The code does not compile, instead I get an error:

      .\src\runtimecompiler\SampleEnum.java:12: error: call to super not allowed in enum constructor
          SampleEnum(int value) {
                                ^
      1 error
      res:false

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      .\src\runtimecompiler\SampleEnum.java:12: error: call to super not allowed in enum constructor
          SampleEnum(int value) {
                                ^
      1 error

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      package runtimecompiler;

      import com.sun.source.tree.ClassTree;
      import com.sun.source.tree.VariableTree;
      import com.sun.source.util.TreePath;
      import com.sun.source.util.TreePathScanner;
      import com.sun.source.util.Trees;
      import java.io.File;
      import java.util.ArrayList;
      import java.util.LinkedList;
      import java.util.List;
      import java.util.Set;
      import javax.annotation.processing.AbstractProcessor;
      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.JavaCompiler;
      import javax.tools.JavaFileObject;
      import javax.tools.StandardJavaFileManager;
      import javax.tools.ToolProvider;


      public class RuntimeCompiler {

          static class CodeAnalyzerProcessor {

              private final CodeAnalyzerTreeVisitor treeVisitor = new CodeAnalyzerTreeVisitor();

              private class CodeAnalyzerTreeVisitor extends TreePathScanner<Object, Trees> {
                  public ClassTree LastTree;
                  
                  @Override
                  public Object visitClass(ClassTree classTree, Trees trees) {
                      LastTree = classTree;
                      return super.visitClass(classTree, trees);
                  }

                  @Override
                  public Object visitVariable(VariableTree vt, Trees trees) {
                      TreePath path = getCurrentPath();
                      //Crashes
                      Element variableElement = trees.getElement(path);
                      return super.visitVariable(vt, trees);
                  }
              }

              //JAVA8: Adding Support for Release 8 code ( Java 8 )
              @SupportedSourceVersion(SourceVersion.RELEASE_8)
              @SupportedAnnotationTypes("*")
              class CodeAnalyzer extends AbstractProcessor {
                  private Trees trees;

                  @Override
                  public void init(ProcessingEnvironment pe) {
                      try {
                          super.init(pe);
                          trees = Trees.instance(pe);
                      } catch (Exception ex) {
                          ex.printStackTrace();
                      }
                  }

                  @Override
                  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
                      for (Element e : roundEnvironment.getRootElements()) {
                          if (e instanceof TypeElement) {
                              TreePath tp = trees.getPath(e);
                              treeVisitor.scan(tp, trees);
                          }
                      }
                      return true;
                  }
              }
          }
          
          /**
           * @param args the command line arguments
           */
          public static void main(String[] args) {

              List<File> files = new ArrayList<File>();
              files.add(new File(".\\src\\runtimecompiler\\SampleEnum.java"));

              JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

              StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
              Iterable<? extends JavaFileObject> compilationUnits1 = fileManager.getJavaFileObjectsFromFiles(files);
              
              JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits1);

              // Create a list to hold annotation processors
              LinkedList<AbstractProcessor> processors = new LinkedList<AbstractProcessor>();
              CodeAnalyzerProcessor proc = new CodeAnalyzerProcessor();

              // Add an annotation processor to the list
              processors.add(proc.new CodeAnalyzer());

              // Set the annotation processor to the compiler task
              task.setProcessors(processors);
              boolean res = task.call();
              System.out.println("res:" + res);
          }
      }


      in: SampleEnum.java:

      package runtimecompiler;

      public enum SampleEnum {
          Zero(1);

          SampleEnum(int value) { this.value = (byte) value; }
          
          private byte value;
          
          public long getValue() {
              long result = value;
              return value;
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Do not use the Trees.getElement(path) method, seems to fix the problem, but then you can't actually get useful information from the scanner.

            jlahoda Jan Lahoda
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: