Skip to content

Commit 7525934

Browse files
committed
fix(csi): sort ArgumentSpecification by index when generating partial-argument advice
Stream.sorted() without a comparator requires Comparable, but ArgumentSpecification does not implement it. This caused a ClassCastException in AdviceGeneratorImpl.writeStackOperations whenever an advice method captured a strict subset (≥2 args) of a target method's parameters positionally — e.g. capturing args 0 and 1 from a 3-arg target method. Single-argument partial captures were unaffected because TimSort never calls compare() on a one-element list. Fix: use Comparator.comparingInt on ArgumentSpecification.getIndex(). Add a regression test with a @before advice that captures two of three arguments from String.format(Locale, String, Object[]).
1 parent 42f154d commit 7525934

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

buildSrc/call-site-instrumentation-plugin/src/main/java/datadog/trace/plugin/csi/impl/AdviceGeneratorImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import java.io.File;
5959
import java.util.ArrayList;
6060
import java.util.Arrays;
61+
import java.util.Comparator;
6162
import java.util.List;
6263
import java.util.stream.Collectors;
6364
import javax.annotation.Nonnull;
@@ -266,7 +267,7 @@ private static void writeStackOperations(final AdviceSpecification advice, final
266267
final List<Expression> parameterIndicesValues =
267268
advice
268269
.getArguments()
269-
.sorted()
270+
.sorted(Comparator.comparingInt(argSpec -> argSpec.getIndex()))
270271
.map(argSpec -> intLiteral(argSpec.getIndex()))
271272
.collect(Collectors.toList());
272273
final VariableDeclarator parameterIndices =

buildSrc/call-site-instrumentation-plugin/src/test/java/datadog/trace/plugin/csi/impl/AdviceGeneratorTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,44 @@ void partialArgumentsWithBeforeAdvice() {
426426
});
427427
}
428428

429+
/**
430+
* Captures two of three arguments positionally. Before the fix, this would throw a
431+
* ClassCastException because ArgumentSpecification does not implement Comparable and the
432+
* generator called Stream.sorted() without an explicit comparator.
433+
*/
434+
@CallSite(spi = CallSites.class)
435+
public static class MultiplePartialArgumentsBeforeAdvice {
436+
@CallSite.Before(
437+
"java.lang.String java.lang.String.format(java.util.Locale, java.lang.String, java.lang.Object[])")
438+
public static void before(
439+
@CallSite.Argument(0) java.util.Locale locale, @CallSite.Argument(1) String format) {}
440+
}
441+
442+
@Test
443+
void multiplePartialArgumentsWithBeforeAdvice() {
444+
CallSiteSpecification spec =
445+
buildClassSpecification(MultiplePartialArgumentsBeforeAdvice.class);
446+
AdviceGenerator generator = buildAdviceGenerator(buildDir);
447+
448+
CallSiteResult result = generator.generate(spec);
449+
450+
assertNoErrors(result);
451+
CallSiteAssert asserter = assertCallSites(result.getFile());
452+
asserter.advices(
453+
0,
454+
advice -> {
455+
advice.pointcut(
456+
"java/lang/String",
457+
"format",
458+
"(Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;");
459+
advice.statements(
460+
"int[] parameterIndices = new int[] { 0, 1 };",
461+
"handler.dupParameters(descriptor, parameterIndices, null);",
462+
"handler.advice(\"datadog/trace/plugin/csi/impl/AdviceGeneratorTest$MultiplePartialArgumentsBeforeAdvice\", \"before\", \"(Ljava/util/Locale;Ljava/lang/String;)V\");",
463+
"handler.method(opcode, owner, name, descriptor, isInterface);");
464+
});
465+
}
466+
429467
@CallSite(spi = CallSites.class)
430468
public static class SuperTypeReturnAdvice {
431469
@CallSite.After("void java.lang.StringBuilder.<init>(java.lang.String)")

0 commit comments

Comments
 (0)