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

raphw / byte-buddy / #873

11 Apr 2026 07:35AM UTC coverage: 83.95% (+0.001%) from 83.949%
#873

push

raphw
Disable unsafe dispatcher by default when run on Java 25 or later.

3 of 3 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

29970 of 35700 relevant lines covered (83.95%)

0.84 hits per line

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

54.51
/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/loading/ClassInjector.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 com.sun.jna.FunctionMapper;
19
import com.sun.jna.JNIEnv;
20
import com.sun.jna.LastErrorException;
21
import com.sun.jna.Library;
22
import com.sun.jna.Native;
23
import com.sun.jna.NativeLibrary;
24
import com.sun.jna.Platform;
25
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
26
import net.bytebuddy.ByteBuddy;
27
import net.bytebuddy.ClassFileVersion;
28
import net.bytebuddy.asm.MemberRemoval;
29
import net.bytebuddy.build.AccessControllerPlugin;
30
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
31
import net.bytebuddy.description.modifier.Visibility;
32
import net.bytebuddy.description.type.PackageDescription;
33
import net.bytebuddy.description.type.TypeDescription;
34
import net.bytebuddy.dynamic.ClassFileLocator;
35
import net.bytebuddy.dynamic.DynamicType;
36
import net.bytebuddy.dynamic.scaffold.TypeValidation;
37
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
38
import net.bytebuddy.implementation.FixedValue;
39
import net.bytebuddy.implementation.MethodCall;
40
import net.bytebuddy.utility.GraalImageCode;
41
import net.bytebuddy.utility.JavaModule;
42
import net.bytebuddy.utility.JavaType;
43
import net.bytebuddy.utility.RandomString;
44
import net.bytebuddy.utility.dispatcher.JavaDispatcher;
45
import net.bytebuddy.utility.nullability.AlwaysNull;
46
import net.bytebuddy.utility.nullability.MaybeNull;
47
import net.bytebuddy.utility.nullability.UnknownNull;
48
import net.bytebuddy.utility.privilege.GetMethodAction;
49

50
import java.io.File;
51
import java.io.FileOutputStream;
52
import java.io.IOException;
53
import java.io.OutputStream;
54
import java.lang.instrument.Instrumentation;
55
import java.lang.reflect.AccessibleObject;
56
import java.lang.reflect.Field;
57
import java.lang.reflect.InvocationTargetException;
58
import java.lang.reflect.Method;
59
import java.net.URL;
60
import java.security.Permission;
61
import java.security.PrivilegedAction;
62
import java.security.ProtectionDomain;
63
import java.util.Collections;
64
import java.util.HashMap;
65
import java.util.HashSet;
66
import java.util.LinkedHashMap;
67
import java.util.LinkedHashSet;
68
import java.util.List;
69
import java.util.Locale;
70
import java.util.Map;
71
import java.util.Set;
72
import java.util.jar.JarEntry;
73
import java.util.jar.JarFile;
74
import java.util.jar.JarOutputStream;
75
import java.util.zip.ZipFile;
76

77
import static net.bytebuddy.matcher.ElementMatchers.any;
78
import static net.bytebuddy.matcher.ElementMatchers.named;
79

80
/**
81
 * <p>
82
 * A class injector is capable of injecting classes into a {@link java.lang.ClassLoader} without
83
 * requiring the class loader to being able to explicitly look up these classes.
84
 * </p>
85
 * <p>
86
 * <b>Important</b>: Byte Buddy does not supply privileges when injecting code. When using a {@code java.lang.SecurityManager},
87
 * the user of this injector is responsible for providing access to non-public properties.
88
 * </p>
89
 */
90
public interface ClassInjector {
91

92
    /**
93
     * Determines the default behavior for type injections when a type is already loaded.
94
     */
95
    boolean ALLOW_EXISTING_TYPES = false;
96

97
    /**
98
     * Indicates if this class injector is available on the current VM.
99
     *
100
     * @return {@code true} if this injector is available on the current VM.
101
     */
102
    boolean isAlive();
103

104
    /**
105
     * Injects the given types into the represented class loader.
106
     *
107
     * @param types            The types to load via injection.
108
     * @param classFileLocator The class file locator to use for resolving binary representations.
109
     * @return The loaded types that were passed as arguments.
110
     */
111
    Map<TypeDescription, Class<?>> inject(Set<? extends TypeDescription> types, ClassFileLocator classFileLocator);
112

113
    /**
114
     * Injects the given types into the represented class loader.
115
     *
116
     * @param names            The names of the types to load via injection.
117
     * @param classFileLocator The class file locator to use for resolving binary representations.
118
     * @return The loaded types that were passed as arguments.
119
     */
120
    Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator);
121

122
    /**
123
     * Injects the given types into the represented class loader.
124
     *
125
     * @param types The types to load via injection.
126
     * @return The loaded types that were passed as arguments.
127
     */
128
    Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types);
129

130
    /**
131
     * Injects the given types into the represented class loader.
132
     *
133
     * @param types The names of the type to load via injection.
134
     * @return The loaded types that were passed as arguments.
135
     */
136
    Map<String, Class<?>> injectRaw(Map<String, byte[]> types);
137

138
    /**
139
     * An abstract base implementation of a class injector.
140
     */
141
    abstract class AbstractBase implements ClassInjector {
1✔
142

143
        /**
144
         * A permission for the {@code suppressAccessChecks} permission or {@code null} if not supported.
145
         */
146
        @MaybeNull
147
        protected static final Permission SUPPRESS_ACCESS_CHECKS = toSuppressAccessChecks();
1✔
148

149
        /**
150
         * Returns a permission for the {@code suppressAccessChecks} permission or {@code null} if not supported.
151
         *
152
         * @return A permission for the {@code suppressAccessChecks} permission or {@code null} if not supported.
153
         */
154
        @MaybeNull
155
        @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but return null.")
156
        private static Permission toSuppressAccessChecks() {
157
            try {
158
                return (Permission) Class.forName("java.lang.reflect.ReflectPermission")
1✔
159
                        .getConstructor(String.class)
1✔
160
                        .newInstance("suppressAccessChecks");
1✔
161
            } catch (Exception ignored) {
×
162
                return null;
×
163
            }
164
        }
165

166
        /**
167
         * {@inheritDoc}
168
         */
169
        public Map<TypeDescription, Class<?>> inject(Set<? extends TypeDescription> types, ClassFileLocator classFileLocator) {
170
            Set<String> names = new LinkedHashSet<String>();
1✔
171
            for (TypeDescription type : types) {
1✔
172
                names.add(type.getName());
1✔
173
            }
1✔
174
            Map<String, Class<?>> loadedTypes = injectRaw(names, classFileLocator);
1✔
175
            Map<TypeDescription, Class<?>> result = new HashMap<TypeDescription, Class<?>>();
1✔
176
            for (TypeDescription type : types) {
1✔
177
                result.put(type, loadedTypes.get(type.getName()));
1✔
178
            }
1✔
179
            return result;
1✔
180
        }
181

182
        /**
183
         * {@inheritDoc}
184
         */
185
        public Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types) {
186
            Map<String, byte[]> binaryRepresentations = new LinkedHashMap<String, byte[]>();
1✔
187
            for (Map.Entry<? extends TypeDescription, byte[]> entry : types.entrySet()) {
1✔
188
                binaryRepresentations.put(entry.getKey().getName(), entry.getValue());
1✔
189
            }
1✔
190
            Map<String, Class<?>> loadedTypes = injectRaw(binaryRepresentations);
1✔
191
            Map<TypeDescription, Class<?>> result = new HashMap<TypeDescription, Class<?>>();
1✔
192
            for (TypeDescription typeDescription : types.keySet()) {
1✔
193
                result.put(typeDescription, loadedTypes.get(typeDescription.getName()));
1✔
194
            }
1✔
195
            return result;
1✔
196
        }
197

198
        /**
199
         * {@inheritDoc}
200
         */
201
        public Map<String, Class<?>> injectRaw(Map<String, byte[]> types) {
202
            return injectRaw(types.keySet(), new ClassFileLocator.Simple(types));
1✔
203
        }
204
    }
205

206
    /**
207
     * A class injector that uses reflective method calls.
208
     */
209
    @HashCodeAndEqualsPlugin.Enhance
210
    class UsingReflection extends AbstractBase {
211

212
        /**
213
         * The dispatcher to use for accessing a class loader via reflection.
214
         */
215
        private static final Dispatcher.Initializable DISPATCHER = doPrivileged(Dispatcher.CreationAction.INSTANCE);
1✔
216

217
        /**
218
         * A proxy for {@code java.lang.System} to access the security manager if available.
219
         */
220
        private static final System SYSTEM = doPrivileged(JavaDispatcher.of(System.class));
1✔
221

222
        /**
223
         * The {@code java.lang.SecurityManager#checkPermission} method or {@code null} if not available.
224
         */
225
        private static final Method CHECK_PERMISSION = doPrivileged(new GetMethodAction("java.lang.SecurityManager",
1✔
226
                "checkPermission",
227
                Permission.class));
228

229
        /**
230
         * The class loader into which the classes are to be injected.
231
         */
232
        private final ClassLoader classLoader;
233

234
        /**
235
         * The protection domain that is used when loading classes.
236
         */
237
        @MaybeNull
238
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
239
        private final ProtectionDomain protectionDomain;
240

241
        /**
242
         * The package definer to be queried for package definitions.
243
         */
244
        private final PackageDefinitionStrategy packageDefinitionStrategy;
245

246
        /**
247
         * Determines if an exception should be thrown when attempting to load a type that already exists.
248
         */
249
        private final boolean forbidExisting;
250

251
        /**
252
         * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link java.security.ProtectionDomain} and a
253
         * trivial {@link PackageDefinitionStrategy} which does not trigger an error when discovering existent classes.
254
         *
255
         * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
256
         */
257
        public UsingReflection(ClassLoader classLoader) {
258
            this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
259
        }
1✔
260

261
        /**
262
         * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link PackageDefinitionStrategy} where the
263
         * injection of existent classes does not trigger an error.
264
         *
265
         * @param classLoader      The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
266
         * @param protectionDomain The protection domain to apply during class definition.
267
         */
268
        public UsingReflection(ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
269
            this(classLoader,
1✔
270
                    protectionDomain,
271
                    PackageDefinitionStrategy.Trivial.INSTANCE,
272
                    ALLOW_EXISTING_TYPES);
273
        }
1✔
274

275
        /**
276
         * Creates a new injector for the given {@link java.lang.ClassLoader} and {@link java.security.ProtectionDomain}.
277
         *
278
         * @param classLoader               The {@link java.lang.ClassLoader} into which new class definitions are to be injected.Must  not be the bootstrap loader.
279
         * @param protectionDomain          The protection domain to apply during class definition.
280
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
281
         * @param forbidExisting            Determines if an exception should be thrown when attempting to load a type that already exists.
282
         */
283
        public UsingReflection(ClassLoader classLoader,
284
                               @MaybeNull ProtectionDomain protectionDomain,
285
                               PackageDefinitionStrategy packageDefinitionStrategy,
286
                               boolean forbidExisting) {
1✔
287
            if (classLoader == null) {
1✔
288
                throw new IllegalArgumentException("Cannot inject classes into the bootstrap class loader");
1✔
289
            }
290
            this.classLoader = classLoader;
1✔
291
            this.protectionDomain = protectionDomain;
1✔
292
            this.packageDefinitionStrategy = packageDefinitionStrategy;
1✔
293
            this.forbidExisting = forbidExisting;
1✔
294
        }
1✔
295

296
        /**
297
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
298
         *
299
         * @param action The action to execute from a privileged context.
300
         * @param <T>    The type of the action's resolved value.
301
         * @return The action's resolved value.
302
         */
303
        @AccessControllerPlugin.Enhance
304
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
305
            return action.run();
×
306
        }
307

308
        /**
309
         * {@inheritDoc}
310
         */
311
        public boolean isAlive() {
312
            return isAvailable();
1✔
313
        }
314

315
        /**
316
         * {@inheritDoc}
317
         */
318
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
319
            Dispatcher dispatcher = DISPATCHER.initialize();
1✔
320
            Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1✔
321
            for (String name : names) {
1✔
322
                synchronized (dispatcher.getClassLoadingLock(classLoader, name)) {
1✔
323
                    Class<?> type = dispatcher.findClass(classLoader, name);
1✔
324
                    if (type == null) {
1✔
325
                        int packageIndex = name.lastIndexOf('.');
1✔
326
                        if (packageIndex != -1) {
1✔
327
                            String packageName = name.substring(0, packageIndex);
1✔
328
                            PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(classLoader, packageName, name);
1✔
329
                            if (definition.isDefined()) {
1✔
330
                                Package definedPackage = dispatcher.getDefinedPackage(classLoader, packageName);
1✔
331
                                if (definedPackage == null) {
1✔
332
                                    try {
333
                                        dispatcher.definePackage(classLoader,
1✔
334
                                                packageName,
335
                                                definition.getSpecificationTitle(),
1✔
336
                                                definition.getSpecificationVersion(),
1✔
337
                                                definition.getSpecificationVendor(),
1✔
338
                                                definition.getImplementationTitle(),
1✔
339
                                                definition.getImplementationVersion(),
1✔
340
                                                definition.getImplementationVendor(),
1✔
341
                                                definition.getSealBase());
1✔
342
                                    } catch (IllegalStateException exception) {
×
343
                                        // Custom classloaders may call getPackage (instead of getDefinedPackage) from
344
                                        // within definePackage, which can cause the package to be defined in an
345
                                        // ancestor classloader or find a previously defined one from an ancestor. In
346
                                        // this case definePackage will also throw since it considers that package
347
                                        // already loaded and will not allow to define it directly in this classloader.
348
                                        // To make sure this is the case, call getPackage instead of getDefinedPackage
349
                                        // here and verify that we actually have a compatible package defined in an
350
                                        // ancestor classloader. This issue is known to happen on WLS14+JDK11.
351
                                        definedPackage = dispatcher.getPackage(classLoader, packageName);
×
352
                                        if (definedPackage == null) {
×
353
                                            throw exception;
×
354
                                        } else if (!definition.isCompatibleTo(definedPackage)) {
×
355
                                            throw new SecurityException("Sealing violation for package " + packageName + " (getPackage fallback)");
×
356
                                        }
357
                                    }
1✔
358
                                } else if (!definition.isCompatibleTo(definedPackage)) {
1✔
359
                                    throw new SecurityException("Sealing violation for package " + packageName);
×
360
                                }
361
                            }
362
                        }
363
                        try {
364
                            type = dispatcher.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain);
1✔
365
                        } catch (IOException exception) {
×
366
                            throw new IllegalStateException("Could not resolve type description for " + name, exception);
×
367
                        }
1✔
368
                    } else if (forbidExisting) {
1✔
369
                        throw new IllegalStateException("Cannot inject already loaded type: " + type);
1✔
370
                    }
371
                    result.put(name, type);
1✔
372
                }
1✔
373
            }
1✔
374
            return result;
1✔
375
        }
376

377
        /**
378
         * Indicates if this class injection is available on the current VM.
379
         *
380
         * @return {@code true} if this class injection is available.
381
         */
382
        public static boolean isAvailable() {
383
            return DISPATCHER.isAvailable();
1✔
384
        }
385

386
        /**
387
         * Creates a class injector for the system class loader.
388
         *
389
         * @return A class injector for the system class loader.
390
         */
391
        public static ClassInjector ofSystemClassLoader() {
392
            return new UsingReflection(ClassLoader.getSystemClassLoader());
1✔
393
        }
394

395
        /**
396
         * A dispatcher for accessing a {@link ClassLoader} reflectively.
397
         */
398
        protected interface Dispatcher {
399

400
            /**
401
             * Indicates a class that is currently not defined.
402
             */
403
            @AlwaysNull
404
            Class<?> UNDEFINED = null;
1✔
405

406
            /**
407
             * Returns the lock for loading the specified class.
408
             *
409
             * @param classLoader the class loader to inject the class into.
410
             * @param name        The name of the class.
411
             * @return The lock for loading this class.
412
             */
413
            Object getClassLoadingLock(ClassLoader classLoader, String name);
414

415
            /**
416
             * Looks up a class from the given class loader.
417
             *
418
             * @param classLoader The class loader for which a class should be located.
419
             * @param name        The binary name of the class that should be located.
420
             * @return The class for the binary name or {@code null} if no such class is defined for the provided class loader.
421
             */
422
            @MaybeNull
423
            Class<?> findClass(ClassLoader classLoader, String name);
424

425
            /**
426
             * Defines a class for the given class loader.
427
             *
428
             * @param classLoader          The class loader for which a new class should be defined.
429
             * @param name                 The binary name of the class that should be defined.
430
             * @param binaryRepresentation The binary representation of the class.
431
             * @param protectionDomain     The protection domain for the defined class.
432
             * @return The defined, loaded class.
433
             */
434
            Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain);
435

436
            /**
437
             * Looks up a package from a class loader. If the operation is not supported, falls back to {@link #getPackage(ClassLoader, String)}
438
             *
439
             * @param classLoader The class loader to query.
440
             * @param name        The binary name of the package.
441
             * @return The package for the given name as defined by the provided class loader or {@code null} if no such package exists.
442
             */
443
            @MaybeNull
444
            Package getDefinedPackage(ClassLoader classLoader, String name);
445

446
            /**
447
             * Looks up a package from a class loader or its ancestor.
448
             *
449
             * @param classLoader The class loader to query.
450
             * @param name        The binary name of the package.
451
             * @return The package for the given name as defined by the provided class loader or its ancestor, or {@code null} if no such package exists.
452
             */
453
            @MaybeNull
454
            Package getPackage(ClassLoader classLoader, String name);
455

456
            /**
457
             * Defines a package for the given class loader.
458
             *
459
             * @param classLoader           The class loader for which a package is to be defined.
460
             * @param name                  The binary name of the package.
461
             * @param specificationTitle    The specification title of the package or {@code null} if no specification title exists.
462
             * @param specificationVersion  The specification version of the package or {@code null} if no specification version exists.
463
             * @param specificationVendor   The specification vendor of the package or {@code null} if no specification vendor exists.
464
             * @param implementationTitle   The implementation title of the package or {@code null} if no implementation title exists.
465
             * @param implementationVersion The implementation version of the package or {@code null} if no implementation version exists.
466
             * @param implementationVendor  The implementation vendor of the package or {@code null} if no implementation vendor exists.
467
             * @param sealBase              The seal base URL or {@code null} if the package should not be sealed.
468
             * @return The defined package.
469
             */
470
            Package definePackage(ClassLoader classLoader,
471
                                  String name,
472
                                  @MaybeNull String specificationTitle,
473
                                  @MaybeNull String specificationVersion,
474
                                  @MaybeNull String specificationVendor,
475
                                  @MaybeNull String implementationTitle,
476
                                  @MaybeNull String implementationVersion,
477
                                  @MaybeNull String implementationVendor,
478
                                  @MaybeNull URL sealBase);
479

480
            /**
481
             * Initializes a dispatcher to make non-accessible APIs accessible.
482
             */
483
            interface Initializable {
484

485
                /**
486
                 * Indicates if this dispatcher is available.
487
                 *
488
                 * @return {@code true} if this dispatcher is available.
489
                 */
490
                boolean isAvailable();
491

492
                /**
493
                 * Initializes this dispatcher.
494
                 *
495
                 * @return The initialized dispatcher.
496
                 */
497
                Dispatcher initialize();
498

499
                /**
500
                 * Represents an unsuccessfully loaded method lookup.
501
                 */
502
                @HashCodeAndEqualsPlugin.Enhance
503
                class Unavailable implements Dispatcher, Initializable {
504

505
                    /**
506
                     * The reason why this dispatcher is not available.
507
                     */
508
                    private final String message;
509

510
                    /**
511
                     * Creates a new faulty reflection store.
512
                     *
513
                     * @param message The reason why this dispatcher is not available.
514
                     */
515
                    protected Unavailable(String message) {
1✔
516
                        this.message = message;
1✔
517
                    }
1✔
518

519
                    /**
520
                     * {@inheritDoc}
521
                     */
522
                    public boolean isAvailable() {
523
                        return false;
1✔
524
                    }
525

526
                    /**
527
                     * {@inheritDoc}
528
                     */
529
                    public Dispatcher initialize() {
530
                        return this;
1✔
531
                    }
532

533
                    /**
534
                     * {@inheritDoc}
535
                     */
536
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
537
                        return classLoader;
×
538
                    }
539

540
                    /**
541
                     * {@inheritDoc}
542
                     */
543
                    public Class<?> findClass(ClassLoader classLoader, String name) {
544
                        try {
545
                            return classLoader.loadClass(name);
1✔
546
                        } catch (ClassNotFoundException ignored) {
1✔
547
                            return UNDEFINED;
1✔
548
                        }
549
                    }
550

551
                    /**
552
                     * {@inheritDoc}
553
                     */
554
                    public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
555
                        throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
1✔
556
                    }
557

558
                    /**
559
                     * {@inheritDoc}
560
                     */
561
                    public Package getDefinedPackage(ClassLoader classLoader, String name) {
562
                        throw new UnsupportedOperationException("Cannot get defined package using reflection: " + message);
1✔
563
                    }
564

565
                    /**
566
                     * {@inheritDoc}
567
                     */
568
                    public Package getPackage(ClassLoader classLoader, String name) {
569
                        throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
×
570
                    }
571

572
                    /**
573
                     * {@inheritDoc}
574
                     */
575
                    public Package definePackage(ClassLoader classLoader,
576
                                                 String name,
577
                                                 @MaybeNull String specificationTitle,
578
                                                 @MaybeNull String specificationVersion,
579
                                                 @MaybeNull String specificationVendor,
580
                                                 @MaybeNull String implementationTitle,
581
                                                 @MaybeNull String implementationVersion,
582
                                                 @MaybeNull String implementationVendor,
583
                                                 @MaybeNull URL sealBase) {
584
                        throw new UnsupportedOperationException("Cannot define package using injection: " + message);
1✔
585
                    }
586
                }
587
            }
588

589
            /**
590
             * A creation action for a dispatcher.
591
             */
592
            enum CreationAction implements PrivilegedAction<Initializable> {
1✔
593

594
                /**
595
                 * The singleton instance.
596
                 */
597
                INSTANCE;
1✔
598

599
                /**
600
                 * {@inheritDoc}
601
                 */
602
                @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
603
                public Initializable run() {
604
                    try {
605
                        if (JavaModule.isSupported()) {
1✔
606
                            return UsingUnsafe.isAvailable()
×
607
                                    ? UsingUnsafeInjection.make()
×
608
                                    : UsingUnsafeOverride.make();
×
609
                        } else {
610
                            return Direct.make();
1✔
611
                        }
612
                    } catch (InvocationTargetException exception) {
×
613
                        return new Initializable.Unavailable(exception.getTargetException().getMessage());
×
614
                    } catch (Exception exception) {
×
615
                        return new Initializable.Unavailable(exception.getMessage());
×
616
                    }
617
                }
618
            }
619

620
            /**
621
             * A class injection dispatcher that is using reflection on the {@link ClassLoader} methods.
622
             */
623
            @HashCodeAndEqualsPlugin.Enhance
624
            abstract class Direct implements Dispatcher, Initializable {
625

626
                /**
627
                 * An instance of {@link ClassLoader#findLoadedClass(String)}.
628
                 */
629
                protected final Method findLoadedClass;
630

631
                /**
632
                 * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
633
                 */
634
                protected final Method defineClass;
635

636
                /**
637
                 * An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
638
                 */
639
                @UnknownNull
640
                protected final Method getDefinedPackage;
641

642
                /**
643
                 * An instance of {@link ClassLoader#getPackage(String)}.
644
                 */
645
                protected final Method getPackage;
646

647
                /**
648
                 * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
649
                 */
650
                protected final Method definePackage;
651

652
                /**
653
                 * Creates a new direct injection dispatcher.
654
                 *
655
                 * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
656
                 * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
657
                 * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
658
                 * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
659
                 * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
660
                 */
661
                protected Direct(Method findLoadedClass,
662
                                 Method defineClass,
663
                                 @MaybeNull Method getDefinedPackage,
664
                                 Method getPackage,
665
                                 Method definePackage) {
1✔
666
                    this.findLoadedClass = findLoadedClass;
1✔
667
                    this.defineClass = defineClass;
1✔
668
                    this.getDefinedPackage = getDefinedPackage;
1✔
669
                    this.getPackage = getPackage;
1✔
670
                    this.definePackage = definePackage;
1✔
671
                }
1✔
672

673
                /**
674
                 * Creates a direct dispatcher.
675
                 *
676
                 * @return A direct dispatcher for class injection.
677
                 * @throws Exception If the creation is impossible.
678
                 */
679
                @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
680
                protected static Initializable make() throws Exception {
681
                    Method getDefinedPackage;
682
                    if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
1✔
683
                        try {
684
                            getDefinedPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class);
×
685
                        } catch (NoSuchMethodException ignored) {
×
686
                            getDefinedPackage = null;
×
687
                        }
×
688
                    } else {
689
                        getDefinedPackage = null;
1✔
690
                    }
691
                    Method getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
1✔
692
                    getPackage.setAccessible(true);
1✔
693
                    Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
1✔
694
                    findLoadedClass.setAccessible(true);
1✔
695
                    Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
1✔
696
                            String.class,
697
                            byte[].class,
698
                            int.class,
699
                            int.class,
700
                            ProtectionDomain.class);
701
                    defineClass.setAccessible(true);
1✔
702
                    Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage",
1✔
703
                            String.class,
704
                            String.class,
705
                            String.class,
706
                            String.class,
707
                            String.class,
708
                            String.class,
709
                            String.class,
710
                            URL.class);
711
                    definePackage.setAccessible(true);
1✔
712
                    try {
713
                        Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
1✔
714
                        getClassLoadingLock.setAccessible(true);
1✔
715
                        return new ForJava7CapableVm(findLoadedClass,
1✔
716
                                defineClass,
717
                                getDefinedPackage,
718
                                getPackage,
719
                                definePackage,
720
                                getClassLoadingLock);
721
                    } catch (NoSuchMethodException ignored) {
×
722
                        return new ForLegacyVm(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
×
723
                    }
724
                }
725

726
                /**
727
                 * {@inheritDoc}
728
                 */
729
                public boolean isAvailable() {
730
                    return true;
1✔
731
                }
732

733
                /**
734
                 * {@inheritDoc}
735
                 */
736
                public Dispatcher initialize() {
737
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
738
                    if (securityManager != null) {
1✔
739
                        try {
740
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
741
                        } catch (InvocationTargetException exception) {
×
742
                            return new Dispatcher.Unavailable(exception.getTargetException().getMessage());
×
743
                        } catch (Exception exception) {
×
744
                            return new Dispatcher.Unavailable(exception.getMessage());
×
745
                        }
×
746
                    }
747
                    return this;
1✔
748
                }
749

750
                /**
751
                 * {@inheritDoc}
752
                 */
753
                public Class<?> findClass(ClassLoader classLoader, String name) {
754
                    try {
755
                        return (Class<?>) findLoadedClass.invoke(classLoader, name);
1✔
756
                    } catch (IllegalAccessException exception) {
×
757
                        throw new IllegalStateException(exception);
×
758
                    } catch (InvocationTargetException exception) {
×
759
                        throw new IllegalStateException(exception.getTargetException());
×
760
                    }
761
                }
762

763
                /**
764
                 * {@inheritDoc}
765
                 */
766
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
767
                    try {
768
                        return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
1✔
769
                    } catch (IllegalAccessException exception) {
×
770
                        throw new IllegalStateException(exception);
×
771
                    } catch (InvocationTargetException exception) {
×
772
                        throw new IllegalStateException(exception.getTargetException());
×
773
                    }
774
                }
775

776
                /**
777
                 * {@inheritDoc}
778
                 */
779
                @MaybeNull
780
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
781
                    if (getDefinedPackage == null) {
1✔
782
                        return getPackage(classLoader, name);
1✔
783
                    }
784
                    try {
785
                        return (Package) getDefinedPackage.invoke(classLoader, name);
×
786
                    } catch (IllegalAccessException exception) {
×
787
                        throw new IllegalStateException(exception);
×
788
                    } catch (InvocationTargetException exception) {
×
789
                        throw new IllegalStateException(exception.getTargetException());
×
790
                    }
791
                }
792

793
                /**
794
                 * {@inheritDoc}
795
                 */
796
                public Package getPackage(ClassLoader classLoader, String name) {
797
                    try {
798
                        return (Package) getPackage.invoke(classLoader, name);
1✔
799
                    } catch (IllegalAccessException exception) {
×
800
                        throw new IllegalStateException(exception);
×
801
                    } catch (InvocationTargetException exception) {
×
802
                        throw new IllegalStateException(exception.getTargetException());
×
803
                    }
804
                }
805

806
                /**
807
                 * {@inheritDoc}
808
                 */
809
                public Package definePackage(ClassLoader classLoader,
810
                                             String name,
811
                                             @MaybeNull String specificationTitle,
812
                                             @MaybeNull String specificationVersion,
813
                                             @MaybeNull String specificationVendor,
814
                                             @MaybeNull String implementationTitle,
815
                                             @MaybeNull String implementationVersion,
816
                                             @MaybeNull String implementationVendor,
817
                                             @MaybeNull URL sealBase) {
818
                    try {
819
                        return (Package) definePackage.invoke(classLoader,
1✔
820
                                name,
821
                                specificationTitle,
822
                                specificationVersion,
823
                                specificationVendor,
824
                                implementationTitle,
825
                                implementationVersion,
826
                                implementationVendor,
827
                                sealBase);
828
                    } catch (IllegalAccessException exception) {
×
829
                        throw new IllegalStateException(exception);
×
830
                    } catch (InvocationTargetException exception) {
×
831
                        throw new IllegalStateException(exception.getTargetException());
×
832
                    }
833
                }
834

835
                /**
836
                 * A resolved class dispatcher for a class injector on a VM running at least Java 7.
837
                 */
838
                @HashCodeAndEqualsPlugin.Enhance
839
                protected static class ForJava7CapableVm extends Direct {
840

841
                    /**
842
                     * An instance of {@code ClassLoader#getClassLoadingLock(String)}.
843
                     */
844
                    private final Method getClassLoadingLock;
845

846
                    /**
847
                     * Creates a new resolved reflection store for a VM running at least Java 7.
848
                     *
849
                     * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
850
                     * @param findLoadedClass     An instance of {@link ClassLoader#findLoadedClass(String)}.
851
                     * @param defineClass         An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
852
                     * @param getDefinedPackage   An instance of {@code java.lang,ClassLoader#getDefinedPackage(String)}. May be {@code null}.
853
                     * @param getPackage          An instance of {@link ClassLoader#getPackage(String)}.
854
                     * @param definePackage       An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
855
                     */
856
                    protected ForJava7CapableVm(Method findLoadedClass,
857
                                                Method defineClass,
858
                                                @MaybeNull Method getDefinedPackage,
859
                                                Method getPackage,
860
                                                Method definePackage,
861
                                                Method getClassLoadingLock) {
862
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
1✔
863
                        this.getClassLoadingLock = getClassLoadingLock;
1✔
864
                    }
1✔
865

866
                    /**
867
                     * {@inheritDoc}
868
                     */
869
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
870
                        try {
871
                            return getClassLoadingLock.invoke(classLoader, name);
1✔
872
                        } catch (IllegalAccessException exception) {
×
873
                            throw new IllegalStateException(exception);
×
874
                        } catch (InvocationTargetException exception) {
×
875
                            throw new IllegalStateException(exception.getTargetException());
×
876
                        }
877
                    }
878
                }
879

880
                /**
881
                 * A resolved class dispatcher for a class injector prior to Java 7.
882
                 */
883
                protected static class ForLegacyVm extends Direct {
884

885
                    /**
886
                     * Creates a new resolved reflection store for a VM prior to Java 8.
887
                     *
888
                     * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
889
                     * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
890
                     * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
891
                     * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
892
                     * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
893
                     */
894
                    protected ForLegacyVm(Method findLoadedClass,
895
                                          Method defineClass,
896
                                          @MaybeNull Method getDefinedPackage,
897
                                          Method getPackage,
898
                                          Method definePackage) {
899
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
1✔
900
                    }
1✔
901

902
                    /**
903
                     * {@inheritDoc}
904
                     */
905
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
906
                        return classLoader;
1✔
907
                    }
908
                }
909
            }
910

911
            /**
912
             * An indirect dispatcher that uses a redirection accessor class that was injected into the bootstrap class loader.
913
             */
914
            @HashCodeAndEqualsPlugin.Enhance
915
            class UsingUnsafeInjection implements Dispatcher, Initializable {
916

917
                /**
918
                 * An instance of the accessor class that is required for using it's intentionally non-static methods.
919
                 */
920
                private final Object accessor;
921

922
                /**
923
                 * The accessor method for using {@link ClassLoader#findLoadedClass(String)}.
924
                 */
925
                private final Method findLoadedClass;
926

927
                /**
928
                 * The accessor method for using {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
929
                 */
930
                private final Method defineClass;
931

932
                /**
933
                 * The accessor method for using {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
934
                 */
935
                @UnknownNull
936
                private final Method getDefinedPackage;
937

938
                /**
939
                 * The accessor method for using {@link ClassLoader#getPackage(String)}.
940
                 */
941
                private final Method getPackage;
942

943
                /**
944
                 * The accessor method for using {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
945
                 */
946
                private final Method definePackage;
947

948
                /**
949
                 * The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the supplied {@link ClassLoader}
950
                 * if this method does not exist on the current VM.
951
                 */
952
                private final Method getClassLoadingLock;
953

954
                /**
955
                 * Creates a new class loading injection dispatcher using an unsafe injected dispatcher.
956
                 *
957
                 * @param accessor            An instance of the accessor class that is required for using it's intentionally non-static methods.
958
                 * @param findLoadedClass     An instance of {@link ClassLoader#findLoadedClass(String)}.
959
                 * @param defineClass         An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
960
                 * @param getDefinedPackage   An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
961
                 * @param getPackage          An instance of {@link ClassLoader#getPackage(String)}.
962
                 * @param definePackage       An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
963
                 * @param getClassLoadingLock The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the
964
                 *                            supplied {@link ClassLoader} if this method does not exist on the current VM.
965
                 */
966
                protected UsingUnsafeInjection(Object accessor,
967
                                               Method findLoadedClass,
968
                                               Method defineClass,
969
                                               @MaybeNull Method getDefinedPackage,
970
                                               Method getPackage,
971
                                               Method definePackage,
972
                                               Method getClassLoadingLock) {
1✔
973
                    this.accessor = accessor;
1✔
974
                    this.findLoadedClass = findLoadedClass;
1✔
975
                    this.defineClass = defineClass;
1✔
976
                    this.getDefinedPackage = getDefinedPackage;
1✔
977
                    this.getPackage = getPackage;
1✔
978
                    this.definePackage = definePackage;
1✔
979
                    this.getClassLoadingLock = getClassLoadingLock;
1✔
980
                }
1✔
981

982
                /**
983
                 * Creates an indirect dispatcher.
984
                 *
985
                 * @return An indirect dispatcher for class creation.
986
                 * @throws Exception If the dispatcher cannot be created.
987
                 */
988
                @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
989
                protected static Initializable make() throws Exception {
990
                    if (Boolean.parseBoolean(java.lang.System.getProperty(UsingUnsafe.SAFE_PROPERTY, Boolean.toString(ClassFileVersion
1✔
991
                            .ofThisVm()
1✔
992
                            .isAtLeast(ClassFileVersion.JAVA_V25) || GraalImageCode.getCurrent().isDefined())))) {
1✔
UNCOV
993
                        return new Initializable.Unavailable("Use of Unsafe was disabled by system property");
×
994
                    }
995
                    Class<?> unsafe = Class.forName("sun.misc.Unsafe");
1✔
996
                    Field theUnsafe = unsafe.getDeclaredField("theUnsafe");
1✔
997
                    theUnsafe.setAccessible(true);
1✔
998
                    Object unsafeInstance = theUnsafe.get(null);
1✔
999
                    Method getDefinedPackage;
1000
                    if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
1✔
1001
                        try {
1002
                            getDefinedPackage = ClassLoader.class.getDeclaredMethod("getDefinedPackage", String.class);
×
1003
                        } catch (NoSuchMethodException ignored) {
×
1004
                            getDefinedPackage = null;
×
1005
                        }
×
1006
                    } else {
1007
                        getDefinedPackage = null;
1✔
1008
                    }
1009
                    DynamicType.Builder<?> builder = new ByteBuddy()
1✔
1010
                            .with(TypeValidation.DISABLED)
1✔
1011
                            .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
1✔
1012
                            .name(ClassLoader.class.getName() + "$ByteBuddyAccessor$V1")
1✔
1013
                            .defineMethod("findLoadedClass", Class.class, Visibility.PUBLIC)
1✔
1014
                            .withParameters(ClassLoader.class, String.class)
1✔
1015
                            .intercept(MethodCall.invoke(ClassLoader.class
1✔
1016
                                            .getDeclaredMethod("findLoadedClass", String.class))
1✔
1017
                                    .onArgument(0)
1✔
1018
                                    .withArgument(1))
1✔
1019
                            .defineMethod("defineClass", Class.class, Visibility.PUBLIC)
1✔
1020
                            .withParameters(ClassLoader.class, String.class, byte[].class, int.class, int.class,
1✔
1021
                                    ProtectionDomain.class)
1022
                            .intercept(MethodCall.invoke(ClassLoader.class
1✔
1023
                                            .getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class))
1✔
1024
                                    .onArgument(0)
1✔
1025
                                    .withArgument(1, 2, 3, 4, 5))
1✔
1026
                            .defineMethod("getPackage", Package.class, Visibility.PUBLIC)
1✔
1027
                            .withParameters(ClassLoader.class, String.class)
1✔
1028
                            .intercept(MethodCall.invoke(ClassLoader.class
1✔
1029
                                            .getDeclaredMethod("getPackage", String.class))
1✔
1030
                                    .onArgument(0)
1✔
1031
                                    .withArgument(1))
1✔
1032
                            .defineMethod("definePackage", Package.class, Visibility.PUBLIC)
1✔
1033
                            .withParameters(ClassLoader.class, String.class, String.class, String.class, String.class,
1✔
1034
                                    String.class, String.class, String.class, URL.class)
1035
                            .intercept(MethodCall.invoke(ClassLoader.class
1✔
1036
                                            .getDeclaredMethod("definePackage", String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class))
1✔
1037
                                    .onArgument(0)
1✔
1038
                                    .withArgument(1, 2, 3, 4, 5, 6, 7, 8));
1✔
1039
                    if (getDefinedPackage != null) {
1✔
1040
                        builder = builder
×
1041
                                .defineMethod("getDefinedPackage", Package.class, Visibility.PUBLIC)
×
1042
                                .withParameters(ClassLoader.class, String.class)
×
1043
                                .intercept(MethodCall.invoke(getDefinedPackage)
×
1044
                                        .onArgument(0)
×
1045
                                        .withArgument(1));
×
1046
                    }
1047
                    try {
1048
                        builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC)
1✔
1049
                                .withParameters(ClassLoader.class, String.class)
1✔
1050
                                .intercept(MethodCall.invoke(ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class))
1✔
1051
                                        .onArgument(0)
1✔
1052
                                        .withArgument(1));
1✔
1053
                    } catch (NoSuchMethodException ignored) {
×
1054
                        builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC)
×
1055
                                .withParameters(ClassLoader.class, String.class)
×
1056
                                .intercept(FixedValue.argument(0));
×
1057
                    }
1✔
1058
                    Class<?> type = builder.make()
1✔
1059
                            .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, new ClassLoadingStrategy.ForUnsafeInjection())
1✔
1060
                            .getLoaded();
1✔
1061
                    return new UsingUnsafeInjection(
1✔
1062
                            unsafe.getMethod("allocateInstance", Class.class).invoke(unsafeInstance, type),
1✔
1063
                            type.getMethod("findLoadedClass", ClassLoader.class, String.class),
1✔
1064
                            type.getMethod("defineClass", ClassLoader.class, String.class, byte[].class, int.class, int.class, ProtectionDomain.class),
1✔
1065
                            getDefinedPackage != null ? type.getMethod("getDefinedPackage", ClassLoader.class, String.class) : null,
1✔
1066
                            type.getMethod("getPackage", ClassLoader.class, String.class),
1✔
1067
                            type.getMethod("definePackage", ClassLoader.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class),
1✔
1068
                            type.getMethod("getClassLoadingLock", ClassLoader.class, String.class));
1✔
1069
                }
1070

1071
                /**
1072
                 * {@inheritDoc}
1073
                 */
1074
                public boolean isAvailable() {
1075
                    return true;
×
1076
                }
1077

1078
                /**
1079
                 * {@inheritDoc}
1080
                 */
1081
                public Dispatcher initialize() {
1082
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
1083
                    if (securityManager != null) {
1✔
1084
                        try {
1085
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
1086
                        } catch (InvocationTargetException exception) {
×
1087
                            return new Dispatcher.Unavailable(exception.getTargetException().getMessage());
×
1088
                        } catch (Exception exception) {
×
1089
                            return new Dispatcher.Unavailable(exception.getMessage());
×
1090
                        }
×
1091
                    }
1092
                    return this;
1✔
1093
                }
1094

1095
                /**
1096
                 * {@inheritDoc}
1097
                 */
1098
                public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1099
                    try {
1100
                        return getClassLoadingLock.invoke(accessor, classLoader, name);
×
1101
                    } catch (IllegalAccessException exception) {
×
1102
                        throw new IllegalStateException(exception);
×
1103
                    } catch (InvocationTargetException exception) {
×
1104
                        throw new IllegalStateException(exception.getTargetException());
×
1105
                    }
1106
                }
1107

1108
                /**
1109
                 * {@inheritDoc}
1110
                 */
1111
                public Class<?> findClass(ClassLoader classLoader, String name) {
1112
                    try {
1113
                        return (Class<?>) findLoadedClass.invoke(accessor, classLoader, name);
1✔
1114
                    } catch (IllegalAccessException exception) {
×
1115
                        throw new IllegalStateException(exception);
×
1116
                    } catch (InvocationTargetException exception) {
×
1117
                        throw new IllegalStateException(exception.getTargetException());
×
1118
                    }
1119
                }
1120

1121
                /**
1122
                 * {@inheritDoc}
1123
                 */
1124
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
1125
                    try {
1126
                        return (Class<?>) defineClass.invoke(accessor, classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
1✔
1127
                    } catch (IllegalAccessException exception) {
×
1128
                        throw new IllegalStateException(exception);
×
1129
                    } catch (InvocationTargetException exception) {
×
1130
                        throw new IllegalStateException(exception.getTargetException());
×
1131
                    }
1132
                }
1133

1134
                /**
1135
                 * {@inheritDoc}
1136
                 */
1137
                @MaybeNull
1138
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
1139
                    if (getDefinedPackage == null) {
1✔
1140
                        return getPackage(classLoader, name);
1✔
1141
                    }
1142
                    try {
1143
                        return (Package) getDefinedPackage.invoke(accessor, classLoader, name);
×
1144
                    } catch (IllegalAccessException exception) {
×
1145
                        throw new IllegalStateException(exception);
×
1146
                    } catch (InvocationTargetException exception) {
×
1147
                        throw new IllegalStateException(exception.getTargetException());
×
1148
                    }
1149
                }
1150

1151
                /**
1152
                 * {@inheritDoc}
1153
                 */
1154
                public Package getPackage(ClassLoader classLoader, String name) {
1155
                    try {
1156
                        return (Package) getPackage.invoke(accessor, classLoader, name);
1✔
1157
                    } catch (IllegalAccessException exception) {
×
1158
                        throw new IllegalStateException(exception);
×
1159
                    } catch (InvocationTargetException exception) {
×
1160
                        throw new IllegalStateException(exception.getTargetException());
×
1161
                    }
1162
                }
1163

1164
                /**
1165
                 * {@inheritDoc}
1166
                 */
1167
                public Package definePackage(ClassLoader classLoader,
1168
                                             String name,
1169
                                             @MaybeNull String specificationTitle,
1170
                                             @MaybeNull String specificationVersion,
1171
                                             @MaybeNull String specificationVendor,
1172
                                             @MaybeNull String implementationTitle,
1173
                                             @MaybeNull String implementationVersion,
1174
                                             @MaybeNull String implementationVendor,
1175
                                             @MaybeNull URL sealBase) {
1176
                    try {
1177
                        return (Package) definePackage.invoke(accessor,
1✔
1178
                                classLoader,
1179
                                name,
1180
                                specificationTitle,
1181
                                specificationVersion,
1182
                                specificationVendor,
1183
                                implementationTitle,
1184
                                implementationVersion,
1185
                                implementationVendor,
1186
                                sealBase);
1187
                    } catch (IllegalAccessException exception) {
×
1188
                        throw new IllegalStateException(exception);
×
1189
                    } catch (InvocationTargetException exception) {
×
1190
                        throw new IllegalStateException(exception.getTargetException());
×
1191
                    }
1192
                }
1193
            }
1194

1195
            /**
1196
             * A dispatcher implementation that uses {@code sun.misc.Unsafe#putBoolean} to set the {@link AccessibleObject} field
1197
             * for making methods accessible.
1198
             */
1199
            abstract class UsingUnsafeOverride implements Dispatcher, Initializable {
1200

1201
                /**
1202
                 * An instance of {@link ClassLoader#findLoadedClass(String)}.
1203
                 */
1204
                protected final Method findLoadedClass;
1205

1206
                /**
1207
                 * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1208
                 */
1209
                protected final Method defineClass;
1210

1211
                /**
1212
                 * An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1213
                 */
1214
                @MaybeNull
1215
                protected final Method getDefinedPackage;
1216

1217
                /**
1218
                 * An instance of {@link ClassLoader#getPackage(String)}.
1219
                 */
1220
                protected final Method getPackage;
1221

1222
                /**
1223
                 * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1224
                 */
1225
                protected final Method definePackage;
1226

1227
                /**
1228
                 * Creates a new unsafe field injecting injection dispatcher.
1229
                 *
1230
                 * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
1231
                 * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1232
                 * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1233
                 * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
1234
                 * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1235
                 */
1236
                protected UsingUnsafeOverride(Method findLoadedClass,
1237
                                              Method defineClass,
1238
                                              @MaybeNull Method getDefinedPackage,
1239
                                              Method getPackage,
1240
                                              Method definePackage) {
1✔
1241
                    this.findLoadedClass = findLoadedClass;
1✔
1242
                    this.defineClass = defineClass;
1✔
1243
                    this.getDefinedPackage = getDefinedPackage;
1✔
1244
                    this.getPackage = getPackage;
1✔
1245
                    this.definePackage = definePackage;
1✔
1246
                }
1✔
1247

1248
                /**
1249
                 * Creates a new initializable class injector using an unsafe field injection.
1250
                 *
1251
                 * @return An appropriate initializable.
1252
                 * @throws Exception If the injector cannot be created.
1253
                 */
1254
                @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Assuring privilege is explicit user responsibility.")
1255
                protected static Initializable make() throws Exception {
1256
                    if (Boolean.parseBoolean(java.lang.System.getProperty(UsingUnsafe.SAFE_PROPERTY, Boolean.toString(GraalImageCode.getCurrent().isDefined())))) {
1✔
1257
                        return new Initializable.Unavailable("Use of Unsafe was disabled by system property");
×
1258
                    }
1259
                    Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
1✔
1260
                    Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
1✔
1261
                    theUnsafe.setAccessible(true);
1✔
1262
                    Object unsafe = theUnsafe.get(null);
1✔
1263
                    Field override;
1264
                    try {
1265
                        override = AccessibleObject.class.getDeclaredField("override");
1✔
1266
                    } catch (NoSuchFieldException ignored) {
×
1267
                        // Since Java 12, the override field is hidden from the reflection API. To circumvent this, we
1268
                        // create a mirror class of AccessibleObject that defines the same fields and has the same field
1269
                        // layout such that the override field will receive the same class offset. Doing so, we can write to
1270
                        // the offset location and still set a value to it, despite it being hidden from the reflection API.
1271
                        override = new ByteBuddy()
×
1272
                                .redefine(AccessibleObject.class)
×
1273
                                .name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName())
×
1274
                                .noNestMate()
×
1275
                                .visit(new MemberRemoval().stripInvokables(any()))
×
1276
                                .make()
×
1277
                                .load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER.with(AccessibleObject.class.getProtectionDomain()))
×
1278
                                .getLoaded()
×
1279
                                .getDeclaredField("override");
×
1280
                    }
1✔
1281
                    long offset = (Long) unsafeType
1✔
1282
                            .getMethod("objectFieldOffset", Field.class)
1✔
1283
                            .invoke(unsafe, override);
1✔
1284
                    Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class);
1✔
1285
                    Method getDefinedPackage;
1286
                    if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
1✔
1287
                        try {
1288
                            getDefinedPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class);
×
1289
                        } catch (NoSuchMethodException ignored) {
×
1290
                            getDefinedPackage = null;
×
1291
                        }
×
1292
                    } else {
1293
                        getDefinedPackage = null;
1✔
1294
                    }
1295
                    Method getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
1✔
1296
                    putBoolean.invoke(unsafe, getPackage, offset, true);
1✔
1297
                    Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
1✔
1298
                    Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
1✔
1299
                            String.class,
1300
                            byte[].class,
1301
                            int.class,
1302
                            int.class,
1303
                            ProtectionDomain.class);
1304
                    Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage",
1✔
1305
                            String.class,
1306
                            String.class,
1307
                            String.class,
1308
                            String.class,
1309
                            String.class,
1310
                            String.class,
1311
                            String.class,
1312
                            URL.class);
1313
                    putBoolean.invoke(unsafe, defineClass, offset, true);
1✔
1314
                    putBoolean.invoke(unsafe, findLoadedClass, offset, true);
1✔
1315
                    putBoolean.invoke(unsafe, definePackage, offset, true);
1✔
1316
                    try {
1317
                        Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
1✔
1318
                        putBoolean.invoke(unsafe, getClassLoadingLock, offset, true);
1✔
1319
                        return new ForJava7CapableVm(findLoadedClass,
1✔
1320
                                defineClass,
1321
                                getDefinedPackage,
1322
                                getPackage,
1323
                                definePackage,
1324
                                getClassLoadingLock);
1325
                    } catch (NoSuchMethodException ignored) {
×
1326
                        return new ForLegacyVm(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
×
1327
                    }
1328
                }
1329

1330
                /**
1331
                 * {@inheritDoc}
1332
                 */
1333
                public boolean isAvailable() {
1334
                    return true;
×
1335
                }
1336

1337
                /**
1338
                 * {@inheritDoc}
1339
                 */
1340
                public Dispatcher initialize() {
1341
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
1342
                    if (securityManager != null) {
1✔
1343
                        try {
1344
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
1345
                        } catch (InvocationTargetException exception) {
×
1346
                            return new Dispatcher.Unavailable(exception.getTargetException().getMessage());
×
1347
                        } catch (Exception exception) {
×
1348
                            return new Dispatcher.Unavailable(exception.getMessage());
×
1349
                        }
×
1350
                    }
1351
                    return this;
1✔
1352
                }
1353

1354
                /**
1355
                 * {@inheritDoc}
1356
                 */
1357
                public Class<?> findClass(ClassLoader classLoader, String name) {
1358
                    try {
1359
                        return (Class<?>) findLoadedClass.invoke(classLoader, name);
1✔
1360
                    } catch (IllegalAccessException exception) {
×
1361
                        throw new IllegalStateException(exception);
×
1362
                    } catch (InvocationTargetException exception) {
×
1363
                        throw new IllegalStateException(exception.getTargetException());
×
1364
                    }
1365
                }
1366

1367
                /**
1368
                 * {@inheritDoc}
1369
                 */
1370
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
1371
                    try {
1372
                        return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
1✔
1373
                    } catch (IllegalAccessException exception) {
×
1374
                        throw new IllegalStateException(exception);
×
1375
                    } catch (InvocationTargetException exception) {
×
1376
                        throw new IllegalStateException(exception.getTargetException());
×
1377
                    }
1378
                }
1379

1380
                /**
1381
                 * {@inheritDoc}
1382
                 */
1383
                @MaybeNull
1384
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
1385
                    if (getDefinedPackage == null) {
1✔
1386
                        return getPackage(classLoader, name);
1✔
1387
                    }
1388
                    try {
1389
                        return (Package) getDefinedPackage.invoke(classLoader, name);
×
1390
                    } catch (IllegalAccessException exception) {
×
1391
                        throw new IllegalStateException(exception);
×
1392
                    } catch (InvocationTargetException exception) {
×
1393
                        throw new IllegalStateException(exception.getTargetException());
×
1394
                    }
1395
                }
1396

1397
                /**
1398
                 * {@inheritDoc}
1399
                 */
1400
                public Package getPackage(ClassLoader classLoader, String name) {
1401
                    try {
1402
                        return (Package) getPackage.invoke(classLoader, name);
1✔
1403
                    } catch (IllegalAccessException exception) {
×
1404
                        throw new IllegalStateException(exception);
×
1405
                    } catch (InvocationTargetException exception) {
×
1406
                        throw new IllegalStateException(exception.getTargetException());
×
1407
                    }
1408
                }
1409

1410
                /**
1411
                 * {@inheritDoc}
1412
                 */
1413
                public Package definePackage(ClassLoader classLoader,
1414
                                             String name,
1415
                                             @MaybeNull String specificationTitle,
1416
                                             @MaybeNull String specificationVersion,
1417
                                             @MaybeNull String specificationVendor,
1418
                                             @MaybeNull String implementationTitle,
1419
                                             @MaybeNull String implementationVersion,
1420
                                             @MaybeNull String implementationVendor,
1421
                                             @MaybeNull URL sealBase) {
1422
                    try {
1423
                        return (Package) definePackage.invoke(classLoader,
1✔
1424
                                name,
1425
                                specificationTitle,
1426
                                specificationVersion,
1427
                                specificationVendor,
1428
                                implementationTitle,
1429
                                implementationVersion,
1430
                                implementationVendor,
1431
                                sealBase);
1432
                    } catch (IllegalAccessException exception) {
×
1433
                        throw new IllegalStateException(exception);
×
1434
                    } catch (InvocationTargetException exception) {
×
1435
                        throw new IllegalStateException(exception.getTargetException());
×
1436
                    }
1437
                }
1438

1439
                /**
1440
                 * A resolved class dispatcher using unsafe field injection for a class injector on a VM running at least Java 7.
1441
                 */
1442
                @HashCodeAndEqualsPlugin.Enhance
1443
                protected static class ForJava7CapableVm extends UsingUnsafeOverride {
1444

1445
                    /**
1446
                     * An instance of {@code ClassLoader#getClassLoadingLock(String)}.
1447
                     */
1448
                    private final Method getClassLoadingLock;
1449

1450
                    /**
1451
                     * Creates a new resolved class injector using unsafe field injection for a VM running at least Java 7.
1452
                     *
1453
                     * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
1454
                     * @param findLoadedClass     An instance of {@link ClassLoader#findLoadedClass(String)}.
1455
                     * @param defineClass         An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1456
                     * @param getDefinedPackage   An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1457
                     * @param getPackage          An instance of {@link ClassLoader#getPackage(String)}.
1458
                     * @param definePackage       An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1459
                     */
1460
                    protected ForJava7CapableVm(Method findLoadedClass,
1461
                                                Method defineClass,
1462
                                                @MaybeNull Method getDefinedPackage,
1463
                                                Method getPackage,
1464
                                                Method definePackage,
1465
                                                Method getClassLoadingLock) {
1466
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
1✔
1467
                        this.getClassLoadingLock = getClassLoadingLock;
1✔
1468
                    }
1✔
1469

1470
                    /**
1471
                     * {@inheritDoc}
1472
                     */
1473
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1474
                        try {
1475
                            return getClassLoadingLock.invoke(classLoader, name);
×
1476
                        } catch (IllegalAccessException exception) {
×
1477
                            throw new IllegalStateException(exception);
×
1478
                        } catch (InvocationTargetException exception) {
×
1479
                            throw new IllegalStateException(exception.getTargetException());
×
1480
                        }
1481
                    }
1482
                }
1483

1484
                /**
1485
                 * A resolved class dispatcher using unsafe field injection for a class injector prior to Java 7.
1486
                 */
1487
                protected static class ForLegacyVm extends UsingUnsafeOverride {
1488

1489
                    /**
1490
                     * Creates a new resolved class injector using unsafe field injection for a VM prior to Java 7.
1491
                     *
1492
                     * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
1493
                     * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1494
                     * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1495
                     * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
1496
                     * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1497
                     */
1498
                    protected ForLegacyVm(Method findLoadedClass,
1499
                                          Method defineClass,
1500
                                          @MaybeNull Method getDefinedPackage,
1501
                                          Method getPackage,
1502
                                          Method definePackage) {
1503
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
×
1504
                    }
×
1505

1506
                    /**
1507
                     * {@inheritDoc}
1508
                     */
1509
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1510
                        return classLoader;
×
1511
                    }
1512
                }
1513
            }
1514

1515
            /**
1516
             * Represents an unsuccessfully loaded method lookup.
1517
             */
1518
            @HashCodeAndEqualsPlugin.Enhance
1519
            class Unavailable implements Dispatcher {
1520

1521
                /**
1522
                 * The error message being displayed.
1523
                 */
1524
                private final String message;
1525

1526
                /**
1527
                 * Creates a dispatcher for a VM that does not support reflective injection.
1528
                 *
1529
                 * @param message The error message being displayed.
1530
                 */
1531
                protected Unavailable(String message) {
1✔
1532
                    this.message = message;
1✔
1533
                }
1✔
1534

1535
                /**
1536
                 * {@inheritDoc}
1537
                 */
1538
                public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1539
                    return classLoader;
1✔
1540
                }
1541

1542
                /**
1543
                 * {@inheritDoc}
1544
                 */
1545
                public Class<?> findClass(ClassLoader classLoader, String name) {
1546
                    try {
1547
                        return classLoader.loadClass(name);
×
1548
                    } catch (ClassNotFoundException ignored) {
×
1549
                        return UNDEFINED;
×
1550
                    }
1551
                }
1552

1553
                /**
1554
                 * {@inheritDoc}
1555
                 */
1556
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
1557
                    throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
1✔
1558
                }
1559

1560
                /**
1561
                 * {@inheritDoc}
1562
                 */
1563
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
1564
                    throw new UnsupportedOperationException("Cannot get defined package using reflection: " + message);
1✔
1565
                }
1566

1567
                /**
1568
                 * {@inheritDoc}
1569
                 */
1570
                public Package getPackage(ClassLoader classLoader, String name) {
1571
                    throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
×
1572
                }
1573

1574
                /**
1575
                 * {@inheritDoc}
1576
                 */
1577
                public Package definePackage(ClassLoader classLoader,
1578
                                             String name,
1579
                                             @MaybeNull String specificationTitle,
1580
                                             @MaybeNull String specificationVersion,
1581
                                             @MaybeNull String specificationVendor,
1582
                                             @MaybeNull String implementationTitle,
1583
                                             @MaybeNull String implementationVersion,
1584
                                             @MaybeNull String implementationVendor,
1585
                                             @MaybeNull URL sealBase) {
1586
                    throw new UnsupportedOperationException("Cannot define package using injection: " + message);
1✔
1587
                }
1588
            }
1589
        }
1590

1591
        /**
1592
         * A proxy of {@code java.lang.System}.
1593
         */
1594
        @JavaDispatcher.Proxied("java.lang.System")
1595
        protected interface System {
1596

1597
            /**
1598
             * Returns the current security manager or {@code null} if not available.
1599
             *
1600
             * @return The current security manager or {@code null} if not available.
1601
             */
1602
            @MaybeNull
1603
            @JavaDispatcher.IsStatic
1604
            @JavaDispatcher.Defaults
1605
            Object getSecurityManager();
1606
        }
1607
    }
1608

1609
    /**
1610
     * <p>
1611
     * A class injector that uses a {@code java.lang.invoke.MethodHandles$Lookup} object for defining a class.
1612
     * </p>
1613
     * <p>
1614
     * <b>Important</b>: This functionality is only available starting from Java 9.
1615
     * </p>
1616
     */
1617
    @HashCodeAndEqualsPlugin.Enhance
1618
    class UsingLookup extends AbstractBase {
1619

1620
        /**
1621
         * The dispatcher to interacting with instances of {@code java.lang.invoke.MethodHandles}.
1622
         */
1623
        private static final MethodHandles METHOD_HANDLES = doPrivileged(JavaDispatcher.of(MethodHandles.class));
1✔
1624

1625
        /**
1626
         * The dispatcher to interacting with {@code java.lang.invoke.MethodHandles$Lookup}.
1627
         */
1628
        private static final MethodHandles.Lookup METHOD_HANDLES_LOOKUP = doPrivileged(JavaDispatcher.of(MethodHandles.Lookup.class));
1✔
1629

1630
        /**
1631
         * Indicates a lookup instance's package lookup mode.
1632
         */
1633
        private static final int PACKAGE_LOOKUP = 0x8;
1634

1635
        /**
1636
         * The {@code java.lang.invoke.MethodHandles$Lookup} to use.
1637
         */
1638
        private final Object lookup;
1639

1640
        /**
1641
         * Creates a new class injector using a lookup instance.
1642
         *
1643
         * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
1644
         */
1645
        protected UsingLookup(Object lookup) {
×
1646
            this.lookup = lookup;
×
1647
        }
×
1648

1649
        /**
1650
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1651
         *
1652
         * @param action The action to execute from a privileged context.
1653
         * @param <T>    The type of the action's resolved value.
1654
         * @return The action's resolved value.
1655
         */
1656
        @AccessControllerPlugin.Enhance
1657
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
1658
            return action.run();
×
1659
        }
1660

1661
        /**
1662
         * Creates class injector that defines a class using a method handle lookup.
1663
         *
1664
         * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
1665
         * @return An appropriate class injector.
1666
         */
1667
        public static UsingLookup of(Object lookup) {
1668
            if (!isAvailable()) {
×
1669
                throw new IllegalStateException("The current VM does not support class definition via method handle lookups");
×
1670
            } else if (!JavaType.METHOD_HANDLES_LOOKUP.isInstance(lookup)) {
×
1671
                throw new IllegalArgumentException("Not a method handle lookup: " + lookup);
×
1672
            } else if ((METHOD_HANDLES_LOOKUP.lookupModes(lookup) & PACKAGE_LOOKUP) == 0) {
×
1673
                throw new IllegalArgumentException("Lookup does not imply package-access: " + lookup);
×
1674
            }
1675
            return new UsingLookup(lookup);
×
1676
        }
1677

1678
        /**
1679
         * Returns the lookup type this injector is based upon.
1680
         *
1681
         * @return The lookup type.
1682
         */
1683
        public Class<?> lookupType() {
1684
            return METHOD_HANDLES_LOOKUP.lookupClass(lookup);
×
1685
        }
1686

1687
        /**
1688
         * Resolves this injector to use the supplied type's scope.
1689
         *
1690
         * @param type The type to resolve the access scope for.
1691
         * @return An new injector with the specified scope.
1692
         */
1693
        public UsingLookup in(Class<?> type) {
1694
            try {
1695
                return new UsingLookup(METHOD_HANDLES.privateLookupIn(type, lookup));
×
1696
            } catch (IllegalAccessException exception) {
×
1697
                throw new IllegalStateException("Cannot access " + type.getName() + " from " + lookup, exception);
×
1698
            }
1699
        }
1700

1701
        /**
1702
         * {@inheritDoc}
1703
         */
1704
        public boolean isAlive() {
1705
            return isAvailable();
×
1706
        }
1707

1708
        /**
1709
         * {@inheritDoc}
1710
         */
1711
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
1712
            PackageDescription target = TypeDescription.ForLoadedType.of(lookupType()).getPackage();
×
1713
            if (target == null) {
×
1714
                throw new IllegalArgumentException("Cannot inject array or primitive type");
×
1715
            }
1716
            Map<String, Class<?>> result = new HashMap<String, Class<?>>();
×
1717
            for (String name : names) {
×
1718
                int index = name.lastIndexOf('.');
×
1719
                if (!target.getName().equals(index == -1 ? "" : name.substring(0, index))) {
×
1720
                    throw new IllegalArgumentException(name + " must be defined in the same package as " + lookup);
×
1721
                }
1722
                try {
1723
                    result.put(name, METHOD_HANDLES_LOOKUP.defineClass(lookup, classFileLocator.locate(name).resolve()));
×
1724
                } catch (Exception exception) {
×
1725
                    throw new IllegalStateException(exception);
×
1726
                }
×
1727
            }
×
1728
            return result;
×
1729
        }
1730

1731
        /**
1732
         * Checks if the current VM is capable of defining classes using a method handle lookup.
1733
         *
1734
         * @return {@code true} if the current VM is capable of defining classes using a lookup.
1735
         */
1736
        public static boolean isAvailable() {
1737
            return JavaType.MODULE.isAvailable();
1✔
1738
        }
1739

1740
        /**
1741
         * A dispatcher for {@code java.lang.invoke.MethodHandles}.
1742
         */
1743
        @JavaDispatcher.Proxied("java.lang.invoke.MethodHandles")
1744
        protected interface MethodHandles {
1745

1746
            /**
1747
             * Resolves the supplied lookup instance's access scope for the supplied type.
1748
             *
1749
             * @param type   The type to resolve the scope for.
1750
             * @param lookup The lookup to resolve.
1751
             * @return An appropriate lookup instance.
1752
             * @throws IllegalAccessException If an illegal access occurs.
1753
             */
1754
            @JavaDispatcher.IsStatic
1755
            Object privateLookupIn(Class<?> type, @JavaDispatcher.Proxied("java.lang.invoke.MethodHandles$Lookup") Object lookup) throws IllegalAccessException;
1756

1757
            /**
1758
             * A dispatcher for {@code java.lang.invoke.MethodHandles$Lookup}.
1759
             */
1760
            @JavaDispatcher.Proxied("java.lang.invoke.MethodHandles$Lookup")
1761
            interface Lookup {
1762

1763
                /**
1764
                 * Returns the lookup type for a given method handle lookup.
1765
                 *
1766
                 * @param lookup The lookup instance.
1767
                 * @return The lookup type.
1768
                 */
1769
                Class<?> lookupClass(Object lookup);
1770

1771
                /**
1772
                 * Returns a lookup objects lookup types.
1773
                 *
1774
                 * @param lookup The lookup instance.
1775
                 * @return The modifiers indicating the instance's lookup modes.
1776
                 */
1777
                int lookupModes(Object lookup);
1778

1779
                /**
1780
                 * Defines the represented class.
1781
                 *
1782
                 * @param lookup               The lookup instance.
1783
                 * @param binaryRepresentation The binary representation.
1784
                 * @return The defined class.
1785
                 * @throws IllegalAccessException If the definition implies an illegal access.
1786
                 */
1787
                Class<?> defineClass(Object lookup, byte[] binaryRepresentation) throws IllegalAccessException;
1788
            }
1789
        }
1790
    }
1791

1792
    /**
1793
     * A class injector that uses {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe} to inject classes.
1794
     */
1795
    @HashCodeAndEqualsPlugin.Enhance
1796
    class UsingUnsafe extends AbstractBase {
1797

1798
        /**
1799
         * If this property is set, Byte Buddy does not make use of any {@code Unsafe} class.
1800
         */
1801
        public static final String SAFE_PROPERTY = "net.bytebuddy.safe";
1802

1803
        /**
1804
         * The dispatcher to use.
1805
         */
1806
        private static final Dispatcher.Initializable DISPATCHER = doPrivileged(Dispatcher.CreationAction.INSTANCE);
1✔
1807

1808
        /**
1809
         * A proxy for {@code java.lang.System} to access the security manager if available.
1810
         */
1811
        private static final System SYSTEM = doPrivileged(JavaDispatcher.of(System.class));
1✔
1812

1813
        /**
1814
         * The {@code java.lang.SecurityManager#checkPermission} method or {@code null} if not available.
1815
         */
1816
        private static final Method CHECK_PERMISSION = doPrivileged(new GetMethodAction("java.lang.SecurityManager",
1✔
1817
                "checkPermission",
1818
                Permission.class));
1819

1820
        /**
1821
         * A lock for the bootstrap loader when injecting.
1822
         */
1823
        private static final Object BOOTSTRAP_LOADER_LOCK = new Object();
1✔
1824

1825
        /**
1826
         * The class loader to inject classes into or {@code null} for the bootstrap loader.
1827
         */
1828
        @MaybeNull
1829
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1830
        private final ClassLoader classLoader;
1831

1832
        /**
1833
         * The protection domain to use or {@code null} for no protection domain.
1834
         */
1835
        @MaybeNull
1836
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1837
        private final ProtectionDomain protectionDomain;
1838

1839
        /**
1840
         * The dispatcher to use.
1841
         */
1842
        private final Dispatcher.Initializable dispatcher;
1843

1844
        /**
1845
         * Creates a new unsafe injector for the given class loader with a default protection domain.
1846
         *
1847
         * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
1848
         */
1849
        public UsingUnsafe(@MaybeNull ClassLoader classLoader) {
1850
            this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
1851
        }
1✔
1852

1853
        /**
1854
         * Creates a new unsafe injector for the given class loader with a default protection domain.
1855
         *
1856
         * @param classLoader      The class loader to inject classes into or {@code null} for the bootstrap loader.
1857
         * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
1858
         */
1859
        public UsingUnsafe(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
1860
            this(classLoader, protectionDomain, DISPATCHER);
1✔
1861
        }
1✔
1862

1863
        /**
1864
         * Creates a new unsafe injector for the given class loader with a default protection domain.
1865
         *
1866
         * @param classLoader      The class loader to inject classes into or {@code null} for the bootstrap loader.
1867
         * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
1868
         * @param dispatcher       The dispatcher to use.
1869
         */
1870
        protected UsingUnsafe(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain, Dispatcher.Initializable dispatcher) {
1✔
1871
            this.classLoader = classLoader;
1✔
1872
            this.protectionDomain = protectionDomain;
1✔
1873
            this.dispatcher = dispatcher;
1✔
1874
        }
1✔
1875

1876
        /**
1877
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1878
         *
1879
         * @param action The action to execute from a privileged context.
1880
         * @param <T>    The type of the action's resolved value.
1881
         * @return The action's resolved value.
1882
         */
1883
        @AccessControllerPlugin.Enhance
1884
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
1885
            return action.run();
×
1886
        }
1887

1888
        /**
1889
         * {@inheritDoc}
1890
         */
1891
        public boolean isAlive() {
1892
            return dispatcher.isAvailable();
1✔
1893
        }
1894

1895
        /**
1896
         * {@inheritDoc}
1897
         */
1898
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
1899
            Dispatcher dispatcher = this.dispatcher.initialize();
1✔
1900
            Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1✔
1901
            synchronized (classLoader == null
1✔
1902
                    ? BOOTSTRAP_LOADER_LOCK
1903
                    : classLoader) {
1904
                for (String name : names) {
1✔
1905
                    try {
1906
                        result.put(name, Class.forName(name, false, classLoader));
1✔
1907
                    } catch (ClassNotFoundException ignored) {
1✔
1908
                        try {
1909
                            result.put(name, dispatcher.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain));
1✔
1910
                        } catch (
×
1911
                                RuntimeException exception) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
1912
                            try {
1913
                                result.put(name, Class.forName(name, false, classLoader));
×
1914
                            } catch (ClassNotFoundException ignored2) {
×
1915
                                throw exception;
×
1916
                            }
×
1917
                        } catch (IOException exception) {
×
1918
                            throw new IllegalStateException("Failed to resolve binary representation of " + name, exception);
×
1919
                        } catch (
×
1920
                                Error error) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
1921
                            try {
1922
                                result.put(name, Class.forName(name, false, classLoader));
×
1923
                            } catch (ClassNotFoundException ignored2) {
×
1924
                                throw error;
×
1925
                            }
×
1926
                        }
1✔
1927
                    }
1✔
1928
                }
1✔
1929
            }
1✔
1930
            return result;
1✔
1931
        }
1932

1933
        /**
1934
         * Checks if unsafe class injection is available on the current VM.
1935
         *
1936
         * @return {@code true} if unsafe class injection is available on the current VM.
1937
         */
1938
        public static boolean isAvailable() {
1939
            return DISPATCHER.isAvailable();
1✔
1940
        }
1941

1942
        /**
1943
         * Returns an unsafe class injector for the system class loader.
1944
         *
1945
         * @return A class injector for the system class loader.
1946
         */
1947
        public static ClassInjector ofSystemLoader() {
1948
            return new UsingUnsafe(ClassLoader.getSystemClassLoader());
1✔
1949
        }
1950

1951
        /**
1952
         * Returns an unsafe class injector for the platform class loader. For VMs of version 8 or older,
1953
         * the extension class loader is represented instead.
1954
         *
1955
         * @return A class injector for the platform class loader.
1956
         */
1957
        public static ClassInjector ofPlatformLoader() {
1958
            return new UsingUnsafe(ClassLoader.getSystemClassLoader().getParent());
1✔
1959
        }
1960

1961
        /**
1962
         * Returns an unsafe class injector for the boot class loader.
1963
         *
1964
         * @return A class injector for the boot loader.
1965
         */
1966
        public static ClassInjector ofBootLoader() {
1967
            return new UsingUnsafe(ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
1968
        }
1969

1970
        /**
1971
         * A dispatcher for using {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe}.
1972
         */
1973
        protected interface Dispatcher {
1974

1975
            /**
1976
             * Defines a class.
1977
             *
1978
             * @param classLoader          The class loader to inject the class into.
1979
             * @param name                 The type's name.
1980
             * @param binaryRepresentation The type's binary representation.
1981
             * @param protectionDomain     The type's protection domain.
1982
             * @return The defined class.
1983
             */
1984
            Class<?> defineClass(@MaybeNull ClassLoader classLoader,
1985
                                 String name,
1986
                                 byte[] binaryRepresentation,
1987
                                 @MaybeNull ProtectionDomain protectionDomain);
1988

1989
            /**
1990
             * A class injection dispatcher that is not yet initialized.
1991
             */
1992
            interface Initializable {
1993

1994
                /**
1995
                 * Checks if unsafe class injection is available on the current VM.
1996
                 *
1997
                 * @return {@code true} if unsafe class injection is available.
1998
                 */
1999
                boolean isAvailable();
2000

2001
                /**
2002
                 * Initializes the dispatcher.
2003
                 *
2004
                 * @return The initialized dispatcher.
2005
                 */
2006
                Dispatcher initialize();
2007
            }
2008

2009
            /**
2010
             * A privileged action for creating a dispatcher.
2011
             */
2012
            enum CreationAction implements PrivilegedAction<Initializable> {
1✔
2013

2014
                /**
2015
                 * The singleton instance.
2016
                 */
2017
                INSTANCE;
1✔
2018

2019
                /**
2020
                 * {@inheritDoc}
2021
                 */
2022
                @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
2023
                public Initializable run() {
2024
                    if (Boolean.parseBoolean(java.lang.System.getProperty(SAFE_PROPERTY, Boolean.toString(GraalImageCode.getCurrent().isDefined())))) {
1✔
2025
                        return new Unavailable("Use of Unsafe was disabled by system property");
×
2026
                    }
2027
                    try {
2028
                        Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
1✔
2029
                        Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
1✔
2030
                        theUnsafe.setAccessible(true);
1✔
2031
                        Object unsafe = theUnsafe.get(null);
1✔
2032
                        try {
2033
                            Method defineClass = unsafeType.getMethod("defineClass",
1✔
2034
                                    String.class,
2035
                                    byte[].class,
2036
                                    int.class,
2037
                                    int.class,
2038
                                    ClassLoader.class,
2039
                                    ProtectionDomain.class);
2040
                            defineClass.setAccessible(true);
1✔
2041
                            return new Enabled(unsafe, defineClass);
1✔
2042
                        } catch (Exception exception) {
×
2043
                            try {
2044
                                Field override;
2045
                                try {
2046
                                    override = AccessibleObject.class.getDeclaredField("override");
×
2047
                                } catch (NoSuchFieldException ignored) {
×
2048
                                    // Since Java 12, the override field is hidden from the reflection API. To circumvent this, we
2049
                                    // create a mirror class of AccessibleObject that defines the same fields and has the same field
2050
                                    // layout such that the override field will receive the same class offset. Doing so, we can write to
2051
                                    // the offset location and still set a value to it, despite it being hidden from the reflection API.
2052
                                    override = new ByteBuddy()
×
2053
                                            .redefine(AccessibleObject.class)
×
2054
                                            .name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName())
×
2055
                                            .noNestMate()
×
2056
                                            .visit(new MemberRemoval().stripInvokables(any()))
×
2057
                                            .make()
×
2058
                                            .load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER.with(AccessibleObject.class.getProtectionDomain()))
×
2059
                                            .getLoaded()
×
2060
                                            .getDeclaredField("override");
×
2061
                                }
×
2062
                                long offset = (Long) unsafeType
×
2063
                                        .getMethod("objectFieldOffset", Field.class)
×
2064
                                        .invoke(unsafe, override);
×
2065
                                Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class);
×
2066
                                Class<?> internalUnsafe = Class.forName("jdk.internal.misc.Unsafe");
×
2067
                                Field theUnsafeInternal = internalUnsafe.getDeclaredField("theUnsafe");
×
2068
                                putBoolean.invoke(unsafe, theUnsafeInternal, offset, true);
×
2069
                                Method defineClassInternal = internalUnsafe.getMethod("defineClass",
×
2070
                                        String.class,
2071
                                        byte[].class,
2072
                                        int.class,
2073
                                        int.class,
2074
                                        ClassLoader.class,
2075
                                        ProtectionDomain.class);
2076
                                putBoolean.invoke(unsafe, defineClassInternal, offset, true);
×
2077
                                return new Enabled(theUnsafeInternal.get(null), defineClassInternal);
×
2078
                            } catch (Exception ignored) {
×
2079
                                throw exception;
×
2080
                            }
2081
                        }
2082
                    } catch (Exception exception) {
×
2083
                        return new Unavailable(exception.getMessage());
×
2084
                    }
2085
                }
2086
            }
2087

2088
            /**
2089
             * An enabled dispatcher.
2090
             */
2091
            @HashCodeAndEqualsPlugin.Enhance
2092
            class Enabled implements Dispatcher, Initializable {
2093

2094
                /**
2095
                 * An instance of {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe}.
2096
                 */
2097
                private final Object unsafe;
2098

2099
                /**
2100
                 * The {@code sun.misc.Unsafe#defineClass} or {@code jdk.internal.misc.Unsafe#defineClass} method.
2101
                 */
2102
                private final Method defineClass;
2103

2104
                /**
2105
                 * Creates an enabled dispatcher.
2106
                 *
2107
                 * @param unsafe      An instance of {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe}.
2108
                 * @param defineClass The {@code sun.misc.Unsafe#defineClass} or {@code jdk.internal.misc.Unsafe#defineClass} method.
2109
                 */
2110
                protected Enabled(Object unsafe, Method defineClass) {
1✔
2111
                    this.unsafe = unsafe;
1✔
2112
                    this.defineClass = defineClass;
1✔
2113
                }
1✔
2114

2115
                /**
2116
                 * {@inheritDoc}
2117
                 */
2118
                public boolean isAvailable() {
2119
                    return true;
1✔
2120
                }
2121

2122
                /**
2123
                 * {@inheritDoc}
2124
                 */
2125
                public Dispatcher initialize() {
2126
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
2127
                    if (securityManager != null) {
1✔
2128
                        try {
2129
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
2130
                        } catch (InvocationTargetException exception) {
×
2131
                            return new Unavailable(exception.getTargetException().getMessage());
×
2132
                        } catch (Exception exception) {
×
2133
                            return new Unavailable(exception.getMessage());
×
2134
                        }
×
2135
                    }
2136
                    return this;
1✔
2137
                }
2138

2139
                /**
2140
                 * {@inheritDoc}
2141
                 */
2142
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2143
                    try {
2144
                        return (Class<?>) defineClass.invoke(unsafe,
1✔
2145
                                name,
2146
                                binaryRepresentation,
2147
                                0,
1✔
2148
                                binaryRepresentation.length,
1✔
2149
                                classLoader,
2150
                                protectionDomain);
2151
                    } catch (IllegalAccessException exception) {
×
2152
                        throw new IllegalStateException(exception);
×
2153
                    } catch (InvocationTargetException exception) {
×
2154
                        throw new IllegalStateException(exception.getTargetException());
×
2155
                    }
2156
                }
2157
            }
2158

2159
            /**
2160
             * A disabled dispatcher.
2161
             */
2162
            @HashCodeAndEqualsPlugin.Enhance
2163
            class Unavailable implements Dispatcher, Initializable {
2164

2165
                /**
2166
                 * The reason why this dispatcher is not available.
2167
                 */
2168
                private final String message;
2169

2170
                /**
2171
                 * Creates a disabled dispatcher.
2172
                 *
2173
                 * @param message The reason why this dispatcher is not available.
2174
                 */
2175
                protected Unavailable(String message) {
1✔
2176
                    this.message = message;
1✔
2177
                }
1✔
2178

2179
                /**
2180
                 * {@inheritDoc}
2181
                 */
2182
                public boolean isAvailable() {
2183
                    return false;
1✔
2184
                }
2185

2186
                /**
2187
                 * {@inheritDoc}
2188
                 */
2189
                public Dispatcher initialize() {
2190
                    throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
1✔
2191
                }
2192

2193
                /**
2194
                 * {@inheritDoc}
2195
                 */
2196
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2197
                    throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
×
2198
                }
2199
            }
2200
        }
2201

2202
        /**
2203
         * A factory for creating a {@link ClassInjector} that uses {@code sun.misc.Unsafe} if available but attempts a fallback
2204
         * to using {@code jdk.internal.misc.Unsafe} if the {@code jdk.internal} module is not resolved or unavailable.
2205
         */
2206
        @HashCodeAndEqualsPlugin.Enhance
2207
        public static class Factory {
2208

2209
            /**
2210
             * The dispatcher to use.
2211
             */
2212
            private final Dispatcher.Initializable dispatcher;
2213

2214
            /**
2215
             * Creates a new factory for an unsafe class injector that uses Byte Buddy's privileges to
2216
             * accessing {@code jdk.internal.misc.Unsafe} if available.
2217
             */
2218
            public Factory() {
2219
                this(AccessResolver.Default.INSTANCE);
1✔
2220
            }
1✔
2221

2222
            /**
2223
             * Creates a new factory for an unsafe class injector.
2224
             *
2225
             * @param accessResolver The access resolver to use.
2226
             */
2227
            @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
2228
            public Factory(AccessResolver accessResolver) {
1✔
2229
                Dispatcher.Initializable dispatcher;
2230
                if (DISPATCHER.isAvailable()) {
1✔
2231
                    dispatcher = DISPATCHER;
1✔
2232
                } else {
2233
                    try {
2234
                        Class<?> unsafeType = Class.forName("jdk.internal.misc.Unsafe");
×
2235
                        Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
×
2236
                        accessResolver.apply(theUnsafe);
×
2237
                        Object unsafe = theUnsafe.get(null);
×
2238
                        Method defineClass = unsafeType.getMethod("defineClass",
×
2239
                                String.class,
2240
                                byte[].class,
2241
                                int.class,
2242
                                int.class,
2243
                                ClassLoader.class,
2244
                                ProtectionDomain.class);
2245
                        accessResolver.apply(defineClass);
×
2246
                        dispatcher = new Dispatcher.Enabled(unsafe, defineClass);
×
2247
                    } catch (Exception exception) {
×
2248
                        dispatcher = new Dispatcher.Unavailable(exception.getMessage());
×
2249
                    }
×
2250
                }
2251
                this.dispatcher = dispatcher;
1✔
2252
            }
1✔
2253

2254
            /**
2255
             * Creates a new factory.
2256
             *
2257
             * @param dispatcher The dispatcher to use.
2258
             */
2259
            protected Factory(Dispatcher.Initializable dispatcher) {
×
2260
                this.dispatcher = dispatcher;
×
2261
            }
×
2262

2263
            /**
2264
             * Resolves an injection strategy that uses unsafe injection if available and also attempts to open and use
2265
             * {@code jdk.internal.misc.Unsafe} as a fallback. This method generates a new class and module for opening the
2266
             * internal package to avoid its exposure to any non-trusted code.
2267
             *
2268
             * @param instrumentation The instrumentation instance to use for opening the internal package if required.
2269
             * @return An appropriate injection strategy.
2270
             */
2271
            public static Factory resolve(Instrumentation instrumentation) {
2272
                return resolve(instrumentation, false);
1✔
2273
            }
2274

2275
            /**
2276
             * Resolves an injection strategy that uses unsafe injection if available and also attempts to open and use
2277
             * {@code jdk.internal.misc.Unsafe} as a fallback.
2278
             *
2279
             * @param instrumentation The instrumentation instance to use for opening the internal package if required.
2280
             * @param local           {@code false} if a new class should in a separated class loader and module should be created for
2281
             *                        opening the {@code jdk.internal.misc} package. This way, the internal package is not exposed to any
2282
             *                        other classes within this class's module.
2283
             * @return An appropriate injection strategy.
2284
             */
2285
            @SuppressFBWarnings(
2286
                    value = {"REC_CATCH_EXCEPTION", "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"},
2287
                    justification = "Exception intends to trigger disabled injection strategy. Modules are assumed if module system is supported.")
2288
            public static Factory resolve(Instrumentation instrumentation, boolean local) {
2289
                if (ClassInjector.UsingUnsafe.isAvailable() || !JavaModule.isSupported()) {
1✔
2290
                    return new Factory();
1✔
2291
                } else {
2292
                    try {
2293
                        Class<?> type = Class.forName("jdk.internal.misc.Unsafe");
×
2294
                        PackageDescription packageDescription = new PackageDescription.ForLoadedPackage(type.getPackage());
×
2295
                        JavaModule source = JavaModule.ofType(type), target = JavaModule.ofType(ClassInjector.UsingUnsafe.class);
×
2296
                        if (source.isOpened(packageDescription, target)) {
×
2297
                            return new Factory();
×
2298
                        } else if (local) {
×
2299
                            JavaModule module = JavaModule.ofType(AccessResolver.Default.class);
×
2300
                            UsingInstrumentation.redefineModule(instrumentation,
×
2301
                                    source,
2302
                                    Collections.singleton(module),
×
2303
                                    Collections.<String, Set<JavaModule>>emptyMap(),
×
2304
                                    Collections.singletonMap(packageDescription.getName(), Collections.singleton(module)),
×
2305
                                    Collections.<Class<?>>emptySet(),
×
2306
                                    Collections.<Class<?>, List<Class<?>>>emptyMap());
×
2307
                            return new Factory();
×
2308
                        } else {
2309
                            Class<? extends AccessResolver> resolver = new ByteBuddy()
×
2310
                                    .subclass(AccessResolver.class)
×
2311
                                    .method(named("apply"))
×
2312
                                    .intercept(MethodCall.invoke(AccessibleObject.class.getMethod("setAccessible", boolean.class))
×
2313
                                            .onArgument(0)
×
2314
                                            .with(true))
×
2315
                                    .make()
×
2316
                                    .load(AccessResolver.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER.with(AccessResolver.class.getProtectionDomain()))
×
2317
                                    .getLoaded();
×
2318
                            JavaModule module = JavaModule.ofType(resolver);
×
2319
                            ClassInjector.UsingInstrumentation.redefineModule(instrumentation,
×
2320
                                    source,
2321
                                    Collections.singleton(module),
×
2322
                                    Collections.<String, Set<JavaModule>>emptyMap(),
×
2323
                                    Collections.singletonMap(packageDescription.getName(), Collections.singleton(module)),
×
2324
                                    Collections.<Class<?>>emptySet(),
×
2325
                                    Collections.<Class<?>, List<Class<?>>>emptyMap());
×
2326
                            return new ClassInjector.UsingUnsafe.Factory(resolver.getConstructor().newInstance());
×
2327
                        }
2328
                    } catch (Exception exception) {
×
2329
                        return new Factory(new Dispatcher.Unavailable(exception.getMessage()));
×
2330
                    }
2331
                }
2332
            }
2333

2334
            /**
2335
             * Returns {@code true} if this factory creates a valid dispatcher.
2336
             *
2337
             * @return {@code true} if this factory creates a valid dispatcher.
2338
             */
2339
            public boolean isAvailable() {
2340
                return dispatcher.isAvailable();
1✔
2341
            }
2342

2343
            /**
2344
             * Creates a new class injector for the given class loader without a {@link ProtectionDomain}.
2345
             *
2346
             * @param classLoader The class loader to inject into or {@code null} to inject into the bootstrap loader.
2347
             * @return An appropriate class injector.
2348
             */
2349
            public ClassInjector make(@MaybeNull ClassLoader classLoader) {
2350
                return make(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
2351
            }
2352

2353
            /**
2354
             * Creates a new class injector for the given class loader and protection domain.
2355
             *
2356
             * @param classLoader      The class loader to inject into or {@code null} to inject into the bootstrap loader.
2357
             * @param protectionDomain The protection domain to apply or {@code null} if no protection domain should be used.
2358
             * @return An appropriate class injector.
2359
             */
2360
            public ClassInjector make(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
2361
                return new UsingUnsafe(classLoader, protectionDomain, dispatcher);
1✔
2362
            }
2363

2364
            /**
2365
             * An access resolver that invokes {@link AccessibleObject#setAccessible(boolean)} to {@code true} in a given privilege scope.
2366
             */
2367
            public interface AccessResolver {
2368

2369
                /**
2370
                 * Applies this access resolver.
2371
                 *
2372
                 * @param accessibleObject The accessible object to make accessible.
2373
                 */
2374
                void apply(AccessibleObject accessibleObject);
2375

2376
                /**
2377
                 * A default access resolver that uses Byte Buddy's privilege scope.
2378
                 */
2379
                enum Default implements AccessResolver {
1✔
2380

2381
                    /**
2382
                     * The singleton instance.
2383
                     */
2384
                    INSTANCE;
1✔
2385

2386
                    /**
2387
                     * {@inheritDoc}
2388
                     */
2389
                    public void apply(AccessibleObject accessibleObject) {
2390
                        accessibleObject.setAccessible(true);
×
2391
                    }
×
2392
                }
2393
            }
2394
        }
2395

2396
        /**
2397
         * A proxy of {@code java.lang.System}.
2398
         */
2399
        @JavaDispatcher.Proxied("java.lang.System")
2400
        protected interface System {
2401

2402
            /**
2403
             * Returns the current security manager or {@code null} if not available.
2404
             *
2405
             * @return The current security manager or {@code null} if not available.
2406
             */
2407
            @MaybeNull
2408
            @JavaDispatcher.IsStatic
2409
            @JavaDispatcher.Defaults
2410
            Object getSecurityManager();
2411
        }
2412
    }
2413

2414
    /**
2415
     * A class injector using a {@link java.lang.instrument.Instrumentation} to append to either the boot classpath
2416
     * or the system class path.
2417
     */
2418
    @HashCodeAndEqualsPlugin.Enhance
2419
    class UsingInstrumentation extends AbstractBase {
2420

2421
        /**
2422
         * The jar file name extension.
2423
         */
2424
        private static final String JAR = "jar";
2425

2426
        /**
2427
         * The class file extension.
2428
         */
2429
        private static final String CLASS_FILE_EXTENSION = ".class";
2430

2431
        /**
2432
         * A dispatcher for interacting with the instrumentation API.
2433
         */
2434
        private static final Dispatcher DISPATCHER = doPrivileged(JavaDispatcher.of(Dispatcher.class));
1✔
2435

2436
        /**
2437
         * The instrumentation to use for appending to the class path or the boot path.
2438
         */
2439
        private final Instrumentation instrumentation;
2440

2441
        /**
2442
         * A representation of the target path to which classes are to be appended.
2443
         */
2444
        private final Target target;
2445

2446
        /**
2447
         * The folder to be used for storing jar files.
2448
         */
2449
        private final File folder;
2450

2451
        /**
2452
         * A random string generator for creating file names.
2453
         */
2454
        private final RandomString randomString;
2455

2456
        /**
2457
         * Creates an instrumentation-based class injector.
2458
         *
2459
         * @param folder          The folder to be used for storing jar files.
2460
         * @param target          A representation of the target path to which classes are to be appended.
2461
         * @param instrumentation The instrumentation to use for appending to the class path or the boot path.
2462
         * @param randomString    The random string generator to use.
2463
         */
2464
        protected UsingInstrumentation(File folder,
2465
                                       Target target,
2466
                                       Instrumentation instrumentation,
2467
                                       RandomString randomString) {
1✔
2468
            this.folder = folder;
1✔
2469
            this.target = target;
1✔
2470
            this.instrumentation = instrumentation;
1✔
2471
            this.randomString = randomString;
1✔
2472
        }
1✔
2473

2474
        /**
2475
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
2476
         *
2477
         * @param action The action to execute from a privileged context.
2478
         * @param <T>    The type of the action's resolved value.
2479
         * @return The action's resolved value.
2480
         */
2481
        @AccessControllerPlugin.Enhance
2482
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
2483
            return action.run();
×
2484
        }
2485

2486
        /**
2487
         * Modifies a module's properties using {@link Instrumentation}.
2488
         *
2489
         * @param instrumentation The {@link Instrumentation} instance to use for applying the modification.
2490
         * @param target          The target module that should be modified.
2491
         * @param reads           A set of additional modules this module should read.
2492
         * @param exports         A map of packages to export to a set of modules.
2493
         * @param opens           A map of packages to open to a set of modules.
2494
         * @param uses            A set of provider interfaces to use by this module.
2495
         * @param provides        A map of provider interfaces to provide by this module mapped to the provider implementations.
2496
         */
2497
        public static void redefineModule(Instrumentation instrumentation,
2498
                                          JavaModule target,
2499
                                          Set<JavaModule> reads,
2500
                                          Map<String, Set<JavaModule>> exports,
2501
                                          Map<String, Set<JavaModule>> opens,
2502
                                          Set<Class<?>> uses,
2503
                                          Map<Class<?>, List<Class<?>>> provides) {
2504
            if (!DISPATCHER.isModifiableModule(instrumentation, target.unwrap())) {
×
2505
                throw new IllegalArgumentException("Cannot modify module: " + target);
×
2506
            }
2507
            Set<Object> unwrappedReads = new HashSet<Object>();
×
2508
            for (JavaModule read : reads) {
×
2509
                unwrappedReads.add(read.unwrap());
×
2510
            }
×
2511
            Map<String, Set<?>> unwrappedExports = new HashMap<String, Set<?>>();
×
2512
            for (Map.Entry<String, Set<JavaModule>> entry : exports.entrySet()) {
×
2513
                Set<Object> modules = new HashSet<Object>();
×
2514
                for (JavaModule module : entry.getValue()) {
×
2515
                    modules.add(module.unwrap());
×
2516
                }
×
2517
                unwrappedExports.put(entry.getKey(), modules);
×
2518
            }
×
2519
            Map<String, Set<?>> unwrappedOpens = new HashMap<String, Set<?>>();
×
2520
            for (Map.Entry<String, Set<JavaModule>> entry : opens.entrySet()) {
×
2521
                Set<Object> modules = new HashSet<Object>();
×
2522
                for (JavaModule module : entry.getValue()) {
×
2523
                    modules.add(module.unwrap());
×
2524
                }
×
2525
                unwrappedOpens.put(entry.getKey(), modules);
×
2526
            }
×
2527
            DISPATCHER.redefineModule(instrumentation, target.unwrap(), unwrappedReads, unwrappedExports, unwrappedOpens, uses, provides);
×
2528
        }
×
2529

2530
        /**
2531
         * Creates an instrumentation-based class injector.
2532
         *
2533
         * @param folder          The folder to be used for storing jar files.
2534
         * @param target          A representation of the target path to which classes are to be appended.
2535
         * @param instrumentation The instrumentation to use for appending to the class path or the boot path.
2536
         * @return An appropriate class injector that applies instrumentation.
2537
         */
2538
        public static ClassInjector of(File folder, Target target, Instrumentation instrumentation) {
2539
            return new UsingInstrumentation(folder, target, instrumentation, new RandomString());
1✔
2540
        }
2541

2542
        /**
2543
         * {@inheritDoc}
2544
         */
2545
        public boolean isAlive() {
2546
            return isAvailable();
1✔
2547
        }
2548

2549
        /**
2550
         * {@inheritDoc}
2551
         */
2552
        @SuppressFBWarnings(value = "OS_OPEN_STREAM_EXCEPTION_PATH", justification = "Outer stream holds file handle and is closed")
2553
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
2554
            File file = new File(folder, JAR + randomString.nextString() + "." + JAR);
1✔
2555
            try {
2556
                if (!file.createNewFile()) {
1✔
2557
                    throw new IllegalStateException("Cannot create file " + file);
×
2558
                }
2559
                try {
2560
                    OutputStream outputStream = new FileOutputStream(file);
1✔
2561
                    try {
2562
                        JarOutputStream jarOutputStream = new JarOutputStream(outputStream);
1✔
2563
                        for (String name : names) {
1✔
2564
                            jarOutputStream.putNextEntry(new JarEntry(name.replace('.', '/') + CLASS_FILE_EXTENSION));
1✔
2565
                            jarOutputStream.write(classFileLocator.locate(name).resolve());
1✔
2566
                        }
1✔
2567
                        jarOutputStream.close();
1✔
2568
                    } finally {
2569
                        outputStream.close();
1✔
2570
                    }
2571
                    JarFile jarFile = new JarFile(file, false, ZipFile.OPEN_READ);
1✔
2572
                    try {
2573
                        target.inject(instrumentation, jarFile);
1✔
2574
                    } finally {
2575
                        jarFile.close();
1✔
2576
                    }
2577
                    Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1✔
2578
                    for (String name : names) {
1✔
2579
                        result.put(name, Class.forName(name, false, target.getClassLoader()));
1✔
2580
                    }
1✔
2581
                    return result;
1✔
2582
                } finally {
2583
                    if (!file.delete()) {
1✔
2584
                        file.deleteOnExit();
×
2585
                    }
2586
                }
2587
            } catch (IOException exception) {
×
2588
                throw new IllegalStateException("Cannot write jar file to disk", exception);
×
2589
            } catch (ClassNotFoundException exception) {
×
2590
                throw new IllegalStateException("Cannot load injected class", exception);
×
2591
            }
2592
        }
2593

2594
        /**
2595
         * Returns {@code true} if this class injector is available on this VM.
2596
         *
2597
         * @return {@code true} if this class injector is available on this VM.
2598
         */
2599
        public static boolean isAvailable() {
2600
            return ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5).isAtLeast(ClassFileVersion.JAVA_V6);
1✔
2601
        }
2602

2603
        /**
2604
         * A dispatcher to interact with the instrumentation API.
2605
         */
2606
        @JavaDispatcher.Proxied("java.lang.instrument.Instrumentation")
2607
        protected interface Dispatcher {
2608

2609
            /**
2610
             * Appends a jar file to the bootstrap class loader.
2611
             *
2612
             * @param instrumentation The instrumentation instance to interact with.
2613
             * @param jarFile         The jar file to append.
2614
             */
2615
            void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
2616

2617
            /**
2618
             * Appends a jar file to the system class loader.
2619
             *
2620
             * @param instrumentation The instrumentation instance to interact with.
2621
             * @param jarFile         The jar file to append.
2622
             */
2623
            void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
2624

2625
            /**
2626
             * Checks if a module is modifiable.
2627
             *
2628
             * @param instrumentation The instrumentation instance to use for checking for modifiability.
2629
             * @param module          The {@code java.lang.Module} to examine.
2630
             * @return {@code true} if the supplied module is modifiable.
2631
             */
2632
            boolean isModifiableModule(Instrumentation instrumentation, @JavaDispatcher.Proxied("java.lang.Module") Object module);
2633

2634
            /**
2635
             * Redefines an existing module.
2636
             *
2637
             * @param instrumentation The instrumentation instance to redefine.
2638
             * @param module          The {@code java.lang.Module} to redefine.
2639
             * @param reads           A set of {@code java.lang.Module}s that are to be read additionally.
2640
             * @param exports         A map of packages to a set of {@code java.lang.Module}s to read additionally.
2641
             * @param opens           A map of packages to a set of {@code java.lang.Module}s to open to additionally.
2642
             * @param uses            A list of types to use additionally.
2643
             * @param provides        A list of types to their implementations to offer additionally.
2644
             */
2645
            void redefineModule(Instrumentation instrumentation,
2646
                                @JavaDispatcher.Proxied("java.lang.Module") Object module,
2647
                                Set<?> reads,
2648
                                Map<String, Set<?>> exports,
2649
                                Map<String, Set<?>> opens,
2650
                                Set<Class<?>> uses,
2651
                                Map<Class<?>, List<Class<?>>> provides);
2652
        }
2653

2654
        /**
2655
         * A representation of the target to which Java classes should be appended to.
2656
         */
2657
        public enum Target {
1✔
2658

2659
            /**
2660
             * Representation of the bootstrap class loader.
2661
             */
2662
            BOOTSTRAP(null) {
1✔
2663
                @Override
2664
                protected void inject(Instrumentation instrumentation, JarFile jarFile) {
2665
                    DISPATCHER.appendToBootstrapClassLoaderSearch(instrumentation, jarFile);
1✔
2666
                }
1✔
2667
            },
2668

2669
            /**
2670
             * Representation of the system class loader.
2671
             */
2672
            SYSTEM(ClassLoader.getSystemClassLoader()) {
1✔
2673
                @Override
2674
                protected void inject(Instrumentation instrumentation, JarFile jarFile) {
2675
                    DISPATCHER.appendToSystemClassLoaderSearch(instrumentation, jarFile);
1✔
2676
                }
1✔
2677
            };
2678

2679
            /**
2680
             * The class loader to load classes from.
2681
             */
2682
            @MaybeNull
2683
            private final ClassLoader classLoader;
2684

2685
            /**
2686
             * Creates a new injection target.
2687
             *
2688
             * @param classLoader The class loader to load classes from.
2689
             */
2690
            Target(@MaybeNull ClassLoader classLoader) {
1✔
2691
                this.classLoader = classLoader;
1✔
2692
            }
1✔
2693

2694
            /**
2695
             * Returns the class loader to load classes from.
2696
             *
2697
             * @return The class loader to load classes from.
2698
             */
2699
            @MaybeNull
2700
            protected ClassLoader getClassLoader() {
2701
                return classLoader;
1✔
2702
            }
2703

2704
            /**
2705
             * Adds the given classes to the represented class loader.
2706
             *
2707
             * @param instrumentation The instrumentation instance to use.
2708
             * @param jarFile         The jar file to append.
2709
             */
2710
            protected abstract void inject(Instrumentation instrumentation, JarFile jarFile);
2711
        }
2712
    }
2713

2714
    /**
2715
     * A class injector using JNA to invoke JNI's define class utility for defining a class. This injector is only
2716
     * available if JNA is available on the class loader. Some JVM implementations might not support this injection
2717
     * method.
2718
     */
2719
    @HashCodeAndEqualsPlugin.Enhance
2720
    class UsingJna extends AbstractBase {
2721

2722
        /**
2723
         * The dispatcher to use.
2724
         */
2725
        private static final Dispatcher DISPATCHER = doPrivileged(Dispatcher.CreationAction.INSTANCE);
1✔
2726

2727
        /**
2728
         * A lock for the bootstrap loader when injecting.
2729
         */
2730
        private static final Object BOOTSTRAP_LOADER_LOCK = new Object();
1✔
2731

2732
        /**
2733
         * The class loader to inject classes into or {@code null} for the bootstrap loader.
2734
         */
2735
        @MaybeNull
2736
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
2737
        private final ClassLoader classLoader;
2738

2739
        /**
2740
         * The protection domain to use or {@code null} for no protection domain.
2741
         */
2742
        @MaybeNull
2743
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
2744
        private final ProtectionDomain protectionDomain;
2745

2746
        /**
2747
         * Creates a new unsafe injector for the given class loader with a default protection domain.
2748
         *
2749
         * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
2750
         */
2751
        public UsingJna(@MaybeNull ClassLoader classLoader) {
2752
            this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
2753
        }
1✔
2754

2755
        /**
2756
         * Creates a new JNA injector for the given class loader with a default protection domain.
2757
         *
2758
         * @param classLoader      The class loader to inject classes into or {@code null} for the bootstrap loader.
2759
         * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
2760
         */
2761
        public UsingJna(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
1✔
2762
            this.classLoader = classLoader;
1✔
2763
            this.protectionDomain = protectionDomain;
1✔
2764
        }
1✔
2765

2766
        /**
2767
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
2768
         *
2769
         * @param action The action to execute from a privileged context.
2770
         * @param <T>    The type of the action's resolved value.
2771
         * @return The action's resolved value.
2772
         */
2773
        @AccessControllerPlugin.Enhance
2774
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
2775
            return action.run();
×
2776
        }
2777

2778
        /**
2779
         * Checks if JNA class injection is available on the current VM.
2780
         *
2781
         * @return {@code true} if JNA class injection is available on the current VM.
2782
         */
2783
        public static boolean isAvailable() {
2784
            return DISPATCHER.isAvailable();
1✔
2785
        }
2786

2787
        /**
2788
         * Returns an JNA class injector for the system class loader.
2789
         *
2790
         * @return A class injector for the system class loader.
2791
         */
2792
        public static ClassInjector ofSystemLoader() {
2793
            return new UsingJna(ClassLoader.getSystemClassLoader());
1✔
2794
        }
2795

2796
        /**
2797
         * Returns an JNA class injector for the platform class loader. For VMs of version 8 or older,
2798
         * the extension class loader is represented instead.
2799
         *
2800
         * @return A class injector for the platform class loader.
2801
         */
2802
        public static ClassInjector ofPlatformLoader() {
2803
            return new UsingJna(ClassLoader.getSystemClassLoader().getParent());
1✔
2804
        }
2805

2806
        /**
2807
         * Returns an JNA class injector for the boot class loader.
2808
         *
2809
         * @return A class injector for the boot loader.
2810
         */
2811
        public static ClassInjector ofBootLoader() {
2812
            return new UsingJna(ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
2813
        }
2814

2815
        /**
2816
         * {@inheritDoc}
2817
         */
2818
        public boolean isAlive() {
2819
            return DISPATCHER.isAvailable();
1✔
2820
        }
2821

2822
        /**
2823
         * {@inheritDoc}
2824
         */
2825
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
2826
            Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1✔
2827
            synchronized (classLoader == null
1✔
2828
                    ? BOOTSTRAP_LOADER_LOCK
2829
                    : classLoader) {
2830
                for (String name : names) {
1✔
2831
                    try {
2832
                        result.put(name, Class.forName(name, false, classLoader));
×
2833
                    } catch (ClassNotFoundException ignored) {
1✔
2834
                        try {
2835
                            result.put(name, DISPATCHER.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain));
1✔
2836
                        } catch (IOException exception) {
×
2837
                            throw new IllegalStateException("Failed to resolve binary representation of " + name, exception);
×
2838
                        }
1✔
2839
                    }
×
2840
                }
1✔
2841
            }
1✔
2842
            return result;
1✔
2843
        }
2844

2845
        /**
2846
         * A dispatcher for JNA class injection.
2847
         */
2848
        protected interface Dispatcher {
2849

2850
            /**
2851
             * Checks if this dispatcher is available for use.
2852
             *
2853
             * @return {@code true} if this dispatcher is available for use.
2854
             */
2855
            boolean isAvailable();
2856

2857
            /**
2858
             * Defines a class.
2859
             *
2860
             * @param classLoader          The class loader or {@code null} if a class should be injected into the bootstrap loader.
2861
             * @param name                 The class's name.
2862
             * @param binaryRepresentation The class's class file.
2863
             * @param protectionDomain     The protection domain to use or {@code null} if no protection domain should be used.
2864
             * @return The class that was defined.
2865
             */
2866
            Class<?> defineClass(@MaybeNull ClassLoader classLoader,
2867
                                 String name,
2868
                                 byte[] binaryRepresentation,
2869
                                 @MaybeNull ProtectionDomain protectionDomain);
2870

2871
            /**
2872
             * An action for creating a JNA dispatcher.
2873
             */
2874
            enum CreationAction implements PrivilegedAction<Dispatcher> {
1✔
2875

2876
                /**
2877
                 * The singleton instance.
2878
                 */
2879
                INSTANCE;
1✔
2880

2881
                /**
2882
                 * {@inheritDoc}
2883
                 */
2884
                @SuppressWarnings("deprecation")
2885
                public Dispatcher run() {
2886
                    if (System.getProperty("java.vm.name", "").toUpperCase(Locale.US).contains("J9")) {
1✔
2887
                        return new Unavailable("J9 does not support JNA-based class definition");
×
2888
                    }
2889
                    try {
2890
                        Map<String, Object> options = new HashMap<String, Object>();
1✔
2891
                        options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);
1✔
2892
                        if (Platform.isWindows() && !Platform.is64Bit()) {
1✔
2893
                            options.put(Library.OPTION_FUNCTION_MAPPER, Windows32BitFunctionMapper.INSTANCE);
×
2894
                        }
2895
                        return new Enabled(Native.loadLibrary("jvm", Jvm.class, options));
1✔
2896
                    } catch (Throwable throwable) {
×
2897
                        return new Unavailable(throwable.getMessage());
×
2898
                    }
2899
                }
2900
            }
2901

2902
            /**
2903
             * A mapper for 32-bit Windows functions where names are defined with different convention.
2904
             */
2905
            enum Windows32BitFunctionMapper implements FunctionMapper {
×
2906

2907
                /**
2908
                 * The singleton instance.
2909
                 */
2910
                INSTANCE;
×
2911

2912
                /**
2913
                 * {@inheritDoc}
2914
                 */
2915
                public String getFunctionName(NativeLibrary library, Method method) {
2916
                    if (method.getName().equals("JVM_DefineClass")) {
×
2917
                        return "_JVM_DefineClass@24";
×
2918
                    }
2919
                    return method.getName();
×
2920
                }
2921
            }
2922

2923
            /**
2924
             * An enabled dispatcher for JNA-based class injection.
2925
             */
2926
            @HashCodeAndEqualsPlugin.Enhance
2927
            class Enabled implements Dispatcher {
2928

2929
                /**
2930
                 * The JNA-dispatcher to use for invoking JNI's class definition utilities.
2931
                 */
2932
                private final Jvm jvm;
2933

2934
                /**
2935
                 * Creates a new dispatcher for a JNI's class definition utilities.
2936
                 *
2937
                 * @param jvm The JNA-dispatcher to use for invoking JNI's class definition utilities.
2938
                 */
2939
                protected Enabled(Jvm jvm) {
1✔
2940
                    this.jvm = jvm;
1✔
2941
                }
1✔
2942

2943
                /**
2944
                 * {@inheritDoc}
2945
                 */
2946
                public boolean isAvailable() {
2947
                    return true;
1✔
2948
                }
2949

2950
                /**
2951
                 * {@inheritDoc}
2952
                 */
2953
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2954
                    return jvm.JVM_DefineClass(JNIEnv.CURRENT,
1✔
2955
                            name.replace('.', '/'),
1✔
2956
                            classLoader,
2957
                            binaryRepresentation,
2958
                            binaryRepresentation.length,
2959
                            protectionDomain);
2960
                }
2961
            }
2962

2963
            /**
2964
             * An unavailable dispatcher for JNA-based class injection.
2965
             */
2966
            @HashCodeAndEqualsPlugin.Enhance
2967
            class Unavailable implements Dispatcher {
2968

2969
                /**
2970
                 * The exception's error message when attempting to resolve the JNA dispatcher.
2971
                 */
2972
                private final String error;
2973

2974
                /**
2975
                 * Creates a new unavailable JNA-based class injector.
2976
                 *
2977
                 * @param error The exception's error message when attempting to resolve the JNA dispatcher.
2978
                 */
2979
                protected Unavailable(String error) {
1✔
2980
                    this.error = error;
1✔
2981
                }
1✔
2982

2983
                /**
2984
                 * {@inheritDoc}
2985
                 */
2986
                public boolean isAvailable() {
2987
                    return false;
1✔
2988
                }
2989

2990
                /**
2991
                 * {@inheritDoc}
2992
                 */
2993
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2994
                    throw new UnsupportedOperationException("JNA is not available and JNA-based injection cannot be used: " + error);
1✔
2995
                }
2996
            }
2997

2998
            /**
2999
             * A JNA dispatcher for the JVM's <i>JVM_DefineClass</i> method.
3000
             */
3001
            interface Jvm extends Library {
3002

3003
                /**
3004
                 * Defines a new class into a given class loader.
3005
                 *
3006
                 * @param env                  The JNI environment.
3007
                 * @param name                 The internal name of the class.
3008
                 * @param classLoader          The class loader to inject into or {@code null} if injecting into the bootstrap loader.
3009
                 * @param binaryRepresentation The class's binary representation.
3010
                 * @param length               The length of the class's binary representation.
3011
                 * @param protectionDomain     The protection domain or {@code null} if no explicit protection domain should be used.
3012
                 * @return The class that was defined.
3013
                 * @throws LastErrorException If an error occurs during injection.
3014
                 */
3015
                @SuppressWarnings("checkstyle:methodname")
3016
                Class<?> JVM_DefineClass(JNIEnv env,
3017
                                         String name,
3018
                                         @MaybeNull ClassLoader classLoader,
3019
                                         byte[] binaryRepresentation,
3020
                                         int length,
3021
                                         @MaybeNull ProtectionDomain protectionDomain) throws LastErrorException;
3022
            }
3023
        }
3024
    }
3025
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc