• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #762

31 Mar 2025 09:34PM UTC coverage: 85.143% (-0.06%) from 85.202%
#762

push

raphw
Add cast for old JVMs.

29360 of 34483 relevant lines covered (85.14%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

46.6
/byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/ResettableClassFileTransformer.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy.agent.builder;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ByteBuddy;
20
import net.bytebuddy.build.AccessControllerPlugin;
21
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
22
import net.bytebuddy.description.type.TypeDescription;
23
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
24
import net.bytebuddy.dynamic.scaffold.TypeValidation;
25
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
26
import net.bytebuddy.implementation.MethodCall;
27
import net.bytebuddy.implementation.bytecode.assign.Assigner;
28
import net.bytebuddy.utility.JavaModule;
29
import net.bytebuddy.utility.JavaType;
30
import net.bytebuddy.utility.nullability.MaybeNull;
31

32
import java.lang.instrument.ClassFileTransformer;
33
import java.lang.instrument.IllegalClassFormatException;
34
import java.lang.instrument.Instrumentation;
35
import java.lang.reflect.Constructor;
36
import java.lang.reflect.InvocationTargetException;
37
import java.security.PrivilegedAction;
38
import java.security.ProtectionDomain;
39
import java.util.Iterator;
40

41
import static net.bytebuddy.matcher.ElementMatchers.*;
42

43
/**
44
 * A class file transformer that can reset its transformation.
45
 */
46
public interface ResettableClassFileTransformer extends ClassFileTransformer {
47

48
    /**
49
     * Creates an iterator over the transformers that are applied for a given type.
50
     *
51
     * @param typeDescription     A description of a type.
52
     * @param classLoader         The type's class loader or {@code null} if the boot loader.
53
     * @param module              The type's module or {@code null} if the module system is not supported by the current VM.
54
     * @param classBeingRedefined The class being redefined or {@code null} if the type is not yet loaded.
55
     * @param protectionDomain    The type's protection domain or {@code null} if not available.
56
     * @return An iterator over the transformers that are applied by this class file transformer if the given type is discovered.
57
     */
58
    Iterator<AgentBuilder.Transformer> iterator(TypeDescription typeDescription,
59
                                                @MaybeNull ClassLoader classLoader,
60
                                                @MaybeNull JavaModule module,
61
                                                @MaybeNull Class<?> classBeingRedefined,
62
                                                @MaybeNull ProtectionDomain protectionDomain);
63

64
    /**
65
     * <p>
66
     * Deregisters this class file transformer and redefines any transformed class to its state without this
67
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
68
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
69
     * resetting without undoing any code changes.
70
     * </p>
71
     * <p>
72
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
73
     * which built the transformer should be asked to install a new transformer.
74
     * </p>
75
     * <p>
76
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
77
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
78
     * {@link AgentBuilder#disableClassFormatChanges()}.
79
     * </p>
80
     *
81
     * @param instrumentation      The instrumentation instance from which to deregister the transformer.
82
     * @param redefinitionStrategy The redefinition to apply.
83
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
84
     */
85
    boolean reset(Instrumentation instrumentation, AgentBuilder.RedefinitionStrategy redefinitionStrategy);
86

87
    /**
88
     * <p>
89
     * Deregisters this class file transformer and redefines any transformed class to its state without this
90
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
91
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
92
     * resetting without undoing any code changes.
93
     * </p>
94
     * <p>
95
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
96
     * which built the transformer should be asked to install a new transformer.
97
     * </p>
98
     * <p>
99
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
100
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
101
     * {@link AgentBuilder#disableClassFormatChanges()}.
102
     * </p>
103
     *
104
     * @param instrumentation            The instrumentation instance from which to deregister the transformer.
105
     * @param redefinitionStrategy       The redefinition to apply.
106
     * @param redefinitionBatchAllocator The batch allocator to use.
107
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
108
     */
109
    @SuppressWarnings("overloads")
110
    boolean reset(Instrumentation instrumentation,
111
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
112
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator);
113

114
    /**
115
     * <p>
116
     * Deregisters this class file transformer and redefines any transformed class to its state without this
117
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
118
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
119
     * resetting without undoing any code changes.
120
     * </p>
121
     * <p>
122
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
123
     * which built the transformer should be asked to install a new transformer.
124
     * </p>
125
     * <p>
126
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
127
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
128
     * {@link AgentBuilder#disableClassFormatChanges()}.
129
     * </p>
130
     *
131
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
132
     * @param redefinitionStrategy          The redefinition to apply.
133
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
134
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
135
     */
136
    @SuppressWarnings("overloads")
137
    boolean reset(Instrumentation instrumentation,
138
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
139
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy);
140

141
    /**
142
     * <p>
143
     * Deregisters this class file transformer and redefines any transformed class to its state without this
144
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
145
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
146
     * resetting without undoing any code changes.
147
     * </p>
148
     * <p>
149
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
150
     * which built the transformer should be asked to install a new transformer.
151
     * </p>
152
     * <p>
153
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
154
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
155
     * {@link AgentBuilder#disableClassFormatChanges()}.
156
     * </p>
157
     *
158
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
159
     * @param redefinitionStrategy          The redefinition to apply.
160
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
161
     * @param redefinitionBatchAllocator    The batch allocator to use.
162
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
163
     */
164
    boolean reset(Instrumentation instrumentation,
165
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
166
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
167
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy);
168

169
    /**
170
     * <p>
171
     * Deregisters this class file transformer and redefines any transformed class to its state without this
172
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
173
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
174
     * resetting without undoing any code changes.
175
     * </p>
176
     * <p>
177
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
178
     * which built the transformer should be asked to install a new transformer.
179
     * </p>
180
     * <p>
181
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
182
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
183
     * {@link AgentBuilder#disableClassFormatChanges()}.
184
     * </p>
185
     *
186
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
187
     * @param redefinitionStrategy          The redefinition to apply.
188
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
189
     * @param redefinitionListener          The redefinition listener to apply.
190
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
191
     */
192
    @SuppressWarnings("overloads")
193
    boolean reset(Instrumentation instrumentation,
194
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
195
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
196
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
197

198
    /**
199
     * <p>
200
     * Deregisters this class file transformer and redefines any transformed class to its state without this
201
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
202
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
203
     * resetting without undoing any code changes.
204
     * </p>
205
     * <p>
206
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
207
     * which built the transformer should be asked to install a new transformer.
208
     * </p>
209
     * <p>
210
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
211
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
212
     * {@link AgentBuilder#disableClassFormatChanges()}.
213
     * </p>
214
     *
215
     * @param instrumentation            The instrumentation instance from which to deregister the transformer.
216
     * @param redefinitionStrategy       The redefinition to apply.
217
     * @param redefinitionBatchAllocator The batch allocator to use.
218
     * @param redefinitionListener       The redefinition listener to apply.
219
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
220
     */
221
    @SuppressWarnings("overloads")
222
    boolean reset(Instrumentation instrumentation,
223
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
224
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
225
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
226

227
    /**
228
     * <p>
229
     * Deregisters this class file transformer and redefines any transformed class to its state without this
230
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
231
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
232
     * resetting without undoing any code changes.
233
     * </p>
234
     * <p>
235
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
236
     * which built the transformer should be asked to install a new transformer.
237
     * </p>
238
     * <p>
239
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
240
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
241
     * {@link AgentBuilder#disableClassFormatChanges()}.
242
     * </p>
243
     *
244
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
245
     * @param redefinitionStrategy          The redefinition to apply.
246
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
247
     * @param redefinitionBatchAllocator    The batch allocator to use.
248
     * @param redefinitionListener          The redefinition listener to apply.
249
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
250
     */
251
    boolean reset(Instrumentation instrumentation,
252
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
253
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
254
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
255
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
256

257

258
    /**
259
     * <p>
260
     * Deregisters this class file transformer and redefines any transformed class to its state without this
261
     * class file transformer applied, if the supplied redefinition strategy is enabled. If it is not enabled,
262
     * only the {@link net.bytebuddy.agent.builder.AgentBuilder.InstallationListener} is informed about the
263
     * resetting without undoing any code changes.
264
     * </p>
265
     * <p>
266
     * <b>Note</b>: A reset class file transformer should not be reinstalled. Instead, the {@link AgentBuilder}
267
     * which built the transformer should be asked to install a new transformer.
268
     * </p>
269
     * <p>
270
     * <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already
271
     * loaded. Therefore, it is typically required that this class file transformer was built while enabling
272
     * {@link AgentBuilder#disableClassFormatChanges()}.
273
     * </p>
274
     *
275
     * @param instrumentation               The instrumentation instance from which to deregister the transformer.
276
     * @param classFileTransformer          The actual class file transformer to deregister which might be {@code this} instance or any wrapper.
277
     * @param redefinitionStrategy          The redefinition to apply.
278
     * @param redefinitionDiscoveryStrategy The discovery strategy for the types to reset.
279
     * @param redefinitionBatchAllocator    The batch allocator to use.
280
     * @param redefinitionListener          The redefinition listener to apply.
281
     * @return {@code true} if a reset was applied and this transformer was not previously removed.
282
     */
283
    boolean reset(Instrumentation instrumentation,
284
                  ResettableClassFileTransformer classFileTransformer,
285
                  AgentBuilder.RedefinitionStrategy redefinitionStrategy,
286
                  AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
287
                  AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
288
                  AgentBuilder.RedefinitionStrategy.Listener redefinitionListener);
289

290
    /**
291
     * A {@link ResettableClassFileTransformer} which allows for substitution the actual class file transformer without
292
     * changes in the order of the transformer chain.
293
     */
294
    interface Substitutable extends ResettableClassFileTransformer {
295

296
        /**
297
         * Substitutes the current class file transformer.
298
         *
299
         * @param classFileTransformer The class file transformer to use.
300
         */
301
        void substitute(ResettableClassFileTransformer classFileTransformer);
302

303
        /**
304
         * Returns the underlying non-substitutable class file transformer.
305
         *
306
         * @return The underlying non-substitutable class file transformer.
307
         */
308
        ResettableClassFileTransformer unwrap();
309
    }
310

311
    /**
312
     * An abstract base implementation of a {@link ResettableClassFileTransformer}.
313
     */
314
    abstract class AbstractBase implements ResettableClassFileTransformer {
1✔
315

316
        /**
317
         * {@inheritDoc}
318
         */
319
        public boolean reset(Instrumentation instrumentation, AgentBuilder.RedefinitionStrategy redefinitionStrategy) {
320
            return reset(instrumentation, redefinitionStrategy, AgentBuilder.RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE);
1✔
321
        }
322

323
        /**
324
         * {@inheritDoc}
325
         */
326
        public boolean reset(Instrumentation instrumentation,
327
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
328
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator) {
329
            return reset(instrumentation, redefinitionStrategy, redefinitionBatchAllocator, AgentBuilder.RedefinitionStrategy.Listener.NoOp.INSTANCE);
1✔
330
        }
331

332
        /**
333
         * {@inheritDoc}
334
         */
335
        public boolean reset(Instrumentation instrumentation,
336
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
337
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy) {
338
            return reset(instrumentation, redefinitionStrategy, redefinitionDiscoveryStrategy, AgentBuilder.RedefinitionStrategy.Listener.NoOp.INSTANCE);
×
339
        }
340

341
        /**
342
         * {@inheritDoc}
343
         */
344
        public boolean reset(Instrumentation instrumentation,
345
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
346
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
347
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy) {
348
            return reset(instrumentation, redefinitionStrategy, redefinitionDiscoveryStrategy, redefinitionBatchAllocator, AgentBuilder.RedefinitionStrategy.Listener.NoOp.INSTANCE);
×
349
        }
350

351
        /**
352
         * {@inheritDoc}
353
         */
354
        public boolean reset(Instrumentation instrumentation,
355
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
356
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
357
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
358
            return reset(instrumentation, redefinitionStrategy, redefinitionDiscoveryStrategy, AgentBuilder.RedefinitionStrategy.BatchAllocator.ForTotal.INSTANCE, redefinitionListener);
×
359
        }
360

361
        /**
362
         * {@inheritDoc}
363
         */
364
        public boolean reset(Instrumentation instrumentation,
365
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
366
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
367
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
368
            return reset(instrumentation, redefinitionStrategy, AgentBuilder.RedefinitionStrategy.DiscoveryStrategy.SinglePass.INSTANCE, redefinitionBatchAllocator, redefinitionListener);
1✔
369
        }
370

371
        /**
372
         * {@inheritDoc}
373
         */
374
        public boolean reset(Instrumentation instrumentation,
375
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
376
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
377
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
378
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
379
            return reset(instrumentation, this, redefinitionStrategy, redefinitionDiscoveryStrategy, redefinitionBatchAllocator, redefinitionListener);
1✔
380
        }
381
    }
382

383
    /**
384
     * Implements a resettable class file transformer that allows for the delegation of a transformation. Typically implemented
385
     * when using a {@link net.bytebuddy.agent.builder.AgentBuilder.TransformerDecorator}.
386
     */
387
    @HashCodeAndEqualsPlugin.Enhance
388
    abstract class WithDelegation extends AbstractBase {
389

390
        /**
391
         * The class file transformer to delegate to.
392
         */
393
        protected final ResettableClassFileTransformer classFileTransformer;
394

395
        /**
396
         * Creates a new delegating resettable class file transformer.
397
         *
398
         * @param classFileTransformer The class file transformer to delegate to.
399
         */
400
        protected WithDelegation(ResettableClassFileTransformer classFileTransformer) {
1✔
401
            this.classFileTransformer = classFileTransformer;
1✔
402
        }
1✔
403

404
        /**
405
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
406
         *
407
         * @param action The action to execute from a privileged context.
408
         * @param <T>    The type of the action's resolved value.
409
         * @return The action's resolved value.
410
         */
411
        @AccessControllerPlugin.Enhance
412
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
413
            return action.run();
×
414
        }
415

416
        /**
417
         * Creates a resettable class file transformer that wraps another transformer and adds a callback to the
418
         * beginning and end of each transformation. If the module system is supported on the current JVM, the supplied
419
         * module is retained.
420
         *
421
         * @param classFileTransformer The class file transformer to delegate to.
422
         * @param callback             The callback to invoke.
423
         * @return A resettable class file transformer that delegates while also invoking the callback.
424
         */
425
        public static ResettableClassFileTransformer of(ResettableClassFileTransformer classFileTransformer, Callback<?> callback) {
426
            return WithCallback.DISPATCHER.make(classFileTransformer, callback);
1✔
427
        }
428

429
        /**
430
         * {@inheritDoc}
431
         */
432
        public Iterator<AgentBuilder.Transformer> iterator(TypeDescription typeDescription,
433
                                                           @MaybeNull ClassLoader classLoader,
434
                                                           @MaybeNull JavaModule module,
435
                                                           @MaybeNull Class<?> classBeingRedefined,
436
                                                           @MaybeNull ProtectionDomain protectionDomain) {
437
            return classFileTransformer.iterator(typeDescription, classLoader, module, classBeingRedefined, protectionDomain);
×
438
        }
439

440
        /**
441
         * {@inheritDoc}
442
         */
443
        public boolean reset(Instrumentation instrumentation,
444
                             ResettableClassFileTransformer classFileTransformer,
445
                             AgentBuilder.RedefinitionStrategy redefinitionStrategy,
446
                             AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
447
                             AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
448
                             AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
449
            return this.classFileTransformer.reset(instrumentation,
1✔
450
                    classFileTransformer,
451
                    redefinitionStrategy,
452
                    redefinitionDiscoveryStrategy,
453
                    redefinitionBatchAllocator,
454
                    redefinitionListener);
455
        }
456

457
        /**
458
         * A standard implementation of a substitutable {@link ResettableClassFileTransformer}.
459
         */
460
        @HashCodeAndEqualsPlugin.Enhance
461
        protected static class Substitutable extends AbstractBase implements ResettableClassFileTransformer.Substitutable {
462

463
            /**
464
             * A dispatcher for invoking the correct transformer method.
465
             */
466
            private static final Factory DISPATCHER = doPrivileged(Factory.CreationAction.INSTANCE);
1✔
467

468
            /**
469
             * The class file transformer to delegate to.
470
             */
471
            protected volatile ResettableClassFileTransformer classFileTransformer;
472

473
            /**
474
             * Creates a new delegating resettable class file transformer.
475
             *
476
             * @param classFileTransformer The class file transformer to delegate to.
477
             */
478
            protected Substitutable(ResettableClassFileTransformer classFileTransformer) {
1✔
479
                this.classFileTransformer = classFileTransformer;
1✔
480
            }
1✔
481

482
            /**
483
             * Creates a new substitutable class file transformer of another class file transformer.
484
             *
485
             * @param classFileTransformer The class file transformer to wrap.
486
             * @return A substitutable version of the supplied class file transformer.
487
             */
488
            public static Substitutable of(ResettableClassFileTransformer classFileTransformer) {
489
                return DISPATCHER.make(classFileTransformer);
1✔
490
            }
491

492
            /**
493
             * {@inheritDoc}
494
             */
495
            public void substitute(ResettableClassFileTransformer classFileTransformer) {
496
                while (classFileTransformer instanceof Substitutable) {
1✔
497
                    classFileTransformer = ((Substitutable) classFileTransformer).unwrap();
×
498
                }
499
                this.classFileTransformer = classFileTransformer;
1✔
500
            }
1✔
501

502
            /**
503
             * {@inheritDoc}
504
             */
505
            public ResettableClassFileTransformer unwrap() {
506
                return classFileTransformer;
×
507
            }
508

509
            /**
510
             * {@inheritDoc}
511
             */
512
            public Iterator<AgentBuilder.Transformer> iterator(TypeDescription typeDescription,
513
                                                               @MaybeNull ClassLoader classLoader,
514
                                                               @MaybeNull JavaModule module,
515
                                                               @MaybeNull Class<?> classBeingRedefined,
516
                                                               @MaybeNull ProtectionDomain protectionDomain) {
517
                return classFileTransformer.iterator(typeDescription, classLoader, module, classBeingRedefined, protectionDomain);
×
518
            }
519

520
            /**
521
             * {@inheritDoc}
522
             */
523
            public boolean reset(Instrumentation instrumentation,
524
                                 ResettableClassFileTransformer classFileTransformer,
525
                                 AgentBuilder.RedefinitionStrategy redefinitionStrategy,
526
                                 AgentBuilder.RedefinitionStrategy.DiscoveryStrategy redefinitionDiscoveryStrategy,
527
                                 AgentBuilder.RedefinitionStrategy.BatchAllocator redefinitionBatchAllocator,
528
                                 AgentBuilder.RedefinitionStrategy.Listener redefinitionListener) {
529
                return this.classFileTransformer.reset(instrumentation,
×
530
                        classFileTransformer,
531
                        redefinitionStrategy,
532
                        redefinitionDiscoveryStrategy,
533
                        redefinitionBatchAllocator,
534
                        redefinitionListener);
535
            }
536

537
            /**
538
             * {@inheritDoc}
539
             */
540
            public byte[] transform(@MaybeNull ClassLoader classLoader,
541
                                    @MaybeNull String internalName,
542
                                    @MaybeNull Class<?> classBeingRedefined,
543
                                    @MaybeNull ProtectionDomain protectionDomain,
544
                                    byte[] binaryRepresentation) throws IllegalClassFormatException {
545
                return classFileTransformer.transform(classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
546
            }
547

548
            /**
549
             * A factory for creating a subclass of {@link WithDelegation.Substitutable} that supports the module system, if available.
550
             */
551
            interface Factory {
552

553
                /**
554
                 * Creates a new substitutable class file transformer.
555
                 *
556
                 * @param classFileTransformer The class file transformer to wrap.
557
                 * @return The wrapping class file transformer.
558
                 */
559
                Substitutable make(ResettableClassFileTransformer classFileTransformer);
560

561
                /**
562
                 * An action to create a suitable factory.
563
                 */
564
                enum CreationAction implements PrivilegedAction<Factory> {
1✔
565

566
                    /**
567
                     * The singleton instance.
568
                     */
569
                    INSTANCE;
1✔
570

571
                    /**
572
                     * {@inheritDoc}
573
                     */
574
                    @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
575
                    public Factory run() {
576
                        try {
577
                            return new ForJava9CapableVm(new ByteBuddy()
1✔
578
                                    .with(TypeValidation.DISABLED)
1✔
579
                                    .subclass(WithDelegation.Substitutable.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
1✔
580
                                    .name(WithDelegation.Substitutable.class.getName() + "$ByteBuddy$ModuleSupport")
1✔
581
                                    .method(named("transform").and(takesArgument(0, JavaType.MODULE.load())))
×
582
                                    .intercept(MethodCall.invoke(ClassFileTransformer.class.getMethod("transform",
×
583
                                            JavaType.MODULE.load(),
×
584
                                            ClassLoader.class,
585
                                            String.class,
586
                                            Class.class,
587
                                            ProtectionDomain.class,
588
                                            byte[].class)).onField("classFileTransformer").withAllArguments())
×
589
                                    .make()
×
590
                                    .load(WithDelegation.Substitutable.class.getClassLoader(),
×
591
                                            ClassLoadingStrategy.Default.WRAPPER_PERSISTENT.with(WithDelegation.Substitutable.class.getProtectionDomain()))
×
592
                                    .getLoaded()
×
593
                                    .getConstructor(ResettableClassFileTransformer.class));
×
594
                        } catch (Exception ignored) {
1✔
595
                            return ForLegacyVm.INSTANCE;
1✔
596
                        }
597
                    }
598
                }
599

600
                /**
601
                 * A factory for creating a substitutable class file transformer when the module system is supported.
602
                 */
603
                @HashCodeAndEqualsPlugin.Enhance
604
                class ForJava9CapableVm implements Factory {
605

606
                    /**
607
                     * The constructor to invoke.
608
                     */
609
                    private final Constructor<? extends Substitutable> substitutable;
610

611
                    /**
612
                     * Creates a new Java 9 capable factory.
613
                     *
614
                     * @param substitutable The constructor to invoke.
615
                     */
616
                    protected ForJava9CapableVm(Constructor<? extends Substitutable> substitutable) {
×
617
                        this.substitutable = substitutable;
×
618
                    }
×
619

620
                    /**
621
                     * {@inheritDoc}
622
                     */
623
                    public Substitutable make(ResettableClassFileTransformer classFileTransformer) {
624
                        try {
625
                            return substitutable.newInstance(classFileTransformer);
×
626
                        } catch (IllegalAccessException exception) {
×
627
                            throw new IllegalStateException("Cannot access " + substitutable, exception);
×
628
                        } catch (InstantiationException exception) {
×
629
                            throw new IllegalStateException("Cannot instantiate " + substitutable.getDeclaringClass(), exception);
×
630
                        } catch (InvocationTargetException exception) {
×
631
                            throw new IllegalStateException("Cannot invoke " + substitutable, exception.getTargetException());
×
632
                        }
633
                    }
634
                }
635

636
                /**
637
                 * A factory for a substitutable class file transformer when the module system is not supported.
638
                 */
639
                enum ForLegacyVm implements Factory {
1✔
640

641
                    /**
642
                     * The singleton instance.
643
                     */
644
                    INSTANCE;
1✔
645

646
                    /**
647
                     * {@inheritDoc}
648
                     */
649
                    public Substitutable make(ResettableClassFileTransformer classFileTransformer) {
650
                        return new WithDelegation.Substitutable(classFileTransformer);
1✔
651
                    }
652
                }
653
            }
654
        }
655

656
        /**
657
         * A class file transformer with a callback.
658
         *
659
         * @param <T> The type of the value that is passed between the callback methods.
660
         */
661
        @HashCodeAndEqualsPlugin.Enhance
662
        protected static class WithCallback<T> extends WithDelegation {
663

664
            /**
665
             * The dispatcher that creates the wrapper instance.
666
             */
667
            private static final Factory DISPATCHER = doPrivileged(Factory.CreationAction.INSTANCE);
1✔
668

669
            /**
670
             * The callback to invoke.
671
             */
672
            private final Callback<T> callback;
673

674
            /**
675
             * Creates a delegating class file transformer with callback.
676
             *
677
             * @param classFileTransformer The class file transformer to delegate to.
678
             * @param callback             The callback to invoke.
679
             */
680
            protected WithCallback(ResettableClassFileTransformer classFileTransformer, Callback<T> callback) {
681
                super(classFileTransformer);
1✔
682
                this.callback = callback;
1✔
683
            }
1✔
684

685
            /**
686
             * {@inheritDoc}
687
             */
688
            @MaybeNull
689
            public byte[] transform(@MaybeNull ClassLoader classLoader,
690
                                    @MaybeNull String internalName,
691
                                    @MaybeNull Class<?> classBeingRedefined,
692
                                    @MaybeNull ProtectionDomain protectionDomain,
693
                                    byte[] binaryRepresentation) throws IllegalClassFormatException {
694
                T value = callback.onBeforeTransform(null, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
695
                try {
696
                    return classFileTransformer.transform(classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
697
                } finally {
698
                    callback.onAfterTransform(value, null, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
1✔
699
                }
700
            }
701

702
            /**
703
             * A method to delegate to when the module system is supported on the current JVM.
704
             *
705
             * @param rawModule            The instrumented class's {@code java.lang.Module}.
706
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
707
             * @param internalName         The internal name of the instrumented class.
708
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
709
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
710
             * @param binaryRepresentation The class file of the instrumented class in its current state.
711
             * @return The binary representation of the transformed class file or {@code null} if no transformation should be applied.
712
             * @throws IllegalClassFormatException If the class file was found invalid.
713
             */
714
            @MaybeNull
715
            protected byte[] transform(Object rawModule,
716
                                       @MaybeNull ClassLoader classLoader,
717
                                       @MaybeNull String internalName,
718
                                       @MaybeNull Class<?> classBeingRedefined,
719
                                       @MaybeNull ProtectionDomain protectionDomain,
720
                                       byte[] binaryRepresentation) throws IllegalClassFormatException {
721
                JavaModule module = JavaModule.of(rawModule);
×
722
                T value = callback.onBeforeTransform(module, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
×
723
                try {
724
                    return doTransform(rawModule, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
×
725
                } finally {
726
                    callback.onAfterTransform(value, module, classLoader, internalName, classBeingRedefined, protectionDomain, binaryRepresentation);
×
727
                }
728
            }
729

730
            /**
731
             * Callback between the transformation callbacks.
732
             *
733
             * @param rawModule            The instrumented class's {@code java.lang.Module}.
734
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
735
             * @param internalName         The internal name of the instrumented class.
736
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
737
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
738
             * @param binaryRepresentation The class file of the instrumented class in its current state.
739
             * @return The binary representation of the transformed class file or {@code null} if no transformation should be applied.
740
             * @throws IllegalClassFormatException If the class file was found invalid.
741
             */
742
            @MaybeNull
743
            protected byte[] doTransform(Object rawModule,
744
                                         @MaybeNull ClassLoader classLoader,
745
                                         @MaybeNull String internalName,
746
                                         @MaybeNull Class<?> classBeingRedefined,
747
                                         @MaybeNull ProtectionDomain protectionDomain,
748
                                         byte[] binaryRepresentation) throws IllegalClassFormatException {
749
                throw new UnsupportedOperationException();
×
750
            }
751

752
            /**
753
             * A factory that creates a resettable class file transformer depending on the availability of the module system.
754
             */
755
            interface Factory {
756

757
                /**
758
                 * Creates a new substitutable class file transformer.
759
                 *
760
                 * @param classFileTransformer The class file transformer to wrap.
761
                 * @param callback             The callback to invoke.
762
                 * @return The wrapping class file transformer.
763
                 */
764
                ResettableClassFileTransformer make(ResettableClassFileTransformer classFileTransformer, Callback<?> callback);
765

766
                /**
767
                 * An action to create a suitable factory.
768
                 */
769
                enum CreationAction implements PrivilegedAction<Factory> {
1✔
770

771
                    /**
772
                     * The singleton instance.
773
                     */
774
                    INSTANCE;
1✔
775

776
                    /**
777
                     * {@inheritDoc}
778
                     */
779
                    @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
780
                    public Factory run() {
781
                        try {
782
                            return new Factory.ForJava9CapableVm(new ByteBuddy()
1✔
783
                                    .with(TypeValidation.DISABLED)
1✔
784
                                    .subclass(WithCallback.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
1✔
785
                                    .name(WithCallback.class.getName() + "$ByteBuddy$ModuleSupport")
1✔
786
                                    .method(named("transform").and(takesArguments(JavaType.MODULE.load(),
×
787
                                            ClassLoader.class,
788
                                            String.class,
789
                                            Class.class,
790
                                            ProtectionDomain.class,
791
                                            byte[].class)))
792
                                    .intercept(MethodCall.invoke(WithCallback.class.getDeclaredMethod("transform",
×
793
                                            Object.class,
794
                                            ClassLoader.class,
795
                                            String.class,
796
                                            Class.class,
797
                                            ProtectionDomain.class,
798
                                            byte[].class)).withAllArguments())
×
799
                                    .method(named("doTransform").and(takesArguments(Object.class,
×
800
                                            ClassLoader.class,
801
                                            String.class,
802
                                            Class.class,
803
                                            ProtectionDomain.class,
804
                                            byte[].class)))
805
                                    .intercept(MethodCall.invoke(ClassFileTransformer.class.getMethod("transform",
×
806
                                            JavaType.MODULE.load(),
×
807
                                            ClassLoader.class,
808
                                            String.class,
809
                                            Class.class,
810
                                            ProtectionDomain.class,
811
                                            byte[].class)).onField("classFileTransformer").withAllArguments().withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC))
×
812
                                    .make()
×
813
                                    .load(WithCallback.class.getClassLoader(),
×
814
                                            ClassLoadingStrategy.Default.WRAPPER_PERSISTENT.with(WithCallback.class.getProtectionDomain()))
×
815
                                    .getLoaded()
×
816
                                    .getConstructor(ResettableClassFileTransformer.class, Callback.class));
×
817
                        } catch (Exception ignored) {
1✔
818
                            return Factory.ForLegacyVm.INSTANCE;
1✔
819
                        }
820
                    }
821
                }
822

823
                /**
824
                 * A factory for creating a substitutable class file transformer when the module system is supported.
825
                 */
826
                @HashCodeAndEqualsPlugin.Enhance
827
                class ForJava9CapableVm implements Factory {
828

829
                    /**
830
                     * The constructor to invoke.
831
                     */
832
                    private final Constructor<? extends ResettableClassFileTransformer> withCallback;
833

834
                    /**
835
                     * Creates a new Java 9 capable factory.
836
                     *
837
                     * @param withCallback The constructor to invoke.
838
                     */
839
                    protected ForJava9CapableVm(Constructor<? extends ResettableClassFileTransformer> withCallback) {
×
840
                        this.withCallback = withCallback;
×
841
                    }
×
842

843
                    /**
844
                     * {@inheritDoc}
845
                     */
846
                    public ResettableClassFileTransformer make(ResettableClassFileTransformer classFileTransformer, Callback<?> callback) {
847
                        try {
848
                            return withCallback.newInstance(classFileTransformer, callback);
×
849
                        } catch (IllegalAccessException exception) {
×
850
                            throw new IllegalStateException("Cannot access " + withCallback, exception);
×
851
                        } catch (InstantiationException exception) {
×
852
                            throw new IllegalStateException("Cannot instantiate " + withCallback.getDeclaringClass(), exception);
×
853
                        } catch (InvocationTargetException exception) {
×
854
                            throw new IllegalStateException("Cannot invoke " + withCallback, exception.getTargetException());
×
855
                        }
856
                    }
857
                }
858

859
                /**
860
                 * A factory for a substitutable class file transformer when the module system is not supported.
861
                 */
862
                enum ForLegacyVm implements Factory {
1✔
863

864
                    /**
865
                     * The singleton instance.
866
                     */
867
                    INSTANCE;
1✔
868

869
                    /**
870
                     * {@inheritDoc}
871
                     */
872
                    @SuppressWarnings({"unchecked", "rawtypes"})
873
                    public ResettableClassFileTransformer make(ResettableClassFileTransformer classFileTransformer,
874
                                                               Callback<?> callback) {
875
                        return new WithCallback(classFileTransformer, callback);
1✔
876
                    }
877
                }
878
            }
879
        }
880

881
        /**
882
         * A callback that is invoked upon class file transformation.
883
         *
884
         * @param <T> The type of the value that is passed between the callback methods.
885
         */
886
        public interface Callback<T> {
887

888
            /**
889
             * Invoked before the transformation is applied.
890
             *
891
             * @param module               The instrumented class's Java module or {@code null} if the module system is not supported.
892
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
893
             * @param internalName         The internal name of the instrumented class.
894
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
895
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
896
             * @param binaryRepresentation The class file of the instrumented class in its current state.
897
             * @return A value to pass to the method that is invoked after transformation or {@code null}.
898
             * @throws IllegalClassFormatException If the class file was found invalid.
899
             */
900
            @MaybeNull
901
            T onBeforeTransform(@MaybeNull JavaModule module,
902
                                @MaybeNull ClassLoader classLoader,
903
                                @MaybeNull String internalName,
904
                                @MaybeNull Class<?> classBeingRedefined,
905
                                @MaybeNull ProtectionDomain protectionDomain,
906
                                byte[] binaryRepresentation) throws IllegalClassFormatException;
907

908
            /**
909
             * Invoked after the transformation is applied.
910
             *
911
             * @param value                The value that was returned before transformation.
912
             * @param module               The instrumented class's Java module or {@code null} if the module system is not supported.
913
             * @param classLoader          The type's class loader or {@code null} if the type is loaded by the bootstrap loader.
914
             * @param internalName         The internal name of the instrumented class.
915
             * @param classBeingRedefined  The loaded {@link Class} being redefined or {@code null} if no such class exists.
916
             * @param protectionDomain     The instrumented type's protection domain or {@code null} if not available.
917
             * @param binaryRepresentation The class file of the instrumented class in its current state.
918
             * @throws IllegalClassFormatException If the class file was found invalid.
919
             */
920
            void onAfterTransform(@MaybeNull T value,
921
                                  @MaybeNull JavaModule module,
922
                                  @MaybeNull ClassLoader classLoader,
923
                                  @MaybeNull String internalName,
924
                                  @MaybeNull Class<?> classBeingRedefined,
925
                                  @MaybeNull ProtectionDomain protectionDomain,
926
                                  byte[] binaryRepresentation) throws IllegalClassFormatException;
927
        }
928
    }
929
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc