Skip to content

Commit 297d9a6

Browse files
committed
HHH-14694 Use stable instantiator and access optimizer names to reduce generated classes
1 parent d600a30 commit 297d9a6

File tree

2 files changed

+106
-52
lines changed

2 files changed

+106
-52
lines changed

hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/ByteBuddyState.java

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,9 @@ Class<?> loadBasicProxy(Class<?> referenceClass, String proxyClassName,
154154
* @return The loaded generated class.
155155
*/
156156
public Class<?> load(Class<?> referenceClass, Function<ByteBuddy, DynamicType.Builder<?>> makeClassFunction) {
157-
Unloaded<?> result =
158-
make( makeClassFunction.apply( byteBuddy ) );
159-
if (DEBUG) {
160-
try {
161-
result.saveIn( new File( System.getProperty( "java.io.tmpdir" ) + "/bytebuddy/" ) );
162-
}
163-
catch (IOException e) {
164-
LOG.warn( "Unable to save generated class %1$s", result.getTypeDescription().getName(), e );
165-
}
166-
}
167-
return result.load( referenceClass.getClassLoader(), resolveClassLoadingStrategy( referenceClass ) )
168-
.getLoaded();
157+
return make( makeClassFunction.apply( byteBuddy ) )
158+
.load( referenceClass.getClassLoader(), resolveClassLoadingStrategy( referenceClass ) )
159+
.getLoaded();
169160
}
170161

171162
/**
@@ -213,15 +204,23 @@ void clearState() {
213204
basicProxyCache.clear();
214205
}
215206

216-
private Class<?> load(Class<?> referenceClass, String proxyClassName, BiFunction<ByteBuddy, NamingStrategy, DynamicType.Builder<?>> makeProxyFunction) {
207+
/**
208+
* Load a class generated by ByteBuddy.
209+
*
210+
* @param referenceClass The main class for which to create a class - might be an interface.
211+
* @param className The name under which the class shall be created.
212+
* @param makeClassFunction A function building the class.
213+
* @return The loaded generated class.
214+
*/
215+
public Class<?> load(Class<?> referenceClass, String className, BiFunction<ByteBuddy, NamingStrategy, DynamicType.Builder<?>> makeClassFunction) {
217216
try {
218-
return referenceClass.getClassLoader().loadClass( proxyClassName );
217+
return referenceClass.getClassLoader().loadClass( className );
219218
}
220219
catch (ClassNotFoundException e) {
221220
// Ignore
222221
}
223222
try {
224-
return make( makeProxyFunction.apply( byteBuddy, new FixedNamingStrategy( proxyClassName ) ) )
223+
return make( makeClassFunction.apply( byteBuddy, new FixedNamingStrategy( className ) ) )
225224
.load(
226225
referenceClass.getClassLoader(),
227226
resolveClassLoadingStrategy( referenceClass )
@@ -230,10 +229,10 @@ private Class<?> load(Class<?> referenceClass, String proxyClassName, BiFunction
230229
}
231230
catch (LinkageError e) {
232231
try {
233-
return referenceClass.getClassLoader().loadClass( proxyClassName );
232+
return referenceClass.getClassLoader().loadClass( className );
234233
}
235234
catch (ClassNotFoundException ex) {
236-
throw new RuntimeException( "Couldn't load or define class [" + proxyClassName + "]", e );
235+
throw new RuntimeException( "Couldn't load or define class [" + className + "]", e );
237236
}
238237
}
239238
}

hibernate-core/src/main/java/org/hibernate/bytecode/internal/bytebuddy/BytecodeProviderImpl.java

Lines changed: 90 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.lang.reflect.Member;
1111
import java.lang.reflect.Method;
1212
import java.lang.reflect.Modifier;
13+
import java.nio.charset.StandardCharsets;
1314
import java.util.ArrayList;
1415
import java.util.Arrays;
1516
import java.util.Collections;
@@ -146,11 +147,9 @@ public ReflectionOptimizer getReflectionOptimizer(
146147
fastClass = null;
147148
}
148149
else {
149-
fastClass = byteBuddyState.load( clazz, byteBuddy -> byteBuddy
150-
.with( new NamingStrategy.SuffixingRandom(
151-
INSTANTIATOR_PROXY_NAMING_SUFFIX,
152-
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() )
153-
) )
150+
final String className = clazz.getName() + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX;
151+
fastClass = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
152+
.with( namingStrategy )
154153
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
155154
.method( newInstanceMethodName )
156155
.intercept( MethodCall.construct( constructor ) )
@@ -210,11 +209,9 @@ public ReflectionOptimizer getReflectionOptimizer(
210209
fastClass = null;
211210
}
212211
else {
213-
fastClass = byteBuddyState.load( clazz, byteBuddy -> byteBuddy
214-
.with( new NamingStrategy.SuffixingRandom(
215-
INSTANTIATOR_PROXY_NAMING_SUFFIX,
216-
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() )
217-
) )
212+
final String className = clazz.getName() + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX;
213+
fastClass = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
214+
.with( namingStrategy )
218215
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
219216
.method( newInstanceMethodName )
220217
.intercept( MethodCall.construct( constructor ) )
@@ -235,23 +232,41 @@ public ReflectionOptimizer getReflectionOptimizer(
235232
return null;
236233
}
237234

238-
Class<?> superClass = determineAccessOptimizerSuperClass( clazz, getters, setters );
239-
240235
final String[] propertyNames = propertyAccessMap.keySet().toArray( new String[0] );
241-
final Class<?> bulkAccessor = byteBuddyState.load( clazz, byteBuddy -> byteBuddy
242-
.with( new NamingStrategy.SuffixingRandom(
243-
OPTIMIZER_PROXY_NAMING_SUFFIX,
244-
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() )
245-
) )
246-
.subclass( superClass )
247-
.implement( ReflectionOptimizer.AccessOptimizer.class )
248-
.method( getPropertyValuesMethodName )
249-
.intercept( new Implementation.Simple( new GetPropertyValues( clazz, propertyNames, getters ) ) )
250-
.method( setPropertyValuesMethodName )
251-
.intercept( new Implementation.Simple( new SetPropertyValues( clazz, propertyNames, setters ) ) )
252-
.method( getPropertyNamesMethodName )
253-
.intercept( MethodCall.call( new CloningPropertyCall( propertyNames ) ) )
254-
);
236+
final Class<?> superClass = determineAccessOptimizerSuperClass( clazz, propertyNames, getters, setters );
237+
238+
final String className = clazz.getName() + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName( propertyNames, getters, setters );
239+
final Class<?> bulkAccessor;
240+
if ( className.getBytes( StandardCharsets.UTF_8 ).length >= 0x10000 ) {
241+
// The JVM has a 64K byte limit on class name length, so fallback to random name if encoding exceeds that
242+
bulkAccessor = byteBuddyState.load( clazz, byteBuddy -> byteBuddy
243+
.with( new NamingStrategy.SuffixingRandom(
244+
OPTIMIZER_PROXY_NAMING_SUFFIX,
245+
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() )
246+
) )
247+
.subclass( superClass )
248+
.implement( ReflectionOptimizer.AccessOptimizer.class )
249+
.method( getPropertyValuesMethodName )
250+
.intercept( new Implementation.Simple( new GetPropertyValues( clazz, propertyNames, getters ) ) )
251+
.method( setPropertyValuesMethodName )
252+
.intercept( new Implementation.Simple( new SetPropertyValues( clazz, propertyNames, setters ) ) )
253+
.method( getPropertyNamesMethodName )
254+
.intercept( MethodCall.call( new CloningPropertyCall( propertyNames ) ) )
255+
);
256+
}
257+
else {
258+
bulkAccessor = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
259+
.with( namingStrategy )
260+
.subclass( superClass )
261+
.implement( ReflectionOptimizer.AccessOptimizer.class )
262+
.method( getPropertyValuesMethodName )
263+
.intercept( new Implementation.Simple( new GetPropertyValues( clazz, propertyNames, getters ) ) )
264+
.method( setPropertyValuesMethodName )
265+
.intercept( new Implementation.Simple( new SetPropertyValues( clazz, propertyNames, setters ) ) )
266+
.method( getPropertyNamesMethodName )
267+
.intercept( MethodCall.call( new CloningPropertyCall( propertyNames ) ) )
268+
);
269+
}
255270

256271
try {
257272
return new ReflectionOptimizerImpl(
@@ -266,6 +281,7 @@ public ReflectionOptimizer getReflectionOptimizer(
266281

267282
private static class ForeignPackageClassInfo {
268283
final Class<?> clazz;
284+
final List<String> propertyNames = new ArrayList<>();
269285
final List<Member> getters = new ArrayList<>();
270286
final List<Member> setters = new ArrayList<>();
271287

@@ -274,7 +290,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
274290
}
275291
}
276292

277-
private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] getters, Member[] setters) {
293+
private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, String[] propertyNames, Member[] getters, Member[] setters) {
278294
if ( clazz.isInterface() ) {
279295
return Object.class;
280296
}
@@ -288,11 +304,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
288304
for ( int i = 0; i < getters.length; i++ ) {
289305
final Member getter = getters[i];
290306
final Member setter = setters[i];
307+
boolean found = false;
291308
if ( getter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( getter.getModifiers() ) ) {
292309
foreignPackageClassInfo.getters.add( getter );
310+
found = true;
293311
}
294312
if ( setter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( setter.getModifiers() ) ) {
295313
foreignPackageClassInfo.setters.add( setter );
314+
found = true;
315+
}
316+
if ( found ) {
317+
foreignPackageClassInfo.propertyNames.add( propertyNames[i] );
296318
}
297319
}
298320
if ( foreignPackageClassInfo.getters.isEmpty() && foreignPackageClassInfo.setters.isEmpty() ) {
@@ -304,16 +326,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
304326
for ( int i = foreignPackageClassInfos.size() - 1; i >= 0; i-- ) {
305327
final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos.get( i );
306328
final Class<?> newSuperClass = superClass;
329+
330+
final String className = foreignPackageClassInfo.clazz.getName() + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName( foreignPackageClassInfo.propertyNames, foreignPackageClassInfo.getters, foreignPackageClassInfo.setters );
307331
superClass = byteBuddyState.load(
308332
foreignPackageClassInfo.clazz,
309-
byteBuddy -> {
310-
DynamicType.Builder<?> builder = byteBuddy.with(
311-
new NamingStrategy.SuffixingRandom(
312-
OPTIMIZER_PROXY_NAMING_SUFFIX,
313-
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue(
314-
foreignPackageClassInfo.clazz.getName() )
315-
)
316-
).subclass( newSuperClass );
333+
className,
334+
(byteBuddy, namingStrategy) -> {
335+
DynamicType.Builder<?> builder = byteBuddy.with( namingStrategy ).subclass( newSuperClass );
317336
for ( Member getter : foreignPackageClassInfo.getters ) {
318337
final Class<?> getterType;
319338
if ( getter instanceof Field ) {
@@ -383,6 +402,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
383402
return superClass;
384403
}
385404

405+
private static String encodeName(String[] propertyNames, Member[] getters, Member[] setters) {
406+
return encodeName( Arrays.asList( propertyNames ), Arrays.asList( getters ), Arrays.asList( setters ) );
407+
}
408+
409+
private static String encodeName(List<String> propertyNames, List<Member> getters, List<Member> setters) {
410+
final StringBuilder sb = new StringBuilder();
411+
for ( int i = 0; i < propertyNames.size(); i++ ) {
412+
final String propertyName = propertyNames.get( i );
413+
final Member getter = getters.get( i );
414+
final Member setter = setters.get( i );
415+
// Encode the two member types as 4 bit integer encoded as hex character
416+
sb.append( Integer.toHexString( getKind( getter ) << 2 | getKind( setter ) ) );
417+
sb.append( propertyName );
418+
}
419+
return sb.toString();
420+
}
421+
422+
private static int getKind(Member member) {
423+
// Encode the member type as 2 bit integer
424+
if ( member == EMBEDDED_MEMBER ) {
425+
return 0;
426+
}
427+
else if ( member instanceof Field ) {
428+
return 1;
429+
}
430+
else if ( member instanceof Method ) {
431+
return 2;
432+
}
433+
else if ( member instanceof ForeignPackageMember ) {
434+
return 3;
435+
}
436+
else {
437+
throw new IllegalArgumentException( "Unknown member type: " + member );
438+
}
439+
}
440+
386441
private static class ForeignPackageMember implements Member {
387442

388443
private final Class<?> foreignPackageAccessor;

0 commit comments

Comments
 (0)