Skip to content

Commit bed64c8

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

File tree

2 files changed

+105
-52
lines changed

2 files changed

+105
-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: 89 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,9 @@ public ReflectionOptimizer getReflectionOptimizer(
146146
fastClass = null;
147147
}
148148
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-
) )
149+
final String className = clazz.getName() + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX;
150+
fastClass = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
151+
.with( namingStrategy )
154152
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
155153
.method( newInstanceMethodName )
156154
.intercept( MethodCall.construct( constructor ) )
@@ -210,11 +208,9 @@ public ReflectionOptimizer getReflectionOptimizer(
210208
fastClass = null;
211209
}
212210
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-
) )
211+
final String className = clazz.getName() + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX;
212+
fastClass = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
213+
.with( namingStrategy )
218214
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
219215
.method( newInstanceMethodName )
220216
.intercept( MethodCall.construct( constructor ) )
@@ -235,23 +231,41 @@ public ReflectionOptimizer getReflectionOptimizer(
235231
return null;
236232
}
237233

238-
Class<?> superClass = determineAccessOptimizerSuperClass( clazz, getters, setters );
239-
240234
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-
);
235+
final Class<?> superClass = determineAccessOptimizerSuperClass( clazz, propertyNames, getters, setters );
236+
237+
final String className = clazz.getName() + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName( propertyNames, getters, setters );
238+
final Class<?> bulkAccessor;
239+
if ( className.length() >= 0x10000 ) {
240+
// The JVM has a 16K limit on the class name length, so fallback to random name if the encoding exceeds that
241+
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+
);
255+
}
256+
else {
257+
bulkAccessor = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
258+
.with( namingStrategy )
259+
.subclass( superClass )
260+
.implement( ReflectionOptimizer.AccessOptimizer.class )
261+
.method( getPropertyValuesMethodName )
262+
.intercept( new Implementation.Simple( new GetPropertyValues( clazz, propertyNames, getters ) ) )
263+
.method( setPropertyValuesMethodName )
264+
.intercept( new Implementation.Simple( new SetPropertyValues( clazz, propertyNames, setters ) ) )
265+
.method( getPropertyNamesMethodName )
266+
.intercept( MethodCall.call( new CloningPropertyCall( propertyNames ) ) )
267+
);
268+
}
255269

256270
try {
257271
return new ReflectionOptimizerImpl(
@@ -266,6 +280,7 @@ public ReflectionOptimizer getReflectionOptimizer(
266280

267281
private static class ForeignPackageClassInfo {
268282
final Class<?> clazz;
283+
final List<String> propertyNames = new ArrayList<>();
269284
final List<Member> getters = new ArrayList<>();
270285
final List<Member> setters = new ArrayList<>();
271286

@@ -274,7 +289,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
274289
}
275290
}
276291

277-
private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] getters, Member[] setters) {
292+
private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, String[] propertyNames, Member[] getters, Member[] setters) {
278293
if ( clazz.isInterface() ) {
279294
return Object.class;
280295
}
@@ -288,11 +303,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
288303
for ( int i = 0; i < getters.length; i++ ) {
289304
final Member getter = getters[i];
290305
final Member setter = setters[i];
306+
boolean found = false;
291307
if ( getter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( getter.getModifiers() ) ) {
292308
foreignPackageClassInfo.getters.add( getter );
309+
found = true;
293310
}
294311
if ( setter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( setter.getModifiers() ) ) {
295312
foreignPackageClassInfo.setters.add( setter );
313+
found = true;
314+
}
315+
if ( found ) {
316+
foreignPackageClassInfo.propertyNames.add( propertyNames[i] );
296317
}
297318
}
298319
if ( foreignPackageClassInfo.getters.isEmpty() && foreignPackageClassInfo.setters.isEmpty() ) {
@@ -304,16 +325,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
304325
for ( int i = foreignPackageClassInfos.size() - 1; i >= 0; i-- ) {
305326
final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos.get( i );
306327
final Class<?> newSuperClass = superClass;
328+
329+
final String className = foreignPackageClassInfo.clazz.getName() + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName( foreignPackageClassInfo.propertyNames, foreignPackageClassInfo.getters, foreignPackageClassInfo.setters );
307330
superClass = byteBuddyState.load(
308331
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 );
332+
className,
333+
(byteBuddy, namingStrategy) -> {
334+
DynamicType.Builder<?> builder = byteBuddy.with( namingStrategy ).subclass( newSuperClass );
317335
for ( Member getter : foreignPackageClassInfo.getters ) {
318336
final Class<?> getterType;
319337
if ( getter instanceof Field ) {
@@ -383,6 +401,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
383401
return superClass;
384402
}
385403

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

388442
private final Class<?> foreignPackageAccessor;

0 commit comments

Comments
 (0)