1818import java .util .List ;
1919import java .util .Map ;
2020import java .util .Set ;
21- import java .util .function .Predicate ;
2221import net .bytebuddy .asm .AsmVisitorWrapper ;
2322import net .bytebuddy .description .field .FieldDescription ;
2423import net .bytebuddy .description .field .FieldList ;
@@ -88,8 +87,7 @@ public ClassVisitor wrap(
8887 private static Reference [] generateReferences (
8988 Instrumenter .HasMethodAdvice instrumenter ,
9089 AdviceShader adviceShader ,
91- Set <String > allAdviceClasses ,
92- Predicate <String > shouldRecurse ) {
90+ Set <String > allAdviceClasses ) {
9391 // track sources we've generated references from to avoid recursion
9492 final Set <String > referenceSources = new HashSet <>();
9593 final Map <String , Reference > references = new LinkedHashMap <>();
@@ -107,8 +105,7 @@ private static Reference[] generateReferences(
107105 for (String adviceClass : adviceClasses ) {
108106 if (referenceSources .add (adviceClass )) {
109107 for (Map .Entry <String , Reference > entry :
110- ReferenceCreator .createReferencesFrom (
111- adviceClass , adviceShader , contextClassLoader , shouldRecurse )
108+ ReferenceCreator .createReferencesFrom (adviceClass , adviceShader , contextClassLoader )
112109 .entrySet ()) {
113110 Reference toMerge = references .get (entry .getKey ());
114111 if (null == toMerge ) {
@@ -128,22 +125,19 @@ private byte[] generateMuzzleClass(InstrumenterModule module) {
128125 AdviceShader adviceShader = AdviceShader .with (module .adviceShading ());
129126 HelperClassPredicate helperPredicate = new HelperClassPredicate (this ::isOwnOutput );
130127
131- // Crawl advice, following references into our own helper classes (even in library packages ).
128+ // Crawl advice for muzzle references (only recursing into the instrumentation package ).
132129 Set <String > adviceClasses = new HashSet <>();
133130 List <Reference > allReferences = new ArrayList <>();
134131 for (Instrumenter instrumenter : module .typeInstrumentations ()) {
135132 if (instrumenter instanceof Instrumenter .HasMethodAdvice ) {
136133 Collections .addAll (
137134 allReferences ,
138135 generateReferences (
139- (Instrumenter .HasMethodAdvice ) instrumenter ,
140- adviceShader ,
141- adviceClasses ,
142- helperPredicate ::isHelperClass ));
136+ (Instrumenter .HasMethodAdvice ) instrumenter , adviceShader , adviceClasses ));
143137 }
144138 }
145139
146- // Advice roots are inlined into the target method, not injected, so they aren't helpers .
140+ // Inferred helpers = our classes referenced from the advice, minus the advice roots .
147141 Set <String > inferredHelpers = new LinkedHashSet <>();
148142 for (Reference reference : allReferences ) {
149143 if (!adviceClasses .contains (reference .className )
@@ -153,21 +147,26 @@ private byte[] generateMuzzleClass(InstrumenterModule module) {
153147 }
154148
155149 // Manual additions cover helpers the crawl can't see (reflection, SPI, advice-less injectors).
156- Set <String > helperClasses = new LinkedHashSet <>(inferredHelpers );
157- Collections .addAll (helperClasses , module .helperClassNames ());
158- for (String helper : new ArrayList <>(helperClasses )) {
150+ Set <String > manualHelpers = new LinkedHashSet <>(asList (module .helperClassNames ()));
151+ Set <String > seedHelpers = new LinkedHashSet <>(inferredHelpers );
152+ seedHelpers .addAll (manualHelpers );
153+ for (String helper : new ArrayList <>(seedHelpers )) {
159154 if (isOwnOutput (helper )) {
160- addNestedClasses (helper , helperClasses );
155+ addNestedClasses (helper , seedHelpers );
161156 }
162157 }
163158
164159 String [] orderedHelpers =
165- orderHelpers (helperClasses , Thread .currentThread ().getContextClassLoader ());
160+ discoverAndOrderHelpers (
161+ seedHelpers ,
162+ manualHelpers ,
163+ helperPredicate ,
164+ Thread .currentThread ().getContextClassLoader ());
166165
167- writeInferenceReport (module , adviceClasses .isEmpty (), inferredHelpers , helperClasses );
166+ writeInferenceReport (module , adviceClasses .isEmpty (), inferredHelpers , orderedHelpers );
168167
169168 // Injected helpers are our own classes, so they must not be asserted as library references.
170- Set <String > ignoredClassNames = new HashSet <>(helperClasses );
169+ Set <String > ignoredClassNames = new HashSet <>(asList ( orderedHelpers ) );
171170 Collections .addAll (ignoredClassNames , module .muzzleIgnoredClassNames ());
172171 List <Reference > references = new ArrayList <>();
173172 for (Reference reference : allReferences ) {
@@ -225,19 +224,21 @@ private byte[] generateMuzzleClass(InstrumenterModule module) {
225224 mv .visitMaxs (0 , 0 );
226225 mv .visitEnd ();
227226
228- // Generated: public static String[] helperClassNames()
229- MethodVisitor hv =
230- cw .visitMethod (
231- Opcodes .ACC_PUBLIC | Opcodes .ACC_STATIC ,
232- "helperClassNames" ,
233- "()[Ljava/lang/String;" ,
234- null ,
235- null );
236- hv .visitCode ();
237- writeStrings (hv , orderedHelpers );
238- hv .visitInsn (Opcodes .ARETURN );
239- hv .visitMaxs (0 , 0 );
240- hv .visitEnd ();
227+ // Generated: public static String[] helperClassNames() — omitted for helper-less modules.
228+ if (orderedHelpers .length > 0 ) {
229+ MethodVisitor hv =
230+ cw .visitMethod (
231+ Opcodes .ACC_PUBLIC | Opcodes .ACC_STATIC ,
232+ "helperClassNames" ,
233+ "()[Ljava/lang/String;" ,
234+ null ,
235+ null );
236+ hv .visitCode ();
237+ writeStrings (hv , orderedHelpers );
238+ hv .visitInsn (Opcodes .ARETURN );
239+ hv .visitMaxs (0 , 0 );
240+ hv .visitEnd ();
241+ }
241242
242243 return cw .toByteArray ();
243244 }
@@ -270,28 +271,35 @@ private void addNestedClasses(String className, Set<String> helperClasses) {
270271 }
271272
272273 /**
273- * Load-orders helpers (dependencies first) via {@link HelperScanner}, keeping only our helpers
274- * (the scanner may pull in library classes) and retaining any helper it could not locate.
274+ * Runs {@link HelperScanner} over the seed helpers to both discover their transitive helper
275+ * dependencies and load-order the result (dependencies first). Keeps only our own helpers (plus
276+ * manual additions), dropping library classes the scanner pulls in, and retains every seed even
277+ * if it could not be located.
275278 */
276- private static String [] orderHelpers (Set <String > helpers , ClassLoader loader ) {
277- if (helpers .isEmpty ()) {
279+ private static String [] discoverAndOrderHelpers (
280+ Set <String > seedHelpers ,
281+ Set <String > manualHelpers ,
282+ HelperClassPredicate helperPredicate ,
283+ ClassLoader loader ) {
284+ if (seedHelpers .isEmpty ()) {
278285 return new String [0 ];
279286 }
280- List <String > ordered = new ArrayList <>(helpers . size () );
287+ List <String > ordered = new ArrayList <>();
281288 try {
282289 for (String name :
283290 HelperScanner .withClassDependencies (
284- ClassFileLocator .ForClassLoader .of (loader ), helpers .toArray (new String [0 ]))) {
285- if (helpers .contains (name ) && !ordered .contains (name )) {
291+ ClassFileLocator .ForClassLoader .of (loader ), seedHelpers .toArray (new String [0 ]))) {
292+ if ((helperPredicate .isHelperClass (name ) || manualHelpers .contains (name ))
293+ && !ordered .contains (name )) {
286294 ordered .add (name );
287295 }
288296 }
289297 } catch (Throwable ignore ) {
290- // best-effort ordering; unordered helpers are appended below
298+ // best-effort ordering; unordered seeds are appended below
291299 }
292- for (String name : helpers ) {
293- if (!ordered .contains (name )) {
294- ordered .add (name );
300+ for (String seed : seedHelpers ) {
301+ if (!ordered .contains (seed )) {
302+ ordered .add (seed );
295303 }
296304 }
297305 return ordered .toArray (new String [0 ]);
@@ -305,7 +313,7 @@ private void writeInferenceReport(
305313 InstrumenterModule module ,
306314 boolean adviceLess ,
307315 Set <String > inferredHelpers ,
308- Set < String > allHelpers ) {
316+ String [] injectedHelpers ) {
309317 try {
310318 Set <String > declared = new LinkedHashSet <>(asList (module .helperClassNames ()));
311319 if (declared .isEmpty () && inferredHelpers .isEmpty ()) {
@@ -323,7 +331,7 @@ private void writeInferenceReport(
323331 StringBuilder sb = new StringBuilder ();
324332 sb .append ("module: " ).append (module .getClass ().getName ()).append ('\n' );
325333 sb .append ("has-advice: " ).append (!adviceLess ).append ('\n' );
326- sb .append ("injected-helpers: " ).append (allHelpers . size () ).append ('\n' );
334+ sb .append ("injected-helpers: " ).append (injectedHelpers . length ).append ('\n' );
327335 if (adviceLess && !declared .isEmpty ()) {
328336 sb .append ("crawl-cannot-cover: advice-less module; declared helpers are all manual-only\n " );
329337 }
0 commit comments