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

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

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

81.1
/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassReloadingStrategy.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.dynamic.loading;
17

18
import net.bytebuddy.agent.builder.AgentBuilder;
19
import net.bytebuddy.build.AccessControllerPlugin;
20
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
21
import net.bytebuddy.description.type.TypeDescription;
22
import net.bytebuddy.dynamic.ClassFileLocator;
23
import net.bytebuddy.utility.JavaModule;
24
import net.bytebuddy.utility.dispatcher.JavaDispatcher;
25
import net.bytebuddy.utility.nullability.AlwaysNull;
26
import net.bytebuddy.utility.nullability.MaybeNull;
27

28
import java.io.File;
29
import java.io.IOException;
30
import java.lang.instrument.ClassDefinition;
31
import java.lang.instrument.ClassFileTransformer;
32
import java.lang.instrument.Instrumentation;
33
import java.lang.instrument.UnmodifiableClassException;
34
import java.security.PrivilegedAction;
35
import java.security.ProtectionDomain;
36
import java.util.Arrays;
37
import java.util.Collections;
38
import java.util.HashMap;
39
import java.util.LinkedHashMap;
40
import java.util.List;
41
import java.util.Map;
42
import java.util.concurrent.ConcurrentHashMap;
43

44
/**
45
 * <p>
46
 * The class reloading strategy allows to redefine loaded {@link java.lang.Class}es. Note that this strategy
47
 * will always attempt to load an existing class prior to its redefinition, even if this class is not yet loaded.
48
 * </p>
49
 * <p>
50
 * <b>Note</b>: In order to redefine any type, neither its name or its modifiers must be changed. Furthermore, no
51
 * fields or methods must be removed or added. This makes this strategy generally incompatible to applying it to a
52
 * rebased class definition as by {@link net.bytebuddy.ByteBuddy#rebase(Class)} which copies the original method
53
 * implementations to additional methods. Furthermore, even the {@link net.bytebuddy.ByteBuddy#redefine(Class)}
54
 * adds a method if the original class contains an explicit <i>class initializer</i>. For these reasons, it is not
55
 * recommended to use this {@link ClassLoadingStrategy} with arbitrary classes.
56
 * </p>
57
 */
58
@HashCodeAndEqualsPlugin.Enhance
59
public class ClassReloadingStrategy implements ClassLoadingStrategy<ClassLoader> {
60

61
    /**
62
     * A dispatcher to use with some methods of the {@link Instrumentation} API.
63
     */
64
    protected static final Dispatcher DISPATCHER = doPrivileged(JavaDispatcher.of(Dispatcher.class));
1✔
65

66
    /**
67
     * This instance's instrumentation.
68
     */
69
    private final Instrumentation instrumentation;
70

71
    /**
72
     * An strategy which performs the actual redefinition of a {@link java.lang.Class}.
73
     */
74
    private final Strategy strategy;
75

76
    /**
77
     * The strategy to apply for injecting classes into the bootstrap class loader.
78
     */
79
    private final BootstrapInjection bootstrapInjection;
80

81
    /**
82
     * The preregistered types of this instance.
83
     */
84
    private final Map<String, Class<?>> preregisteredTypes;
85

86
    /**
87
     * Creates a class reloading strategy for the given instrumentation using an explicit transformation strategy which
88
     * is represented by an {@link Strategy}. The given instrumentation
89
     * must support the strategy's transformation type.
90
     *
91
     * @param instrumentation The instrumentation to be used by this reloading strategy.
92
     * @param strategy        A strategy which performs the actual redefinition of a {@link java.lang.Class}.
93
     */
94
    public ClassReloadingStrategy(Instrumentation instrumentation, Strategy strategy) {
95
        this(instrumentation,
1✔
96
                strategy,
97
                BootstrapInjection.Disabled.INSTANCE,
98
                Collections.<String, Class<?>>emptyMap());
1✔
99
    }
1✔
100

101
    /**
102
     * Creates a new class reloading strategy.
103
     *
104
     * @param instrumentation    The instrumentation to be used by this reloading strategy.
105
     * @param strategy           An strategy which performs the actual redefinition of a {@link java.lang.Class}.
106
     * @param bootstrapInjection The bootstrap class loader injection strategy to use.
107
     * @param preregisteredTypes The preregistered types of this instance.
108
     */
109
    protected ClassReloadingStrategy(Instrumentation instrumentation,
110
                                     Strategy strategy,
111
                                     BootstrapInjection bootstrapInjection,
112
                                     Map<String, Class<?>> preregisteredTypes) {
1✔
113
        this.instrumentation = instrumentation;
1✔
114
        this.strategy = strategy.validate(instrumentation);
1✔
115
        this.bootstrapInjection = bootstrapInjection;
1✔
116
        this.preregisteredTypes = preregisteredTypes;
1✔
117
    }
1✔
118

119
    /**
120
     * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
121
     *
122
     * @param action The action to execute from a privileged context.
123
     * @param <T>    The type of the action's resolved value.
124
     * @return The action's resolved value.
125
     */
126
    @AccessControllerPlugin.Enhance
127
    private static <T> T doPrivileged(PrivilegedAction<T> action) {
128
        return action.run();
×
129
    }
130

131
    /**
132
     * Creates a class reloading strategy for the given instrumentation. The given instrumentation must either
133
     * support {@link java.lang.instrument.Instrumentation#isRedefineClassesSupported()} or
134
     * {@link java.lang.instrument.Instrumentation#isRetransformClassesSupported()}. If both modes are supported,
135
     * classes will be transformed using a class retransformation.
136
     *
137
     * @param instrumentation The instrumentation to be used by this reloading strategy.
138
     * @return A suitable class reloading strategy.
139
     */
140
    public static ClassReloadingStrategy of(Instrumentation instrumentation) {
141
        if (DISPATCHER.isRetransformClassesSupported(instrumentation)) {
1✔
142
            return new ClassReloadingStrategy(instrumentation, Strategy.RETRANSFORMATION);
1✔
143
        } else if (instrumentation.isRedefineClassesSupported()) {
1✔
144
            return new ClassReloadingStrategy(instrumentation, Strategy.REDEFINITION);
1✔
145
        } else {
146
            throw new IllegalArgumentException("Instrumentation does not support reloading of classes: " + instrumentation);
1✔
147
        }
148
    }
149

150
    /**
151
     * Resolves the instrumentation provided by {@code net.bytebuddy.agent.Installer}.
152
     *
153
     * @return The installed instrumentation instance.
154
     */
155
    private static Instrumentation resolveByteBuddyAgentInstrumentation() {
156
        try {
157
            Class<?> installer = ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.Installer");
1✔
158
            JavaModule source = JavaModule.ofType(AgentBuilder.class), target = JavaModule.ofType(installer);
1✔
159
            if (source != null && !source.canRead(target)) {
1✔
160
                Class<?> module = Class.forName("java.lang.Module");
×
161
                module.getMethod("addReads", module).invoke(source.unwrap(), target.unwrap());
×
162
            }
163
            return (Instrumentation) installer.getMethod("getInstrumentation").invoke(null);
1✔
164
        } catch (RuntimeException exception) {
×
165
            throw exception;
×
166
        } catch (Exception exception) {
×
167
            throw new IllegalStateException("The Byte Buddy agent is not installed or not accessible", exception);
×
168
        }
169
    }
170

171
    /**
172
     * <p>
173
     * Obtains a {@link net.bytebuddy.dynamic.loading.ClassReloadingStrategy} from an installed Byte Buddy agent. This
174
     * agent must be installed either by adding the {@code byte-buddy-agent.jar} when starting up the JVM by
175
     * </p>
176
     * <p>
177
     * <code>
178
     * java -javaagent:byte-buddy-agent.jar -jar app.jar
179
     * </code>
180
     * </p>
181
     * or after the start up using the Attach API. A convenience installer for the OpenJDK is provided by the
182
     * {@code ByteBuddyAgent} within the {@code byte-buddy-agent} module. The strategy is determined by the agent's support
183
     * for redefinition where are retransformation is preferred over a redefinition.
184
     *
185
     * @return A class reloading strategy which uses the Byte Buddy agent's {@link java.lang.instrument.Instrumentation}.
186
     */
187
    public static ClassReloadingStrategy fromInstalledAgent() {
188
        return ClassReloadingStrategy.of(resolveByteBuddyAgentInstrumentation());
1✔
189
    }
190

191
    /**
192
     * <p>
193
     * Obtains a {@link net.bytebuddy.dynamic.loading.ClassReloadingStrategy} from an installed Byte Buddy agent. This
194
     * agent must be installed either by adding the {@code byte-buddy-agent.jar} when starting up the JVM by
195
     * </p>
196
     * <p>
197
     * <code>
198
     * java -javaagent:byte-buddy-agent.jar -jar app.jar
199
     * </code>
200
     * </p>
201
     * or after the start up using the Attach API. A convenience installer for the OpenJDK is provided by the
202
     * {@code ByteBuddyAgent} within the {@code byte-buddy-agent} module.
203
     *
204
     * @param strategy The strategy to use.
205
     * @return A class reloading strategy which uses the Byte Buddy agent's {@link java.lang.instrument.Instrumentation}.
206
     */
207
    public static ClassReloadingStrategy fromInstalledAgent(Strategy strategy) {
208
        return new ClassReloadingStrategy(resolveByteBuddyAgentInstrumentation(), strategy);
1✔
209
    }
210

211
    /**
212
     * {@inheritDoc}
213
     */
214
    public Map<TypeDescription, Class<?>> load(@MaybeNull ClassLoader classLoader, Map<TypeDescription, byte[]> types) {
215
        Map<String, Class<?>> availableTypes = new HashMap<String, Class<?>>(preregisteredTypes);
1✔
216
        for (Class<?> type : instrumentation.getInitiatedClasses(classLoader)) {
1✔
217
            availableTypes.put(TypeDescription.ForLoadedType.getName(type), type);
1✔
218
        }
219
        Map<Class<?>, ClassDefinition> classDefinitions = new ConcurrentHashMap<Class<?>, ClassDefinition>();
1✔
220
        Map<TypeDescription, Class<?>> loadedClasses = new HashMap<TypeDescription, Class<?>>();
1✔
221
        Map<TypeDescription, byte[]> unloadedClasses = new LinkedHashMap<TypeDescription, byte[]>();
1✔
222
        for (Map.Entry<TypeDescription, byte[]> entry : types.entrySet()) {
1✔
223
            Class<?> type = availableTypes.get(entry.getKey().getName());
1✔
224
            if (type != null) {
1✔
225
                classDefinitions.put(type, new ClassDefinition(type, entry.getValue()));
1✔
226
                loadedClasses.put(entry.getKey(), type);
1✔
227
            } else {
228
                unloadedClasses.put(entry.getKey(), entry.getValue());
1✔
229
            }
230
        }
1✔
231
        try {
232
            strategy.apply(instrumentation, classDefinitions);
1✔
233
            if (!unloadedClasses.isEmpty()) {
1✔
234
                loadedClasses.putAll((classLoader == null
1✔
235
                        ? bootstrapInjection.make(instrumentation)
1✔
236
                        : new ClassInjector.UsingReflection(classLoader)).inject(unloadedClasses));
1✔
237
            }
238
        } catch (ClassNotFoundException exception) {
×
239
            throw new IllegalArgumentException("Could not locate classes for redefinition", exception);
×
240
        } catch (UnmodifiableClassException exception) {
×
241
            throw new IllegalStateException("Cannot redefine specified class", exception);
×
242
        }
1✔
243
        return loadedClasses;
1✔
244
    }
245

246
    /**
247
     * Resets all classes to their original definition while using the first type's class loader as a class file locator.
248
     *
249
     * @param type The types to reset.
250
     * @return This class reloading strategy.
251
     * @throws IOException If a class file locator causes an IO exception.
252
     */
253
    public ClassReloadingStrategy reset(Class<?>... type) throws IOException {
254
        return type.length == 0
1✔
255
                ? this
256
                : reset(ClassFileLocator.ForClassLoader.of(type[0].getClassLoader()), type);
1✔
257
    }
258

259
    /**
260
     * Resets all classes to their original definition.
261
     *
262
     * @param classFileLocator The class file locator to use.
263
     * @param type             The types to reset.
264
     * @return This class reloading strategy.
265
     * @throws IOException If a class file locator causes an IO exception.
266
     */
267
    public ClassReloadingStrategy reset(ClassFileLocator classFileLocator, Class<?>... type) throws IOException {
268
        if (type.length > 0) {
1✔
269
            try {
270
                strategy.reset(instrumentation, classFileLocator, Arrays.asList(type));
1✔
271
            } catch (ClassNotFoundException exception) {
×
272
                throw new IllegalArgumentException("Cannot locate types " + Arrays.toString(type), exception);
×
273
            } catch (UnmodifiableClassException exception) {
×
274
                throw new IllegalStateException("Cannot reset types " + Arrays.toString(type), exception);
×
275
            }
1✔
276
        }
277
        return this;
1✔
278
    }
279

280
    /**
281
     * Enables bootstrap injection for this class reloading strategy.
282
     *
283
     * @param folder The folder to save jar files in that are appended to the bootstrap class path.
284
     * @return A class reloading strategy with bootstrap injection enabled.
285
     */
286
    public ClassReloadingStrategy enableBootstrapInjection(File folder) {
287
        return new ClassReloadingStrategy(instrumentation, strategy, new BootstrapInjection.Enabled(folder), preregisteredTypes);
×
288
    }
289

290
    /**
291
     * Registers a type to be explicitly available without explicit lookup.
292
     *
293
     * @param type The loaded types that are explicitly available.
294
     * @return This class reloading strategy with the given types being explicitly available.
295
     */
296
    public ClassReloadingStrategy preregistered(Class<?>... type) {
297
        Map<String, Class<?>> preregisteredTypes = new HashMap<String, Class<?>>(this.preregisteredTypes);
1✔
298
        for (Class<?> aType : type) {
1✔
299
            preregisteredTypes.put(TypeDescription.ForLoadedType.getName(aType), aType);
1✔
300
        }
301
        return new ClassReloadingStrategy(instrumentation, strategy, bootstrapInjection, preregisteredTypes);
1✔
302
    }
303

304
    /**
305
     * A dispatcher to interact with the instrumentation API.
306
     */
307
    @JavaDispatcher.Proxied("java.lang.instrument.Instrumentation")
308
    protected interface Dispatcher {
309

310
        /**
311
         * Invokes the {@code Instrumentation#isModifiableClass} method.
312
         *
313
         * @param instrumentation The instrumentation instance to invoke the method on.
314
         * @param type            The type to consider for modifiability.
315
         * @return {@code true} if the supplied type can be modified.
316
         */
317
        boolean isModifiableClass(Instrumentation instrumentation, Class<?> type);
318

319
        /**
320
         * Invokes the {@code Instrumentation#isRetransformClassesSupported} method.
321
         *
322
         * @param instrumentation The instrumentation instance to invoke the method on.
323
         * @return {@code true} if the supplied instrumentation instance supports retransformation.
324
         */
325
        boolean isRetransformClassesSupported(Instrumentation instrumentation);
326

327
        /**
328
         * Registers a transformer.
329
         *
330
         * @param instrumentation      The instrumentation instance to invoke the method on.
331
         * @param classFileTransformer The class file transformer to register.
332
         * @param canRetransform       {@code true} if the class file transformer should be invoked upon a retransformation.
333
         */
334
        void addTransformer(Instrumentation instrumentation, ClassFileTransformer classFileTransformer, boolean canRetransform);
335

336
        /**
337
         * Retransforms the supplied classes.
338
         *
339
         * @param instrumentation The instrumentation instance to invoke the method on.
340
         * @param type            The types to retransform.
341
         * @throws UnmodifiableClassException If any of the supplied types are unmodifiable.
342
         */
343
        void retransformClasses(Instrumentation instrumentation, Class<?>[] type) throws UnmodifiableClassException;
344
    }
345

346
    /**
347
     * A strategy which performs the actual redefinition of a {@link java.lang.Class}.
348
     */
349
    public enum Strategy {
1✔
350

351
        /**
352
         * <p>
353
         * Redefines a class using {@link java.lang.instrument.Instrumentation#redefineClasses(java.lang.instrument.ClassDefinition...)}.
354
         * </p>
355
         * <p>
356
         * This strategy can be more efficient. However, the redefinition strategy does not allow to reset VM anonymous classes (e.g.
357
         * classes that represent lambda expressions).
358
         * </p>
359
         */
360
        REDEFINITION(true) {
1✔
361
            @Override
362
            protected void apply(Instrumentation instrumentation, Map<Class<?>, ClassDefinition> classDefinitions)
363
                    throws UnmodifiableClassException, ClassNotFoundException {
364
                instrumentation.redefineClasses(classDefinitions.values().toArray(new ClassDefinition[0]));
1✔
365
            }
1✔
366

367
            @Override
368
            protected Strategy validate(Instrumentation instrumentation) {
369
                if (!instrumentation.isRedefineClassesSupported()) {
1✔
370
                    throw new IllegalArgumentException("Does not support redefinition: " + instrumentation);
1✔
371
                }
372
                return this;
1✔
373
            }
374

375
            @Override
376
            public void reset(Instrumentation instrumentation, ClassFileLocator classFileLocator, List<Class<?>> types)
377
                    throws IOException, UnmodifiableClassException, ClassNotFoundException {
378
                Map<Class<?>, ClassDefinition> classDefinitions = new HashMap<Class<?>, ClassDefinition>(types.size());
1✔
379
                for (Class<?> type : types) {
1✔
380
                    classDefinitions.put(type, new ClassDefinition(type, classFileLocator.locate(TypeDescription.ForLoadedType.getName(type)).resolve()));
1✔
381
                }
1✔
382
                apply(instrumentation, classDefinitions);
1✔
383
            }
1✔
384
        },
385

386
        /**
387
         * <p>
388
         * Redefines a class using
389
         * {@link java.lang.instrument.Instrumentation#retransformClasses(Class[])}. This requires synchronization on
390
         * the {@link net.bytebuddy.dynamic.loading.ClassReloadingStrategy#instrumentation} object.
391
         * </p>
392
         * <p>
393
         * This strategy can require more time to be applied but does not struggle to reset VM anonymous classes (e.g. classes
394
         * that represent lambda expressions).
395
         * </p>
396
         */
397
        RETRANSFORMATION(false) {
1✔
398
            @Override
399
            protected void apply(Instrumentation instrumentation, Map<Class<?>, ClassDefinition> classDefinitions) throws UnmodifiableClassException {
400
                ClassRedefinitionTransformer classRedefinitionTransformer = new ClassRedefinitionTransformer(classDefinitions);
1✔
401
                synchronized (this) {
1✔
402
                    DISPATCHER.addTransformer(instrumentation, classRedefinitionTransformer, REDEFINE_CLASSES);
1✔
403
                    try {
404
                        DISPATCHER.retransformClasses(instrumentation, classDefinitions.keySet().toArray(new Class<?>[0]));
1✔
405
                    } finally {
406
                        instrumentation.removeTransformer(classRedefinitionTransformer);
1✔
407
                    }
408
                }
1✔
409
                classRedefinitionTransformer.assertTransformation();
1✔
410
            }
1✔
411

412
            @Override
413
            protected Strategy validate(Instrumentation instrumentation) {
414
                if (!DISPATCHER.isRetransformClassesSupported(instrumentation)) {
1✔
415
                    throw new IllegalArgumentException("Does not support retransformation: " + instrumentation);
1✔
416
                }
417
                return this;
1✔
418
            }
419

420
            @Override
421
            public void reset(Instrumentation instrumentation, ClassFileLocator classFileLocator, List<Class<?>> types) throws UnmodifiableClassException, ClassNotFoundException {
422
                for (Class<?> type : types) {
1✔
423
                    if (!DISPATCHER.isModifiableClass(instrumentation, type)) {
1✔
424
                        throw new IllegalArgumentException("Cannot modify type: " + type);
×
425
                    }
426
                }
1✔
427
                DISPATCHER.addTransformer(instrumentation, ClassResettingTransformer.INSTANCE, REDEFINE_CLASSES);
1✔
428
                try {
429
                    DISPATCHER.retransformClasses(instrumentation, types.toArray(new Class<?>[0]));
1✔
430
                } finally {
431
                    instrumentation.removeTransformer(ClassResettingTransformer.INSTANCE);
1✔
432
                }
433
            }
1✔
434
        };
435

436
        /**
437
         * Indicates that a class is not redefined.
438
         */
439
        @AlwaysNull
440
        private static final byte[] NO_REDEFINITION = null;
1✔
441

442
        /**
443
         * Instructs a {@link java.lang.instrument.ClassFileTransformer} to redefine classes.
444
         */
445
        private static final boolean REDEFINE_CLASSES = true;
446

447
        /**
448
         * {@code true} if the {@link Strategy#REDEFINITION} strategy
449
         * is used.
450
         */
451
        private final boolean redefinition;
452

453
        /**
454
         * Creates a new strategy.
455
         *
456
         * @param redefinition {@code true} if the {@link Strategy#REDEFINITION} strategy is used.
457
         */
458
        Strategy(boolean redefinition) {
1✔
459
            this.redefinition = redefinition;
1✔
460
        }
1✔
461

462
        /**
463
         * Applies this strategy for the given arguments.
464
         *
465
         * @param instrumentation  The instrumentation to be used for applying the redefinition.
466
         * @param classDefinitions A mapping of the classes to be redefined to their redefinition.
467
         * @throws UnmodifiableClassException If a class is not modifiable.
468
         * @throws ClassNotFoundException     If a class was not found.
469
         */
470
        protected abstract void apply(Instrumentation instrumentation, Map<Class<?>, ClassDefinition> classDefinitions)
471
                throws UnmodifiableClassException, ClassNotFoundException;
472

473
        /**
474
         * Validates that this strategy supports a given transformation type.
475
         *
476
         * @param instrumentation The instrumentation instance being used.
477
         * @return This strategy.
478
         */
479
        protected abstract Strategy validate(Instrumentation instrumentation);
480

481
        /**
482
         * Returns {@code true} if this strategy represents {@link Strategy#REDEFINITION}.
483
         *
484
         * @return {@code true} if this strategy represents {@link Strategy#REDEFINITION}.
485
         */
486
        public boolean isRedefinition() {
487
            return redefinition;
1✔
488
        }
489

490
        /**
491
         * Resets the provided types to their original format.
492
         *
493
         * @param instrumentation  The instrumentation instance to use for class redefinition or retransformation.
494
         * @param classFileLocator The class file locator to use.
495
         * @param types            The types to reset.
496
         * @throws IOException                If an I/O exception occurs.
497
         * @throws UnmodifiableClassException If a class is not modifiable.
498
         * @throws ClassNotFoundException     If a class could not be found.
499
         */
500
        public abstract void reset(Instrumentation instrumentation, ClassFileLocator classFileLocator, List<Class<?>> types) throws IOException, UnmodifiableClassException, ClassNotFoundException;
501

502
        /**
503
         * A class file transformer that applies a given {@link java.lang.instrument.ClassDefinition}.
504
         */
505
        protected static class ClassRedefinitionTransformer implements ClassFileTransformer {
506

507
            /**
508
             * A mapping of classes to be redefined to their redefined class definitions.
509
             */
510
            private final Map<Class<?>, ClassDefinition> redefinedClasses;
511

512
            /**
513
             * Creates a new class redefinition transformer.
514
             *
515
             * @param redefinedClasses A mapping of classes to be redefined to their redefined class definitions.
516
             */
517
            protected ClassRedefinitionTransformer(Map<Class<?>, ClassDefinition> redefinedClasses) {
1✔
518
                this.redefinedClasses = redefinedClasses;
1✔
519
            }
1✔
520

521
            /**
522
             * {@inheritDoc}
523
             */
524
            @MaybeNull
525
            public byte[] transform(@MaybeNull ClassLoader classLoader,
526
                                    @MaybeNull String internalTypeName,
527
                                    @MaybeNull Class<?> classBeingRedefined,
528
                                    @MaybeNull ProtectionDomain protectionDomain,
529
                                    byte[] classfileBuffer) {
530
                if (internalTypeName == null) {
1✔
531
                    return NO_REDEFINITION;
×
532
                }
533
                ClassDefinition redefinedClass = redefinedClasses.remove(classBeingRedefined);
1✔
534
                return redefinedClass == null
1✔
535
                        ? NO_REDEFINITION
1✔
536
                        : redefinedClass.getDefinitionClassFile();
1✔
537
            }
538

539
            /**
540
             * Validates that all given classes were redefined.
541
             */
542
            public void assertTransformation() {
543
                if (!redefinedClasses.isEmpty()) {
1✔
544
                    throw new IllegalStateException("Could not transform: " + redefinedClasses.keySet());
×
545
                }
546
            }
1✔
547
        }
548

549
        /**
550
         * A transformer that indicates that a class file should not be transformed.
551
         */
552
        protected enum ClassResettingTransformer implements ClassFileTransformer {
1✔
553

554
            /**
555
             * The singleton instance.
556
             */
557
            INSTANCE;
1✔
558

559
            /**
560
             * {@inheritDoc}
561
             */
562
            @MaybeNull
563
            public byte[] transform(@MaybeNull ClassLoader classLoader,
564
                                    @MaybeNull String internalTypeName,
565
                                    @MaybeNull Class<?> classBeingRedefined,
566
                                    @MaybeNull ProtectionDomain protectionDomain,
567
                                    byte[] classfileBuffer) {
568
                return NO_REDEFINITION;
1✔
569
            }
570
        }
571
    }
572

573
    /**
574
     * A strategy to apply for injecting classes into the bootstrap class loader.
575
     */
576
    protected interface BootstrapInjection {
577

578
        /**
579
         * Creates a class injector to use.
580
         *
581
         * @param instrumentation The instrumentation of this instance.
582
         * @return A class injector for the bootstrap class loader.
583
         */
584
        ClassInjector make(Instrumentation instrumentation);
585

586
        /**
587
         * A disabled bootstrap injection strategy.
588
         */
589
        enum Disabled implements BootstrapInjection {
1✔
590

591
            /**
592
             * The singleton instance.
593
             */
594
            INSTANCE;
1✔
595

596
            /**
597
             * {@inheritDoc}
598
             */
599
            public ClassInjector make(Instrumentation instrumentation) {
600
                throw new IllegalStateException("Bootstrap injection is not enabled");
×
601
            }
602
        }
603

604
        /**
605
         * An enabled bootstrap class loader injection strategy.
606
         */
607
        @HashCodeAndEqualsPlugin.Enhance
608
        class Enabled implements BootstrapInjection {
609

610
            /**
611
             * The folder to save jar files in.
612
             */
613
            private final File folder;
614

615
            /**
616
             * Creates an enabled bootstrap class injection strategy.
617
             *
618
             * @param folder The folder to save jar files in.
619
             */
620
            protected Enabled(File folder) {
×
621
                this.folder = folder;
×
622
            }
×
623

624
            /**
625
             * {@inheritDoc}
626
             */
627
            public ClassInjector make(Instrumentation instrumentation) {
628
                return ClassInjector.UsingInstrumentation.of(folder, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation);
×
629
            }
630
        }
631
    }
632
}
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