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

raphw / byte-buddy / #792

05 Oct 2025 08:51PM UTC coverage: 85.115% (-0.01%) from 85.128%
#792

push

raphw
Add missing checksum.

29562 of 34732 relevant lines covered (85.11%)

0.85 hits per line

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

54.39
/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.*;
19
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
20
import net.bytebuddy.ByteBuddy;
21
import net.bytebuddy.ClassFileVersion;
22
import net.bytebuddy.asm.MemberRemoval;
23
import net.bytebuddy.build.AccessControllerPlugin;
24
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
25
import net.bytebuddy.description.modifier.Visibility;
26
import net.bytebuddy.description.type.PackageDescription;
27
import net.bytebuddy.description.type.TypeDescription;
28
import net.bytebuddy.dynamic.ClassFileLocator;
29
import net.bytebuddy.dynamic.DynamicType;
30
import net.bytebuddy.dynamic.scaffold.TypeValidation;
31
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
32
import net.bytebuddy.implementation.FixedValue;
33
import net.bytebuddy.implementation.MethodCall;
34
import net.bytebuddy.utility.GraalImageCode;
35
import net.bytebuddy.utility.JavaModule;
36
import net.bytebuddy.utility.JavaType;
37
import net.bytebuddy.utility.RandomString;
38
import net.bytebuddy.utility.dispatcher.JavaDispatcher;
39
import net.bytebuddy.utility.nullability.AlwaysNull;
40
import net.bytebuddy.utility.nullability.MaybeNull;
41
import net.bytebuddy.utility.nullability.UnknownNull;
42
import net.bytebuddy.utility.privilege.GetMethodAction;
43

44
import java.io.File;
45
import java.io.FileOutputStream;
46
import java.io.IOException;
47
import java.io.OutputStream;
48
import java.lang.instrument.Instrumentation;
49
import java.lang.reflect.AccessibleObject;
50
import java.lang.reflect.Field;
51
import java.lang.reflect.InvocationTargetException;
52
import java.lang.reflect.Method;
53
import java.net.URL;
54
import java.security.Permission;
55
import java.security.PrivilegedAction;
56
import java.security.ProtectionDomain;
57
import java.util.*;
58
import java.util.jar.JarEntry;
59
import java.util.jar.JarFile;
60
import java.util.jar.JarOutputStream;
61
import java.util.zip.ZipFile;
62

63
import static net.bytebuddy.matcher.ElementMatchers.any;
64
import static net.bytebuddy.matcher.ElementMatchers.named;
65

66
/**
67
 * <p>
68
 * A class injector is capable of injecting classes into a {@link java.lang.ClassLoader} without
69
 * requiring the class loader to being able to explicitly look up these classes.
70
 * </p>
71
 * <p>
72
 * <b>Important</b>: Byte Buddy does not supply privileges when injecting code. When using a {@code java.lang.SecurityManager},
73
 * the user of this injector is responsible for providing access to non-public properties.
74
 * </p>
75
 */
76
public interface ClassInjector {
77

78
    /**
79
     * Determines the default behavior for type injections when a type is already loaded.
80
     */
81
    boolean ALLOW_EXISTING_TYPES = false;
82

83
    /**
84
     * Indicates if this class injector is available on the current VM.
85
     *
86
     * @return {@code true} if this injector is available on the current VM.
87
     */
88
    boolean isAlive();
89

90
    /**
91
     * Injects the given types into the represented class loader.
92
     *
93
     * @param types            The types to load via injection.
94
     * @param classFileLocator The class file locator to use for resolving binary representations.
95
     * @return The loaded types that were passed as arguments.
96
     */
97
    Map<TypeDescription, Class<?>> inject(Set<? extends TypeDescription> types, ClassFileLocator classFileLocator);
98

99
    /**
100
     * Injects the given types into the represented class loader.
101
     *
102
     * @param names            The names of the types to load via injection.
103
     * @param classFileLocator The class file locator to use for resolving binary representations.
104
     * @return The loaded types that were passed as arguments.
105
     */
106
    Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator);
107

108
    /**
109
     * Injects the given types into the represented class loader.
110
     *
111
     * @param types The types to load via injection.
112
     * @return The loaded types that were passed as arguments.
113
     */
114
    Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types);
115

116
    /**
117
     * Injects the given types into the represented class loader.
118
     *
119
     * @param types The names of the type to load via injection.
120
     * @return The loaded types that were passed as arguments.
121
     */
122
    Map<String, Class<?>> injectRaw(Map<String, byte[]> types);
123

124
    /**
125
     * An abstract base implementation of a class injector.
126
     */
127
    abstract class AbstractBase implements ClassInjector {
1✔
128

129
        /**
130
         * A permission for the {@code suppressAccessChecks} permission or {@code null} if not supported.
131
         */
132
        @MaybeNull
133
        protected static final Permission SUPPRESS_ACCESS_CHECKS = toSuppressAccessChecks();
1✔
134

135
        /**
136
         * Returns a permission for the {@code suppressAccessChecks} permission or {@code null} if not supported.
137
         *
138
         * @return A permission for the {@code suppressAccessChecks} permission or {@code null} if not supported.
139
         */
140
        @MaybeNull
141
        @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown define null as value.")
142
        private static Permission toSuppressAccessChecks() {
143
            try {
144
                return (Permission) Class.forName("java.lang.reflect.ReflectPermission")
1✔
145
                        .getConstructor(String.class)
1✔
146
                        .newInstance("suppressAccessChecks");
1✔
147
            } catch (Exception ignored) {
×
148
                return null;
×
149
            }
150
        }
151

152
        /**
153
         * {@inheritDoc}
154
         */
155
        public Map<TypeDescription, Class<?>> inject(Set<? extends TypeDescription> types, ClassFileLocator classFileLocator) {
156
            Set<String> names = new LinkedHashSet<String>();
1✔
157
            for (TypeDescription type : types) {
1✔
158
                names.add(type.getName());
1✔
159
            }
1✔
160
            Map<String, Class<?>> loadedTypes = injectRaw(names, classFileLocator);
1✔
161
            Map<TypeDescription, Class<?>> result = new HashMap<TypeDescription, Class<?>>();
1✔
162
            for (TypeDescription type : types) {
1✔
163
                result.put(type, loadedTypes.get(type.getName()));
1✔
164
            }
1✔
165
            return result;
1✔
166
        }
167

168
        /**
169
         * {@inheritDoc}
170
         */
171
        public Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types) {
172
            Map<String, byte[]> binaryRepresentations = new LinkedHashMap<String, byte[]>();
1✔
173
            for (Map.Entry<? extends TypeDescription, byte[]> entry : types.entrySet()) {
1✔
174
                binaryRepresentations.put(entry.getKey().getName(), entry.getValue());
1✔
175
            }
1✔
176
            Map<String, Class<?>> loadedTypes = injectRaw(binaryRepresentations);
1✔
177
            Map<TypeDescription, Class<?>> result = new HashMap<TypeDescription, Class<?>>();
1✔
178
            for (TypeDescription typeDescription : types.keySet()) {
1✔
179
                result.put(typeDescription, loadedTypes.get(typeDescription.getName()));
1✔
180
            }
1✔
181
            return result;
1✔
182
        }
183

184
        /**
185
         * {@inheritDoc}
186
         */
187
        public Map<String, Class<?>> injectRaw(Map<String, byte[]> types) {
188
            return injectRaw(types.keySet(), new ClassFileLocator.Simple(types));
1✔
189
        }
190
    }
191

192
    /**
193
     * A class injector that uses reflective method calls.
194
     */
195
    @HashCodeAndEqualsPlugin.Enhance
196
    class UsingReflection extends AbstractBase {
197

198
        /**
199
         * The dispatcher to use for accessing a class loader via reflection.
200
         */
201
        private static final Dispatcher.Initializable DISPATCHER = doPrivileged(Dispatcher.CreationAction.INSTANCE);
1✔
202

203
        /**
204
         * A proxy for {@code java.lang.System} to access the security manager if available.
205
         */
206
        private static final System SYSTEM = doPrivileged(JavaDispatcher.of(System.class));
1✔
207

208
        /**
209
         * The {@code java.lang.SecurityManager#checkPermission} method or {@code null} if not available.
210
         */
211
        private static final Method CHECK_PERMISSION = doPrivileged(new GetMethodAction("java.lang.SecurityManager",
1✔
212
                "checkPermission",
213
                Permission.class));
214

215
        /**
216
         * The class loader into which the classes are to be injected.
217
         */
218
        private final ClassLoader classLoader;
219

220
        /**
221
         * The protection domain that is used when loading classes.
222
         */
223
        @MaybeNull
224
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
225
        private final ProtectionDomain protectionDomain;
226

227
        /**
228
         * The package definer to be queried for package definitions.
229
         */
230
        private final PackageDefinitionStrategy packageDefinitionStrategy;
231

232
        /**
233
         * Determines if an exception should be thrown when attempting to load a type that already exists.
234
         */
235
        private final boolean forbidExisting;
236

237
        /**
238
         * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link java.security.ProtectionDomain} and a
239
         * trivial {@link PackageDefinitionStrategy} which does not trigger an error when discovering existent classes.
240
         *
241
         * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
242
         */
243
        public UsingReflection(ClassLoader classLoader) {
244
            this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
245
        }
1✔
246

247
        /**
248
         * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link PackageDefinitionStrategy} where the
249
         * injection of existent classes does not trigger an error.
250
         *
251
         * @param classLoader      The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
252
         * @param protectionDomain The protection domain to apply during class definition.
253
         */
254
        public UsingReflection(ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
255
            this(classLoader,
1✔
256
                    protectionDomain,
257
                    PackageDefinitionStrategy.Trivial.INSTANCE,
258
                    ALLOW_EXISTING_TYPES);
259
        }
1✔
260

261
        /**
262
         * Creates a new injector for the given {@link java.lang.ClassLoader} and {@link java.security.ProtectionDomain}.
263
         *
264
         * @param classLoader               The {@link java.lang.ClassLoader} into which new class definitions are to be injected.Must  not be the bootstrap loader.
265
         * @param protectionDomain          The protection domain to apply during class definition.
266
         * @param packageDefinitionStrategy The package definer to be queried for package definitions.
267
         * @param forbidExisting            Determines if an exception should be thrown when attempting to load a type that already exists.
268
         */
269
        public UsingReflection(ClassLoader classLoader,
270
                               @MaybeNull ProtectionDomain protectionDomain,
271
                               PackageDefinitionStrategy packageDefinitionStrategy,
272
                               boolean forbidExisting) {
1✔
273
            if (classLoader == null) {
1✔
274
                throw new IllegalArgumentException("Cannot inject classes into the bootstrap class loader");
1✔
275
            }
276
            this.classLoader = classLoader;
1✔
277
            this.protectionDomain = protectionDomain;
1✔
278
            this.packageDefinitionStrategy = packageDefinitionStrategy;
1✔
279
            this.forbidExisting = forbidExisting;
1✔
280
        }
1✔
281

282
        /**
283
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
284
         *
285
         * @param action The action to execute from a privileged context.
286
         * @param <T>    The type of the action's resolved value.
287
         * @return The action's resolved value.
288
         */
289
        @AccessControllerPlugin.Enhance
290
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
291
            return action.run();
×
292
        }
293

294
        /**
295
         * {@inheritDoc}
296
         */
297
        public boolean isAlive() {
298
            return isAvailable();
1✔
299
        }
300

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

363
        /**
364
         * Indicates if this class injection is available on the current VM.
365
         *
366
         * @return {@code true} if this class injection is available.
367
         */
368
        public static boolean isAvailable() {
369
            return DISPATCHER.isAvailable();
1✔
370
        }
371

372
        /**
373
         * Creates a class injector for the system class loader.
374
         *
375
         * @return A class injector for the system class loader.
376
         */
377
        public static ClassInjector ofSystemClassLoader() {
378
            return new UsingReflection(ClassLoader.getSystemClassLoader());
1✔
379
        }
380

381
        /**
382
         * A dispatcher for accessing a {@link ClassLoader} reflectively.
383
         */
384
        protected interface Dispatcher {
385

386
            /**
387
             * Indicates a class that is currently not defined.
388
             */
389
            @AlwaysNull
390
            Class<?> UNDEFINED = null;
1✔
391

392
            /**
393
             * Returns the lock for loading the specified class.
394
             *
395
             * @param classLoader the class loader to inject the class into.
396
             * @param name        The name of the class.
397
             * @return The lock for loading this class.
398
             */
399
            Object getClassLoadingLock(ClassLoader classLoader, String name);
400

401
            /**
402
             * Looks up a class from the given class loader.
403
             *
404
             * @param classLoader The class loader for which a class should be located.
405
             * @param name        The binary name of the class that should be located.
406
             * @return The class for the binary name or {@code null} if no such class is defined for the provided class loader.
407
             */
408
            @MaybeNull
409
            Class<?> findClass(ClassLoader classLoader, String name);
410

411
            /**
412
             * Defines a class for the given class loader.
413
             *
414
             * @param classLoader          The class loader for which a new class should be defined.
415
             * @param name                 The binary name of the class that should be defined.
416
             * @param binaryRepresentation The binary representation of the class.
417
             * @param protectionDomain     The protection domain for the defined class.
418
             * @return The defined, loaded class.
419
             */
420
            Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain);
421

422
            /**
423
             * Looks up a package from a class loader. If the operation is not supported, falls back to {@link #getPackage(ClassLoader, String)}
424
             *
425
             * @param classLoader The class loader to query.
426
             * @param name        The binary name of the package.
427
             * @return The package for the given name as defined by the provided class loader or {@code null} if no such package exists.
428
             */
429
            @MaybeNull
430
            Package getDefinedPackage(ClassLoader classLoader, String name);
431

432
            /**
433
             * Looks up a package from a class loader or its ancestor.
434
             *
435
             * @param classLoader The class loader to query.
436
             * @param name        The binary name of the package.
437
             * @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.
438
             */
439
            @MaybeNull
440
            Package getPackage(ClassLoader classLoader, String name);
441

442
            /**
443
             * Defines a package for the given class loader.
444
             *
445
             * @param classLoader           The class loader for which a package is to be defined.
446
             * @param name                  The binary name of the package.
447
             * @param specificationTitle    The specification title of the package or {@code null} if no specification title exists.
448
             * @param specificationVersion  The specification version of the package or {@code null} if no specification version exists.
449
             * @param specificationVendor   The specification vendor of the package or {@code null} if no specification vendor exists.
450
             * @param implementationTitle   The implementation title of the package or {@code null} if no implementation title exists.
451
             * @param implementationVersion The implementation version of the package or {@code null} if no implementation version exists.
452
             * @param implementationVendor  The implementation vendor of the package or {@code null} if no implementation vendor exists.
453
             * @param sealBase              The seal base URL or {@code null} if the package should not be sealed.
454
             * @return The defined package.
455
             */
456
            Package definePackage(ClassLoader classLoader,
457
                                  String name,
458
                                  @MaybeNull String specificationTitle,
459
                                  @MaybeNull String specificationVersion,
460
                                  @MaybeNull String specificationVendor,
461
                                  @MaybeNull String implementationTitle,
462
                                  @MaybeNull String implementationVersion,
463
                                  @MaybeNull String implementationVendor,
464
                                  @MaybeNull URL sealBase);
465

466
            /**
467
             * Initializes a dispatcher to make non-accessible APIs accessible.
468
             */
469
            interface Initializable {
470

471
                /**
472
                 * Indicates if this dispatcher is available.
473
                 *
474
                 * @return {@code true} if this dispatcher is available.
475
                 */
476
                boolean isAvailable();
477

478
                /**
479
                 * Initializes this dispatcher.
480
                 *
481
                 * @return The initialized dispatcher.
482
                 */
483
                Dispatcher initialize();
484

485
                /**
486
                 * Represents an unsuccessfully loaded method lookup.
487
                 */
488
                @HashCodeAndEqualsPlugin.Enhance
489
                class Unavailable implements Dispatcher, Initializable {
490

491
                    /**
492
                     * The reason why this dispatcher is not available.
493
                     */
494
                    private final String message;
495

496
                    /**
497
                     * Creates a new faulty reflection store.
498
                     *
499
                     * @param message The reason why this dispatcher is not available.
500
                     */
501
                    protected Unavailable(String message) {
1✔
502
                        this.message = message;
1✔
503
                    }
1✔
504

505
                    /**
506
                     * {@inheritDoc}
507
                     */
508
                    public boolean isAvailable() {
509
                        return false;
1✔
510
                    }
511

512
                    /**
513
                     * {@inheritDoc}
514
                     */
515
                    public Dispatcher initialize() {
516
                        return this;
1✔
517
                    }
518

519
                    /**
520
                     * {@inheritDoc}
521
                     */
522
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
523
                        return classLoader;
×
524
                    }
525

526
                    /**
527
                     * {@inheritDoc}
528
                     */
529
                    public Class<?> findClass(ClassLoader classLoader, String name) {
530
                        try {
531
                            return classLoader.loadClass(name);
1✔
532
                        } catch (ClassNotFoundException ignored) {
1✔
533
                            return UNDEFINED;
1✔
534
                        }
535
                    }
536

537
                    /**
538
                     * {@inheritDoc}
539
                     */
540
                    public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
541
                        throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
1✔
542
                    }
543

544
                    /**
545
                     * {@inheritDoc}
546
                     */
547
                    public Package getDefinedPackage(ClassLoader classLoader, String name) {
548
                        throw new UnsupportedOperationException("Cannot get defined package using reflection: " + message);
1✔
549
                    }
550

551
                    /**
552
                     * {@inheritDoc}
553
                     */
554
                    public Package getPackage(ClassLoader classLoader, String name) {
555
                        throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
×
556
                    }
557

558
                    /**
559
                     * {@inheritDoc}
560
                     */
561
                    public Package definePackage(ClassLoader classLoader,
562
                                                 String name,
563
                                                 @MaybeNull String specificationTitle,
564
                                                 @MaybeNull String specificationVersion,
565
                                                 @MaybeNull String specificationVendor,
566
                                                 @MaybeNull String implementationTitle,
567
                                                 @MaybeNull String implementationVersion,
568
                                                 @MaybeNull String implementationVendor,
569
                                                 @MaybeNull URL sealBase) {
570
                        throw new UnsupportedOperationException("Cannot define package using injection: " + message);
1✔
571
                    }
572
                }
573
            }
574

575
            /**
576
             * A creation action for a dispatcher.
577
             */
578
            enum CreationAction implements PrivilegedAction<Initializable> {
1✔
579

580
                /**
581
                 * The singleton instance.
582
                 */
583
                INSTANCE;
1✔
584

585
                /**
586
                 * {@inheritDoc}
587
                 */
588
                @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
589
                public Initializable run() {
590
                    try {
591
                        if (JavaModule.isSupported()) {
1✔
592
                            return UsingUnsafe.isAvailable()
×
593
                                    ? UsingUnsafeInjection.make()
×
594
                                    : UsingUnsafeOverride.make();
×
595
                        } else {
596
                            return Direct.make();
1✔
597
                        }
598
                    } catch (InvocationTargetException exception) {
×
599
                        return new Initializable.Unavailable(exception.getTargetException().getMessage());
×
600
                    } catch (Exception exception) {
×
601
                        return new Initializable.Unavailable(exception.getMessage());
×
602
                    }
603
                }
604
            }
605

606
            /**
607
             * A class injection dispatcher that is using reflection on the {@link ClassLoader} methods.
608
             */
609
            @HashCodeAndEqualsPlugin.Enhance
610
            abstract class Direct implements Dispatcher, Initializable {
611

612
                /**
613
                 * An instance of {@link ClassLoader#findLoadedClass(String)}.
614
                 */
615
                protected final Method findLoadedClass;
616

617
                /**
618
                 * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
619
                 */
620
                protected final Method defineClass;
621

622
                /**
623
                 * An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
624
                 */
625
                @UnknownNull
626
                protected final Method getDefinedPackage;
627

628
                /**
629
                 * An instance of {@link ClassLoader#getPackage(String)}.
630
                 */
631
                protected final Method getPackage;
632

633
                /**
634
                 * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
635
                 */
636
                protected final Method definePackage;
637

638
                /**
639
                 * Creates a new direct injection dispatcher.
640
                 *
641
                 * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
642
                 * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
643
                 * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
644
                 * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
645
                 * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
646
                 */
647
                protected Direct(Method findLoadedClass,
648
                                 Method defineClass,
649
                                 @MaybeNull Method getDefinedPackage,
650
                                 Method getPackage,
651
                                 Method definePackage) {
1✔
652
                    this.findLoadedClass = findLoadedClass;
1✔
653
                    this.defineClass = defineClass;
1✔
654
                    this.getDefinedPackage = getDefinedPackage;
1✔
655
                    this.getPackage = getPackage;
1✔
656
                    this.definePackage = definePackage;
1✔
657
                }
1✔
658

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

712
                /**
713
                 * {@inheritDoc}
714
                 */
715
                public boolean isAvailable() {
716
                    return true;
1✔
717
                }
718

719
                /**
720
                 * {@inheritDoc}
721
                 */
722
                public Dispatcher initialize() {
723
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
724
                    if (securityManager != null) {
1✔
725
                        try {
726
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
727
                        } catch (InvocationTargetException exception) {
×
728
                            return new Dispatcher.Unavailable(exception.getTargetException().getMessage());
×
729
                        } catch (Exception exception) {
×
730
                            return new Dispatcher.Unavailable(exception.getMessage());
×
731
                        }
×
732
                    }
733
                    return this;
1✔
734
                }
735

736
                /**
737
                 * {@inheritDoc}
738
                 */
739
                public Class<?> findClass(ClassLoader classLoader, String name) {
740
                    try {
741
                        return (Class<?>) findLoadedClass.invoke(classLoader, name);
1✔
742
                    } catch (IllegalAccessException exception) {
×
743
                        throw new IllegalStateException(exception);
×
744
                    } catch (InvocationTargetException exception) {
×
745
                        throw new IllegalStateException(exception.getTargetException());
×
746
                    }
747
                }
748

749
                /**
750
                 * {@inheritDoc}
751
                 */
752
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
753
                    try {
754
                        return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
1✔
755
                    } catch (IllegalAccessException exception) {
×
756
                        throw new IllegalStateException(exception);
×
757
                    } catch (InvocationTargetException exception) {
×
758
                        throw new IllegalStateException(exception.getTargetException());
×
759
                    }
760
                }
761

762
                /**
763
                 * {@inheritDoc}
764
                 */
765
                @MaybeNull
766
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
767
                    if (getDefinedPackage == null) {
1✔
768
                        return getPackage(classLoader, name);
1✔
769
                    }
770
                    try {
771
                        return (Package) getDefinedPackage.invoke(classLoader, name);
×
772
                    } catch (IllegalAccessException exception) {
×
773
                        throw new IllegalStateException(exception);
×
774
                    } catch (InvocationTargetException exception) {
×
775
                        throw new IllegalStateException(exception.getTargetException());
×
776
                    }
777
                }
778

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

792
                /**
793
                 * {@inheritDoc}
794
                 */
795
                public Package definePackage(ClassLoader classLoader,
796
                                             String name,
797
                                             @MaybeNull String specificationTitle,
798
                                             @MaybeNull String specificationVersion,
799
                                             @MaybeNull String specificationVendor,
800
                                             @MaybeNull String implementationTitle,
801
                                             @MaybeNull String implementationVersion,
802
                                             @MaybeNull String implementationVendor,
803
                                             @MaybeNull URL sealBase) {
804
                    try {
805
                        return (Package) definePackage.invoke(classLoader,
1✔
806
                                name,
807
                                specificationTitle,
808
                                specificationVersion,
809
                                specificationVendor,
810
                                implementationTitle,
811
                                implementationVersion,
812
                                implementationVendor,
813
                                sealBase);
814
                    } catch (IllegalAccessException exception) {
×
815
                        throw new IllegalStateException(exception);
×
816
                    } catch (InvocationTargetException exception) {
×
817
                        throw new IllegalStateException(exception.getTargetException());
×
818
                    }
819
                }
820

821
                /**
822
                 * A resolved class dispatcher for a class injector on a VM running at least Java 7.
823
                 */
824
                @HashCodeAndEqualsPlugin.Enhance
825
                protected static class ForJava7CapableVm extends Direct {
826

827
                    /**
828
                     * An instance of {@code ClassLoader#getClassLoadingLock(String)}.
829
                     */
830
                    private final Method getClassLoadingLock;
831

832
                    /**
833
                     * Creates a new resolved reflection store for a VM running at least Java 7.
834
                     *
835
                     * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
836
                     * @param findLoadedClass     An instance of {@link ClassLoader#findLoadedClass(String)}.
837
                     * @param defineClass         An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
838
                     * @param getDefinedPackage   An instance of {@code java.lang,ClassLoader#getDefinedPackage(String)}. May be {@code null}.
839
                     * @param getPackage          An instance of {@link ClassLoader#getPackage(String)}.
840
                     * @param definePackage       An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
841
                     */
842
                    protected ForJava7CapableVm(Method findLoadedClass,
843
                                                Method defineClass,
844
                                                @MaybeNull Method getDefinedPackage,
845
                                                Method getPackage,
846
                                                Method definePackage,
847
                                                Method getClassLoadingLock) {
848
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
1✔
849
                        this.getClassLoadingLock = getClassLoadingLock;
1✔
850
                    }
1✔
851

852
                    /**
853
                     * {@inheritDoc}
854
                     */
855
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
856
                        try {
857
                            return getClassLoadingLock.invoke(classLoader, name);
1✔
858
                        } catch (IllegalAccessException exception) {
×
859
                            throw new IllegalStateException(exception);
×
860
                        } catch (InvocationTargetException exception) {
×
861
                            throw new IllegalStateException(exception.getTargetException());
×
862
                        }
863
                    }
864
                }
865

866
                /**
867
                 * A resolved class dispatcher for a class injector prior to Java 7.
868
                 */
869
                protected static class ForLegacyVm extends Direct {
870

871
                    /**
872
                     * Creates a new resolved reflection store for a VM prior to Java 8.
873
                     *
874
                     * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
875
                     * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
876
                     * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
877
                     * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
878
                     * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
879
                     */
880
                    protected ForLegacyVm(Method findLoadedClass,
881
                                          Method defineClass,
882
                                          @MaybeNull Method getDefinedPackage,
883
                                          Method getPackage,
884
                                          Method definePackage) {
885
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
1✔
886
                    }
1✔
887

888
                    /**
889
                     * {@inheritDoc}
890
                     */
891
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
892
                        return classLoader;
1✔
893
                    }
894
                }
895
            }
896

897
            /**
898
             * An indirect dispatcher that uses a redirection accessor class that was injected into the bootstrap class loader.
899
             */
900
            @HashCodeAndEqualsPlugin.Enhance
901
            class UsingUnsafeInjection implements Dispatcher, Initializable {
902

903
                /**
904
                 * An instance of the accessor class that is required for using it's intentionally non-static methods.
905
                 */
906
                private final Object accessor;
907

908
                /**
909
                 * The accessor method for using {@link ClassLoader#findLoadedClass(String)}.
910
                 */
911
                private final Method findLoadedClass;
912

913
                /**
914
                 * The accessor method for using {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
915
                 */
916
                private final Method defineClass;
917

918
                /**
919
                 * The accessor method for using {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
920
                 */
921
                @UnknownNull
922
                private final Method getDefinedPackage;
923

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

929
                /**
930
                 * The accessor method for using {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
931
                 */
932
                private final Method definePackage;
933

934
                /**
935
                 * The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the supplied {@link ClassLoader}
936
                 * if this method does not exist on the current VM.
937
                 */
938
                private final Method getClassLoadingLock;
939

940
                /**
941
                 * Creates a new class loading injection dispatcher using an unsafe injected dispatcher.
942
                 *
943
                 * @param accessor            An instance of the accessor class that is required for using it's intentionally non-static methods.
944
                 * @param findLoadedClass     An instance of {@link ClassLoader#findLoadedClass(String)}.
945
                 * @param defineClass         An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
946
                 * @param getDefinedPackage   An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
947
                 * @param getPackage          An instance of {@link ClassLoader#getPackage(String)}.
948
                 * @param definePackage       An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
949
                 * @param getClassLoadingLock The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the
950
                 *                            supplied {@link ClassLoader} if this method does not exist on the current VM.
951
                 */
952
                protected UsingUnsafeInjection(Object accessor,
953
                                               Method findLoadedClass,
954
                                               Method defineClass,
955
                                               @MaybeNull Method getDefinedPackage,
956
                                               Method getPackage,
957
                                               Method definePackage,
958
                                               Method getClassLoadingLock) {
1✔
959
                    this.accessor = accessor;
1✔
960
                    this.findLoadedClass = findLoadedClass;
1✔
961
                    this.defineClass = defineClass;
1✔
962
                    this.getDefinedPackage = getDefinedPackage;
1✔
963
                    this.getPackage = getPackage;
1✔
964
                    this.definePackage = definePackage;
1✔
965
                    this.getClassLoadingLock = getClassLoadingLock;
1✔
966
                }
1✔
967

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

1055
                /**
1056
                 * {@inheritDoc}
1057
                 */
1058
                public boolean isAvailable() {
1059
                    return true;
×
1060
                }
1061

1062
                /**
1063
                 * {@inheritDoc}
1064
                 */
1065
                public Dispatcher initialize() {
1066
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
1067
                    if (securityManager != null) {
1✔
1068
                        try {
1069
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
1070
                        } catch (InvocationTargetException exception) {
×
1071
                            return new Dispatcher.Unavailable(exception.getTargetException().getMessage());
×
1072
                        } catch (Exception exception) {
×
1073
                            return new Dispatcher.Unavailable(exception.getMessage());
×
1074
                        }
×
1075
                    }
1076
                    return this;
1✔
1077
                }
1078

1079
                /**
1080
                 * {@inheritDoc}
1081
                 */
1082
                public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1083
                    try {
1084
                        return getClassLoadingLock.invoke(accessor, classLoader, name);
×
1085
                    } catch (IllegalAccessException exception) {
×
1086
                        throw new IllegalStateException(exception);
×
1087
                    } catch (InvocationTargetException exception) {
×
1088
                        throw new IllegalStateException(exception.getTargetException());
×
1089
                    }
1090
                }
1091

1092
                /**
1093
                 * {@inheritDoc}
1094
                 */
1095
                public Class<?> findClass(ClassLoader classLoader, String name) {
1096
                    try {
1097
                        return (Class<?>) findLoadedClass.invoke(accessor, classLoader, name);
1✔
1098
                    } catch (IllegalAccessException exception) {
×
1099
                        throw new IllegalStateException(exception);
×
1100
                    } catch (InvocationTargetException exception) {
×
1101
                        throw new IllegalStateException(exception.getTargetException());
×
1102
                    }
1103
                }
1104

1105
                /**
1106
                 * {@inheritDoc}
1107
                 */
1108
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
1109
                    try {
1110
                        return (Class<?>) defineClass.invoke(accessor, classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
1✔
1111
                    } catch (IllegalAccessException exception) {
×
1112
                        throw new IllegalStateException(exception);
×
1113
                    } catch (InvocationTargetException exception) {
×
1114
                        throw new IllegalStateException(exception.getTargetException());
×
1115
                    }
1116
                }
1117

1118
                /**
1119
                 * {@inheritDoc}
1120
                 */
1121
                @MaybeNull
1122
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
1123
                    if (getDefinedPackage == null) {
1✔
1124
                        return getPackage(classLoader, name);
1✔
1125
                    }
1126
                    try {
1127
                        return (Package) getDefinedPackage.invoke(accessor, classLoader, name);
×
1128
                    } catch (IllegalAccessException exception) {
×
1129
                        throw new IllegalStateException(exception);
×
1130
                    } catch (InvocationTargetException exception) {
×
1131
                        throw new IllegalStateException(exception.getTargetException());
×
1132
                    }
1133
                }
1134

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

1148
                /**
1149
                 * {@inheritDoc}
1150
                 */
1151
                public Package definePackage(ClassLoader classLoader,
1152
                                             String name,
1153
                                             @MaybeNull String specificationTitle,
1154
                                             @MaybeNull String specificationVersion,
1155
                                             @MaybeNull String specificationVendor,
1156
                                             @MaybeNull String implementationTitle,
1157
                                             @MaybeNull String implementationVersion,
1158
                                             @MaybeNull String implementationVendor,
1159
                                             @MaybeNull URL sealBase) {
1160
                    try {
1161
                        return (Package) definePackage.invoke(accessor,
1✔
1162
                                classLoader,
1163
                                name,
1164
                                specificationTitle,
1165
                                specificationVersion,
1166
                                specificationVendor,
1167
                                implementationTitle,
1168
                                implementationVersion,
1169
                                implementationVendor,
1170
                                sealBase);
1171
                    } catch (IllegalAccessException exception) {
×
1172
                        throw new IllegalStateException(exception);
×
1173
                    } catch (InvocationTargetException exception) {
×
1174
                        throw new IllegalStateException(exception.getTargetException());
×
1175
                    }
1176
                }
1177
            }
1178

1179
            /**
1180
             * A dispatcher implementation that uses {@code sun.misc.Unsafe#putBoolean} to set the {@link AccessibleObject} field
1181
             * for making methods accessible.
1182
             */
1183
            abstract class UsingUnsafeOverride implements Dispatcher, Initializable {
1184

1185
                /**
1186
                 * An instance of {@link ClassLoader#findLoadedClass(String)}.
1187
                 */
1188
                protected final Method findLoadedClass;
1189

1190
                /**
1191
                 * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1192
                 */
1193
                protected final Method defineClass;
1194

1195
                /**
1196
                 * An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1197
                 */
1198
                @MaybeNull
1199
                protected final Method getDefinedPackage;
1200

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

1206
                /**
1207
                 * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1208
                 */
1209
                protected final Method definePackage;
1210

1211
                /**
1212
                 * Creates a new unsafe field injecting injection dispatcher.
1213
                 *
1214
                 * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
1215
                 * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1216
                 * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1217
                 * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
1218
                 * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1219
                 */
1220
                protected UsingUnsafeOverride(Method findLoadedClass,
1221
                                              Method defineClass,
1222
                                              @MaybeNull Method getDefinedPackage,
1223
                                              Method getPackage,
1224
                                              Method definePackage) {
1✔
1225
                    this.findLoadedClass = findLoadedClass;
1✔
1226
                    this.defineClass = defineClass;
1✔
1227
                    this.getDefinedPackage = getDefinedPackage;
1✔
1228
                    this.getPackage = getPackage;
1✔
1229
                    this.definePackage = definePackage;
1✔
1230
                }
1✔
1231

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

1314
                /**
1315
                 * {@inheritDoc}
1316
                 */
1317
                public boolean isAvailable() {
1318
                    return true;
×
1319
                }
1320

1321
                /**
1322
                 * {@inheritDoc}
1323
                 */
1324
                public Dispatcher initialize() {
1325
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
1326
                    if (securityManager != null) {
1✔
1327
                        try {
1328
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
1329
                        } catch (InvocationTargetException exception) {
×
1330
                            return new Dispatcher.Unavailable(exception.getTargetException().getMessage());
×
1331
                        } catch (Exception exception) {
×
1332
                            return new Dispatcher.Unavailable(exception.getMessage());
×
1333
                        }
×
1334
                    }
1335
                    return this;
1✔
1336
                }
1337

1338
                /**
1339
                 * {@inheritDoc}
1340
                 */
1341
                public Class<?> findClass(ClassLoader classLoader, String name) {
1342
                    try {
1343
                        return (Class<?>) findLoadedClass.invoke(classLoader, name);
1✔
1344
                    } catch (IllegalAccessException exception) {
×
1345
                        throw new IllegalStateException(exception);
×
1346
                    } catch (InvocationTargetException exception) {
×
1347
                        throw new IllegalStateException(exception.getTargetException());
×
1348
                    }
1349
                }
1350

1351
                /**
1352
                 * {@inheritDoc}
1353
                 */
1354
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
1355
                    try {
1356
                        return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
1✔
1357
                    } catch (IllegalAccessException exception) {
×
1358
                        throw new IllegalStateException(exception);
×
1359
                    } catch (InvocationTargetException exception) {
×
1360
                        throw new IllegalStateException(exception.getTargetException());
×
1361
                    }
1362
                }
1363

1364
                /**
1365
                 * {@inheritDoc}
1366
                 */
1367
                @MaybeNull
1368
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
1369
                    if (getDefinedPackage == null) {
1✔
1370
                        return getPackage(classLoader, name);
1✔
1371
                    }
1372
                    try {
1373
                        return (Package) getDefinedPackage.invoke(classLoader, name);
×
1374
                    } catch (IllegalAccessException exception) {
×
1375
                        throw new IllegalStateException(exception);
×
1376
                    } catch (InvocationTargetException exception) {
×
1377
                        throw new IllegalStateException(exception.getTargetException());
×
1378
                    }
1379
                }
1380

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

1394
                /**
1395
                 * {@inheritDoc}
1396
                 */
1397
                public Package definePackage(ClassLoader classLoader,
1398
                                             String name,
1399
                                             @MaybeNull String specificationTitle,
1400
                                             @MaybeNull String specificationVersion,
1401
                                             @MaybeNull String specificationVendor,
1402
                                             @MaybeNull String implementationTitle,
1403
                                             @MaybeNull String implementationVersion,
1404
                                             @MaybeNull String implementationVendor,
1405
                                             @MaybeNull URL sealBase) {
1406
                    try {
1407
                        return (Package) definePackage.invoke(classLoader,
1✔
1408
                                name,
1409
                                specificationTitle,
1410
                                specificationVersion,
1411
                                specificationVendor,
1412
                                implementationTitle,
1413
                                implementationVersion,
1414
                                implementationVendor,
1415
                                sealBase);
1416
                    } catch (IllegalAccessException exception) {
×
1417
                        throw new IllegalStateException(exception);
×
1418
                    } catch (InvocationTargetException exception) {
×
1419
                        throw new IllegalStateException(exception.getTargetException());
×
1420
                    }
1421
                }
1422

1423
                /**
1424
                 * A resolved class dispatcher using unsafe field injection for a class injector on a VM running at least Java 7.
1425
                 */
1426
                @HashCodeAndEqualsPlugin.Enhance
1427
                protected static class ForJava7CapableVm extends UsingUnsafeOverride {
1428

1429
                    /**
1430
                     * An instance of {@code ClassLoader#getClassLoadingLock(String)}.
1431
                     */
1432
                    private final Method getClassLoadingLock;
1433

1434
                    /**
1435
                     * Creates a new resolved class injector using unsafe field injection for a VM running at least Java 7.
1436
                     *
1437
                     * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
1438
                     * @param findLoadedClass     An instance of {@link ClassLoader#findLoadedClass(String)}.
1439
                     * @param defineClass         An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1440
                     * @param getDefinedPackage   An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1441
                     * @param getPackage          An instance of {@link ClassLoader#getPackage(String)}.
1442
                     * @param definePackage       An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1443
                     */
1444
                    protected ForJava7CapableVm(Method findLoadedClass,
1445
                                                Method defineClass,
1446
                                                @MaybeNull Method getDefinedPackage,
1447
                                                Method getPackage,
1448
                                                Method definePackage,
1449
                                                Method getClassLoadingLock) {
1450
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
1✔
1451
                        this.getClassLoadingLock = getClassLoadingLock;
1✔
1452
                    }
1✔
1453

1454
                    /**
1455
                     * {@inheritDoc}
1456
                     */
1457
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1458
                        try {
1459
                            return getClassLoadingLock.invoke(classLoader, name);
×
1460
                        } catch (IllegalAccessException exception) {
×
1461
                            throw new IllegalStateException(exception);
×
1462
                        } catch (InvocationTargetException exception) {
×
1463
                            throw new IllegalStateException(exception.getTargetException());
×
1464
                        }
1465
                    }
1466
                }
1467

1468
                /**
1469
                 * A resolved class dispatcher using unsafe field injection for a class injector prior to Java 7.
1470
                 */
1471
                protected static class ForLegacyVm extends UsingUnsafeOverride {
1472

1473
                    /**
1474
                     * Creates a new resolved class injector using unsafe field injection for a VM prior to Java 7.
1475
                     *
1476
                     * @param findLoadedClass   An instance of {@link ClassLoader#findLoadedClass(String)}.
1477
                     * @param defineClass       An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1478
                     * @param getDefinedPackage An instance of {@code java.lang.ClassLoader#getDefinedPackage(String)}. May be {@code null}.
1479
                     * @param getPackage        An instance of {@link ClassLoader#getPackage(String)}.
1480
                     * @param definePackage     An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1481
                     */
1482
                    protected ForLegacyVm(Method findLoadedClass,
1483
                                          Method defineClass,
1484
                                          @MaybeNull Method getDefinedPackage,
1485
                                          Method getPackage,
1486
                                          Method definePackage) {
1487
                        super(findLoadedClass, defineClass, getDefinedPackage, getPackage, definePackage);
×
1488
                    }
×
1489

1490
                    /**
1491
                     * {@inheritDoc}
1492
                     */
1493
                    public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1494
                        return classLoader;
×
1495
                    }
1496
                }
1497
            }
1498

1499
            /**
1500
             * Represents an unsuccessfully loaded method lookup.
1501
             */
1502
            @HashCodeAndEqualsPlugin.Enhance
1503
            class Unavailable implements Dispatcher {
1504

1505
                /**
1506
                 * The error message being displayed.
1507
                 */
1508
                private final String message;
1509

1510
                /**
1511
                 * Creates a dispatcher for a VM that does not support reflective injection.
1512
                 *
1513
                 * @param message The error message being displayed.
1514
                 */
1515
                protected Unavailable(String message) {
1✔
1516
                    this.message = message;
1✔
1517
                }
1✔
1518

1519
                /**
1520
                 * {@inheritDoc}
1521
                 */
1522
                public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1523
                    return classLoader;
1✔
1524
                }
1525

1526
                /**
1527
                 * {@inheritDoc}
1528
                 */
1529
                public Class<?> findClass(ClassLoader classLoader, String name) {
1530
                    try {
1531
                        return classLoader.loadClass(name);
×
1532
                    } catch (ClassNotFoundException ignored) {
×
1533
                        return UNDEFINED;
×
1534
                    }
1535
                }
1536

1537
                /**
1538
                 * {@inheritDoc}
1539
                 */
1540
                public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
1541
                    throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
1✔
1542
                }
1543

1544
                /**
1545
                 * {@inheritDoc}
1546
                 */
1547
                public Package getDefinedPackage(ClassLoader classLoader, String name) {
1548
                    throw new UnsupportedOperationException("Cannot get defined package using reflection: " + message);
1✔
1549
                }
1550

1551
                /**
1552
                 * {@inheritDoc}
1553
                 */
1554
                public Package getPackage(ClassLoader classLoader, String name) {
1555
                    throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
×
1556
                }
1557

1558
                /**
1559
                 * {@inheritDoc}
1560
                 */
1561
                public Package definePackage(ClassLoader classLoader,
1562
                                             String name,
1563
                                             @MaybeNull String specificationTitle,
1564
                                             @MaybeNull String specificationVersion,
1565
                                             @MaybeNull String specificationVendor,
1566
                                             @MaybeNull String implementationTitle,
1567
                                             @MaybeNull String implementationVersion,
1568
                                             @MaybeNull String implementationVendor,
1569
                                             @MaybeNull URL sealBase) {
1570
                    throw new UnsupportedOperationException("Cannot define package using injection: " + message);
1✔
1571
                }
1572
            }
1573
        }
1574

1575
        /**
1576
         * A proxy of {@code java.lang.System}.
1577
         */
1578
        @JavaDispatcher.Proxied("java.lang.System")
1579
        protected interface System {
1580

1581
            /**
1582
             * Returns the current security manager or {@code null} if not available.
1583
             *
1584
             * @return The current security manager or {@code null} if not available.
1585
             */
1586
            @MaybeNull
1587
            @JavaDispatcher.IsStatic
1588
            @JavaDispatcher.Defaults
1589
            Object getSecurityManager();
1590
        }
1591
    }
1592

1593
    /**
1594
     * <p>
1595
     * A class injector that uses a {@code java.lang.invoke.MethodHandles$Lookup} object for defining a class.
1596
     * </p>
1597
     * <p>
1598
     * <b>Important</b>: This functionality is only available starting from Java 9.
1599
     * </p>
1600
     */
1601
    @HashCodeAndEqualsPlugin.Enhance
1602
    class UsingLookup extends AbstractBase {
1603

1604
        /**
1605
         * The dispatcher to interacting with instances of {@code java.lang.invoke.MethodHandles}.
1606
         */
1607
        private static final MethodHandles METHOD_HANDLES = doPrivileged(JavaDispatcher.of(MethodHandles.class));
1✔
1608

1609
        /**
1610
         * The dispatcher to interacting with {@code java.lang.invoke.MethodHandles$Lookup}.
1611
         */
1612
        private static final MethodHandles.Lookup METHOD_HANDLES_LOOKUP = doPrivileged(JavaDispatcher.of(MethodHandles.Lookup.class));
1✔
1613

1614
        /**
1615
         * Indicates a lookup instance's package lookup mode.
1616
         */
1617
        private static final int PACKAGE_LOOKUP = 0x8;
1618

1619
        /**
1620
         * The {@code java.lang.invoke.MethodHandles$Lookup} to use.
1621
         */
1622
        private final Object lookup;
1623

1624
        /**
1625
         * Creates a new class injector using a lookup instance.
1626
         *
1627
         * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
1628
         */
1629
        protected UsingLookup(Object lookup) {
×
1630
            this.lookup = lookup;
×
1631
        }
×
1632

1633
        /**
1634
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1635
         *
1636
         * @param action The action to execute from a privileged context.
1637
         * @param <T>    The type of the action's resolved value.
1638
         * @return The action's resolved value.
1639
         */
1640
        @AccessControllerPlugin.Enhance
1641
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
1642
            return action.run();
×
1643
        }
1644

1645
        /**
1646
         * Creates class injector that defines a class using a method handle lookup.
1647
         *
1648
         * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
1649
         * @return An appropriate class injector.
1650
         */
1651
        public static UsingLookup of(Object lookup) {
1652
            if (!isAvailable()) {
×
1653
                throw new IllegalStateException("The current VM does not support class definition via method handle lookups");
×
1654
            } else if (!JavaType.METHOD_HANDLES_LOOKUP.isInstance(lookup)) {
×
1655
                throw new IllegalArgumentException("Not a method handle lookup: " + lookup);
×
1656
            } else if ((METHOD_HANDLES_LOOKUP.lookupModes(lookup) & PACKAGE_LOOKUP) == 0) {
×
1657
                throw new IllegalArgumentException("Lookup does not imply package-access: " + lookup);
×
1658
            }
1659
            return new UsingLookup(lookup);
×
1660
        }
1661

1662
        /**
1663
         * Returns the lookup type this injector is based upon.
1664
         *
1665
         * @return The lookup type.
1666
         */
1667
        public Class<?> lookupType() {
1668
            return METHOD_HANDLES_LOOKUP.lookupClass(lookup);
×
1669
        }
1670

1671
        /**
1672
         * Resolves this injector to use the supplied type's scope.
1673
         *
1674
         * @param type The type to resolve the access scope for.
1675
         * @return An new injector with the specified scope.
1676
         */
1677
        public UsingLookup in(Class<?> type) {
1678
            try {
1679
                return new UsingLookup(METHOD_HANDLES.privateLookupIn(type, lookup));
×
1680
            } catch (IllegalAccessException exception) {
×
1681
                throw new IllegalStateException("Cannot access " + type.getName() + " from " + lookup, exception);
×
1682
            }
1683
        }
1684

1685
        /**
1686
         * {@inheritDoc}
1687
         */
1688
        public boolean isAlive() {
1689
            return isAvailable();
×
1690
        }
1691

1692
        /**
1693
         * {@inheritDoc}
1694
         */
1695
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
1696
            PackageDescription target = TypeDescription.ForLoadedType.of(lookupType()).getPackage();
×
1697
            if (target == null) {
×
1698
                throw new IllegalArgumentException("Cannot inject array or primitive type");
×
1699
            }
1700
            Map<String, Class<?>> result = new HashMap<String, Class<?>>();
×
1701
            for (String name : names) {
×
1702
                int index = name.lastIndexOf('.');
×
1703
                if (!target.getName().equals(index == -1 ? "" : name.substring(0, index))) {
×
1704
                    throw new IllegalArgumentException(name + " must be defined in the same package as " + lookup);
×
1705
                }
1706
                try {
1707
                    result.put(name, METHOD_HANDLES_LOOKUP.defineClass(lookup, classFileLocator.locate(name).resolve()));
×
1708
                } catch (Exception exception) {
×
1709
                    throw new IllegalStateException(exception);
×
1710
                }
×
1711
            }
×
1712
            return result;
×
1713
        }
1714

1715
        /**
1716
         * Checks if the current VM is capable of defining classes using a method handle lookup.
1717
         *
1718
         * @return {@code true} if the current VM is capable of defining classes using a lookup.
1719
         */
1720
        public static boolean isAvailable() {
1721
            return JavaType.MODULE.isAvailable();
1✔
1722
        }
1723

1724
        /**
1725
         * A dispatcher for {@code java.lang.invoke.MethodHandles}.
1726
         */
1727
        @JavaDispatcher.Proxied("java.lang.invoke.MethodHandles")
1728
        protected interface MethodHandles {
1729

1730
            /**
1731
             * Resolves the supplied lookup instance's access scope for the supplied type.
1732
             *
1733
             * @param type   The type to resolve the scope for.
1734
             * @param lookup The lookup to resolve.
1735
             * @return An appropriate lookup instance.
1736
             * @throws IllegalAccessException If an illegal access occurs.
1737
             */
1738
            @JavaDispatcher.IsStatic
1739
            Object privateLookupIn(Class<?> type, @JavaDispatcher.Proxied("java.lang.invoke.MethodHandles$Lookup") Object lookup) throws IllegalAccessException;
1740

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

1747
                /**
1748
                 * Returns the lookup type for a given method handle lookup.
1749
                 *
1750
                 * @param lookup The lookup instance.
1751
                 * @return The lookup type.
1752
                 */
1753
                Class<?> lookupClass(Object lookup);
1754

1755
                /**
1756
                 * Returns a lookup objects lookup types.
1757
                 *
1758
                 * @param lookup The lookup instance.
1759
                 * @return The modifiers indicating the instance's lookup modes.
1760
                 */
1761
                int lookupModes(Object lookup);
1762

1763
                /**
1764
                 * Defines the represented class.
1765
                 *
1766
                 * @param lookup               The lookup instance.
1767
                 * @param binaryRepresentation The binary representation.
1768
                 * @return The defined class.
1769
                 * @throws IllegalAccessException If the definition implies an illegal access.
1770
                 */
1771
                Class<?> defineClass(Object lookup, byte[] binaryRepresentation) throws IllegalAccessException;
1772
            }
1773
        }
1774
    }
1775

1776
    /**
1777
     * A class injector that uses {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe} to inject classes.
1778
     */
1779
    @HashCodeAndEqualsPlugin.Enhance
1780
    class UsingUnsafe extends AbstractBase {
1781

1782
        /**
1783
         * If this property is set, Byte Buddy does not make use of any {@code Unsafe} class.
1784
         */
1785
        public static final String SAFE_PROPERTY = "net.bytebuddy.safe";
1786

1787
        /**
1788
         * The dispatcher to use.
1789
         */
1790
        private static final Dispatcher.Initializable DISPATCHER = doPrivileged(Dispatcher.CreationAction.INSTANCE);
1✔
1791

1792
        /**
1793
         * A proxy for {@code java.lang.System} to access the security manager if available.
1794
         */
1795
        private static final System SYSTEM = doPrivileged(JavaDispatcher.of(System.class));
1✔
1796

1797
        /**
1798
         * The {@code java.lang.SecurityManager#checkPermission} method or {@code null} if not available.
1799
         */
1800
        private static final Method CHECK_PERMISSION = doPrivileged(new GetMethodAction("java.lang.SecurityManager",
1✔
1801
                "checkPermission",
1802
                Permission.class));
1803

1804
        /**
1805
         * A lock for the bootstrap loader when injecting.
1806
         */
1807
        private static final Object BOOTSTRAP_LOADER_LOCK = new Object();
1✔
1808

1809
        /**
1810
         * The class loader to inject classes into or {@code null} for the bootstrap loader.
1811
         */
1812
        @MaybeNull
1813
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1814
        private final ClassLoader classLoader;
1815

1816
        /**
1817
         * The protection domain to use or {@code null} for no protection domain.
1818
         */
1819
        @MaybeNull
1820
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1821
        private final ProtectionDomain protectionDomain;
1822

1823
        /**
1824
         * The dispatcher to use.
1825
         */
1826
        private final Dispatcher.Initializable dispatcher;
1827

1828
        /**
1829
         * Creates a new unsafe injector for the given class loader with a default protection domain.
1830
         *
1831
         * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
1832
         */
1833
        public UsingUnsafe(@MaybeNull ClassLoader classLoader) {
1834
            this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
1835
        }
1✔
1836

1837
        /**
1838
         * Creates a new unsafe injector for the given class loader with a default protection domain.
1839
         *
1840
         * @param classLoader      The class loader to inject classes into or {@code null} for the bootstrap loader.
1841
         * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
1842
         */
1843
        public UsingUnsafe(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
1844
            this(classLoader, protectionDomain, DISPATCHER);
1✔
1845
        }
1✔
1846

1847
        /**
1848
         * Creates a new unsafe injector for the given class loader with a default protection domain.
1849
         *
1850
         * @param classLoader      The class loader to inject classes into or {@code null} for the bootstrap loader.
1851
         * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
1852
         * @param dispatcher       The dispatcher to use.
1853
         */
1854
        protected UsingUnsafe(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain, Dispatcher.Initializable dispatcher) {
1✔
1855
            this.classLoader = classLoader;
1✔
1856
            this.protectionDomain = protectionDomain;
1✔
1857
            this.dispatcher = dispatcher;
1✔
1858
        }
1✔
1859

1860
        /**
1861
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1862
         *
1863
         * @param action The action to execute from a privileged context.
1864
         * @param <T>    The type of the action's resolved value.
1865
         * @return The action's resolved value.
1866
         */
1867
        @AccessControllerPlugin.Enhance
1868
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
1869
            return action.run();
×
1870
        }
1871

1872
        /**
1873
         * {@inheritDoc}
1874
         */
1875
        public boolean isAlive() {
1876
            return dispatcher.isAvailable();
1✔
1877
        }
1878

1879
        /**
1880
         * {@inheritDoc}
1881
         */
1882
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
1883
            Dispatcher dispatcher = this.dispatcher.initialize();
1✔
1884
            Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1✔
1885
            synchronized (classLoader == null
1✔
1886
                    ? BOOTSTRAP_LOADER_LOCK
1887
                    : classLoader) {
1888
                for (String name : names) {
1✔
1889
                    try {
1890
                        result.put(name, Class.forName(name, false, classLoader));
1✔
1891
                    } catch (ClassNotFoundException ignored) {
1✔
1892
                        try {
1893
                            result.put(name, dispatcher.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain));
1✔
1894
                        } catch (
×
1895
                                RuntimeException exception) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
1896
                            try {
1897
                                result.put(name, Class.forName(name, false, classLoader));
×
1898
                            } catch (ClassNotFoundException ignored2) {
×
1899
                                throw exception;
×
1900
                            }
×
1901
                        } catch (IOException exception) {
×
1902
                            throw new IllegalStateException("Failed to resolve binary representation of " + name, exception);
×
1903
                        } catch (
×
1904
                                Error error) { // The bootstrap loader lock might be replicated throughout multiple class loaders.
1905
                            try {
1906
                                result.put(name, Class.forName(name, false, classLoader));
×
1907
                            } catch (ClassNotFoundException ignored2) {
×
1908
                                throw error;
×
1909
                            }
×
1910
                        }
1✔
1911
                    }
1✔
1912
                }
1✔
1913
            }
1✔
1914
            return result;
1✔
1915
        }
1916

1917
        /**
1918
         * Checks if unsafe class injection is available on the current VM.
1919
         *
1920
         * @return {@code true} if unsafe class injection is available on the current VM.
1921
         */
1922
        public static boolean isAvailable() {
1923
            return DISPATCHER.isAvailable();
1✔
1924
        }
1925

1926
        /**
1927
         * Returns an unsafe class injector for the system class loader.
1928
         *
1929
         * @return A class injector for the system class loader.
1930
         */
1931
        public static ClassInjector ofSystemLoader() {
1932
            return new UsingUnsafe(ClassLoader.getSystemClassLoader());
1✔
1933
        }
1934

1935
        /**
1936
         * Returns an unsafe class injector for the platform class loader. For VMs of version 8 or older,
1937
         * the extension class loader is represented instead.
1938
         *
1939
         * @return A class injector for the platform class loader.
1940
         */
1941
        public static ClassInjector ofPlatformLoader() {
1942
            return new UsingUnsafe(ClassLoader.getSystemClassLoader().getParent());
1✔
1943
        }
1944

1945
        /**
1946
         * Returns an unsafe class injector for the boot class loader.
1947
         *
1948
         * @return A class injector for the boot loader.
1949
         */
1950
        public static ClassInjector ofBootLoader() {
1951
            return new UsingUnsafe(ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
1952
        }
1953

1954
        /**
1955
         * A dispatcher for using {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe}.
1956
         */
1957
        protected interface Dispatcher {
1958

1959
            /**
1960
             * Defines a class.
1961
             *
1962
             * @param classLoader          The class loader to inject the class into.
1963
             * @param name                 The type's name.
1964
             * @param binaryRepresentation The type's binary representation.
1965
             * @param protectionDomain     The type's protection domain.
1966
             * @return The defined class.
1967
             */
1968
            Class<?> defineClass(@MaybeNull ClassLoader classLoader,
1969
                                 String name,
1970
                                 byte[] binaryRepresentation,
1971
                                 @MaybeNull ProtectionDomain protectionDomain);
1972

1973
            /**
1974
             * A class injection dispatcher that is not yet initialized.
1975
             */
1976
            interface Initializable {
1977

1978
                /**
1979
                 * Checks if unsafe class injection is available on the current VM.
1980
                 *
1981
                 * @return {@code true} if unsafe class injection is available.
1982
                 */
1983
                boolean isAvailable();
1984

1985
                /**
1986
                 * Initializes the dispatcher.
1987
                 *
1988
                 * @return The initialized dispatcher.
1989
                 */
1990
                Dispatcher initialize();
1991
            }
1992

1993
            /**
1994
             * A privileged action for creating a dispatcher.
1995
             */
1996
            enum CreationAction implements PrivilegedAction<Initializable> {
1✔
1997

1998
                /**
1999
                 * The singleton instance.
2000
                 */
2001
                INSTANCE;
1✔
2002

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

2072
            /**
2073
             * An enabled dispatcher.
2074
             */
2075
            @HashCodeAndEqualsPlugin.Enhance
2076
            class Enabled implements Dispatcher, Initializable {
2077

2078
                /**
2079
                 * An instance of {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe}.
2080
                 */
2081
                private final Object unsafe;
2082

2083
                /**
2084
                 * The {@code sun.misc.Unsafe#defineClass} or {@code jdk.internal.misc.Unsafe#defineClass} method.
2085
                 */
2086
                private final Method defineClass;
2087

2088
                /**
2089
                 * Creates an enabled dispatcher.
2090
                 *
2091
                 * @param unsafe      An instance of {@code sun.misc.Unsafe} or {@code jdk.internal.misc.Unsafe}.
2092
                 * @param defineClass The {@code sun.misc.Unsafe#defineClass} or {@code jdk.internal.misc.Unsafe#defineClass} method.
2093
                 */
2094
                protected Enabled(Object unsafe, Method defineClass) {
1✔
2095
                    this.unsafe = unsafe;
1✔
2096
                    this.defineClass = defineClass;
1✔
2097
                }
1✔
2098

2099
                /**
2100
                 * {@inheritDoc}
2101
                 */
2102
                public boolean isAvailable() {
2103
                    return true;
1✔
2104
                }
2105

2106
                /**
2107
                 * {@inheritDoc}
2108
                 */
2109
                public Dispatcher initialize() {
2110
                    Object securityManager = SYSTEM.getSecurityManager();
1✔
2111
                    if (securityManager != null) {
1✔
2112
                        try {
2113
                            CHECK_PERMISSION.invoke(securityManager, SUPPRESS_ACCESS_CHECKS);
×
2114
                        } catch (InvocationTargetException exception) {
×
2115
                            return new Unavailable(exception.getTargetException().getMessage());
×
2116
                        } catch (Exception exception) {
×
2117
                            return new Unavailable(exception.getMessage());
×
2118
                        }
×
2119
                    }
2120
                    return this;
1✔
2121
                }
2122

2123
                /**
2124
                 * {@inheritDoc}
2125
                 */
2126
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2127
                    try {
2128
                        return (Class<?>) defineClass.invoke(unsafe,
1✔
2129
                                name,
2130
                                binaryRepresentation,
2131
                                0,
1✔
2132
                                binaryRepresentation.length,
1✔
2133
                                classLoader,
2134
                                protectionDomain);
2135
                    } catch (IllegalAccessException exception) {
×
2136
                        throw new IllegalStateException(exception);
×
2137
                    } catch (InvocationTargetException exception) {
×
2138
                        throw new IllegalStateException(exception.getTargetException());
×
2139
                    }
2140
                }
2141
            }
2142

2143
            /**
2144
             * A disabled dispatcher.
2145
             */
2146
            @HashCodeAndEqualsPlugin.Enhance
2147
            class Unavailable implements Dispatcher, Initializable {
2148

2149
                /**
2150
                 * The reason why this dispatcher is not available.
2151
                 */
2152
                private final String message;
2153

2154
                /**
2155
                 * Creates a disabled dispatcher.
2156
                 *
2157
                 * @param message The reason why this dispatcher is not available.
2158
                 */
2159
                protected Unavailable(String message) {
1✔
2160
                    this.message = message;
1✔
2161
                }
1✔
2162

2163
                /**
2164
                 * {@inheritDoc}
2165
                 */
2166
                public boolean isAvailable() {
2167
                    return false;
1✔
2168
                }
2169

2170
                /**
2171
                 * {@inheritDoc}
2172
                 */
2173
                public Dispatcher initialize() {
2174
                    throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
1✔
2175
                }
2176

2177
                /**
2178
                 * {@inheritDoc}
2179
                 */
2180
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2181
                    throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
×
2182
                }
2183
            }
2184
        }
2185

2186
        /**
2187
         * A factory for creating a {@link ClassInjector} that uses {@code sun.misc.Unsafe} if available but attempts a fallback
2188
         * to using {@code jdk.internal.misc.Unsafe} if the {@code jdk.internal} module is not resolved or unavailable.
2189
         */
2190
        @HashCodeAndEqualsPlugin.Enhance
2191
        public static class Factory {
2192

2193
            /**
2194
             * The dispatcher to use.
2195
             */
2196
            private final Dispatcher.Initializable dispatcher;
2197

2198
            /**
2199
             * Creates a new factory for an unsafe class injector that uses Byte Buddy's privileges to
2200
             * accessing {@code jdk.internal.misc.Unsafe} if available.
2201
             */
2202
            public Factory() {
2203
                this(AccessResolver.Default.INSTANCE);
1✔
2204
            }
1✔
2205

2206
            /**
2207
             * Creates a new factory for an unsafe class injector.
2208
             *
2209
             * @param accessResolver The access resolver to use.
2210
             */
2211
            @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback.")
2212
            public Factory(AccessResolver accessResolver) {
1✔
2213
                Dispatcher.Initializable dispatcher;
2214
                if (DISPATCHER.isAvailable()) {
1✔
2215
                    dispatcher = DISPATCHER;
1✔
2216
                } else {
2217
                    try {
2218
                        Class<?> unsafeType = Class.forName("jdk.internal.misc.Unsafe");
×
2219
                        Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
×
2220
                        accessResolver.apply(theUnsafe);
×
2221
                        Object unsafe = theUnsafe.get(null);
×
2222
                        Method defineClass = unsafeType.getMethod("defineClass",
×
2223
                                String.class,
2224
                                byte[].class,
2225
                                int.class,
2226
                                int.class,
2227
                                ClassLoader.class,
2228
                                ProtectionDomain.class);
2229
                        accessResolver.apply(defineClass);
×
2230
                        dispatcher = new Dispatcher.Enabled(unsafe, defineClass);
×
2231
                    } catch (Exception exception) {
×
2232
                        dispatcher = new Dispatcher.Unavailable(exception.getMessage());
×
2233
                    }
×
2234
                }
2235
                this.dispatcher = dispatcher;
1✔
2236
            }
1✔
2237

2238
            /**
2239
             * Creates a new factory.
2240
             *
2241
             * @param dispatcher The dispatcher to use.
2242
             */
2243
            protected Factory(Dispatcher.Initializable dispatcher) {
×
2244
                this.dispatcher = dispatcher;
×
2245
            }
×
2246

2247
            /**
2248
             * Resolves an injection strategy that uses unsafe injection if available and also attempts to open and use
2249
             * {@code jdk.internal.misc.Unsafe} as a fallback. This method generates a new class and module for opening the
2250
             * internal package to avoid its exposure to any non-trusted code.
2251
             *
2252
             * @param instrumentation The instrumentation instance to use for opening the internal package if required.
2253
             * @return An appropriate injection strategy.
2254
             */
2255
            public static Factory resolve(Instrumentation instrumentation) {
2256
                return resolve(instrumentation, false);
1✔
2257
            }
2258

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

2318
            /**
2319
             * Returns {@code true} if this factory creates a valid dispatcher.
2320
             *
2321
             * @return {@code true} if this factory creates a valid dispatcher.
2322
             */
2323
            public boolean isAvailable() {
2324
                return dispatcher.isAvailable();
1✔
2325
            }
2326

2327
            /**
2328
             * Creates a new class injector for the given class loader without a {@link ProtectionDomain}.
2329
             *
2330
             * @param classLoader The class loader to inject into or {@code null} to inject into the bootstrap loader.
2331
             * @return An appropriate class injector.
2332
             */
2333
            public ClassInjector make(@MaybeNull ClassLoader classLoader) {
2334
                return make(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
2335
            }
2336

2337
            /**
2338
             * Creates a new class injector for the given class loader and protection domain.
2339
             *
2340
             * @param classLoader      The class loader to inject into or {@code null} to inject into the bootstrap loader.
2341
             * @param protectionDomain The protection domain to apply or {@code null} if no protection domain should be used.
2342
             * @return An appropriate class injector.
2343
             */
2344
            public ClassInjector make(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
2345
                return new UsingUnsafe(classLoader, protectionDomain, dispatcher);
1✔
2346
            }
2347

2348
            /**
2349
             * An access resolver that invokes {@link AccessibleObject#setAccessible(boolean)} to {@code true} in a given privilege scope.
2350
             */
2351
            public interface AccessResolver {
2352

2353
                /**
2354
                 * Applies this access resolver.
2355
                 *
2356
                 * @param accessibleObject The accessible object to make accessible.
2357
                 */
2358
                void apply(AccessibleObject accessibleObject);
2359

2360
                /**
2361
                 * A default access resolver that uses Byte Buddy's privilege scope.
2362
                 */
2363
                enum Default implements AccessResolver {
1✔
2364

2365
                    /**
2366
                     * The singleton instance.
2367
                     */
2368
                    INSTANCE;
1✔
2369

2370
                    /**
2371
                     * {@inheritDoc}
2372
                     */
2373
                    public void apply(AccessibleObject accessibleObject) {
2374
                        accessibleObject.setAccessible(true);
×
2375
                    }
×
2376
                }
2377
            }
2378
        }
2379

2380
        /**
2381
         * A proxy of {@code java.lang.System}.
2382
         */
2383
        @JavaDispatcher.Proxied("java.lang.System")
2384
        protected interface System {
2385

2386
            /**
2387
             * Returns the current security manager or {@code null} if not available.
2388
             *
2389
             * @return The current security manager or {@code null} if not available.
2390
             */
2391
            @MaybeNull
2392
            @JavaDispatcher.IsStatic
2393
            @JavaDispatcher.Defaults
2394
            Object getSecurityManager();
2395
        }
2396
    }
2397

2398
    /**
2399
     * A class injector using a {@link java.lang.instrument.Instrumentation} to append to either the boot classpath
2400
     * or the system class path.
2401
     */
2402
    @HashCodeAndEqualsPlugin.Enhance
2403
    class UsingInstrumentation extends AbstractBase {
2404

2405
        /**
2406
         * The jar file name extension.
2407
         */
2408
        private static final String JAR = "jar";
2409

2410
        /**
2411
         * The class file extension.
2412
         */
2413
        private static final String CLASS_FILE_EXTENSION = ".class";
2414

2415
        /**
2416
         * A dispatcher for interacting with the instrumentation API.
2417
         */
2418
        private static final Dispatcher DISPATCHER = doPrivileged(JavaDispatcher.of(Dispatcher.class));
1✔
2419

2420
        /**
2421
         * The instrumentation to use for appending to the class path or the boot path.
2422
         */
2423
        private final Instrumentation instrumentation;
2424

2425
        /**
2426
         * A representation of the target path to which classes are to be appended.
2427
         */
2428
        private final Target target;
2429

2430
        /**
2431
         * The folder to be used for storing jar files.
2432
         */
2433
        private final File folder;
2434

2435
        /**
2436
         * A random string generator for creating file names.
2437
         */
2438
        private final RandomString randomString;
2439

2440
        /**
2441
         * Creates an instrumentation-based class injector.
2442
         *
2443
         * @param folder          The folder to be used for storing jar files.
2444
         * @param target          A representation of the target path to which classes are to be appended.
2445
         * @param instrumentation The instrumentation to use for appending to the class path or the boot path.
2446
         * @param randomString    The random string generator to use.
2447
         */
2448
        protected UsingInstrumentation(File folder,
2449
                                       Target target,
2450
                                       Instrumentation instrumentation,
2451
                                       RandomString randomString) {
1✔
2452
            this.folder = folder;
1✔
2453
            this.target = target;
1✔
2454
            this.instrumentation = instrumentation;
1✔
2455
            this.randomString = randomString;
1✔
2456
        }
1✔
2457

2458
        /**
2459
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
2460
         *
2461
         * @param action The action to execute from a privileged context.
2462
         * @param <T>    The type of the action's resolved value.
2463
         * @return The action's resolved value.
2464
         */
2465
        @AccessControllerPlugin.Enhance
2466
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
2467
            return action.run();
×
2468
        }
2469

2470
        /**
2471
         * Modifies a module's properties using {@link Instrumentation}.
2472
         *
2473
         * @param instrumentation The {@link Instrumentation} instance to use for applying the modification.
2474
         * @param target          The target module that should be modified.
2475
         * @param reads           A set of additional modules this module should read.
2476
         * @param exports         A map of packages to export to a set of modules.
2477
         * @param opens           A map of packages to open to a set of modules.
2478
         * @param uses            A set of provider interfaces to use by this module.
2479
         * @param provides        A map of provider interfaces to provide by this module mapped to the provider implementations.
2480
         */
2481
        public static void redefineModule(Instrumentation instrumentation,
2482
                                          JavaModule target,
2483
                                          Set<JavaModule> reads,
2484
                                          Map<String, Set<JavaModule>> exports,
2485
                                          Map<String, Set<JavaModule>> opens,
2486
                                          Set<Class<?>> uses,
2487
                                          Map<Class<?>, List<Class<?>>> provides) {
2488
            if (!DISPATCHER.isModifiableModule(instrumentation, target.unwrap())) {
×
2489
                throw new IllegalArgumentException("Cannot modify module: " + target);
×
2490
            }
2491
            Set<Object> unwrappedReads = new HashSet<Object>();
×
2492
            for (JavaModule read : reads) {
×
2493
                unwrappedReads.add(read.unwrap());
×
2494
            }
×
2495
            Map<String, Set<?>> unwrappedExports = new HashMap<String, Set<?>>();
×
2496
            for (Map.Entry<String, Set<JavaModule>> entry : exports.entrySet()) {
×
2497
                Set<Object> modules = new HashSet<Object>();
×
2498
                for (JavaModule module : entry.getValue()) {
×
2499
                    modules.add(module.unwrap());
×
2500
                }
×
2501
                unwrappedExports.put(entry.getKey(), modules);
×
2502
            }
×
2503
            Map<String, Set<?>> unwrappedOpens = new HashMap<String, Set<?>>();
×
2504
            for (Map.Entry<String, Set<JavaModule>> entry : opens.entrySet()) {
×
2505
                Set<Object> modules = new HashSet<Object>();
×
2506
                for (JavaModule module : entry.getValue()) {
×
2507
                    modules.add(module.unwrap());
×
2508
                }
×
2509
                unwrappedOpens.put(entry.getKey(), modules);
×
2510
            }
×
2511
            DISPATCHER.redefineModule(instrumentation, target.unwrap(), unwrappedReads, unwrappedExports, unwrappedOpens, uses, provides);
×
2512
        }
×
2513

2514
        /**
2515
         * Creates an instrumentation-based class injector.
2516
         *
2517
         * @param folder          The folder to be used for storing jar files.
2518
         * @param target          A representation of the target path to which classes are to be appended.
2519
         * @param instrumentation The instrumentation to use for appending to the class path or the boot path.
2520
         * @return An appropriate class injector that applies instrumentation.
2521
         */
2522
        public static ClassInjector of(File folder, Target target, Instrumentation instrumentation) {
2523
            return new UsingInstrumentation(folder, target, instrumentation, new RandomString());
1✔
2524
        }
2525

2526
        /**
2527
         * {@inheritDoc}
2528
         */
2529
        public boolean isAlive() {
2530
            return isAvailable();
1✔
2531
        }
2532

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

2578
        /**
2579
         * Returns {@code true} if this class injector is available on this VM.
2580
         *
2581
         * @return {@code true} if this class injector is available on this VM.
2582
         */
2583
        public static boolean isAvailable() {
2584
            return ClassFileVersion.ofThisVm(ClassFileVersion.JAVA_V5).isAtLeast(ClassFileVersion.JAVA_V6);
1✔
2585
        }
2586

2587
        /**
2588
         * A dispatcher to interact with the instrumentation API.
2589
         */
2590
        @JavaDispatcher.Proxied("java.lang.instrument.Instrumentation")
2591
        protected interface Dispatcher {
2592

2593
            /**
2594
             * Appends a jar file to the bootstrap class loader.
2595
             *
2596
             * @param instrumentation The instrumentation instance to interact with.
2597
             * @param jarFile         The jar file to append.
2598
             */
2599
            void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
2600

2601
            /**
2602
             * Appends a jar file to the system class loader.
2603
             *
2604
             * @param instrumentation The instrumentation instance to interact with.
2605
             * @param jarFile         The jar file to append.
2606
             */
2607
            void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
2608

2609
            /**
2610
             * Checks if a module is modifiable.
2611
             *
2612
             * @param instrumentation The instrumentation instance to use for checking for modifiability.
2613
             * @param module          The {@code java.lang.Module} to examine.
2614
             * @return {@code true} if the supplied module is modifiable.
2615
             */
2616
            boolean isModifiableModule(Instrumentation instrumentation, @JavaDispatcher.Proxied("java.lang.Module") Object module);
2617

2618
            /**
2619
             * Redefines an existing module.
2620
             *
2621
             * @param instrumentation The instrumentation instance to redefine.
2622
             * @param module          The {@code java.lang.Module} to redefine.
2623
             * @param reads           A set of {@code java.lang.Module}s that are to be read additionally.
2624
             * @param exports         A map of packages to a set of {@code java.lang.Module}s to read additionally.
2625
             * @param opens           A map of packages to a set of {@code java.lang.Module}s to open to additionally.
2626
             * @param uses            A list of types to use additionally.
2627
             * @param provides        A list of types to their implementations to offer additionally.
2628
             */
2629
            void redefineModule(Instrumentation instrumentation,
2630
                                @JavaDispatcher.Proxied("java.lang.Module") Object module,
2631
                                Set<?> reads,
2632
                                Map<String, Set<?>> exports,
2633
                                Map<String, Set<?>> opens,
2634
                                Set<Class<?>> uses,
2635
                                Map<Class<?>, List<Class<?>>> provides);
2636
        }
2637

2638
        /**
2639
         * A representation of the target to which Java classes should be appended to.
2640
         */
2641
        public enum Target {
1✔
2642

2643
            /**
2644
             * Representation of the bootstrap class loader.
2645
             */
2646
            BOOTSTRAP(null) {
1✔
2647
                @Override
2648
                protected void inject(Instrumentation instrumentation, JarFile jarFile) {
2649
                    DISPATCHER.appendToBootstrapClassLoaderSearch(instrumentation, jarFile);
1✔
2650
                }
1✔
2651
            },
2652

2653
            /**
2654
             * Representation of the system class loader.
2655
             */
2656
            SYSTEM(ClassLoader.getSystemClassLoader()) {
1✔
2657
                @Override
2658
                protected void inject(Instrumentation instrumentation, JarFile jarFile) {
2659
                    DISPATCHER.appendToSystemClassLoaderSearch(instrumentation, jarFile);
1✔
2660
                }
1✔
2661
            };
2662

2663
            /**
2664
             * The class loader to load classes from.
2665
             */
2666
            @MaybeNull
2667
            private final ClassLoader classLoader;
2668

2669
            /**
2670
             * Creates a new injection target.
2671
             *
2672
             * @param classLoader The class loader to load classes from.
2673
             */
2674
            Target(@MaybeNull ClassLoader classLoader) {
1✔
2675
                this.classLoader = classLoader;
1✔
2676
            }
1✔
2677

2678
            /**
2679
             * Returns the class loader to load classes from.
2680
             *
2681
             * @return The class loader to load classes from.
2682
             */
2683
            @MaybeNull
2684
            protected ClassLoader getClassLoader() {
2685
                return classLoader;
1✔
2686
            }
2687

2688
            /**
2689
             * Adds the given classes to the represented class loader.
2690
             *
2691
             * @param instrumentation The instrumentation instance to use.
2692
             * @param jarFile         The jar file to append.
2693
             */
2694
            protected abstract void inject(Instrumentation instrumentation, JarFile jarFile);
2695
        }
2696
    }
2697

2698
    /**
2699
     * A class injector using JNA to invoke JNI's define class utility for defining a class. This injector is only
2700
     * available if JNA is available on the class loader. Some JVM implementations might not support this injection
2701
     * method.
2702
     */
2703
    @HashCodeAndEqualsPlugin.Enhance
2704
    class UsingJna extends AbstractBase {
2705

2706
        /**
2707
         * The dispatcher to use.
2708
         */
2709
        private static final Dispatcher DISPATCHER = doPrivileged(Dispatcher.CreationAction.INSTANCE);
1✔
2710

2711
        /**
2712
         * A lock for the bootstrap loader when injecting.
2713
         */
2714
        private static final Object BOOTSTRAP_LOADER_LOCK = new Object();
1✔
2715

2716
        /**
2717
         * The class loader to inject classes into or {@code null} for the bootstrap loader.
2718
         */
2719
        @MaybeNull
2720
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
2721
        private final ClassLoader classLoader;
2722

2723
        /**
2724
         * The protection domain to use or {@code null} for no protection domain.
2725
         */
2726
        @MaybeNull
2727
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
2728
        private final ProtectionDomain protectionDomain;
2729

2730
        /**
2731
         * Creates a new unsafe injector for the given class loader with a default protection domain.
2732
         *
2733
         * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
2734
         */
2735
        public UsingJna(@MaybeNull ClassLoader classLoader) {
2736
            this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1✔
2737
        }
1✔
2738

2739
        /**
2740
         * Creates a new JNA injector for the given class loader with a default protection domain.
2741
         *
2742
         * @param classLoader      The class loader to inject classes into or {@code null} for the bootstrap loader.
2743
         * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
2744
         */
2745
        public UsingJna(@MaybeNull ClassLoader classLoader, @MaybeNull ProtectionDomain protectionDomain) {
1✔
2746
            this.classLoader = classLoader;
1✔
2747
            this.protectionDomain = protectionDomain;
1✔
2748
        }
1✔
2749

2750
        /**
2751
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
2752
         *
2753
         * @param action The action to execute from a privileged context.
2754
         * @param <T>    The type of the action's resolved value.
2755
         * @return The action's resolved value.
2756
         */
2757
        @AccessControllerPlugin.Enhance
2758
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
2759
            return action.run();
×
2760
        }
2761

2762
        /**
2763
         * Checks if JNA class injection is available on the current VM.
2764
         *
2765
         * @return {@code true} if JNA class injection is available on the current VM.
2766
         */
2767
        public static boolean isAvailable() {
2768
            return DISPATCHER.isAvailable();
1✔
2769
        }
2770

2771
        /**
2772
         * Returns an JNA class injector for the system class loader.
2773
         *
2774
         * @return A class injector for the system class loader.
2775
         */
2776
        public static ClassInjector ofSystemLoader() {
2777
            return new UsingJna(ClassLoader.getSystemClassLoader());
1✔
2778
        }
2779

2780
        /**
2781
         * Returns an JNA class injector for the platform class loader. For VMs of version 8 or older,
2782
         * the extension class loader is represented instead.
2783
         *
2784
         * @return A class injector for the platform class loader.
2785
         */
2786
        public static ClassInjector ofPlatformLoader() {
2787
            return new UsingJna(ClassLoader.getSystemClassLoader().getParent());
1✔
2788
        }
2789

2790
        /**
2791
         * Returns an JNA class injector for the boot class loader.
2792
         *
2793
         * @return A class injector for the boot loader.
2794
         */
2795
        public static ClassInjector ofBootLoader() {
2796
            return new UsingJna(ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
2797
        }
2798

2799
        /**
2800
         * {@inheritDoc}
2801
         */
2802
        public boolean isAlive() {
2803
            return DISPATCHER.isAvailable();
1✔
2804
        }
2805

2806
        /**
2807
         * {@inheritDoc}
2808
         */
2809
        public Map<String, Class<?>> injectRaw(Set<String> names, ClassFileLocator classFileLocator) {
2810
            Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1✔
2811
            synchronized (classLoader == null
1✔
2812
                    ? BOOTSTRAP_LOADER_LOCK
2813
                    : classLoader) {
2814
                for (String name : names) {
1✔
2815
                    try {
2816
                        result.put(name, Class.forName(name, false, classLoader));
×
2817
                    } catch (ClassNotFoundException ignored) {
1✔
2818
                        try {
2819
                            result.put(name, DISPATCHER.defineClass(classLoader, name, classFileLocator.locate(name).resolve(), protectionDomain));
1✔
2820
                        } catch (IOException exception) {
×
2821
                            throw new IllegalStateException("Failed to resolve binary representation of " + name, exception);
×
2822
                        }
1✔
2823
                    }
×
2824
                }
1✔
2825
            }
1✔
2826
            return result;
1✔
2827
        }
2828

2829
        /**
2830
         * A dispatcher for JNA class injection.
2831
         */
2832
        protected interface Dispatcher {
2833

2834
            /**
2835
             * Checks if this dispatcher is available for use.
2836
             *
2837
             * @return {@code true} if this dispatcher is available for use.
2838
             */
2839
            boolean isAvailable();
2840

2841
            /**
2842
             * Defines a class.
2843
             *
2844
             * @param classLoader          The class loader or {@code null} if a class should be injected into the bootstrap loader.
2845
             * @param name                 The class's name.
2846
             * @param binaryRepresentation The class's class file.
2847
             * @param protectionDomain     The protection domain to use or {@code null} if no protection domain should be used.
2848
             * @return The class that was defined.
2849
             */
2850
            Class<?> defineClass(@MaybeNull ClassLoader classLoader,
2851
                                 String name,
2852
                                 byte[] binaryRepresentation,
2853
                                 @MaybeNull ProtectionDomain protectionDomain);
2854

2855
            /**
2856
             * An action for creating a JNA dispatcher.
2857
             */
2858
            enum CreationAction implements PrivilegedAction<Dispatcher> {
1✔
2859

2860
                /**
2861
                 * The singleton instance.
2862
                 */
2863
                INSTANCE;
1✔
2864

2865
                /**
2866
                 * {@inheritDoc}
2867
                 */
2868
                @SuppressWarnings("deprecation")
2869
                public Dispatcher run() {
2870
                    if (System.getProperty("java.vm.name", "").toUpperCase(Locale.US).contains("J9")) {
1✔
2871
                        return new Unavailable("J9 does not support JNA-based class definition");
×
2872
                    }
2873
                    try {
2874
                        Map<String, Object> options = new HashMap<String, Object>();
1✔
2875
                        options.put(Library.OPTION_ALLOW_OBJECTS, Boolean.TRUE);
1✔
2876
                        if (Platform.isWindows() && !Platform.is64Bit()) {
1✔
2877
                            options.put(Library.OPTION_FUNCTION_MAPPER, Windows32BitFunctionMapper.INSTANCE);
×
2878
                        }
2879
                        return new Enabled(Native.loadLibrary("jvm", Jvm.class, options));
1✔
2880
                    } catch (Throwable throwable) {
×
2881
                        return new Unavailable(throwable.getMessage());
×
2882
                    }
2883
                }
2884
            }
2885

2886
            /**
2887
             * A mapper for 32-bit Windows functions where names are defined with different convention.
2888
             */
2889
            enum Windows32BitFunctionMapper implements FunctionMapper {
×
2890

2891
                /**
2892
                 * The singleton instance.
2893
                 */
2894
                INSTANCE;
×
2895

2896
                /**
2897
                 * {@inheritDoc}
2898
                 */
2899
                public String getFunctionName(NativeLibrary library, Method method) {
2900
                    if (method.getName().equals("JVM_DefineClass")) {
×
2901
                        return "_JVM_DefineClass@24";
×
2902
                    }
2903
                    return method.getName();
×
2904
                }
2905
            }
2906

2907
            /**
2908
             * An enabled dispatcher for JNA-based class injection.
2909
             */
2910
            @HashCodeAndEqualsPlugin.Enhance
2911
            class Enabled implements Dispatcher {
2912

2913
                /**
2914
                 * The JNA-dispatcher to use for invoking JNI's class definition utilities.
2915
                 */
2916
                private final Jvm jvm;
2917

2918
                /**
2919
                 * Creates a new dispatcher for a JNI's class definition utilities.
2920
                 *
2921
                 * @param jvm The JNA-dispatcher to use for invoking JNI's class definition utilities.
2922
                 */
2923
                protected Enabled(Jvm jvm) {
1✔
2924
                    this.jvm = jvm;
1✔
2925
                }
1✔
2926

2927
                /**
2928
                 * {@inheritDoc}
2929
                 */
2930
                public boolean isAvailable() {
2931
                    return true;
1✔
2932
                }
2933

2934
                /**
2935
                 * {@inheritDoc}
2936
                 */
2937
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2938
                    return jvm.JVM_DefineClass(JNIEnv.CURRENT,
1✔
2939
                            name.replace('.', '/'),
1✔
2940
                            classLoader,
2941
                            binaryRepresentation,
2942
                            binaryRepresentation.length,
2943
                            protectionDomain);
2944
                }
2945
            }
2946

2947
            /**
2948
             * An unavailable dispatcher for JNA-based class injection.
2949
             */
2950
            @HashCodeAndEqualsPlugin.Enhance
2951
            class Unavailable implements Dispatcher {
2952

2953
                /**
2954
                 * The exception's error message when attempting to resolve the JNA dispatcher.
2955
                 */
2956
                private final String error;
2957

2958
                /**
2959
                 * Creates a new unavailable JNA-based class injector.
2960
                 *
2961
                 * @param error The exception's error message when attempting to resolve the JNA dispatcher.
2962
                 */
2963
                protected Unavailable(String error) {
1✔
2964
                    this.error = error;
1✔
2965
                }
1✔
2966

2967
                /**
2968
                 * {@inheritDoc}
2969
                 */
2970
                public boolean isAvailable() {
2971
                    return false;
1✔
2972
                }
2973

2974
                /**
2975
                 * {@inheritDoc}
2976
                 */
2977
                public Class<?> defineClass(@MaybeNull ClassLoader classLoader, String name, byte[] binaryRepresentation, @MaybeNull ProtectionDomain protectionDomain) {
2978
                    throw new UnsupportedOperationException("JNA is not available and JNA-based injection cannot be used: " + error);
1✔
2979
                }
2980
            }
2981

2982
            /**
2983
             * A JNA dispatcher for the JVM's <i>JVM_DefineClass</i> method.
2984
             */
2985
            interface Jvm extends Library {
2986

2987
                /**
2988
                 * Defines a new class into a given class loader.
2989
                 *
2990
                 * @param env                  The JNI environment.
2991
                 * @param name                 The internal name of the class.
2992
                 * @param classLoader          The class loader to inject into or {@code null} if injecting into the bootstrap loader.
2993
                 * @param binaryRepresentation The class's binary representation.
2994
                 * @param length               The length of the class's binary representation.
2995
                 * @param protectionDomain     The protection domain or {@code null} if no explicit protection domain should be used.
2996
                 * @return The class that was defined.
2997
                 * @throws LastErrorException If an error occurs during injection.
2998
                 */
2999
                @SuppressWarnings("checkstyle:methodname")
3000
                Class<?> JVM_DefineClass(JNIEnv env,
3001
                                         String name,
3002
                                         @MaybeNull ClassLoader classLoader,
3003
                                         byte[] binaryRepresentation,
3004
                                         int length,
3005
                                         @MaybeNull ProtectionDomain protectionDomain) throws LastErrorException;
3006
            }
3007
        }
3008
    }
3009
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc