Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Look up helper class bytes when they are needed #7839

Merged
merged 2 commits into from
Feb 17, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import net.bytebuddy.agent.builder.AgentBuilder.Transformer;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.utility.JavaModule;
import net.bytebuddy.utility.StreamDrainer;

/**
* Injects instrumentation helper classes into the user's class loader.
Expand Down Expand Up @@ -88,7 +89,7 @@ Class<?> inject(ClassLoader classLoader, String className) {
private final List<HelperResource> helperResources;
@Nullable private final ClassLoader helpersSource;
@Nullable private final Instrumentation instrumentation;
private final Map<String, byte[]> dynamicTypeMap = new LinkedHashMap<>();
private final Map<String, Supplier<byte[]>> dynamicTypeMap = new LinkedHashMap<>();

private final Cache<ClassLoader, Boolean> injectedClassLoaders = Cache.weak();
private final Cache<ClassLoader, Boolean> resourcesInjectedClassLoaders = Cache.weak();
Expand Down Expand Up @@ -120,7 +121,9 @@ public HelperInjector(
}

private HelperInjector(
String requestingName, Map<String, byte[]> helperMap, Instrumentation instrumentation) {
String requestingName,
Map<String, Supplier<byte[]>> helperMap,
Instrumentation instrumentation) {
this.requestingName = requestingName;

this.helperClassNames = helperMap.keySet();
Expand All @@ -135,9 +138,9 @@ public static HelperInjector forDynamicTypes(
String requestingName,
Collection<DynamicType.Unloaded<?>> helpers,
Instrumentation instrumentation) {
Map<String, byte[]> bytes = new HashMap<>(helpers.size());
Map<String, Supplier<byte[]>> bytes = new HashMap<>(helpers.size());
for (DynamicType.Unloaded<?> helper : helpers) {
bytes.put(helper.getTypeDescription().getName(), helper.getBytes());
bytes.put(helper.getTypeDescription().getName(), helper::getBytes);
}
return new HelperInjector(requestingName, bytes, instrumentation);
}
Expand All @@ -146,18 +149,29 @@ public static void setHelperInjectorListener(HelperInjectorListener listener) {
helperInjectorListener = listener;
}

private Map<String, byte[]> getHelperMap() throws IOException {
private Map<String, Supplier<byte[]>> getHelperMap() {
if (dynamicTypeMap.isEmpty()) {
Map<String, byte[]> classnameToBytes = new LinkedHashMap<>();

ClassFileLocator locator = ClassFileLocator.ForClassLoader.of(helpersSource);
Map<String, Supplier<byte[]>> result = new LinkedHashMap<>();

for (String helperClassName : helperClassNames) {
byte[] classBytes = locator.locate(helperClassName).resolve();
classnameToBytes.put(helperClassName, classBytes);
result.put(
helperClassName,
() -> {
try {
return StreamDrainer.DEFAULT.drain(
helpersSource.getResourceAsStream(
helperClassName.replace('.', '/') + ".class"));
laurit marked this conversation as resolved.
Show resolved Hide resolved
} catch (IOException exception) {
if (logger.isLoggable(SEVERE)) {
logger.log(
SEVERE, "Failed to read {0}", new Object[] {helperClassName}, exception);
}
throw new IllegalStateException("Failed to read " + helperClassName, exception);
}
});
}

return classnameToBytes;
return result;
} else {
return dynamicTypeMap;
}
Expand Down Expand Up @@ -248,10 +262,10 @@ private void injectHelperClasses(TypeDescription typeDescription, ClassLoader cl
new Object[] {cl, helperClassNames});
}

Map<String, byte[]> classnameToBytes = getHelperMap();
Map<String, Supplier<byte[]>> classnameToBytes = getHelperMap();
Map<String, HelperClassInjector> map =
helperInjectors.computeIfAbsent(cl, (unused) -> new ConcurrentHashMap<>());
for (Map.Entry<String, byte[]> entry : classnameToBytes.entrySet()) {
for (Map.Entry<String, Supplier<byte[]>> entry : classnameToBytes.entrySet()) {
// for boot loader we use a placeholder injector, we only need these classes to be
// in the injected classes map to later tell which of the classes are injected
HelperClassInjector injector =
Expand Down Expand Up @@ -280,9 +294,18 @@ private void injectHelperClasses(TypeDescription typeDescription, ClassLoader cl
});
}

private Map<String, Class<?>> injectBootstrapClassLoader(Map<String, byte[]> classnameToBytes)
private static Map<String, byte[]> resolve(Map<String, Supplier<byte[]>> classes) {
Map<String, byte[]> result = new LinkedHashMap<>();
for (Map.Entry<String, Supplier<byte[]>> entry : classes.entrySet()) {
result.put(entry.getKey(), entry.getValue().get());
}
return result;
}

private Map<String, Class<?>> injectBootstrapClassLoader(Map<String, Supplier<byte[]>> inject)
throws IOException {

Map<String, byte[]> classnameToBytes = resolve(inject);
if (helperInjectorListener != null) {
helperInjectorListener.onInjection(classnameToBytes);
}
Expand Down Expand Up @@ -358,16 +381,16 @@ public static Class<?> loadHelperClass(ClassLoader classLoader, String className
}

private static class HelperClassInjector {
private final byte[] bytes;
private final Supplier<byte[]> bytes;

HelperClassInjector(byte[] bytes) {
HelperClassInjector(Supplier<byte[]> bytes) {
this.bytes = bytes;
}

Class<?> inject(ClassLoader classLoader, String className) {
Map<String, Class<?>> result =
new ClassInjector.UsingReflection(classLoader)
.injectRaw(Collections.singletonMap(className, bytes));
.injectRaw(Collections.singletonMap(className, bytes.get()));
return result.get(className);
}
}
Expand Down