Package: org.codehaus.groovy.control

## [Java] Class ClassNodeResolver

- 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. 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 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
    Modifiers | Name | Description
    --- | --- | ---
    `static class` | `ClassNodeResolver.LookupResult` | Helper class to return either a SourceUnit or ClassNode.

  - ### Field Summary

    Fields
    Modifiers | Name | Description
    --- | --- | ---
    `protected static ClassNode` | `NO_CLASS` | Internal helper used to indicate a cache hit for a class that does not exist.

  - ### Methods Summary

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

  - ### Inherited Methods Summary

    Inherited Methods
    Methods inherited from class | Name
    --- | ---
    `class Object` | `equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Field Detail

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

  - ### Method Detail

    - #### public void cacheClass(String name, ClassNode res)

      caches a ClassNode

      Parameters:
        `name` - - the name of the class
        `res` - - the ClassNode for that name

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

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

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

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