Class DecompiledClassNode
- All Implemented Interfaces:
GroovydocHolder<AnnotatedNode>,NodeMetaDataHandler
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. SeesetName(String),setRedirect(ClassNode),setUsingGenerics(boolean),setGenericsPlaceHolder(boolean). - Always resolved:
isResolved()always returnstrue. 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 viagetTypeClass().
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.
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)AsmReferenceResolverClassStubLazyFieldNodeLazyMethodNodeLazyConstructorNodeClassSignatureParser.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, THISFields inherited from interface groovy.lang.groovydoc.GroovydocHolder
DOC_COMMENT -
Constructor Summary
ConstructorsConstructorDescriptionDecompiledClassNode(ClassStub classData, AsmReferenceResolver resolver) Creates aDecompiledClassNodefrom a class stub extracted from bytecode. -
Method Summary
Modifier and TypeMethodDescriptionReturns all annotations attached to this class.getAnnotations(ClassNode type) Returns annotations of a specific type attached to this class.longExtracts the compilation timestamp from static field metadata if present.Returns all constructors declared by this class.getDeclaredField(String name) Returns a field declared by this class with the given name.getDeclaredMethods(String name) Returns all methods declared by this class with the given name.Returns all fields declared by this class (excluding inherited fields).Returns the generic type parameters of this class.Returns the interfaces implemented by this class.Returns all methods and constructors declared by this class (excluding inherited methods).Returns the record components of this record class (Java 16+).Resolves the runtime JVMClassobject for this decompiled class node.getUnresolvedInterfaces(boolean useRedirect) Returns the unresolved (not yet redirected) interfaces implemented by this class.getUnresolvedSuperClass(boolean useRedirect) Returns the unresolved (not yet redirected) superclass of this class.booleanDetermines whether this class has generic type parameters by inspecting the bytecode signature.booleanReports that this class node is fully resolved from bytecode.booleanisSealed()Determines if this class is sealed, checking both Java sealed class annotations and Groovy@Sealedtransform annotations.booleanIndicates whether this class is using generic types.voidsetGenericsPlaceHolder(boolean b) Prevents marking decompiled class nodes as generic placeholders.Prevents renaming of decompiled class nodes, as they represent immutable bytecode definitions.voidsetRedirect(ClassNode cn) Prevents redirection of decompiled class nodes to alternate implementations.voidsetUsingGenerics(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, getOuterField, getPackage, 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, isStaticClass, isSyntheticPublic, makeArray, parametersEqual, positionStmtsAfterEnumInitStmts, redirect, removeConstructor, removeField, removeMethod, renameField, setAnnotated, setCompileUnit, setEnclosingMethod, setGenericsTypes, setInterfaces, setMixins, setModifiers, setModule, setPermittedSubclasses, setRecordComponents, setScript, setScriptBody, setStaticClass, setSuperClass, setSyntheticPublic, setUnresolvedSuperClass, toString, toString, tryFindPossibleMethod, visitContentsMethods inherited from class org.codehaus.groovy.ast.AnnotatedNode
addAnnotation, addAnnotation, addAnnotations, getDeclaringClass, getGroovydoc, getInstance, hasNoRealSourcePosition, isSynthetic, setDeclaringClass, setHasNoRealSourcePosition, setSyntheticMethods inherited from class org.codehaus.groovy.ast.ASTNode
copyNodeMetaData, getColumnNumber, getLastColumnNumber, getLastLineNumber, getLineNumber, getMetaDataMap, setColumnNumber, setLastColumnNumber, setLastLineNumber, setLineNumber, setMetaDataMap, setSourcePosition, visitMethods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, waitMethods inherited from interface org.codehaus.groovy.ast.NodeMetaDataHandler
copyNodeMetaData, getNodeMetaData, getNodeMetaData, getNodeMetaData, newMetaDataMap, putNodeMetaData, removeNodeMetaData, setNodeMetaData
-
Constructor Details
-
DecompiledClassNode
Creates aDecompiledClassNodefrom a class stub extracted from bytecode.- Parameters:
classData- theClassStubcontaining bytecode-derived metadataresolver- theAsmReferenceResolverused 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_VALUEif not available - See Also:
-
getTypeClass
Resolves the runtime JVMClassobject for this decompiled class node.- Overrides:
getTypeClassin classClassNode- Returns:
- the loaded
Classinstance 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:
trueif 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:
isResolvedin classClassNode- Returns:
- always
truefor decompiled classes
-
isSealed
public boolean isSealed()Determines if this class is sealed, checking both Java sealed class annotations and Groovy@Sealedtransform annotations. -
setName
Prevents renaming of decompiled class nodes, as they represent immutable bytecode definitions.- Overrides:
setNamein classClassNode- Parameters:
name- ignored- Returns:
- never returns normally
- Throws:
UnsupportedOperationException- always, as bytecode classes are immutable
-
setRedirect
Prevents redirection of decompiled class nodes to alternate implementations.- Overrides:
setRedirectin classClassNode- 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:
setUsingGenericsin classClassNode- 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:
setGenericsPlaceHolderin classClassNode- Parameters:
b- ignored- Throws:
UnsupportedOperationException- always, as bytecode classes are immutable
-
getAnnotations
Returns all annotations attached to this class. Triggers lazy initialization of superclass and interface metadata.- Overrides:
getAnnotationsin classClassNode- Returns:
- list of
AnnotationNodeobjects
-
getAnnotations
Returns annotations of a specific type attached to this class. Triggers lazy initialization of superclass and interface metadata.- Overrides:
getAnnotationsin classClassNode- Parameters:
type- the annotation type to filter by- Returns:
- list of matching
AnnotationNodeobjects
-
getGenericsTypes
Returns the generic type parameters of this class. Triggers lazy initialization of generic type information from the class signature.- Overrides:
getGenericsTypesin classClassNode- Returns:
- array of
GenericsTypeobjects, or empty array if not generic - See Also:
-
getInterfaces
Returns the interfaces implemented by this class. Triggers lazy initialization of interface metadata from the bytecode.- Overrides:
getInterfacesin classClassNode- Returns:
- array of
ClassNodeobjects representing interfaces
-
getRecordComponents
Returns the record components of this record class (Java 16+). Triggers lazy initialization of superclass and interface metadata.- Overrides:
getRecordComponentsin classClassNode- Returns:
- list of
RecordComponentNodeobjects, or empty list if not a record
-
getUnresolvedInterfaces
Returns the unresolved (not yet redirected) interfaces implemented by this class. Triggers lazy initialization of interface metadata.- Overrides:
getUnresolvedInterfacesin classClassNode- Parameters:
useRedirect- whether to follow class redirects (typically false for decompiled classes)- Returns:
- array of
ClassNodeobjects representing interfaces - See Also:
-
getUnresolvedSuperClass
Returns the unresolved (not yet redirected) superclass of this class. Triggers lazy initialization of superclass metadata.- Overrides:
getUnresolvedSuperClassin classClassNode- Parameters:
useRedirect- whether to follow class redirects (typically false for decompiled classes)- Returns:
- the
ClassNoderepresenting the superclass - See Also:
-
isUsingGenerics
public boolean isUsingGenerics()Indicates whether this class is using generic types. Triggers lazy initialization of generic type information.- Overrides:
isUsingGenericsin classClassNode- Returns:
trueif the class uses generic type parameters
-
getDeclaredConstructors
Returns all constructors declared by this class. Triggers lazy initialization of class members.- Overrides:
getDeclaredConstructorsin classClassNode- Returns:
- list of
ConstructorNodeobjects
-
getDeclaredField
Returns a field declared by this class with the given name. Triggers lazy initialization of class members.- Overrides:
getDeclaredFieldin classClassNode- Parameters:
name- the field name- Returns:
- the
FieldNodeif found, ornullif no such field exists - See Also:
-
getDeclaredMethods
Returns all methods declared by this class with the given name. Triggers lazy initialization of class members.- Overrides:
getDeclaredMethodsin classClassNode- Parameters:
name- the method name- Returns:
- list of
MethodNodeobjects with the specified name - See Also:
-
getFields
Returns all fields declared by this class (excluding inherited fields). Triggers lazy initialization of class members. -
getMethods
Returns all methods and constructors declared by this class (excluding inherited methods). Triggers lazy initialization of class members.- Overrides:
getMethodsin classClassNode- Returns:
- list of
MethodNodeobjects - See Also:
-