Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-6755

Bad type resolution causing bad classes and TypeNotPresentExceptions with joint compilation, deep generics and star imports

    XMLWordPrintableJSON

Details

    • Patch

    Description

      ResolveVisitor improperly marks generics types as being resolved when sub-generics types haven't been resolved, leading to classes being generated that lack fully qualified generics typing and then subsequent errors while compiling against those generated class files. Here's an example:

      a/Item.java
      package a;
      public interface Item {}
      
      b/ItemListList.groovy
      package b
      import a.*
      
      class ItemListList {
        static List<List<Item>> ITEMS
      }
      

      If you compile this with the joint compiler and inspect ItemListList with javap, the type signature will be:

        public static java.util.List<java.util.List<Item>> ITEMS
      

      Note that Item type is not fully qualified (it should refer to a.Item), and if that class file is accessed during a later compilation (e.g., for an incremental compilation or a groovy server page), an exception with a stack trace similar to the following will be thrown:

          java.lang.TypeNotPresentException: Type Item not present
          	at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
          	at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
          	at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
          	at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
          	at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
          	at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
          	at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
          	at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
          	at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
          	at sun.reflect.generics.repository.FieldRepository.getGenericType(FieldRepository.java:85)
          	at java.lang.reflect.Field.getGenericType(Field.java:237)
          	at org.codehaus.groovy.vmplugin.v5.Java5.configureClassNode(Java5.java:357)
          	at org.codehaus.groovy.ast.ClassNode.lazyClassInit(ClassNode.java:261)
           ...
      

      The problem is that ResolveVisitor marks a GenericsType as resolved iff the type (i.e., ClassNode) has been resolved, but ignores any failure to resolve any generic types associated with the generic type itself. To use the example above, when resolving List<List<Item>> it first attempts to resolve the generic type List<Item>. It successfully resolves List to java.util.List, but isn't yet able to resolve Item to a.Item, as a star import is used and the java-compiler stage of joint compilation has yet to run, so a.Item is unavailable for classloader-based resolution in resolveToOuter to occur. Nonetheless, it incorrectly marks List<Item> as resolved, as it only checks that the primary type was resolved. A later attempt at resolution occurs after the java compilation, at which time an attempt to resolve Item would work since a/Item.class now exists, but no re-attempt is made at this time to resolve the GenericType as it had already been marked as being resolved. The solution is simple: only mark a GenericType as resolved if both its type and any genericsTypes it has are resolved.

      Pull request is at https://github.com/groovy/groovy-core/pull/408

      It's possible that this bug was responsible for GROOVY-3157.

      Attachments

        Activity

          People

            blackdrag Jochen Theodorou
            lujke Luke Kirby
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: