Package org.codehaus.groovy.control

# 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
> Mode | asm | class loader | Lookup
> --- | --- | --- | ---
> default | on | on | ASM first; `loadClass` only if ASM has no match
> ASM-only | on | off | ASM only; class-format errors thrown
> loader-only | off | on | `loadClass` only; no decompile
> neither | off | off | groovy 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)`.

- ## Nested Class Summary

  Nested Classes
  Modifier and Type
  Class
  Description
  `static class`
  `ClassNodeResolver.LookupResult`
  Helper class to return either a SourceUnit or ClassNode.

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `protected static final ClassNode`
  `NO_CLASS`
  Internal helper used to indicate a cache hit for a class that does not exist.

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `ClassNodeResolver()`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `void`
  `cacheClass(String name, ClassNode res)`
  caches a ClassNode
  `ClassNodeResolver.LookupResult`
  `findClassNode(String name, CompilationUnit compilationUnit)`
  Extension point for custom lookup logic.
  `ClassNode`
  `getFromClassCache(String name)`
  returns whatever is stored in the class cache for the given name
  `ClassNodeResolver.LookupResult`
  `resolveName(String name, CompilationUnit compilationUnit)`
  Resolves a class name to a `SourceUnit` or `ClassNode`.
  `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).

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## 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