Hide virtual field accessor interface methods from reflection
MODIFY
instrumentation/internal/internal-reflection/javaagent-integration-tests/src/test/groovy/ReflectionTest.groovy
instrumentation/internal/internal-reflection/javaagent-integration-tests/src/test/groovy/ReflectionTest.groovy
@@ -35,5 +35,18 @@ class ReflectionTest extends AgentInstrumentationSpecification {
}
}
methodFound == false
+
+ and:
+ def interfaceClass = TestClass.getInterfaces().find {
+ it.getName().contains("VirtualFieldAccessor\$")
+ }
+ interfaceClass != null
+ def interfaceMethodFound = false
+ for (Method method : interfaceClass.getDeclaredMethods()) {
+ if (method.getName().contains("__opentelemetry")) {
+ interfaceMethodFound = true
+ }
+ }
+ interfaceMethodFound == false
}
}

MODIFY
instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java
instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java
@@ -22,7 +22,7 @@ public final class ReflectionHelper {
}
List<Field> result = new ArrayList<>(fields.length);
for (Field field : fields) {
- // FieldBackedProvider marks added fields as synthetic
+ // virtual fields are marked as synthetic
if (field.isSynthetic() && field.getName().startsWith("__opentelemetryVirtualField$")) {
continue;
}
@@ -32,13 +32,20 @@ public final class ReflectionHelper {
}
public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
- if (methods.length == 0
- || !VirtualFieldInstalledMarker.class.isAssignableFrom(containingClass)) {
+ if (methods.length == 0) {
+ return methods;
+ } else if (containingClass.isInterface()
+ && containingClass.isSynthetic()
+ && containingClass.getName().contains("VirtualFieldAccessor$")) {
+ // hide all methods from virtual field accessor interfaces
+ return new Method[0];
+ } else if (!VirtualFieldInstalledMarker.class.isAssignableFrom(containingClass)) {
+ // nothing to filter when class does not have any added virtual fields
return methods;
}
List<Method> result = new ArrayList<>(methods.length);
for (Method method : methods) {
- // FieldBackedProvider marks added method as synthetic
+ // virtual field accessor methods are marked as synthetic
if (method.isSynthetic()
&& (method.getName().startsWith("get__opentelemetryVirtualField$")
|| method.getName().startsWith("set__opentelemetryVirtualField$"))) {

MODIFY
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldAccessorInterfacesGenerator.java
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldAccessorInterfacesGenerator.java
@@ -54,10 +54,17 @@ final class FieldAccessorInterfacesGenerator {
.makeInterface()
.merge(SyntheticState.SYNTHETIC)
.name(getFieldAccessorInterfaceName(typeName, fieldTypeName))
- .defineMethod(getRealGetterName(typeName, fieldTypeName), fieldTypeDesc, Visibility.PUBLIC)
+ .defineMethod(
+ getRealGetterName(typeName, fieldTypeName),
+ fieldTypeDesc,
+ Visibility.PUBLIC,
+ SyntheticState.SYNTHETIC)
.withoutCode()
.defineMethod(
- getRealSetterName(typeName, fieldTypeName), TypeDescription.VOID, Visibility.PUBLIC)
+ getRealSetterName(typeName, fieldTypeName),
+ TypeDescription.VOID,
+ Visibility.PUBLIC,
+ SyntheticState.SYNTHETIC)
.withParameter(fieldTypeDesc, "value")
.withoutCode()
.make();

Hide virtual field accessor interface methods from reflection (#4390)