HHH-18094 Bytebuddy enhancer: make better use of constants and memory

This commit is contained in:
Sanne Grinovero 2024-05-09 08:21:13 +01:00 committed by Sanne Grinovero
parent e41071a072
commit 08028be1e9
3 changed files with 93 additions and 91 deletions

View File

@ -21,7 +21,6 @@ import org.hibernate.Version;
import org.hibernate.bytecode.enhance.VersionMismatchException;
import org.hibernate.bytecode.enhance.internal.tracker.CompositeOwnerTracker;
import org.hibernate.bytecode.enhance.internal.tracker.DirtyTracker;
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.EnhancementException;
import org.hibernate.bytecode.enhance.spi.EnhancementInfo;
@ -32,21 +31,18 @@ import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterc
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState;
import org.hibernate.engine.spi.CompositeOwner;
import org.hibernate.engine.spi.CompositeTracker;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ExtendedSelfDirtinessTracker;
import org.hibernate.engine.spi.Managed;
import org.hibernate.engine.spi.ManagedComposite;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.ManagedMappedSuperclass;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Transient;
import jakarta.persistence.metamodel.Type;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.annotation.AnnotationDescription;
@ -54,12 +50,9 @@ import net.bytebuddy.description.annotation.AnnotationList;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldDescription.InDefinedShape;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.modifier.FieldPersistence;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeDescription.Generic;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.FixedValue;
@ -71,48 +64,10 @@ import static net.bytebuddy.matcher.ElementMatchers.isDefaultFinalizer;
public class EnhancerImpl implements Enhancer {
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
private static final Annotation HIBERNATE_VERSION_ANNOTATION;
static {
HIBERNATE_VERSION_ANNOTATION = new EnhancementInfo() {
@Override
public String version() {
return Version.getVersionString();
}
@Override
public Class<? extends Annotation> annotationType() {
return EnhancementInfo.class;
}
};
}
private static final AnnotationDescription TRANSIENT_ANNOTATION = AnnotationDescription.Builder.ofType( Transient.class ).build();
protected final ByteBuddyEnhancementContext enhancementContext;
private final ByteBuddyState byteBuddyState;
private final EnhancerClassLocator typePool;
/**
* Extract the following constants so that enhancement on large projects
* can be done efficiently: otherwise each instance use will trigger a
* resource load on the ClassLoader tree, triggering allocation of
* several streams to unzip each JAR file each time.
*/
private final ClassFileLocator adviceLocator = ClassFileLocator.ForClassLoader.of(CodeTemplates.class.getClassLoader());
private final Implementation implementationTrackChange = Advice.to( CodeTemplates.TrackChange.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationGetDirtyAttributesWithoutCollections = Advice.to( CodeTemplates.GetDirtyAttributesWithoutCollections.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationAreFieldsDirtyWithoutCollections = Advice.to( CodeTemplates.AreFieldsDirtyWithoutCollections.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationClearDirtyAttributesWithoutCollections = Advice.to( CodeTemplates.ClearDirtyAttributesWithoutCollections.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationSuspendDirtyTracking = Advice.to( CodeTemplates.SuspendDirtyTracking.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationGetDirtyAttributes = Advice.to( CodeTemplates.GetDirtyAttributes.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationAreFieldsDirty = Advice.to( CodeTemplates.AreFieldsDirty.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationGetCollectionTrackerWithoutCollections = Advice.to( CodeTemplates.GetCollectionTrackerWithoutCollections.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationClearDirtyAttributes = Advice.to( CodeTemplates.ClearDirtyAttributes.class, adviceLocator ).wrap( StubMethod.INSTANCE );
//In this case we just extract the Advice:
private final Advice adviceInitializeLazyAttributeLoadingInterceptor = Advice.to( CodeTemplates.InitializeLazyAttributeLoadingInterceptor.class, adviceLocator );
private final Implementation implementationSetOwner = Advice.to( CodeTemplates.SetOwner.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final Implementation implementationClearOwner = Advice.to( CodeTemplates.ClearOwner.class, adviceLocator ).wrap( StubMethod.INSTANCE );
private final EnhancerImplConstants constants;
/**
@ -162,7 +117,7 @@ public class EnhancerImpl implements Enhancer {
return byteBuddyState.rewrite( typePool, safeClassName, byteBuddy -> doEnhance(
() -> byteBuddy.ignore( isDefaultFinalizer() )
.redefine( typeDescription, typePool.asClassFileLocator() )
.annotateType( HIBERNATE_VERSION_ANNOTATION ),
.annotateType( constants.HIBERNATE_VERSION_ANNOTATION ),
typeDescription
) );
}
@ -220,26 +175,26 @@ public class EnhancerImpl implements Enhancer {
log.debugf( "Enhancing [%s] as Entity", managedCtClass.getName() );
DynamicType.Builder<?> builder = builderSupplier.get();
builder = builder.implement( ManagedEntity.class )
.defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, Object.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, constants.TypeObject, constants.methodModifierPUBLIC )
.intercept( FixedValue.self() );
builder = addFieldWithGetterAndSetter(
builder,
EntityEntry.class,
constants.TypeEntityEntry,
EnhancerConstants.ENTITY_ENTRY_FIELD_NAME,
EnhancerConstants.ENTITY_ENTRY_GETTER_NAME,
EnhancerConstants.ENTITY_ENTRY_SETTER_NAME
);
builder = addFieldWithGetterAndSetter(
builder,
ManagedEntity.class,
constants.TypeManagedEntity,
EnhancerConstants.PREVIOUS_FIELD_NAME,
EnhancerConstants.PREVIOUS_GETTER_NAME,
EnhancerConstants.PREVIOUS_SETTER_NAME
);
builder = addFieldWithGetterAndSetter(
builder,
ManagedEntity.class,
constants.TypeManagedEntity,
EnhancerConstants.NEXT_FIELD_NAME,
EnhancerConstants.NEXT_GETTER_NAME,
EnhancerConstants.NEXT_SETTER_NAME
@ -247,7 +202,7 @@ public class EnhancerImpl implements Enhancer {
builder = addFieldWithGetterAndSetter(
builder,
boolean.class,
constants.TypeBooleanPrimitive,
EnhancerConstants.USE_TRACKER_FIELD_NAME,
EnhancerConstants.USE_TRACKER_GETTER_NAME,
EnhancerConstants.USE_TRACKER_SETTER_NAME
@ -260,43 +215,43 @@ public class EnhancerImpl implements Enhancer {
if ( collectionFields.isEmpty() ) {
builder = builder.implement( SelfDirtinessTracker.class )
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
.annotateField( TRANSIENT_ANNOTATION )
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, constants.fieldModifierPRIVATE_TRANSIENT )
.annotateField( constants.TRANSIENT_ANNOTATION )
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.withParameters( String.class )
.intercept( constants.implementationTrackChange )
.defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_GET_NAME, constants.Type_Array_String, constants.methodModifierPUBLIC )
.intercept( constants.implementationGetDirtyAttributesWithoutCollections )
.defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, constants.TypeBooleanPrimitive, constants.methodModifierPUBLIC )
.intercept( constants.implementationAreFieldsDirtyWithoutCollections )
.defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.intercept( constants.implementationClearDirtyAttributesWithoutCollections )
.defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC )
.withParameters( boolean.class )
.defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.withParameters( constants.TypeBooleanPrimitive )
.intercept( constants.implementationSuspendDirtyTracking )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, constants.TypeCollectionTracker, constants.methodModifierPUBLIC )
.intercept( constants.implementationGetCollectionTrackerWithoutCollections );
}
else {
//TODO es.enableInterfaceExtendedSelfDirtinessTracker ? Careful with consequences..
builder = builder.implement( ExtendedSelfDirtinessTracker.class )
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
.annotateField( TRANSIENT_ANNOTATION )
.defineField( EnhancerConstants.TRACKER_COLLECTION_NAME, CollectionTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
.annotateField( TRANSIENT_ANNOTATION )
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, constants.fieldModifierPRIVATE_TRANSIENT )
.annotateField( constants.TRANSIENT_ANNOTATION )
.defineField( EnhancerConstants.TRACKER_COLLECTION_NAME, constants.TypeCollectionTracker, constants.fieldModifierPRIVATE_TRANSIENT )
.annotateField( constants.TRANSIENT_ANNOTATION )
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.withParameters( String.class )
.intercept( constants.implementationTrackChange )
.defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_GET_NAME, constants.Type_Array_String, constants.methodModifierPUBLIC )
.intercept( constants.implementationGetDirtyAttributes )
.defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, constants.TypeBooleanPrimitive, constants.methodModifierPUBLIC )
.intercept( constants.implementationAreFieldsDirty )
.defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.intercept( constants.implementationClearDirtyAttributes )
.defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC )
.withParameters( boolean.class )
.defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.withParameters( constants.TypeBooleanPrimitive )
.intercept( constants.implementationSuspendDirtyTracking )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, constants.TypeCollectionTracker, constants.methodModifierPUBLIC )
.intercept( FieldAccessor.ofField( EnhancerConstants.TRACKER_COLLECTION_NAME ) );
Implementation isDirty = StubMethod.INSTANCE, getDirtyNames = StubMethod.INSTANCE, clearDirtyNames = StubMethod.INSTANCE;
@ -358,16 +313,16 @@ public class EnhancerImpl implements Enhancer {
clearDirtyNames = constants.adviceInitializeLazyAttributeLoadingInterceptor.wrap( clearDirtyNames );
}
builder = builder.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME, boolean.class, Visibility.PUBLIC )
builder = builder.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_NAME, constants.TypeBooleanPrimitive, constants.methodModifierPUBLIC )
.intercept( isDirty )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, void.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.withParameters( DirtyTracker.class )
.intercept( getDirtyNames )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME, void.class, Visibility.PUBLIC )
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.intercept( Advice.withCustomMapping()
.to( CodeTemplates.ClearDirtyCollectionNames.class, constants.adviceLocator )
.wrap( StubMethod.INSTANCE ) )
.defineMethod( ExtendedSelfDirtinessTracker.REMOVE_DIRTY_FIELDS_NAME, void.class, Visibility.PUBLIC )
.defineMethod( ExtendedSelfDirtinessTracker.REMOVE_DIRTY_FIELDS_NAME, constants.TypeVoid, constants.methodModifierPUBLIC )
.withParameters( LazyAttributeLoadingInterceptor.class )
.intercept( clearDirtyNames );
}
@ -387,21 +342,20 @@ public class EnhancerImpl implements Enhancer {
.defineField(
EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME,
CompositeOwnerTracker.class,
FieldPersistence.TRANSIENT,
Visibility.PRIVATE
constants.fieldModifierPRIVATE_TRANSIENT
)
.annotateField( TRANSIENT_ANNOTATION )
.annotateField( constants.TRANSIENT_ANNOTATION )
.defineMethod(
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
void.class,
Visibility.PUBLIC
constants.TypeVoid,
constants.methodModifierPUBLIC
)
.withParameters( String.class, CompositeOwner.class )
.intercept( constants.implementationSetOwner )
.defineMethod(
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER,
void.class,
Visibility.PUBLIC
constants.TypeVoid,
constants.methodModifierPUBLIC
)
.withParameters( String.class )
.intercept( constants.implementationClearOwner );
@ -474,7 +428,7 @@ public class EnhancerImpl implements Enhancer {
builder = addFieldWithGetterAndSetter(
builder,
PersistentAttributeInterceptor.class,
constants.TypePersistentAttributeInterceptor,
EnhancerConstants.INTERCEPTOR_FIELD_NAME,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
EnhancerConstants.INTERCEPTOR_SETTER_NAME
@ -484,18 +438,18 @@ public class EnhancerImpl implements Enhancer {
return builder;
}
private static DynamicType.Builder<?> addFieldWithGetterAndSetter(
private DynamicType.Builder<?> addFieldWithGetterAndSetter(
DynamicType.Builder<?> builder,
Class<?> type,
TypeDefinition type,
String fieldName,
String getterName,
String setterName) {
return builder
.defineField( fieldName, type, Visibility.PRIVATE, FieldPersistence.TRANSIENT )
.annotateField( TRANSIENT_ANNOTATION )
.defineMethod( getterName, type, Visibility.PUBLIC )
.defineField( fieldName, type, constants.fieldModifierPRIVATE_TRANSIENT )
.annotateField( constants.TRANSIENT_ANNOTATION )
.defineMethod( getterName, type, constants.methodModifierPUBLIC )
.intercept( FieldAccessor.ofField( fieldName ) )
.defineMethod( setterName, void.class, Visibility.PUBLIC )
.defineMethod( setterName, constants.TypeVoid, constants.methodModifierPUBLIC )
.withParameters( type )
.intercept( FieldAccessor.ofField( fieldName ) );
}

View File

@ -6,11 +6,29 @@
*/
package org.hibernate.bytecode.enhance.internal.bytebuddy;
import jakarta.persistence.Transient;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.modifier.FieldPersistence;
import net.bytebuddy.description.modifier.ModifierContributor;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.StubMethod;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.List;
import org.hibernate.Version;
import org.hibernate.bytecode.enhance.spi.CollectionTracker;
import org.hibernate.bytecode.enhance.spi.EnhancementInfo;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
/**
* Extracts constants used by EnhancerImpl.
* This allows integrators to choose reusing this state for multiple enhancement processes,
@ -19,6 +37,7 @@ import net.bytebuddy.implementation.StubMethod;
*/
public final class EnhancerImplConstants {
//This locator is used to load all "advice", and apparently won't benefit from any caches:
final ClassFileLocator adviceLocator;
final Implementation implementationTrackChange;
@ -35,6 +54,35 @@ public final class EnhancerImplConstants {
final Implementation implementationSetOwner;
final Implementation implementationClearOwner;
//Frequently used Modifiers:
final int methodModifierPUBLIC = ModifierContributor.Resolver.of( List.of( Visibility.PUBLIC ) ).resolve();
final int fieldModifierPRIVATE_TRANSIENT = ModifierContributor.Resolver.of( List.of( FieldPersistence.TRANSIENT, Visibility.PRIVATE ) ).resolve();
//Frequently used annotations, declared as collections as otherwise they get wrapped into them over and over again:
final Collection<? extends AnnotationDescription> TRANSIENT_ANNOTATION = List.of(
AnnotationDescription.Builder.ofType( Transient.class ).build() );
final List<Annotation> HIBERNATE_VERSION_ANNOTATION = List.of( new EnhancementInfo() {
@Override
public String version() {
return Version.getVersionString();
}
@Override
public Class<? extends Annotation> annotationType() {
return EnhancementInfo.class;
}
} );
//Frequently used Types for method signatures:
final TypeDefinition TypeVoid = TypeDescription.ForLoadedType.of( void.class );
final TypeDefinition TypeBooleanPrimitive = TypeDescription.ForLoadedType.of( boolean.class );
final TypeDefinition TypeManagedEntity = TypeDescription.ForLoadedType.of( ManagedEntity.class );
final TypeDefinition TypeEntityEntry = TypeDescription.ForLoadedType.of( EntityEntry.class );
final TypeDefinition TypePersistentAttributeInterceptor = TypeDescription.ForLoadedType.of( PersistentAttributeInterceptor.class );
final TypeDefinition TypeObject = TypeDescription.ForLoadedType.of( Object.class );
final TypeDefinition Type_Array_String = TypeDescription.ForLoadedType.of( String[].class );
final TypeDefinition TypeCollectionTracker = TypeDescription.ForLoadedType.of( CollectionTracker.class );
public EnhancerImplConstants() {
this.adviceLocator = ClassFileLocator.ForClassLoader.of( CodeTemplates.class.getClassLoader() );
this.implementationTrackChange = Advice.to( CodeTemplates.TrackChange.class, adviceLocator )

View File

@ -69,11 +69,11 @@ public final class ByteBuddyState {
private final ByteBuddy byteBuddy;
private static final ProxyDefinitionHelpers proxyDefinitionHelpers = new ProxyDefinitionHelpers();
private final ProxyDefinitionHelpers proxyDefinitionHelpers = new ProxyDefinitionHelpers();
private final ClassRewriter classRewriter;
final EnhancerImplConstants enhancerConstants = new EnhancerImplConstants();
private final EnhancerImplConstants enhancerConstants = new EnhancerImplConstants();
/**
* It will be easier to maintain the cache and its state when it will no longer be static