Class 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:

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)
  • Constructor Details

  • 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:
    • 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:
    • 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:
    • 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:
    • 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:
    • 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:
    • 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:
    • 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:
    • 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: