2121import java .util .Set ;
2222import java .util .WeakHashMap ;
2323import java .util .concurrent .CopyOnWriteArrayList ;
24+ import java .util .function .Supplier ;
2425import net .bytebuddy .description .type .TypeDescription ;
2526import net .bytebuddy .dynamic .ClassFileLocator ;
2627import net .bytebuddy .dynamic .DynamicType ;
@@ -42,7 +43,10 @@ public class HelperInjector implements Instrumenter.TransformingAdvice {
4243 private final AdviceShader adviceShader ;
4344 private final String requestingName ;
4445
45- private final Set <String > helperClassNames ;
46+ // Helper names are resolved lazily: when a supplier is provided, resolution is deferred to the
47+ // first transform() so that we don't load the generated $Muzzle class at agent install.
48+ private final Supplier <String []> helperClassNamesSupplier ;
49+ private volatile Set <String > helperClassNames ;
4650 private final Map <String , byte []> dynamicTypeMap = new LinkedHashMap <>();
4751
4852 private final Map <ClassLoader , Boolean > injectedClassLoaders =
@@ -77,9 +81,27 @@ public HelperInjector(
7781 this .requestingName = requestingName ;
7882 this .adviceShader = adviceShader ;
7983
84+ this .helperClassNamesSupplier = null ;
8085 this .helperClassNames = new LinkedHashSet <>(asList (helperClassNames ));
8186 }
8287
88+ /**
89+ * Construct HelperInjector whose helper names are resolved lazily on first {@link #transform}, to
90+ * avoid resolving them (which may load the generated {@code $Muzzle} class) at agent install.
91+ */
92+ public HelperInjector (
93+ final boolean useAgentCodeSource ,
94+ final AdviceShader adviceShader ,
95+ final String requestingName ,
96+ final Supplier <String []> helperClassNamesSupplier ) {
97+ this .useAgentCodeSource = useAgentCodeSource ;
98+ this .requestingName = requestingName ;
99+ this .adviceShader = adviceShader ;
100+
101+ this .helperClassNamesSupplier = helperClassNamesSupplier ;
102+ this .helperClassNames = null ;
103+ }
104+
83105 public HelperInjector (
84106 final boolean useAgentCodeSource ,
85107 final String requestingName ,
@@ -88,11 +110,27 @@ public HelperInjector(
88110 this .requestingName = requestingName ;
89111 this .adviceShader = null ;
90112
113+ this .helperClassNamesSupplier = null ;
91114 helperClassNames = helperMap .keySet ();
92115 dynamicTypeMap .putAll (helperMap );
93116 }
94117
95- private Map <String , byte []> getHelperMap () throws IOException {
118+ /** Resolves helper class names, deferring to the supplier when one's provided. */
119+ private Set <String > resolveHelperClassNames () {
120+ Set <String > names = helperClassNames ;
121+ if (names == null ) {
122+ synchronized (this ) {
123+ names = helperClassNames ;
124+ if (names == null ) {
125+ names = new LinkedHashSet <>(asList (helperClassNamesSupplier .get ()));
126+ helperClassNames = names ;
127+ }
128+ }
129+ }
130+ return names ;
131+ }
132+
133+ private Map <String , byte []> getHelperMap (final Set <String > helperClassNames ) throws IOException {
96134 if (dynamicTypeMap .isEmpty ()) {
97135 final Map <String , byte []> classnameToBytes = new LinkedHashMap <>();
98136 for (String helperName : helperClassNames ) {
@@ -117,6 +155,7 @@ public DynamicType.Builder<?> transform(
117155 ClassLoader classLoader ,
118156 final JavaModule module ,
119157 final ProtectionDomain pd ) {
158+ final Set <String > helperClassNames = resolveHelperClassNames ();
120159 if (!helperClassNames .isEmpty ()) {
121160 if (classLoader == null ) {
122161 throw new UnsupportedOperationException (
@@ -135,7 +174,7 @@ public DynamicType.Builder<?> transform(
135174 String .join ("," , helperClassNames ));
136175 }
137176
138- final Map <String , byte []> classnameToBytes = getHelperMap ();
177+ final Map <String , byte []> classnameToBytes = getHelperMap (helperClassNames );
139178 final Collection <Class <?>> classes = injectClassLoader (classLoader , classnameToBytes );
140179
141180 // all datadog helper classes are in the unnamed module
0 commit comments