Package org.codehaus.groovy.ast.decompiled

# Class DecompiledClassNode

java.lang.Object
org.codehaus.groovy.ast.ASTNode
org.codehaus.groovy.ast.AnnotatedNode
org.codehaus.groovy.ast.ClassNode
org.codehaus.groovy.ast.decompiled.DecompiledClassNode

All Implemented Interfaces:
  `GroovydocHolder<AnnotatedNode>`, `NodeMetaDataHandler`

public class DecompiledClassNode extends ClassNode
Represents a `ClassNode` for classes loaded from compiled bytecode files decompiled using ASM. This class bridges the gap between raw bytecode metadata and Groovy's AST representation, providing lazy initialization of all class members to enable efficient runtime class loading and introspection without requiring source code.

## Architectural Role

DecompiledClassNode is a deferred factory for AST nodes: it collects bytecode metadata from a `ClassStub` and reconstructs full AST nodes (methods, fields, constructors) on demand via lazy proxy classes (`LazyFieldNode`, `LazyMethodNode`, `LazyConstructorNode`). This enables Groovy to compile and introspect compiled classes without full bytecode decompilation upfront.

## Key Differences from Regular ClassNode

- Immutability: Decompiled classes are read-only representations of bytecode; modification methods throw `UnsupportedOperationException`. See `setName(String)`, `setRedirect(ClassNode)`, `setUsingGenerics(boolean)`, `setGenericsPlaceHolder(boolean)`.
- Always resolved: `isResolved()` always returns `true`. Unlike source-based classes that transition from unresolved to resolved, decompiled classes are inherently resolved because their metadata is extracted from compiled bytecode.
- Non-primary: `isPrimaryNode = false`. This indicates the class is derived from compiled bytecode rather than primary source compilation.
- Lazy initialization: Superclass and member information is initialized on first access, not at construction. This defers expensive metadata parsing until needed.
- JVM introspection fallback: Certain operations (e.g., `isSealed()`) delegate to runtime JVM reflection via `getTypeClass()`.

## Lazy Initialization Strategy

DecompiledClassNode uses a two-phase lazy initialization pattern with double-checked locking:

- Phase 1 - Superclass/Interface metadata: Triggered by accessors like `getGenericsTypes()`, `getInterfaces()`, `getAnnotations()`. `lazyInitSupers()` parses generic signatures and initializes annotations.
- Phase 2 - Class members: Triggered by accessors like `getMethods()`, `getFields()`, `getDeclaredConstructors()`. `lazyInitMembers()` unpacks method and field stubs into AST nodes.

Private members are wrapped in lazy proxies (`LazyFieldNode`, `LazyMethodNode`, `LazyConstructorNode`) to further defer parsing their complex signatures until accessed.

## Thread Safety

DecompiledClassNode is thread-safe for lazy initialization. Both `lazyInitSupers()` and `lazyInitMembers()` use double-checked locking with volatile flags to ensure single initialization across multiple threads. The implementation protects initialization with `lazyInitLock` (inherited from ClassNode) to serialize critical sections.

## Caching Strategy

The ASM decompiler and reference resolver are cached at the DecompiledClassNode level but not across multiple class loads. When the same bytecode is parsed by different `AsmDecompiler` instances, separate DecompiledClassNode instances are created. This ensures consistency within a compilation context.

## Inner Class Modifiers

For nested classes, the INNERCLASS bytecode attribute (JVMS 4.7.6) provides authoritative access modifiers that correctly reflect nested visibility. `getModifiers(ClassStub)` prefers `innerClassModifiers` over the top-level `accessModifiers` when available.

## Timestamp Extraction

Groovy embeds compilation timestamps in synthetic static field names for change detection. `getCompilationTimeStamp()` decodes this metadata using `Verifier.getTimestampFromFieldName(String)`.

See Also:
  - `AsmDecompiler.parseClass(java.net.URL)`
  - `AsmReferenceResolver`
  - `ClassStub`
  - `LazyFieldNode`
  - `LazyMethodNode`
  - `LazyConstructorNode`
  - `ClassSignatureParser.configureClass(DecompiledClassNode, ClassStub, AsmReferenceResolver)`
  - `MemberSignatureParser.createFieldNode(FieldStub, AsmReferenceResolver, DecompiledClassNode)`
  - `MemberSignatureParser.createMethodNode(AsmReferenceResolver, MethodStub)`

- ## Field Summary

  ### Fields inherited from class org.codehaus.groovy.ast.ClassNode

  `clazz, EMPTY_ARRAY, isPrimaryNode, lazyInitLock, SUPER, THIS`

  ### Fields inherited from interface groovy.lang.groovydoc.GroovydocHolder

  `DOC_COMMENT`

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `DecompiledClassNode(ClassStub classData, AsmReferenceResolver resolver)`
  Creates a `DecompiledClassNode` from a class stub extracted from bytecode.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `List<AnnotationNode>`
  `getAnnotations()`
  Returns all annotations attached to this class.
  `List<AnnotationNode>`
  `getAnnotations(ClassNode type)`
  Returns annotations of a specific type attached to this class.
  `long`
  `getCompilationTimeStamp()`
  Extracts the compilation timestamp from static field metadata if present.
  `List<ConstructorNode>`
  `getDeclaredConstructors()`
  Returns all constructors declared by this class.
  `FieldNode`
  `getDeclaredField(String name)`
  Returns a field declared by this class with the given name.
  `List<MethodNode>`
  `getDeclaredMethods(String name)`
  Returns all methods declared by this class with the given name.
  `List<FieldNode>`
  `getFields()`
  Returns all fields declared by this class (excluding inherited fields).
  `GenericsType[]`
  `getGenericsTypes()`
  Returns the generic type parameters of this class.
  `ClassNode[]`
  `getInterfaces()`
  Returns the interfaces implemented by this class.
  `List<MethodNode>`
  `getMethods()`
  Returns all methods and constructors declared by this class (excluding inherited methods).
  `PackageNode`
  `getPackage()`
  Returns the `PackageNode` for this decompiled class, carrying any annotations declared on the package's compiled `package-info.class` (GROOVY-12207).
  `List<RecordComponentNode>`
  `getRecordComponents()`
  Returns the record components of this record class (Java 16+).
  `Class`
  `getTypeClass()`
  Resolves the runtime JVM `Class` object for this decompiled class node.
  `ClassNode[]`
  `getUnresolvedInterfaces(boolean useRedirect)`
  Returns the unresolved (not yet redirected) interfaces implemented by this class.
  `ClassNode`
  `getUnresolvedSuperClass(boolean useRedirect)`
  Returns the unresolved (not yet redirected) superclass of this class.
  `boolean`
  `isParameterized()`
  Determines whether this class has generic type parameters by inspecting the bytecode signature.
  `boolean`
  `isResolved()`
  Reports that this class node is fully resolved from bytecode.
  `boolean`
  `isSealed()`
  Determines if this class is sealed, checking both Java sealed class annotations and Groovy `@Sealed` transform annotations.
  `boolean`
  `isUsingGenerics()`
  Indicates whether this class is using generic types.
  `void`
  `setGenericsPlaceHolder(boolean b)`
  Prevents marking decompiled class nodes as generic placeholders.
  `String`
  `setName(String name)`
  Prevents renaming of decompiled class nodes, as they represent immutable bytecode definitions.
  `void`
  `setRedirect(ClassNode cn)`
  Prevents redirection of decompiled class nodes to alternate implementations.
  `void`
  `setUsingGenerics(boolean b)`
  Prevents modification of generic type usage for decompiled class nodes.

  ### Methods inherited from class org.codehaus.groovy.ast.ClassNode

  `addConstructor, addConstructor, addField, addField, addFieldFirst, addFieldFirst, addInterface, addMethod, addMethod, addMixin, addObjectInitializerStatements, addProperty, addProperty, addStaticInitializerStatements, addSyntheticMethod, addTransform, addTypeAnnotation, addTypeAnnotations, asGenericsType, declaresAnyInterfaces, declaresInterface, equals, getAbstractMethods, getAllDeclaredMethods, getAllInterfaces, getCompileUnit, getComponentType, getDeclaredConstructor, getDeclaredMethod, getDeclaredMethodsMap, getEnclosingMethod, getField, getFieldIndex, getGetterMethod, getGetterMethod, getInnerClasses, getMethod, getMethods, getMixins, getModifiers, getModule, getName, getNameWithoutPackage, getObjectInitializerStatements, getOuterClass, getOuterClasses, getOuterClassType, getOuterField, getPackageName, getPermittedSubclasses, getPlainNodeReference, getPlainNodeReference, getProperties, getProperty, getSetterMethod, getSetterMethod, getSuperClass, getText, getTransforms, getTypeAnnotations, getTypeAnnotations, getUnresolvedInterfaces, getUnresolvedName, getUnresolvedSuperClass, hasDeclaredMethod, hashCode, hasMethod, hasPackageName, hasPossibleMethod, hasPossibleStaticMethod, hasProperty, implementsAnyInterfaces, implementsInterface, isAbstract, isAnnotated, isAnnotationDefinition, isArray, isDerivedFrom, isDerivedFromGroovyObject, isEnum, isGenericsPlaceHolder, isInterface, isPrimaryClassNode, isRecord, isRedirectNode, isScript, isScriptBody, isStatic, isStaticClass, isSyntheticPublic, makeArray, parametersEqual, positionStmtsAfterEnumInitStmts, redirect, removeConstructor, removeField, removeMethod, renameField, setAnnotated, setCompileUnit, setEnclosingMethod, setGenericsTypes, setInterfaces, setMixins, setModifiers, setModule, setOuterClassType, setPermittedSubclasses, setRecordComponents, setScript, setScriptBody, setStaticClass, setSuperClass, setSyntheticPublic, setUnresolvedSuperClass, toString, toString, tryFindPossibleMethod, visitContents`

  ### Methods inherited from class org.codehaus.groovy.ast.AnnotatedNode

  `addAnnotation, addAnnotation, addAnnotations, getDeclaringClass, getGroovydoc, getInstance, hasNoRealSourcePosition, isSynthetic, setDeclaringClass, setHasNoRealSourcePosition, setSynthetic`

  ### Methods inherited from class org.codehaus.groovy.ast.ASTNode

  `copyNodeMetaData, getColumnNumber, getLastColumnNumber, getLastLineNumber, getLineNumber, getMetaDataMap, setColumnNumber, setLastColumnNumber, setLastLineNumber, setLineNumber, setMetaDataMap, setSourcePosition, visit`

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

  `clone, finalize, getClass, notify, notifyAll, wait, wait, wait`

  ### Methods inherited from interface org.codehaus.groovy.ast.NodeMetaDataHandler

  `copyNodeMetaData, getNodeMetaData, getNodeMetaData, getNodeMetaData, newMetaDataMap, putNodeMetaData, removeNodeMetaData, setNodeMetaData`

- ## Constructor Details

  - ### DecompiledClassNode

    public DecompiledClassNode(ClassStub classData, AsmReferenceResolver resolver)
    Creates a `DecompiledClassNode` from a class stub extracted from bytecode.
    Parameters:
      `classData` - the `ClassStub` containing bytecode-derived metadata
      `resolver` - the `AsmReferenceResolver` used to resolve class references

- ## Method Details

  - ### getCompilationTimeStamp

    public long getCompilationTimeStamp()
    Extracts the compilation timestamp from static field metadata if present. Groovy embeds compilation timestamps in synthetic static field names using a special encoding.
    Returns:
      the compilation timestamp in milliseconds, or `Long.MAX_VALUE` if not available
    See Also:
      - `Verifier.getTimestampFromFieldName(String)`

  - ### getTypeClass

    public Class getTypeClass()
    Resolves the runtime JVM `Class` object for this decompiled class node.
    Overrides:
      `getTypeClass` in class `ClassNode`
    Returns:
      the loaded `Class` instance from the current classloader
    Throws:
      `GroovyBugError` - if the class cannot be resolved at runtime
    See Also:
      - `AsmReferenceResolver.resolveJvmClass(String)`

  - ### getPackage

    public PackageNode getPackage()
    Returns the `PackageNode` for this decompiled class, carrying any annotations declared on the package's compiled `package-info.class` (GROOVY-12207). Unlike a source-based `ClassNode`, a decompiled class has no owning module, so the package (and its annotations) is resolved on demand from the class path via the reference resolver's `ClassNodeResolver.resolvePackage` and cached there. Returns `null` for the default package or when the package has no such metadata.
    Overrides:
      `getPackage` in class `ClassNode`
    Returns:
      the package node with package-level annotations, or `null` if none

  - ### isParameterized

    public boolean isParameterized()
    Determines whether this class has generic type parameters by inspecting the bytecode signature. A parameterized class signature begins with `'<'` to indicate type variable declarations.
    Returns:
      `true` if the class signature contains generic type parameters

  - ### isResolved

    public boolean isResolved()
    Reports that this class node is fully resolved from bytecode. Decompiled classes are always resolved, unlike source-based classes which may be unresolved during compilation.
    Overrides:
      `isResolved` in class `ClassNode`
    Returns:
      always `true` for decompiled classes

  - ### isSealed

    public boolean isSealed()
    Determines if this class is sealed, checking both Java sealed class annotations and Groovy `@Sealed` transform annotations.
    Overrides:
      `isSealed` in class `ClassNode`
    Returns:
      `true` if the class is declared as sealed with either Java or Groovy mechanisms

  - ### setName

    public String setName(String name)
    Prevents renaming of decompiled class nodes, as they represent immutable bytecode definitions.
    Overrides:
      `setName` in class `ClassNode`
    Parameters:
      `name` - ignored
    Returns:
      never returns normally
    Throws:
      `UnsupportedOperationException` - always, as bytecode classes are immutable

  - ### setRedirect

    public void setRedirect(ClassNode cn)
    Prevents redirection of decompiled class nodes to alternate implementations.
    Overrides:
      `setRedirect` in class `ClassNode`
    Parameters:
      `cn` - ignored
    Throws:
      `UnsupportedOperationException` - always, as bytecode classes cannot be redirected

  - ### setUsingGenerics

    public void setUsingGenerics(boolean b)
    Prevents modification of generic type usage for decompiled class nodes.
    Overrides:
      `setUsingGenerics` in class `ClassNode`
    Parameters:
      `b` - ignored
    Throws:
      `UnsupportedOperationException` - always, as bytecode classes are immutable

  - ### setGenericsPlaceHolder

    public void setGenericsPlaceHolder(boolean b)
    Prevents marking decompiled class nodes as generic placeholders.
    Overrides:
      `setGenericsPlaceHolder` in class `ClassNode`
    Parameters:
      `b` - ignored
    Throws:
      `UnsupportedOperationException` - always, as bytecode classes are immutable

  - ### getAnnotations

    public List<AnnotationNode> getAnnotations()
    Returns all annotations attached to this class. Triggers lazy initialization of superclass and interface metadata.
    Overrides:
      `getAnnotations` in class `ClassNode`
    Returns:
      list of `AnnotationNode` objects

  - ### getAnnotations

    public List<AnnotationNode> getAnnotations(ClassNode type)
    Returns annotations of a specific type attached to this class. Triggers lazy initialization of superclass and interface metadata.
    Overrides:
      `getAnnotations` in class `ClassNode`
    Parameters:
      `type` - the annotation type to filter by
    Returns:
      list of matching `AnnotationNode` objects

  - ### getGenericsTypes

    public GenericsType[] getGenericsTypes()
    Returns the generic type parameters of this class. Triggers lazy initialization of generic type information from the class signature.
    Overrides:
      `getGenericsTypes` in class `ClassNode`
    Returns:
      array of `GenericsType` objects, or empty array if not generic
    See Also:
      - `ClassNode.setGenericsTypes(GenericsType[])`
      - `ClassNode.isUsingGenerics()`

  - ### getInterfaces

    public ClassNode[] getInterfaces()
    Returns the interfaces implemented by this class. Triggers lazy initialization of interface metadata from the bytecode.
    Overrides:
      `getInterfaces` in class `ClassNode`
    Returns:
      array of `ClassNode` objects representing interfaces

  - ### getRecordComponents

    public List<RecordComponentNode> getRecordComponents()
    Returns the record components of this record class (Java 16+). Triggers lazy initialization of superclass and interface metadata.
    Overrides:
      `getRecordComponents` in class `ClassNode`
    Returns:
      list of `RecordComponentNode` objects, or empty list if not a record

  - ### getUnresolvedInterfaces

    public ClassNode[] getUnresolvedInterfaces(boolean useRedirect)
    Returns the unresolved (not yet redirected) interfaces implemented by this class. Triggers lazy initialization of interface metadata.
    Overrides:
      `getUnresolvedInterfaces` in class `ClassNode`
    Parameters:
      `useRedirect` - whether to follow class redirects (typically false for decompiled classes)
    Returns:
      array of `ClassNode` objects representing interfaces
    See Also:
      - `ClassNode.getUnresolvedInterfaces()`

  - ### getUnresolvedSuperClass

    public ClassNode getUnresolvedSuperClass(boolean useRedirect)
    Returns the unresolved (not yet redirected) superclass of this class. Triggers lazy initialization of superclass metadata.
    Overrides:
      `getUnresolvedSuperClass` in class `ClassNode`
    Parameters:
      `useRedirect` - whether to follow class redirects (typically false for decompiled classes)
    Returns:
      the `ClassNode` representing the superclass
    See Also:
      - `ClassNode.getUnresolvedSuperClass()`

  - ### isUsingGenerics

    public boolean isUsingGenerics()
    Indicates whether this class is using generic types. Triggers lazy initialization of generic type information.
    Overrides:
      `isUsingGenerics` in class `ClassNode`
    Returns:
      `true` if the class uses generic type parameters

  - ### getDeclaredConstructors

    public List<ConstructorNode> getDeclaredConstructors()
    Returns all constructors declared by this class. Triggers lazy initialization of class members.
    Overrides:
      `getDeclaredConstructors` in class `ClassNode`
    Returns:
      list of `ConstructorNode` objects

  - ### getDeclaredField

    public FieldNode getDeclaredField(String name)
    Returns a field declared by this class with the given name. Triggers lazy initialization of class members.
    Overrides:
      `getDeclaredField` in class `ClassNode`
    Parameters:
      `name` - the field name
    Returns:
      the `FieldNode` if found, or `null` if no such field exists
    See Also:
      - `ClassNode.getField(String)`
      - `ClassNode.getFields()`

  - ### getDeclaredMethods

    public List<MethodNode> getDeclaredMethods(String name)
    Returns all methods declared by this class with the given name. Triggers lazy initialization of class members.
    Overrides:
      `getDeclaredMethods` in class `ClassNode`
    Parameters:
      `name` - the method name
    Returns:
      list of `MethodNode` objects with the specified name
    See Also:
      - `ClassNode.getMethods(String)`
      - `ClassNode.getDeclaredMethod(String, Parameter[])`

  - ### getFields

    public List<FieldNode> getFields()
    Returns all fields declared by this class (excluding inherited fields). Triggers lazy initialization of class members.
    Overrides:
      `getFields` in class `ClassNode`
    Returns:
      list of `FieldNode` objects
    See Also:
      - `ClassNode.getDeclaredField(String)`
      - `FieldNode`

  - ### getMethods

    public List<MethodNode> getMethods()
    Returns all methods and constructors declared by this class (excluding inherited methods). Triggers lazy initialization of class members.
    Overrides:
      `getMethods` in class `ClassNode`
    Returns:
      list of `MethodNode` objects
    See Also:
      - `ClassNode.getDeclaredMethods(String)`
      - `ClassNode.getMethod(String, Parameter[])`
      - `MethodNode`