-
Notifications
You must be signed in to change notification settings - Fork 7
Keep instrumenting classes decorated by another agent's synthetic types #333
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,18 @@ dependencies { | |
| compileOnly 'io.projectreactor.netty:reactor-netty-http:1.2.1' // For Spring Webflux | ||
| compileOnly 'io.javalin:javalin:6.4.0' | ||
| compileOnly 'org.springframework:spring-web:5.3.20' | ||
|
|
||
| testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' | ||
| testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2' | ||
| testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.9.2' | ||
| } | ||
|
|
||
| test { | ||
| useJUnitPlatform() | ||
| // The root `test --tests <name>` smoke run targets a test in another module; don't fail here on no match. | ||
| filter { | ||
| setFailOnNoMatchingTests(false) | ||
| } | ||
|
Comment on lines
+21
to
+26
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we really need this? ^^
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, useJUnitPlatform to run
gradle tries to apply it to each project sub-module. |
||
| } | ||
|
|
||
| shadowJar { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| package dev.aikido.agent; | ||
|
|
||
| import net.bytebuddy.agent.builder.AgentBuilder; | ||
| import net.bytebuddy.description.annotation.AnnotationList; | ||
| import net.bytebuddy.description.field.FieldDescription; | ||
| import net.bytebuddy.description.field.FieldList; | ||
| import net.bytebuddy.description.method.MethodDescription; | ||
| import net.bytebuddy.description.method.MethodList; | ||
| import net.bytebuddy.description.type.RecordComponentDescription; | ||
| import net.bytebuddy.description.type.RecordComponentList; | ||
| import net.bytebuddy.description.type.TypeDescription; | ||
| import net.bytebuddy.dynamic.ClassFileLocator; | ||
| import net.bytebuddy.pool.TypePool; | ||
|
|
||
| import java.lang.reflect.Modifier; | ||
| import java.util.Collections; | ||
|
|
||
| // Another agent (e.g. OpenTelemetry) can add synthetic supertypes with no .class resource to core types; | ||
| // the default pool then throws while resolving the hierarchy. Unresolvable types degrade to an empty interface. | ||
| public enum LenientPoolStrategy implements AgentBuilder.PoolStrategy { | ||
| INSTANCE; | ||
|
|
||
| @Override | ||
| public TypePool typePool(ClassFileLocator classFileLocator, ClassLoader classLoader) { | ||
| return new LenientPool(new TypePool.CacheProvider.Simple(), classFileLocator, TypePool.Default.ReaderMode.FAST); | ||
| } | ||
|
|
||
| @Override | ||
| public TypePool typePool(ClassFileLocator classFileLocator, ClassLoader classLoader, String name) { | ||
| return typePool(classFileLocator, classLoader); | ||
| } | ||
|
|
||
| private static final class LenientPool extends TypePool.Default { | ||
| LenientPool(CacheProvider cacheProvider, ClassFileLocator classFileLocator, ReaderMode readerMode) { | ||
| super(cacheProvider, classFileLocator, readerMode); | ||
| } | ||
|
|
||
| @Override | ||
| protected Resolution doDescribe(String name) { | ||
| Resolution resolution = super.doDescribe(name); | ||
| return resolution.isResolved() ? resolution : new Resolution.Simple(new EmptyStubType(name)); | ||
| } | ||
| } | ||
|
|
||
| private static final class EmptyStubType extends TypeDescription.Latent { | ||
| EmptyStubType(String name) { | ||
| super(name, Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.INTERFACE, | ||
| TypeDescription.Generic.OBJECT, Collections.<TypeDescription.Generic>emptyList()); | ||
| } | ||
|
|
||
| @Override | ||
| public MethodList<MethodDescription.InDefinedShape> getDeclaredMethods() { | ||
| return new MethodList.Empty<MethodDescription.InDefinedShape>(); | ||
| } | ||
|
|
||
| @Override | ||
| public FieldList<FieldDescription.InDefinedShape> getDeclaredFields() { | ||
| return new FieldList.Empty<FieldDescription.InDefinedShape>(); | ||
| } | ||
|
|
||
| @Override | ||
| public AnnotationList getDeclaredAnnotations() { | ||
| return new AnnotationList.Empty(); | ||
| } | ||
|
|
||
| @Override | ||
| public RecordComponentList<RecordComponentDescription.InDefinedShape> getRecordComponents() { | ||
| return new RecordComponentList.Empty<RecordComponentDescription.InDefinedShape>(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package dev.aikido.agent; | ||
|
|
||
| import net.bytebuddy.description.type.TypeDescription; | ||
| import net.bytebuddy.dynamic.ClassFileLocator; | ||
| import net.bytebuddy.pool.TypePool; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| class LenientPoolStrategyTest { | ||
| private TypePool pool() { | ||
| ClassLoader classLoader = getClass().getClassLoader(); | ||
| return LenientPoolStrategy.INSTANCE.typePool(ClassFileLocator.ForClassLoader.of(classLoader), classLoader); | ||
| } | ||
|
|
||
| @Test | ||
| void resolvableTypeIsDescribedNormally() { | ||
| TypeDescription type = pool().describe("java.util.ArrayList").resolve(); | ||
|
|
||
| assertEquals("java.util.ArrayList", type.getName()); | ||
| assertFalse(type.isInterface()); | ||
| assertFalse(type.getDeclaredMethods().isEmpty()); | ||
| } | ||
|
|
||
| @Test | ||
| void unresolvableTypeDegradesToEmptyInterface() { | ||
| TypeDescription type = pool().describe("com.acme.Injected$VirtualField$Absent").resolve(); | ||
|
|
||
| assertEquals("com.acme.Injected$VirtualField$Absent", type.getName()); | ||
| assertTrue(type.isInterface()); | ||
| assertTrue(type.getDeclaredMethods().isEmpty()); | ||
| assertTrue(type.getDeclaredFields().isEmpty()); | ||
| assertTrue(type.getInterfaces().isEmpty()); | ||
| assertEquals(Object.class.getName(), type.getSuperClass().asErasure().getName()); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.