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

raphw / byte-buddy / #801

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

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1519
                /**
1520
                 * The error message being displayed.
1521
                 */
1522
                private final String message;
1523

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1837
        /**
1838
         * The dispatcher to use.
1839
         */
1840
        private final Dispatcher.Initializable dispatcher;
1841

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2012
                /**
2013
                 * The singleton instance.
2014
                 */
2015
                INSTANCE;
1✔
2016

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2207
            /**
2208
             * The dispatcher to use.
2209
             */
2210
            private final Dispatcher.Initializable dispatcher;
2211

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

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

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

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

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

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

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

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

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

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

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

2379
                    /**
2380
                     * The singleton instance.
2381
                     */
2382
                    INSTANCE;
1✔
2383

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2874
                /**
2875
                 * The singleton instance.
2876
                 */
2877
                INSTANCE;
1✔
2878

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

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

2905
                /**
2906
                 * The singleton instance.
2907
                 */
2908
                INSTANCE;
×
2909

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

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

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

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

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

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

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

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

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

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

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

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

3001
                /**
3002
                 * Defines a new class into a given class loader.
3003
                 *
3004
                 * @param env                  The JNI environment.
3005
                 * @param name                 The internal name of the class.
3006
                 * @param classLoader          The class loader to inject into or {@code null} if injecting into the bootstrap loader.
3007
                 * @param binaryRepresentation The class's binary representation.
3008
                 * @param length               The length of the class's binary representation.
3009
                 * @param protectionDomain     The protection domain or {@code null} if no explicit protection domain should be used.
3010
                 * @return The class that was defined.
3011
                 * @throws LastErrorException If an error occurs during injection.
3012
                 */
3013
                @SuppressWarnings("checkstyle:methodname")
3014
                Class<?> JVM_DefineClass(JNIEnv env,
3015
                                         String name,
3016
                                         @MaybeNull ClassLoader classLoader,
3017
                                         byte[] binaryRepresentation,
3018
                                         int length,
3019
                                         @MaybeNull ProtectionDomain protectionDomain) throws LastErrorException;
3020
            }
3021
        }
3022
    }
3023
}
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