From 1ba91d0617e9fab5ac6f327563b83c23f5855cd1 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Thu, 31 Oct 2024 23:39:53 +0100 Subject: [PATCH] Add method for lazy class file resolution. --- .../dynamic/loading/ClassInjector.java | 95 ++++++++++++------- 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java index 78a34cf7c6..a98e0d6eef 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.java @@ -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; @@ -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> inject(Set names, ClassFileLocator classFileLocator); + /** * Injects the given types into the represented class loader. * @@ -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> injectRaw(Map types); + Map> injectRaw(Map types); /** * An abstract base implementation of a class injector. @@ -123,6 +133,13 @@ public Map> inject(Map> injectRaw(Map types) { + return inject(types.keySet(), new ClassFileLocator.Simple(types)); + } } /** @@ -237,17 +254,17 @@ public boolean isAlive() { /** * {@inheritDoc} */ - public Map> injectRaw(Map types) { + public Map> inject(Set names, ClassFileLocator classFileLocator) { Dispatcher dispatcher = DISPATCHER.initialize(); Map> result = new HashMap>(); - for (Map.Entry 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) { @@ -282,11 +299,15 @@ public Map> injectRaw(Map 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; @@ -1624,19 +1645,19 @@ public boolean isAlive() { /** * {@inheritDoc} */ - public Map> injectRaw(Map types) { + public Map> inject(Set names, ClassFileLocator classFileLocator) { PackageDescription target = TypeDescription.ForLoadedType.of(lookupType()).getPackage(); if (target == null) { throw new IllegalArgumentException("Cannot inject array or primitive type"); } Map> result = new HashMap>(); - for (Map.Entry 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); } @@ -1811,27 +1832,31 @@ public boolean isAlive() { /** * {@inheritDoc} */ - public Map> injectRaw(Map types) { + public Map> inject(Set names, ClassFileLocator classFileLocator) { Dispatcher dispatcher = this.dispatcher.initialize(); Map> result = new HashMap>(); synchronized (classLoader == null ? BOOTSTRAP_LOADER_LOCK : classLoader) { - for (Map.Entry 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; } @@ -2461,7 +2486,7 @@ public boolean isAlive() { /** * {@inheritDoc} */ - public Map> injectRaw(Map types) { + public Map> inject(Set names, ClassFileLocator classFileLocator) { File file = new File(folder, JAR + randomString.nextString() + "." + JAR); try { if (!file.createNewFile()) { @@ -2470,9 +2495,9 @@ public Map> injectRaw(Map types) { try { JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(file)); try { - for (Map.Entry 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(); @@ -2484,7 +2509,7 @@ public Map> injectRaw(Map types) { jarFile.close(); } Map> result = new HashMap>(); - for (String name : types.keySet()) { + for (String name : names) { result.put(name, Class.forName(name, false, target.getClassLoader())); } return result; @@ -2731,16 +2756,20 @@ public boolean isAlive() { /** * {@inheritDoc} */ - public Map> injectRaw(Map types) { + public Map> inject(Set names, ClassFileLocator classFileLocator) { Map> result = new HashMap>(); synchronized (classLoader == null ? BOOTSTRAP_LOADER_LOCK : classLoader) { - for (Map.Entry 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); + } } } }