Skip to content

Commit

Permalink
Ensure classloader compatibility when generating dynamic subclass
Browse files Browse the repository at this point in the history
  • Loading branch information
jqno committed Dec 2, 2022
1 parent 6a6716f commit 91429f6
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Fix `ClassFormatError` when attempting to create a dynamic subclass for a class that has no package. ([Issue 638](https://github.com/jqno/equalsverifier/issues/638))
- Fix `LinkageError` when running Quarkus dev mode and testing an abstract class. ([Issue 550](https://github.com/jqno/equalsverifier/issues/550))

## [3.12] - 2022-11-30

Expand Down
Expand Up @@ -93,12 +93,16 @@ public static synchronized <S> Class<S> giveDynamicSubclass(
"$" +
nameSuffix;

Class<S> existsAlready = (Class<S>) classForName(name);
Class<?> context = isSystemClass ? Instantiator.class : superclass;
ClassLoader classLoader = context.getClassLoader();

// `mvn quarkus:dev` does strange classloader stuff. We need to make sure that we
// check existence with the correct classloader. I don't know how to unit test this.
Class<S> existsAlready = (Class<S>) classForName(classLoader, name);
if (existsAlready != null) {
return existsAlready;
}

Class<?> context = isSystemClass ? Instantiator.class : superclass;
ClassLoadingStrategy<ClassLoader> cs = getClassLoadingStrategy(context);
DynamicType.Builder<S> builder = new ByteBuddy()
.with(TypeValidation.DISABLED)
Expand All @@ -107,7 +111,7 @@ public static synchronized <S> Class<S> giveDynamicSubclass(

builder = modify.apply(builder);

return (Class<S>) builder.make().load(context.getClassLoader(), cs).getLoaded();
return (Class<S>) builder.make().load(classLoader, cs).getLoaded();
}

private static String getPackageName(Class<?> type) {
Expand Down
Expand Up @@ -27,6 +27,24 @@ public static <T> Class<T> classForName(String className) {
}
}

/**
* Helper method to resolve a Class of a given name through a given ClassLoader.
*
* @param classLoader The class loader to resolve the class against.
* @param className The fully qualified name of the class to resolve.
* @param <T> The type of the class to resolve.
* @return The corresponding class if it exists, null otherwise.
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> classForName(ClassLoader classLoader, String className) {
try {
return (Class<T>) classLoader.loadClass(className);
} catch (ClassNotFoundException | VerifyError e) {
// Catching VerifyError fixes issue #147. I don't know how to unit test it.
return null;
}
}

/**
* Helper method to create an array of Classes.
*
Expand Down
Expand Up @@ -29,6 +29,20 @@ public void forNameReturnsNull_whenTypeDoesntExist() {
assertNull(actual);
}

@Test
public void forNameWithClassLoaderReturnsClass_whenTypeExists() {
ClassLoader cl = getClass().getClassLoader();
Class<?> actual = Util.classForName(cl, "java.util.GregorianCalendar");
assertEquals(actual, GregorianCalendar.class);
}

@Test
public void forNameWithClassLoaderReturnsNull_whenTypeDoesntExist() {
ClassLoader cl = getClass().getClassLoader();
Class<?> actual = Util.classForName(cl, "this.type.does.not.exist");
assertNull(actual);
}

@Test
public void classesReturnsItsArguments() {
Class<?>[] expected = new Class<?>[] { String.class, Object.class };
Expand Down

0 comments on commit 91429f6

Please sign in to comment.