From a83ef40e911a3228ddf999ee66cab652afbcc829 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Tue, 4 Jan 2022 15:57:30 +0200 Subject: [PATCH 1/4] Fix redefinition failure on openj9 --- .../bootstrap/build.gradle.kts | 3 ++ .../internal/reflection/RealInterfaces.java | 36 +++++++++++++++++++ .../javaagent/build.gradle.kts | 2 ++ .../internal/reflection/ReflectionHelper.java | 4 +++ javaagent-tooling/build.gradle.kts | 1 + .../FieldBackedImplementationInstaller.java | 10 +++--- settings.gradle.kts | 1 + 7 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 instrumentation/internal/internal-reflection/bootstrap/build.gradle.kts create mode 100644 instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java diff --git a/instrumentation/internal/internal-reflection/bootstrap/build.gradle.kts b/instrumentation/internal/internal-reflection/bootstrap/build.gradle.kts new file mode 100644 index 000000000000..072a96df450f --- /dev/null +++ b/instrumentation/internal/internal-reflection/bootstrap/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("otel.javaagent-bootstrap") +} diff --git a/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java b/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java new file mode 100644 index 000000000000..3e97055ae85a --- /dev/null +++ b/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.internal.reflection; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +public final class RealInterfaces { + private static final Map, Class[]> realInterfaces = + Collections.synchronizedMap(new WeakHashMap<>()); + + private RealInterfaces() {} + + /** + * Returns the interfaces directly implemented by the given class or interface. We instrument + * Class#getInterfaces() to remove interfaces added by us. This method can be used to get the + * interfaces that the class directly implements including the interfaces that we have added. + * + * @param clazz class or interface + * @return interfaces directly implemented by given class + */ + public static Class[] get(Class clazz) { + // instrumentation calls RealInterfaces#set() from Class#getInterfaces() + Class[] interfaces = clazz.getInterfaces(); + Class[] real = realInterfaces.get(clazz); + return real != null ? real : interfaces; + } + + static void set(Class clazz, Class[] interfaces) { + realInterfaces.put(clazz, interfaces); + } +} diff --git a/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts b/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts index 70f921cc7802..d277bb35681e 100644 --- a/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts +++ b/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts @@ -3,6 +3,8 @@ plugins { } dependencies { + bootstrap(project(":instrumentation:internal:internal-reflection:bootstrap")) + compileOnly(project(":javaagent-bootstrap")) testImplementation(project(":javaagent-bootstrap")) diff --git a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java index bcc0199c389e..baabe17346c7 100644 --- a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java +++ b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java @@ -70,6 +70,10 @@ public static Class[] filterInterfaces(Class[] interfaces, Class contai } result.add(interfaceClass); } + if (result.size() != interfaces.length) { + // for classes that have removed interfaces remember what they really have + RealInterfaces.set(containingClass, interfaces); + } return result.toArray(new Class[0]); } } diff --git a/javaagent-tooling/build.gradle.kts b/javaagent-tooling/build.gradle.kts index 3491e72ecf76..d6650a9ed3a5 100644 --- a/javaagent-tooling/build.gradle.kts +++ b/javaagent-tooling/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(project(":instrumentation-api-appender")) implementation(project(":instrumentation-sdk-appender")) implementation(project(":muzzle")) + implementation(project(":instrumentation:internal:internal-reflection:bootstrap")) implementation("io.opentelemetry:opentelemetry-api") implementation("io.opentelemetry:opentelemetry-api-metrics") diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java index 2b1b01e4b364..6fe78aee2b55 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java @@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder; import io.opentelemetry.javaagent.bootstrap.VirtualFieldInstalledMarker; +import io.opentelemetry.javaagent.instrumentation.internal.reflection.RealInterfaces; import io.opentelemetry.javaagent.tooling.HelperInjector; import io.opentelemetry.javaagent.tooling.TransformSafeLogger; import io.opentelemetry.javaagent.tooling.instrumentation.InstrumentationModuleInstaller; @@ -216,13 +217,12 @@ private static AgentBuilder.RawMatcher safeToInjectFieldsMatcher() { /* * The idea here is that we can add fields if class is just being loaded * (classBeingRedefined == null) and we have to add same fields again if class we added - * fields before is being transformed again. Note: here we assume that Class#getInterfaces() - * returns list of interfaces defined immediately on a given class, not inherited from its - * parents. It looks like current JVM implementation does exactly this but javadoc is not - * explicit about that. + * fields before is being transformed again. As we instrument Class#getInterfaces() to remove + * interfaces added by us, we need to use our own helper class to get all the interfaces that + * the class directly implements. */ return classBeingRedefined == null - || Arrays.asList(classBeingRedefined.getInterfaces()) + || Arrays.asList(RealInterfaces.get(classBeingRedefined)) .contains(VirtualFieldInstalledMarker.class); }; } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7f1692ad8a45..a6154e847f17 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -153,6 +153,7 @@ include(":instrumentation:internal:internal-class-loader:javaagent-integration-t include(":instrumentation:internal:internal-eclipse-osgi-3.6:javaagent") include(":instrumentation:internal:internal-lambda:javaagent") include(":instrumentation:internal:internal-lambda-java9:javaagent") +include(":instrumentation:internal:internal-reflection:bootstrap") include(":instrumentation:internal:internal-reflection:javaagent") include(":instrumentation:internal:internal-reflection:javaagent-integration-tests") include(":instrumentation:internal:internal-url-class-loader:javaagent") From 98e001614de86aaadaec0be015a7359b249804c1 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Wed, 5 Jan 2022 13:02:13 +0200 Subject: [PATCH 2/4] isntead of remembering the list of interfaces the class has remember whether it has any of the virutal field marker interfaces --- .../internal/reflection/RealInterfaces.java | 36 ------------------- .../reflection/VirtualFieldDetector.java | 35 ++++++++++++++++++ .../internal/reflection/ReflectionHelper.java | 9 +++-- .../FieldBackedImplementationInstaller.java | 11 ++---- 4 files changed, 44 insertions(+), 47 deletions(-) delete mode 100644 instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java create mode 100644 instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/VirtualFieldDetector.java diff --git a/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java b/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java deleted file mode 100644 index 3e97055ae85a..000000000000 --- a/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/RealInterfaces.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.internal.reflection; - -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; - -public final class RealInterfaces { - private static final Map, Class[]> realInterfaces = - Collections.synchronizedMap(new WeakHashMap<>()); - - private RealInterfaces() {} - - /** - * Returns the interfaces directly implemented by the given class or interface. We instrument - * Class#getInterfaces() to remove interfaces added by us. This method can be used to get the - * interfaces that the class directly implements including the interfaces that we have added. - * - * @param clazz class or interface - * @return interfaces directly implemented by given class - */ - public static Class[] get(Class clazz) { - // instrumentation calls RealInterfaces#set() from Class#getInterfaces() - Class[] interfaces = clazz.getInterfaces(); - Class[] real = realInterfaces.get(clazz); - return real != null ? real : interfaces; - } - - static void set(Class clazz, Class[] interfaces) { - realInterfaces.put(clazz, interfaces); - } -} diff --git a/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/VirtualFieldDetector.java b/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/VirtualFieldDetector.java new file mode 100644 index 000000000000..398724afa03c --- /dev/null +++ b/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/VirtualFieldDetector.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.internal.reflection; + +import io.opentelemetry.instrumentation.api.cache.Cache; + +/** Helper class for detecting whether given class has virtual fields. */ +public final class VirtualFieldDetector { + + private static final Cache, Boolean> classesWithVirtualFields = Cache.weak(); + + private VirtualFieldDetector() {} + + /** + * Detect whether given class has virtual fields. This method looks for virtual fields only from + * the specified class not its super classes. + * + * @param clazz a class + * @return true if given class has virtual fields + */ + public static boolean hasVirtualFields(Class clazz) { + // clazz.getInterfaces() needs to be called before reading from classesWithVirtualFields + // as the call to clazz.getInterfaces() triggers adding clazz to that map via instrumentation + // calling VirtualFieldDetector#markVirtualFieldsPresent() from Class#getInterfaces() + clazz.getInterfaces(); + return classesWithVirtualFields.get(clazz) != null; + } + + static void markVirtualFieldsPresent(Class clazz) { + classesWithVirtualFields.put(clazz, Boolean.TRUE); + } +} diff --git a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java index baabe17346c7..5bc487914ec0 100644 --- a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java +++ b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java @@ -60,20 +60,23 @@ public static Class[] filterInterfaces(Class[] interfaces, Class contai return interfaces; } List> result = new ArrayList<>(interfaces.length); + boolean hasVirtualFieldMarker = false; for (Class interfaceClass : interfaces) { // filter out virtual field marker and accessor interfaces if (interfaceClass == VirtualFieldInstalledMarker.class || (VirtualFieldAccessorMarker.class.isAssignableFrom(interfaceClass) && interfaceClass.isSynthetic() && interfaceClass.getName().contains("VirtualFieldAccessor$"))) { + hasVirtualFieldMarker = true; continue; } result.add(interfaceClass); } - if (result.size() != interfaces.length) { - // for classes that have removed interfaces remember what they really have - RealInterfaces.set(containingClass, interfaces); + + if (hasVirtualFieldMarker) { + VirtualFieldDetector.markVirtualFieldsPresent(containingClass); } + return result.toArray(new Class[0]); } } diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java index 6fe78aee2b55..c10cc90025cd 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java @@ -12,14 +12,12 @@ import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder; -import io.opentelemetry.javaagent.bootstrap.VirtualFieldInstalledMarker; -import io.opentelemetry.javaagent.instrumentation.internal.reflection.RealInterfaces; +import io.opentelemetry.javaagent.instrumentation.internal.reflection.VirtualFieldDetector; import io.opentelemetry.javaagent.tooling.HelperInjector; import io.opentelemetry.javaagent.tooling.TransformSafeLogger; import io.opentelemetry.javaagent.tooling.instrumentation.InstrumentationModuleInstaller; import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappings; import java.lang.instrument.Instrumentation; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Map; @@ -217,13 +215,10 @@ private static AgentBuilder.RawMatcher safeToInjectFieldsMatcher() { /* * The idea here is that we can add fields if class is just being loaded * (classBeingRedefined == null) and we have to add same fields again if class we added - * fields before is being transformed again. As we instrument Class#getInterfaces() to remove - * interfaces added by us, we need to use our own helper class to get all the interfaces that - * the class directly implements. + * fields before is being transformed again. */ return classBeingRedefined == null - || Arrays.asList(RealInterfaces.get(classBeingRedefined)) - .contains(VirtualFieldInstalledMarker.class); + || VirtualFieldDetector.hasVirtualFields(classBeingRedefined); }; } From 7bec95660944139660dc3903c8edf60fbbd77f93 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Wed, 5 Jan 2022 13:53:26 +0200 Subject: [PATCH 3/4] address review comment --- .../internal/internal-reflection/javaagent/build.gradle.kts | 2 -- .../instrumentation/internal/reflection/ReflectionHelper.java | 1 + .../javaagent/bootstrap}/VirtualFieldDetector.java | 4 ++-- javaagent-tooling/build.gradle.kts | 1 - .../tooling/field/FieldBackedImplementationInstaller.java | 2 +- settings.gradle.kts | 1 - 6 files changed, 4 insertions(+), 7 deletions(-) rename {instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection => javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap}/VirtualFieldDetector.java (89%) diff --git a/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts b/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts index d277bb35681e..70f921cc7802 100644 --- a/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts +++ b/instrumentation/internal/internal-reflection/javaagent/build.gradle.kts @@ -3,8 +3,6 @@ plugins { } dependencies { - bootstrap(project(":instrumentation:internal:internal-reflection:bootstrap")) - compileOnly(project(":javaagent-bootstrap")) testImplementation(project(":javaagent-bootstrap")) diff --git a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java index 5bc487914ec0..33f947491483 100644 --- a/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java +++ b/instrumentation/internal/internal-reflection/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/ReflectionHelper.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.instrumentation.internal.reflection; import io.opentelemetry.javaagent.bootstrap.VirtualFieldAccessorMarker; +import io.opentelemetry.javaagent.bootstrap.VirtualFieldDetector; import io.opentelemetry.javaagent.bootstrap.VirtualFieldInstalledMarker; import java.lang.reflect.Field; import java.lang.reflect.Method; diff --git a/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/VirtualFieldDetector.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/VirtualFieldDetector.java similarity index 89% rename from instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/VirtualFieldDetector.java rename to javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/VirtualFieldDetector.java index 398724afa03c..35dc22056cbf 100644 --- a/instrumentation/internal/internal-reflection/bootstrap/src/main/java/io/opentelemetry/javaagent/instrumentation/internal/reflection/VirtualFieldDetector.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/VirtualFieldDetector.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.internal.reflection; +package io.opentelemetry.javaagent.bootstrap; import io.opentelemetry.instrumentation.api.cache.Cache; @@ -29,7 +29,7 @@ public static boolean hasVirtualFields(Class clazz) { return classesWithVirtualFields.get(clazz) != null; } - static void markVirtualFieldsPresent(Class clazz) { + public static void markVirtualFieldsPresent(Class clazz) { classesWithVirtualFields.put(clazz, Boolean.TRUE); } } diff --git a/javaagent-tooling/build.gradle.kts b/javaagent-tooling/build.gradle.kts index d6650a9ed3a5..3491e72ecf76 100644 --- a/javaagent-tooling/build.gradle.kts +++ b/javaagent-tooling/build.gradle.kts @@ -18,7 +18,6 @@ dependencies { implementation(project(":instrumentation-api-appender")) implementation(project(":instrumentation-sdk-appender")) implementation(project(":muzzle")) - implementation(project(":instrumentation:internal:internal-reflection:bootstrap")) implementation("io.opentelemetry:opentelemetry-api") implementation("io.opentelemetry:opentelemetry-api-metrics") diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java index c10cc90025cd..18e6806206fd 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/field/FieldBackedImplementationInstaller.java @@ -12,7 +12,7 @@ import io.opentelemetry.instrumentation.api.config.Config; import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder; -import io.opentelemetry.javaagent.instrumentation.internal.reflection.VirtualFieldDetector; +import io.opentelemetry.javaagent.bootstrap.VirtualFieldDetector; import io.opentelemetry.javaagent.tooling.HelperInjector; import io.opentelemetry.javaagent.tooling.TransformSafeLogger; import io.opentelemetry.javaagent.tooling.instrumentation.InstrumentationModuleInstaller; diff --git a/settings.gradle.kts b/settings.gradle.kts index a6154e847f17..7f1692ad8a45 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -153,7 +153,6 @@ include(":instrumentation:internal:internal-class-loader:javaagent-integration-t include(":instrumentation:internal:internal-eclipse-osgi-3.6:javaagent") include(":instrumentation:internal:internal-lambda:javaagent") include(":instrumentation:internal:internal-lambda-java9:javaagent") -include(":instrumentation:internal:internal-reflection:bootstrap") include(":instrumentation:internal:internal-reflection:javaagent") include(":instrumentation:internal:internal-reflection:javaagent-integration-tests") include(":instrumentation:internal:internal-url-class-loader:javaagent") From b7c7b8f9ba0f88cc8dfe139e76b52e68dacd19be Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Wed, 5 Jan 2022 16:12:37 +0200 Subject: [PATCH 4/4] ensure virtual field detection works when internal-reflection instrumentation is disabled --- .../javaagent/bootstrap/VirtualFieldDetector.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/VirtualFieldDetector.java b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/VirtualFieldDetector.java index 35dc22056cbf..5e9511c54961 100644 --- a/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/VirtualFieldDetector.java +++ b/javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/VirtualFieldDetector.java @@ -6,6 +6,7 @@ package io.opentelemetry.javaagent.bootstrap; import io.opentelemetry.instrumentation.api.cache.Cache; +import java.util.Arrays; /** Helper class for detecting whether given class has virtual fields. */ public final class VirtualFieldDetector { @@ -25,7 +26,12 @@ public static boolean hasVirtualFields(Class clazz) { // clazz.getInterfaces() needs to be called before reading from classesWithVirtualFields // as the call to clazz.getInterfaces() triggers adding clazz to that map via instrumentation // calling VirtualFieldDetector#markVirtualFieldsPresent() from Class#getInterfaces() - clazz.getInterfaces(); + Class[] interfaces = clazz.getInterfaces(); + // to avoid breaking in case internal-reflection instrumentation is disabled check whether + // interfaces array contains virtual field marker interface + if (Arrays.asList(interfaces).contains(VirtualFieldInstalledMarker.class)) { + return true; + } return classesWithVirtualFields.get(clazz) != null; }