Skip to content

Commit 3d12250

Browse files
committed
HHH-14694 Use stable instantiator and access optimizer names to reduce generated classes
1 parent 8d9b4c3 commit 3d12250

File tree

2 files changed

+105
-53
lines changed

2 files changed

+105
-53
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
@@ -169,18 +169,9 @@ Class<?> loadBasicProxy(Class<?> referenceClass, String proxyClassName,
169169
* @return The loaded generated class.
170170
*/
171171
public Class<?> load(Class<?> referenceClass, Function<ByteBuddy, DynamicType.Builder<?>> makeClassFunction) {
172-
Unloaded<?> result =
173-
make( makeClassFunction.apply( byteBuddy ) );
174-
if (DEBUG) {
175-
try {
176-
result.saveIn( new File( System.getProperty( "java.io.tmpdir" ) + "/bytebuddy/" ) );
177-
}
178-
catch (IOException e) {
179-
LOG.warn( "Unable to save generated class %1$s", result.getTypeDescription().getName(), e );
180-
}
181-
}
182-
return result.load( referenceClass.getClassLoader(), resolveClassLoadingStrategy( referenceClass ) )
183-
.getLoaded();
172+
return make( makeClassFunction.apply( byteBuddy ) )
173+
.load( referenceClass.getClassLoader(), resolveClassLoadingStrategy( referenceClass ) )
174+
.getLoaded();
184175
}
185176

186177
/**
@@ -228,15 +219,23 @@ void clearState() {
228219
basicProxyCache.clear();
229220
}
230221

231-
private Class<?> load(Class<?> referenceClass, String proxyClassName, BiFunction<ByteBuddy, NamingStrategy, DynamicType.Builder<?>> makeProxyFunction) {
222+
/**
223+
* Load a class generated by ByteBuddy.
224+
*
225+
* @param referenceClass The main class for which to create a class - might be an interface.
226+
* @param className The name under which the class shall be created.
227+
* @param makeClassFunction A function building the class.
228+
* @return The loaded generated class.
229+
*/
230+
public Class<?> load(Class<?> referenceClass, String className, BiFunction<ByteBuddy, NamingStrategy, DynamicType.Builder<?>> makeClassFunction) {
232231
try {
233-
return referenceClass.getClassLoader().loadClass( proxyClassName );
232+
return referenceClass.getClassLoader().loadClass( className );
234233
}
235234
catch (ClassNotFoundException e) {
236235
// Ignore
237236
}
238237
try {
239-
return make( makeProxyFunction.apply( byteBuddy, new FixedNamingStrategy( proxyClassName ) ) )
238+
return make( makeClassFunction.apply( byteBuddy, new FixedNamingStrategy( className ) ) )
240239
.load(
241240
referenceClass.getClassLoader(),
242241
resolveClassLoadingStrategy( referenceClass )
@@ -245,10 +244,10 @@ private Class<?> load(Class<?> referenceClass, String proxyClassName, BiFunction
245244
}
246245
catch (LinkageError e) {
247246
try {
248-
return referenceClass.getClassLoader().loadClass( proxyClassName );
247+
return referenceClass.getClassLoader().loadClass( className );
249248
}
250249
catch (ClassNotFoundException ex) {
251-
throw new RuntimeException( "Couldn't load or define class [" + proxyClassName + "]", e );
250+
throw new RuntimeException( "Couldn't load or define class [" + className + "]", e );
252251
}
253252
}
254253
}

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

Lines changed: 89 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@
6868
import net.bytebuddy.jar.asm.Type;
6969
import net.bytebuddy.matcher.ElementMatcher;
7070
import net.bytebuddy.matcher.ElementMatchers;
71-
import net.bytebuddy.pool.TypePool;
7271
import org.checkerframework.checker.nullness.qual.Nullable;
7372

7473
public class BytecodeProviderImpl implements BytecodeProvider {
@@ -149,11 +148,9 @@ public ReflectionOptimizer getReflectionOptimizer(
149148
fastClass = null;
150149
}
151150
else {
152-
fastClass = byteBuddyState.load( clazz, byteBuddy -> byteBuddy
153-
.with( new NamingStrategy.SuffixingRandom(
154-
INSTANTIATOR_PROXY_NAMING_SUFFIX,
155-
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() )
156-
) )
151+
final String className = clazz.getName() + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX;
152+
fastClass = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
153+
.with( namingStrategy )
157154
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
158155
.method( newInstanceMethodName )
159156
.intercept( MethodCall.construct( constructor ) )
@@ -213,11 +210,9 @@ public ReflectionOptimizer getReflectionOptimizer(
213210
fastClass = null;
214211
}
215212
else {
216-
fastClass = byteBuddyState.load( clazz, byteBuddy -> byteBuddy
217-
.with( new NamingStrategy.SuffixingRandom(
218-
INSTANTIATOR_PROXY_NAMING_SUFFIX,
219-
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() )
220-
) )
213+
final String className = clazz.getName() + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX;
214+
fastClass = byteBuddyState.load( clazz, className, (byteBuddy, namingStrategy) -> byteBuddy
215+
.with( namingStrategy )
221216
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
222217
.method( newInstanceMethodName )
223218
.intercept( MethodCall.construct( constructor ) )
@@ -238,23 +233,41 @@ public ReflectionOptimizer getReflectionOptimizer(
238233
return null;
239234
}
240235

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

259272
try {
260273
return new ReflectionOptimizerImpl(
@@ -269,6 +282,7 @@ public ReflectionOptimizer getReflectionOptimizer(
269282

270283
private static class ForeignPackageClassInfo {
271284
final Class<?> clazz;
285+
final List<String> propertyNames = new ArrayList<>();
272286
final List<Member> getters = new ArrayList<>();
273287
final List<Member> setters = new ArrayList<>();
274288

@@ -277,7 +291,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
277291
}
278292
}
279293

280-
private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] getters, Member[] setters) {
294+
private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, String[] propertyNames, Member[] getters, Member[] setters) {
281295
if ( clazz.isInterface() ) {
282296
return Object.class;
283297
}
@@ -291,11 +305,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
291305
for ( int i = 0; i < getters.length; i++ ) {
292306
final Member getter = getters[i];
293307
final Member setter = setters[i];
308+
boolean found = false;
294309
if ( getter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( getter.getModifiers() ) ) {
295310
foreignPackageClassInfo.getters.add( getter );
311+
found = true;
296312
}
297313
if ( setter.getDeclaringClass() == foreignPackageClassInfo.clazz && !Modifier.isPublic( setter.getModifiers() ) ) {
298314
foreignPackageClassInfo.setters.add( setter );
315+
found = true;
316+
}
317+
if ( found ) {
318+
foreignPackageClassInfo.propertyNames.add( propertyNames[i] );
299319
}
300320
}
301321
if ( foreignPackageClassInfo.getters.isEmpty() && foreignPackageClassInfo.setters.isEmpty() ) {
@@ -307,16 +327,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
307327
for ( int i = foreignPackageClassInfos.size() - 1; i >= 0; i-- ) {
308328
final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos.get( i );
309329
final Class<?> newSuperClass = superClass;
330+
331+
final String className = foreignPackageClassInfo.clazz.getName() + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName( foreignPackageClassInfo.propertyNames, foreignPackageClassInfo.getters, foreignPackageClassInfo.setters );
310332
superClass = byteBuddyState.load(
311333
foreignPackageClassInfo.clazz,
312-
byteBuddy -> {
313-
DynamicType.Builder<?> builder = byteBuddy.with(
314-
new NamingStrategy.SuffixingRandom(
315-
OPTIMIZER_PROXY_NAMING_SUFFIX,
316-
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue(
317-
foreignPackageClassInfo.clazz.getName() )
318-
)
319-
).subclass( newSuperClass );
334+
className,
335+
(byteBuddy, namingStrategy) -> {
336+
DynamicType.Builder<?> builder = byteBuddy.with( namingStrategy ).subclass( newSuperClass );
320337
for ( Member getter : foreignPackageClassInfo.getters ) {
321338
final Class<?> getterType;
322339
if ( getter instanceof Field ) {
@@ -386,6 +403,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
386403
return superClass;
387404
}
388405

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

391444
private final Class<?> foreignPackageAccessor;

0 commit comments

Comments
 (0)