Skip to content

Commit

Permalink
HHH-18094 Bytebuddy enhancer: make better use of constants and memory
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanne committed May 10, 2024
1 parent fb7c9a4 commit b98ee83
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
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;
Expand All @@ -32,34 +31,28 @@
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;
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;
Expand All @@ -71,48 +64,10 @@
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;

/**
Expand Down Expand Up @@ -162,7 +117,7 @@ public byte[] enhance(String className, byte[] originalBytes) throws Enhancement
return byteBuddyState.rewrite( typePool, safeClassName, byteBuddy -> doEnhance(
() -> byteBuddy.ignore( isDefaultFinalizer() )
.redefine( typeDescription, typePool.asClassFileLocator() )
.annotateType( HIBERNATE_VERSION_ANNOTATION ),
.annotateType( constants.HIBERNATE_VERSION_ANNOTATION ),
typeDescription
) );
}
Expand Down Expand Up @@ -220,34 +175,34 @@ private DynamicType.Builder<?> doEnhance(Supplier<DynamicType.Builder<?>> builde
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
);

builder = addFieldWithGetterAndSetter(
builder,
boolean.class,
constants.TypeBooleanPrimitive,
EnhancerConstants.USE_TRACKER_FIELD_NAME,
EnhancerConstants.USE_TRACKER_GETTER_NAME,
EnhancerConstants.USE_TRACKER_SETTER_NAME
Expand All @@ -260,43 +215,43 @@ private DynamicType.Builder<?> doEnhance(Supplier<DynamicType.Builder<?>> builde

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;
Expand Down Expand Up @@ -358,16 +313,16 @@ private DynamicType.Builder<?> doEnhance(Supplier<DynamicType.Builder<?>> builde
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 );
}
Expand All @@ -387,21 +342,20 @@ else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
.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 );
Expand Down Expand Up @@ -474,7 +428,7 @@ private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> bui

builder = addFieldWithGetterAndSetter(
builder,
PersistentAttributeInterceptor.class,
constants.TypePersistentAttributeInterceptor,
EnhancerConstants.INTERCEPTOR_FIELD_NAME,
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
EnhancerConstants.INTERCEPTOR_SETTER_NAME
Expand All @@ -484,18 +438,18 @@ private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> bui
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 ) );
}
Expand Down
Loading

0 comments on commit b98ee83

Please sign in to comment.