Class ClassNodeResolver

java.lang.Object
org.codehaus.groovy.control.ClassNodeResolver

public class ClassNodeResolver extends Object
Pluggable lookup of class names to a ClassNode or a SourceUnit.

An instance is installed on a CompilationUnit via CompilationUnit.setClassNodeResolver(ClassNodeResolver). The compilation unit then sets the resolver on ResolveVisitor for each resolving pass. ResolveVisitor prepares the name and asks this resolver whether the class exists. A SourceUnit result means the compiler should add that source to the compilation queue; a ClassNode result completes resolving for that name. The outcome is wrapped in ClassNodeResolver.LookupResult.

Lookup is two independent strategies selected by the compilation unit's optimization options asmResolving and classLoaderResolving (both default on). They are not each other's fallback:

ClassNodeResolver lookup modes
Modeasmclass loaderLookup
defaultononASM first; loadClass only if ASM has no match
ASM-onlyonoffASM only; class-format errors thrown
loader-onlyoffonloadClass only; no decompile
neitheroffoffgroovy source only
ASM describes a type from bytecode without linking it, so a missing superclass does not prevent a ClassNode. loadClass is for types that exist only in memory (or when ASM is off). A ClassHelper hit for an already resolved name is not loadClass.

NoClassDefFoundError means the class was found but could not be linked. It is wrapped with the looked-up name and rethrown, and is not cached as a miss. Script fallback on that error is only for an ASM bytecode-name mismatch (the requested name never existed). A groovy source replaces a found class only when that class came from another loader and the source is newer.

Lookups are cached. Override cacheClass(String, ClassNode) and getFromClassCache(String) to disable or replace the cache. Custom lookup logic belongs in findClassNode(String, CompilationUnit); the entry point is resolveName(String, CompilationUnit).

  • Field Details

    • NO_CLASS

      protected static final ClassNode NO_CLASS
      Internal helper used to indicate a cache hit for a class that does not exist. This way further lookups through a slow findClassNode(String, CompilationUnit) path can be avoided. WARNING: This class is not to be used outside of ClassNodeResolver.
  • Constructor Details

    • ClassNodeResolver

      public ClassNodeResolver()
  • Method Details

    • resolveName

      public ClassNodeResolver.LookupResult resolveName(String name, CompilationUnit compilationUnit)
      Resolves a class name to a SourceUnit or ClassNode. Returns null if neither is found.

      The cache is consulted first. A cached NO_CLASS is returned as null. On a cache miss findClassNode(String, CompilationUnit) is called. A ClassNode result is cached; a SourceUnit result is not, because ResolveVisitor will subsequently find that class in the compilation queue. A miss is cached as NO_CLASS so the slow lookup path is not repeated.

      Parameters:
      name - the fully qualified class name
      compilationUnit - the current compilation unit
      Returns:
      the lookup result, or null if the name cannot be resolved
    • cacheClass

      public void cacheClass(String name, ClassNode res)
      caches a ClassNode
      Parameters:
      name - - the name of the class
      res - - the ClassNode for that name
    • getFromClassCache

      public ClassNode getFromClassCache(String name)
      returns whatever is stored in the class cache for the given name
      Parameters:
      name - - the name of the class
      Returns:
      the result of the lookup, which may be null
    • resolvePackage

      public PackageNode resolvePackage(String packageName, CompilationUnit compilationUnit)
      Resolves a package name to a PackageNode carrying the annotations found on the package's compiled package-info.class, if any (GROOVY-12207). This makes package-level annotations of precompiled dependencies (e.g. JSpecify's @NullMarked) visible to type checkers and AST transforms.

      The package-info.class is located on the compilation unit's class path and decompiled on demand using the same ASM infrastructure as ordinary classes; results are cached per resolver, including a negative cache for packages that have no (annotation-bearing) package-info. Returns null if the package has no such metadata.

      Parameters:
      packageName - the fully qualified package name (no trailing dot), e.g. "foo.bar"
      compilationUnit - the current CompilationUnit
      Returns:
      a PackageNode with the package's annotations, or null if none
    • findClassNode

      public ClassNodeResolver.LookupResult findClassNode(String name, CompilationUnit compilationUnit)
      Extension point for custom lookup logic. The default implementation uses the compilation unit class loader: ASM decompilation of a .class resource first, then ClassLoader.loadClass(String), then a groovy source of the same name if that source is newer than the loaded class (or if no class was found).

      NoClassDefFoundError from class loading is not treated as a miss. It is wrapped and rethrown. Decompilation is not used as a fallback from that error; matching bytecode is the ASM strategy, which runs first when it is enabled. A groovy source replaces an existing class only when it came from another class loader and is newer.

      Parameters:
      name - the fully qualified class name
      compilationUnit - the current compilation unit
      Returns:
      the lookup result, or null if compilationUnit is null or the name cannot be resolved