Allow transforming classes with missing field types (#8393)

This commit is contained in:
Lauri Tulmin 2023-05-10 00:07:50 +03:00 committed by GitHub
parent 50ca91d1c5
commit 43073e7df9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 11 additions and 93 deletions

View File

@ -31,7 +31,6 @@ import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
import io.opentelemetry.javaagent.tooling.asyncannotationsupport.WeakRefAsyncOperationEndStrategies;
import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesBuilderImpl;
import io.opentelemetry.javaagent.tooling.bootstrap.BootstrapPackagesConfigurer;
import io.opentelemetry.javaagent.tooling.bytebuddy.SafeTypeStrategy;
import io.opentelemetry.javaagent.tooling.config.AgentConfig;
import io.opentelemetry.javaagent.tooling.config.ConfigPropertiesBridge;
import io.opentelemetry.javaagent.tooling.config.EarlyInitAgentConfig;
@ -59,6 +58,8 @@ import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.VisibilityBridgeStrategy;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.utility.JavaModule;
@ -131,11 +132,12 @@ public class AgentInstaller {
new AgentBuilder.Default(
// default method graph compiler inspects the class hierarchy, we don't need it, so
// we use a simpler and faster strategy instead
new ByteBuddy().with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE))
new ByteBuddy()
.with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE)
.with(VisibilityBridgeStrategy.Default.NEVER)
.with(InstrumentedType.Factory.Default.FROZEN))
.with(AgentBuilder.TypeStrategy.Default.DECORATE)
.disableClassFormatChanges()
// disableClassFormatChanges sets type strategy to TypeStrategy.Default.REDEFINE_FROZEN
// we'll wrap it with our own strategy
.with(new SafeTypeStrategy(AgentBuilder.TypeStrategy.Default.REDEFINE_FROZEN))
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new RedefinitionDiscoveryStrategy())
.with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY)

View File

@ -1,86 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.tooling.bytebuddy;
import static net.bytebuddy.matcher.ElementMatchers.failSafe;
import static net.bytebuddy.matcher.ElementMatchers.hasParameters;
import static net.bytebuddy.matcher.ElementMatchers.hasType;
import static net.bytebuddy.matcher.ElementMatchers.isVisibleTo;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.whereNone;
import java.security.ProtectionDomain;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
import net.bytebuddy.utility.JavaModule;
/**
* Wrapper for {@link AgentBuilder.TypeStrategy} that excludes methods with missing return or
* parameter types. By default, byte-buddy fails transforming such classes.
*/
public final class SafeTypeStrategy implements AgentBuilder.TypeStrategy {
private final AgentBuilder.TypeStrategy delegate;
public SafeTypeStrategy(AgentBuilder.TypeStrategy delegate) {
this.delegate = delegate;
}
@Override
public DynamicType.Builder<?> builder(
TypeDescription typeDescription,
ByteBuddy byteBuddy,
ClassFileLocator classFileLocator,
MethodNameTransformer methodNameTransformer,
ClassLoader classLoader,
JavaModule module,
ProtectionDomain protectionDomain) {
// type description wrapper that removes methods with missing return or parameter types
TypeDescription newTypeDescription =
new TypeDescription.AbstractBase.OfSimpleType.WithDelegation() {
@Override
public String getName() {
return delegate().getName();
}
@Override
protected TypeDescription delegate() {
return typeDescription;
}
@Override
public MethodList<MethodDescription.InDefinedShape> getDeclaredMethods() {
MethodList<MethodDescription.InDefinedShape> methodList = super.getDeclaredMethods();
return filterMethods(methodList, typeDescription);
}
};
return delegate.builder(
newTypeDescription,
byteBuddy,
classFileLocator,
methodNameTransformer,
classLoader,
module,
protectionDomain);
}
private static <T extends MethodDescription> MethodList<T> filterMethods(
MethodList<T> methodList, TypeDescription viewPoint) {
// filter out methods missing return or parameter types
return methodList.filter(
failSafe(
returns(isVisibleTo(viewPoint))
.and(hasParameters(whereNone(hasType(not(isVisibleTo(viewPoint))))))));
}
}

View File

@ -36,8 +36,8 @@ class MissingTypeTest {
AgentBuilder builder =
new AgentBuilder.Default(
new ByteBuddy().with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE))
.with(AgentBuilder.TypeStrategy.Default.DECORATE)
.disableClassFormatChanges()
.with(new SafeTypeStrategy(AgentBuilder.TypeStrategy.Default.REDEFINE_FROZEN))
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(
new AgentBuilder.Listener.Adapter() {
@ -92,8 +92,10 @@ class MissingTypeTest {
}
// com.google.common.base.Joiner is missing from runtime class path
@SuppressWarnings({"UnusedMethod", "MethodCanBeStatic"})
@SuppressWarnings({"UnusedMethod", "UnusedVariable", "MethodCanBeStatic"})
private static class SomeClass {
public Joiner joiner;
public static boolean isInstrumented() {
return false;
}