Skip to content

Commit

Permalink
Add method for lazy class file resolution.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Oct 31, 2024
1 parent 810fd97 commit 1ba91d0
Showing 1 changed file with 62 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.PackageDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
Expand Down Expand Up @@ -87,6 +88,15 @@ public interface ClassInjector {
*/
boolean isAlive();

/**
* Injects the given types into the represented class loader.
*
* @param names The names of the types to load via injection.
* @param classFileLocator The class file locator to use for resolving binary representations.
* @return The loaded types that were passed as arguments.
*/
Map<String, Class<?>> inject(Set<String> names, ClassFileLocator classFileLocator);

/**
* Injects the given types into the represented class loader.
*
Expand All @@ -101,7 +111,7 @@ public interface ClassInjector {
* @param types The types to load via injection.
* @return The loaded types that were passed as arguments.
*/
Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types);
Map<String, Class<?>> injectRaw(Map<String, byte[]> types);

/**
* An abstract base implementation of a class injector.
Expand All @@ -123,6 +133,13 @@ public Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte
}
return result;
}

/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<String, byte[]> types) {
return inject(types.keySet(), new ClassFileLocator.Simple(types));
}
}

/**
Expand Down Expand Up @@ -237,17 +254,17 @@ public boolean isAlive() {
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
public Map<String, Class<?>> inject(Set<String> names, ClassFileLocator classFileLocator) {
Dispatcher dispatcher = DISPATCHER.initialize();
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
synchronized (dispatcher.getClassLoadingLock(classLoader, entry.getKey())) {
Class<?> type = dispatcher.findClass(classLoader, entry.getKey());
for (String name : names) {
synchronized (dispatcher.getClassLoadingLock(classLoader, name)) {
Class<?> type = dispatcher.findClass(classLoader, name);
if (type == null) {
int packageIndex = entry.getKey().lastIndexOf('.');
int packageIndex = name.lastIndexOf('.');
if (packageIndex != -1) {
String packageName = entry.getKey().substring(0, packageIndex);
PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(classLoader, packageName, entry.getKey());
String packageName = name.substring(0, packageIndex);
PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(classLoader, packageName, name);
if (definition.isDefined()) {
Package definedPackage = dispatcher.getDefinedPackage(classLoader, packageName);
if (definedPackage == null) {
Expand Down Expand Up @@ -282,11 +299,15 @@ public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
}
}
}
type = dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain);
try {
type = dispatcher.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain);
} catch (IOException exception) {
throw new IllegalStateException("Could not resolve type description for " + name, exception);
}
} else if (forbidExisting) {
throw new IllegalStateException("Cannot inject already loaded type: " + type);
}
result.put(entry.getKey(), type);
result.put(name, type);
}
}
return result;
Expand Down Expand Up @@ -1624,19 +1645,19 @@ public boolean isAlive() {
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
public Map<String, Class<?>> inject(Set<String> names, ClassFileLocator classFileLocator) {
PackageDescription target = TypeDescription.ForLoadedType.of(lookupType()).getPackage();
if (target == null) {
throw new IllegalArgumentException("Cannot inject array or primitive type");
}
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
int index = entry.getKey().lastIndexOf('.');
if (!target.getName().equals(index == -1 ? "" : entry.getKey().substring(0, index))) {
throw new IllegalArgumentException(entry.getKey() + " must be defined in the same package as " + lookup);
for (String name : names) {
int index = name.lastIndexOf('.');
if (!target.getName().equals(index == -1 ? "" : name.substring(0, index))) {
throw new IllegalArgumentException(name + " must be defined in the same package as " + lookup);
}
try {
result.put(entry.getKey(), METHOD_HANDLES_LOOKUP.defineClass(lookup, entry.getValue()));
result.put(name, METHOD_HANDLES_LOOKUP.defineClass(lookup, classFileLocator.locate(name).resolve()));
} catch (Exception exception) {
throw new IllegalStateException(exception);
}
Expand Down Expand Up @@ -1811,27 +1832,31 @@ public boolean isAlive() {
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
public Map<String, Class<?>> inject(Set<String> names, ClassFileLocator classFileLocator) {
Dispatcher dispatcher = this.dispatcher.initialize();
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
synchronized (classLoader == null
? BOOTSTRAP_LOADER_LOCK
: classLoader) {
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
for (String name : names) {
try {
result.put(entry.getKey(), Class.forName(entry.getKey(), false, classLoader));
result.put(name, Class.forName(name, false, classLoader));
} catch (ClassNotFoundException ignored) {
try {
result.put(entry.getKey(), dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain));
} catch (RuntimeException exception) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
result.put(name, dispatcher.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain));
} catch (
RuntimeException exception) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
try {
result.put(entry.getKey(), Class.forName(entry.getKey(), false, classLoader));
result.put(name, Class.forName(name, false, classLoader));
} catch (ClassNotFoundException ignored2) {
throw exception;
}
} catch (Error error) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
} catch (IOException exception) {
throw new IllegalStateException("Failed to resolve binary representation of " + name, exception);
} catch (
Error error) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
try {
result.put(entry.getKey(), Class.forName(entry.getKey(), false, classLoader));
result.put(name, Class.forName(name, false, classLoader));
} catch (ClassNotFoundException ignored2) {
throw error;
}
Expand Down Expand Up @@ -2461,7 +2486,7 @@ public boolean isAlive() {
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
public Map<String, Class<?>> inject(Set<String> names, ClassFileLocator classFileLocator) {
File file = new File(folder, JAR + randomString.nextString() + "." + JAR);
try {
if (!file.createNewFile()) {
Expand All @@ -2470,9 +2495,9 @@ public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
try {
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(file));
try {
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
jarOutputStream.putNextEntry(new JarEntry(entry.getKey().replace('.', '/') + CLASS_FILE_EXTENSION));
jarOutputStream.write(entry.getValue());
for (String name : names) {
jarOutputStream.putNextEntry(new JarEntry(name.replace('.', '/') + CLASS_FILE_EXTENSION));
jarOutputStream.write(classFileLocator.locate(name).resolve());
}
} finally {
jarOutputStream.close();
Expand All @@ -2484,7 +2509,7 @@ public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
jarFile.close();
}
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
for (String name : types.keySet()) {
for (String name : names) {
result.put(name, Class.forName(name, false, target.getClassLoader()));
}
return result;
Expand Down Expand Up @@ -2731,16 +2756,20 @@ public boolean isAlive() {
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
public Map<String, Class<?>> inject(Set<String> names, ClassFileLocator classFileLocator) {
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
synchronized (classLoader == null
? BOOTSTRAP_LOADER_LOCK
: classLoader) {
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
for (String name : names) {
try {
result.put(entry.getKey(), Class.forName(entry.getKey(), false, classLoader));
result.put(name, Class.forName(name, false, classLoader));
} catch (ClassNotFoundException ignored) {
result.put(entry.getKey(), DISPATCHER.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain));
try {
result.put(name, DISPATCHER.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain));
} catch (IOException exception) {
throw new IllegalStateException("Failed to resolve binary representation of " + name, exception);
}
}
}
}
Expand Down

0 comments on commit 1ba91d0

Please sign in to comment.