1 /*
2 * Copyright 2014 - 2020 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import net.bytebuddy.ByteBuddy;
20 import net.bytebuddy.asm.MemberRemoval;
21 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
22 import net.bytebuddy.description.modifier.Visibility;
23 import net.bytebuddy.description.type.PackageDescription;
24 import net.bytebuddy.description.type.TypeDescription;
25 import net.bytebuddy.dynamic.DynamicType;
26 import net.bytebuddy.dynamic.scaffold.TypeValidation;
27 import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
28 import net.bytebuddy.implementation.FixedValue;
29 import net.bytebuddy.implementation.MethodCall;
30 import net.bytebuddy.utility.JavaModule;
31 import net.bytebuddy.utility.JavaType;
32 import net.bytebuddy.utility.RandomString;
33
34 import java.io.File;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.lang.instrument.Instrumentation;
38 import java.lang.reflect.*;
39 import java.net.URL;
40 import java.security.AccessController;
41 import java.security.Permission;
42 import java.security.PrivilegedAction;
43 import java.security.ProtectionDomain;
44 import java.util.*;
45 import java.util.jar.JarEntry;
46 import java.util.jar.JarFile;
47 import java.util.jar.JarOutputStream;
48
49 import static net.bytebuddy.matcher.ElementMatchers.any;
50 import static net.bytebuddy.matcher.ElementMatchers.named;
51
52 /**
53 * <p>
54 * A class injector is capable of injecting classes into a {@link java.lang.ClassLoader} without
55 * requiring the class loader to being able to explicitly look up these classes.
56 * </p>
57 * <p>
58 * <b>Important</b>: Byte Buddy does not supply privileges when injecting code. When using a {@link SecurityManager},
59 * the user of this injector is responsible for providing access to non-public properties.
60 * </p>
61 */
62 public interface ClassInjector {
63
64 /**
65 * A permission for the {@code suppressAccessChecks} permission.
66 */
67 Permission SUPPRESS_ACCESS_CHECKS = new ReflectPermission("suppressAccessChecks");
68
69 /**
70 * Determines the default behavior for type injections when a type is already loaded.
71 */
72 boolean ALLOW_EXISTING_TYPES = false;
73
74 /**
75 * Indicates if this class injector is available on the current VM.
76 *
77 * @return {@code true} if this injector is available on the current VM.
78 */
79 boolean isAlive();
80
81 /**
82 * Injects the given types into the represented class loader.
83 *
84 * @param types The types to load via injection.
85 * @return The loaded types that were passed as arguments.
86 */
87 Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types);
88
89 /**
90 * Injects the given types into the represented class loader using a mapping from name to binary representation.
91 *
92 * @param types The types to load via injection.
93 * @return The loaded types that were passed as arguments.
94 */
95 Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types);
96
97 /**
98 * An abstract base implementation of a class injector.
99 */
100 abstract class AbstractBase implements ClassInjector {
101
102 /**
103 * {@inheritDoc}
104 */
105 public Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types) {
106 Map<String, byte[]> binaryRepresentations = new LinkedHashMap<String, byte[]>();
107 for (Map.Entry<? extends TypeDescription, byte[]> entry : types.entrySet()) {
108 binaryRepresentations.put(entry.getKey().getName(), entry.getValue());
109 }
110 Map<String, Class<?>> loadedTypes = injectRaw(binaryRepresentations);
111 Map<TypeDescription, Class<?>> result = new LinkedHashMap<TypeDescription, Class<?>>();
112 for (TypeDescription typeDescription : types.keySet()) {
113 result.put(typeDescription, loadedTypes.get(typeDescription.getName()));
114 }
115 return result;
116 }
117 }
118
119 /**
120 * A class injector that uses reflective method calls.
121 */
122 @HashCodeAndEqualsPlugin.Enhance
123 class UsingReflection extends AbstractBase {
124
125 /**
126 * The dispatcher to use for accessing a class loader via reflection.
127 */
128 private static final Dispatcher.Initializable DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
129
130 /**
131 * The class loader into which the classes are to be injected.
132 */
133 private final ClassLoader classLoader;
134
135 /**
136 * The protection domain that is used when loading classes.
137 */
138 @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
139 private final ProtectionDomain protectionDomain;
140
141 /**
142 * The package definer to be queried for package definitions.
143 */
144 private final PackageDefinitionStrategy packageDefinitionStrategy;
145
146 /**
147 * Determines if an exception should be thrown when attempting to load a type that already exists.
148 */
149 private final boolean forbidExisting;
150
151 /**
152 * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link java.security.ProtectionDomain} and a
153 * trivial {@link PackageDefinitionStrategy} which does not trigger an error when discovering existent classes.
154 *
155 * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
156 */
157 public UsingReflection(ClassLoader classLoader) {
158 this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
159 }
160
161 /**
162 * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link PackageDefinitionStrategy} where the
163 * injection of existent classes does not trigger an error.
164 *
165 * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
166 * @param protectionDomain The protection domain to apply during class definition.
167 */
168 public UsingReflection(ClassLoader classLoader, ProtectionDomain protectionDomain) {
169 this(classLoader,
170 protectionDomain,
171 PackageDefinitionStrategy.Trivial.INSTANCE,
172 ALLOW_EXISTING_TYPES);
173 }
174
175 /**
176 * Creates a new injector for the given {@link java.lang.ClassLoader} and {@link java.security.ProtectionDomain}.
177 *
178 * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected.Must not be the bootstrap loader.
179 * @param protectionDomain The protection domain to apply during class definition.
180 * @param packageDefinitionStrategy The package definer to be queried for package definitions.
181 * @param forbidExisting Determines if an exception should be thrown when attempting to load a type that already exists.
182 */
183 public UsingReflection(ClassLoader classLoader,
184 ProtectionDomain protectionDomain,
185 PackageDefinitionStrategy packageDefinitionStrategy,
186 boolean forbidExisting) {
187 if (classLoader == null) {
188 throw new IllegalArgumentException("Cannot inject classes into the bootstrap class loader");
189 }
190 this.classLoader = classLoader;
191 this.protectionDomain = protectionDomain;
192 this.packageDefinitionStrategy = packageDefinitionStrategy;
193 this.forbidExisting = forbidExisting;
194 }
195
196 /**
197 * {@inheritDoc}
198 */
199 public boolean isAlive() {
200 return isAvailable();
201 }
202
203 /**
204 * {@inheritDoc}
205 */
206 public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
207 Dispatcher dispatcher = DISPATCHER.initialize();
208 Map<String, Class<?>> result = new HashMap<String, Class<?>>();
209 for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
210 synchronized (dispatcher.getClassLoadingLock(classLoader, entry.getKey())) {
211 Class<?> type = dispatcher.findClass(classLoader, entry.getKey());
212 if (type == null) {
213 int packageIndex = entry.getKey().lastIndexOf('.');
214 if (packageIndex != -1) {
215 String packageName = entry.getKey().substring(0, packageIndex);
216 PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(classLoader, packageName, entry.getKey());
217 if (definition.isDefined()) {
218 Package definedPackage = dispatcher.getPackage(classLoader, packageName);
219 if (definedPackage == null) {
220 dispatcher.definePackage(classLoader,
221 packageName,
222 definition.getSpecificationTitle(),
223 definition.getSpecificationVersion(),
224 definition.getSpecificationVendor(),
225 definition.getImplementationTitle(),
226 definition.getImplementationVersion(),
227 definition.getImplementationVendor(),
228 definition.getSealBase());
229 } else if (!definition.isCompatibleTo(definedPackage)) {
230 throw new SecurityException("Sealing violation for package " + packageName);
231 }
232 }
233 }
234 type = dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain);
235 } else if (forbidExisting) {
236 throw new IllegalStateException("Cannot inject already loaded type: " + type);
237 }
238 result.put(entry.getKey(), type);
239 }
240 }
241 return result;
242 }
243
244 /**
245 * Indicates if this class injection is available on the current VM.
246 *
247 * @return {@code true} if this class injection is available.
248 */
249 public static boolean isAvailable() {
250 return DISPATCHER.isAvailable();
251 }
252
253 /**
254 * Creates a class injector for the system class loader.
255 *
256 * @return A class injector for the system class loader.
257 */
258 public static ClassInjector ofSystemClassLoader() {
259 return new UsingReflection(ClassLoader.getSystemClassLoader());
260 }
261
262 /**
263 * A dispatcher for accessing a {@link ClassLoader} reflectively.
264 */
265 protected interface Dispatcher {
266
267 /**
268 * Indicates a class that is currently not defined.
269 */
270 Class<?> UNDEFINED = null;
271
272 /**
273 * Returns the lock for loading the specified class.
274 *
275 * @param classLoader the class loader to inject the class into.
276 * @param name The name of the class.
277 * @return The lock for loading this class.
278 */
279 Object getClassLoadingLock(ClassLoader classLoader, String name);
280
281 /**
282 * Looks up a class from the given class loader.
283 *
284 * @param classLoader The class loader for which a class should be located.
285 * @param name The binary name of the class that should be located.
286 * @return The class for the binary name or {@code null} if no such class is defined for the provided class loader.
287 */
288 Class<?> findClass(ClassLoader classLoader, String name);
289
290 /**
291 * Defines a class for the given class loader.
292 *
293 * @param classLoader The class loader for which a new class should be defined.
294 * @param name The binary name of the class that should be defined.
295 * @param binaryRepresentation The binary representation of the class.
296 * @param protectionDomain The protection domain for the defined class.
297 * @return The defined, loaded class.
298 */
299 Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain);
300
301 /**
302 * Looks up a package from a class loader.
303 *
304 * @param classLoader The class loader to query.
305 * @param name The binary name of the package.
306 * @return The package for the given name as defined by the provided class loader or {@code null} if no such package exists.
307 */
308 Package getPackage(ClassLoader classLoader, String name);
309
310 /**
311 * Defines a package for the given class loader.
312 *
313 * @param classLoader The class loader for which a package is to be defined.
314 * @param name The binary name of the package.
315 * @param specificationTitle The specification title of the package or {@code null} if no specification title exists.
316 * @param specificationVersion The specification version of the package or {@code null} if no specification version exists.
317 * @param specificationVendor The specification vendor of the package or {@code null} if no specification vendor exists.
318 * @param implementationTitle The implementation title of the package or {@code null} if no implementation title exists.
319 * @param implementationVersion The implementation version of the package or {@code null} if no implementation version exists.
320 * @param implementationVendor The implementation vendor of the package or {@code null} if no implementation vendor exists.
321 * @param sealBase The seal base URL or {@code null} if the package should not be sealed.
322 * @return The defined package.
323 */
324 Package definePackage(ClassLoader classLoader,
325 String name,
326 String specificationTitle,
327 String specificationVersion,
328 String specificationVendor,
329 String implementationTitle,
330 String implementationVersion,
331 String implementationVendor,
332 URL sealBase);
333
334 /**
335 * Initializes a dispatcher to make non-accessible APIs accessible.
336 */
337 interface Initializable {
338
339 /**
340 * Indicates if this dispatcher is available.
341 *
342 * @return {@code true} if this dispatcher is available.
343 */
344 boolean isAvailable();
345
346 /**
347 * Initializes this dispatcher.
348 *
349 * @return The initialized dispatcher.
350 */
351 Dispatcher initialize();
352
353 /**
354 * Represents an unsuccessfully loaded method lookup.
355 */
356 @HashCodeAndEqualsPlugin.Enhance
357 class Unavailable implements Dispatcher, Initializable {
358
359 /**
360 * The reason why this dispatcher is not available.
361 */
362 private final String message;
363
364 /**
365 * Creates a new faulty reflection store.
366 *
367 * @param message The reason why this dispatcher is not available.
368 */
369 protected Unavailable(String message) {
370 this.message = message;
371 }
372
373 /**
374 * {@inheritDoc}
375 */
376 public boolean isAvailable() {
377 return false;
378 }
379
380 /**
381 * {@inheritDoc}
382 */
383 public Dispatcher initialize() {
384 return this;
385 }
386
387 /**
388 * {@inheritDoc}
389 */
390 public Object getClassLoadingLock(ClassLoader classLoader, String name) {
391 return classLoader;
392 }
393
394 /**
395 * {@inheritDoc}
396 */
397 public Class<?> findClass(ClassLoader classLoader, String name) {
398 try {
399 return classLoader.loadClass(name);
400 } catch (ClassNotFoundException ignored) {
401 return UNDEFINED;
402 }
403 }
404
405 /**
406 * {@inheritDoc}
407 */
408 public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
409 throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
410 }
411
412 /**
413 * {@inheritDoc}
414 */
415 public Package getPackage(ClassLoader classLoader, String name) {
416 throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
417 }
418
419 /**
420 * {@inheritDoc}
421 */
422 public Package definePackage(ClassLoader classLoader,
423 String name,
424 String specificationTitle,
425 String specificationVersion,
426 String specificationVendor,
427 String implementationTitle,
428 String implementationVersion,
429 String implementationVendor,
430 URL sealBase) {
431 throw new UnsupportedOperationException("Cannot define package using injection: " + message);
432 }
433 }
434 }
435
436 /**
437 * A creation action for a dispatcher.
438 */
439 enum CreationAction implements PrivilegedAction<Initializable> {
440
441 /**
442 * The singleton instance.
443 */
444 INSTANCE;
445
446 /**
447 * {@inheritDoc}
448 */
449 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
450 public Initializable run() {
451 try {
452 if (JavaModule.isSupported()) {
453 return UsingUnsafe.isAvailable()
454 ? UsingUnsafeInjection.make()
455 : UsingUnsafeOverride.make();
456 } else {
457 return Direct.make();
458 }
459 } catch (InvocationTargetException exception) {
460 return new Initializable.Unavailable(exception.getCause().getMessage());
461 } catch (Exception exception) {
462 return new Initializable.Unavailable(exception.getMessage());
463 }
464 }
465 }
466
467 /**
468 * A class injection dispatcher that is using reflection on the {@link ClassLoader} methods.
469 */
470 @HashCodeAndEqualsPlugin.Enhance
471 abstract class Direct implements Dispatcher, Initializable {
472
473 /**
474 * An instance of {@link ClassLoader#findLoadedClass(String)}.
475 */
476 protected final Method findLoadedClass;
477
478 /**
479 * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
480 */
481 protected final Method defineClass;
482
483 /**
484 * An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
485 */
486 protected final Method getPackage;
487
488 /**
489 * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
490 */
491 protected final Method definePackage;
492
493 /**
494 * Creates a new direct injection dispatcher.
495 *
496 * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
497 * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
498 * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
499 * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
500 */
501 protected Direct(Method findLoadedClass,
502 Method defineClass,
503 Method getPackage,
504 Method definePackage) {
505 this.findLoadedClass = findLoadedClass;
506 this.defineClass = defineClass;
507 this.getPackage = getPackage;
508 this.definePackage = definePackage;
509 }
510
511 /**
512 * Creates a direct dispatcher.
513 *
514 * @return A direct dispatcher for class injection.
515 * @throws Exception If the creation is impossible.
516 */
517 @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility")
518 protected static Initializable make() throws Exception {
519 Method getPackage;
520 if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
521 try {
522 getPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class);
523 } catch (NoSuchMethodException ignored) {
524 getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
525 getPackage.setAccessible(true);
526 }
527 } else {
528 getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
529 getPackage.setAccessible(true);
530 }
531 Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
532 findLoadedClass.setAccessible(true);
533 Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
534 String.class,
535 byte[].class,
536 int.class,
537 int.class,
538 ProtectionDomain.class);
539 defineClass.setAccessible(true);
540 Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage",
541 String.class,
542 String.class,
543 String.class,
544 String.class,
545 String.class,
546 String.class,
547 String.class,
548 URL.class);
549 definePackage.setAccessible(true);
550 try {
551 Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
552 getClassLoadingLock.setAccessible(true);
553 return new ForJava7CapableVm(findLoadedClass,
554 defineClass,
555 getPackage,
556 definePackage,
557 getClassLoadingLock);
558 } catch (NoSuchMethodException ignored) {
559 return new ForLegacyVm(findLoadedClass, defineClass, getPackage, definePackage);
560 }
561 }
562
563 /**
564 * {@inheritDoc}
565 */
566 public boolean isAvailable() {
567 return true;
568 }
569
570 /**
571 * {@inheritDoc}
572 */
573 public Dispatcher initialize() {
574 SecurityManager securityManager = System.getSecurityManager();
575 if (securityManager != null) {
576 try {
577 securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
578 } catch (Exception exception) {
579 return new Dispatcher.Unavailable(exception.getMessage());
580 }
581 }
582 return this;
583 }
584
585 /**
586 * {@inheritDoc}
587 */
588 public Class<?> findClass(ClassLoader classLoader, String name) {
589 try {
590 return (Class<?>) findLoadedClass.invoke(classLoader, name);
591 } catch (IllegalAccessException exception) {
592 throw new IllegalStateException("Could not access java.lang.ClassLoader#findClass", exception);
593 } catch (InvocationTargetException exception) {
594 throw new IllegalStateException("Error invoking java.lang.ClassLoader#findClass", exception.getCause());
595 }
596 }
597
598 /**
599 * {@inheritDoc}
600 */
601 public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
602 try {
603 return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
604 } catch (IllegalAccessException exception) {
605 throw new IllegalStateException("Could not access java.lang.ClassLoader#defineClass", exception);
606 } catch (InvocationTargetException exception) {
607 throw new IllegalStateException("Error invoking java.lang.ClassLoader#defineClass", exception.getCause());
608 }
609 }
610
611 /**
612 * {@inheritDoc}
613 */
614 public Package getPackage(ClassLoader classLoader, String name) {
615 try {
616 return (Package) getPackage.invoke(classLoader, name);
617 } catch (IllegalAccessException exception) {
618 throw new IllegalStateException("Could not access java.lang.ClassLoader#getPackage", exception);
619 } catch (InvocationTargetException exception) {
620 throw new IllegalStateException("Error invoking java.lang.ClassLoader#getPackage", exception.getCause());
621 }
622 }
623
624 /**
625 * {@inheritDoc}
626 */
627 public Package definePackage(ClassLoader classLoader,
628 String name,
629 String specificationTitle,
630 String specificationVersion,
631 String specificationVendor,
632 String implementationTitle,
633 String implementationVersion,
634 String implementationVendor,
635 URL sealBase) {
636 try {
637 return (Package) definePackage.invoke(classLoader,
638 name,
639 specificationTitle,
640 specificationVersion,
641 specificationVendor,
642 implementationTitle,
643 implementationVersion,
644 implementationVendor,
645 sealBase);
646 } catch (IllegalAccessException exception) {
647 throw new IllegalStateException("Could not access java.lang.ClassLoader#definePackage", exception);
648 } catch (InvocationTargetException exception) {
649 throw new IllegalStateException("Error invoking java.lang.ClassLoader#definePackage", exception.getCause());
650 }
651 }
652
653 /**
654 * A resolved class dispatcher for a class injector on a VM running at least Java 7.
655 */
656 @HashCodeAndEqualsPlugin.Enhance
657 protected static class ForJava7CapableVm extends Direct {
658
659 /**
660 * An instance of {@code ClassLoader#getClassLoadingLock(String)}.
661 */
662 private final Method getClassLoadingLock;
663
664 /**
665 * Creates a new resolved reflection store for a VM running at least Java 7.
666 *
667 * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
668 * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
669 * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
670 * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
671 * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
672 */
673 protected ForJava7CapableVm(Method findLoadedClass,
674 Method defineClass,
675 Method getPackage,
676 Method definePackage,
677 Method getClassLoadingLock) {
678 super(findLoadedClass, defineClass, getPackage, definePackage);
679 this.getClassLoadingLock = getClassLoadingLock;
680 }
681
682 /**
683 * {@inheritDoc}
684 */
685 public Object getClassLoadingLock(ClassLoader classLoader, String name) {
686 try {
687 return getClassLoadingLock.invoke(classLoader, name);
688 } catch (IllegalAccessException exception) {
689 throw new IllegalStateException("Could not access java.lang.ClassLoader#getClassLoadingLock", exception);
690 } catch (InvocationTargetException exception) {
691 throw new IllegalStateException("Error invoking java.lang.ClassLoader#getClassLoadingLock", exception.getCause());
692 }
693 }
694 }
695
696 /**
697 * A resolved class dispatcher for a class injector prior to Java 7.
698 */
699 protected static class ForLegacyVm extends Direct {
700
701 /**
702 * Creates a new resolved reflection store for a VM prior to Java 8.
703 *
704 * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
705 * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
706 * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
707 * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
708 */
709 protected ForLegacyVm(Method findLoadedClass,
710 Method defineClass,
711 Method getPackage,
712 Method definePackage) {
713 super(findLoadedClass, defineClass, getPackage, definePackage);
714 }
715
716 /**
717 * {@inheritDoc}
718 */
719 public Object getClassLoadingLock(ClassLoader classLoader, String name) {
720 return classLoader;
721 }
722 }
723 }
724
725 /**
726 * An indirect dispatcher that uses a redirection accessor class that was injected into the bootstrap class loader.
727 */
728 @HashCodeAndEqualsPlugin.Enhance
729 class UsingUnsafeInjection implements Dispatcher, Initializable {
730
731 /**
732 * An instance of the accessor class that is required for using it's intentionally non-static methods.
733 */
734 private final Object accessor;
735
736 /**
737 * The accessor method for using {@link ClassLoader#findLoadedClass(String)}.
738 */
739 private final Method findLoadedClass;
740
741 /**
742 * The accessor method for using {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
743 */
744 private final Method defineClass;
745
746 /**
747 * The accessor method for using {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
748 */
749 private final Method getPackage;
750
751 /**
752 * The accessor method for using {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
753 */
754 private final Method definePackage;
755
756 /**
757 * The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the supplied {@link ClassLoader}
758 * if this method does not exist on the current VM.
759 */
760 private final Method getClassLoadingLock;
761
762 /**
763 * Creates a new class loading injection dispatcher using an unsafe injected dispatcher.
764 *
765 * @param accessor An instance of the accessor class that is required for using it's intentionally non-static methods.
766 * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
767 * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
768 * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
769 * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
770 * @param getClassLoadingLock The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the
771 * supplied {@link ClassLoader} if this method does not exist on the current VM.
772 */
773 protected UsingUnsafeInjection(Object accessor,
774 Method findLoadedClass,
775 Method defineClass,
776 Method getPackage,
777 Method definePackage,
778 Method getClassLoadingLock) {
779 this.accessor = accessor;
780 this.findLoadedClass = findLoadedClass;
781 this.defineClass = defineClass;
782 this.getPackage = getPackage;
783 this.definePackage = definePackage;
784 this.getClassLoadingLock = getClassLoadingLock;
785 }
786
787 /**
788 * Creates an indirect dispatcher.
789 *
790 * @return An indirect dispatcher for class creation.
791 * @throws Exception If the dispatcher cannot be created.
792 */
793 @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility")
794 protected static Initializable make() throws Exception {
795 if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) {
796 return new Initializable.Unavailable("Use of Unsafe was disabled by system property");
797 }
798 Class<?> unsafe = Class.forName("sun.misc.Unsafe");
799 Field theUnsafe = unsafe.getDeclaredField("theUnsafe");
800 theUnsafe.setAccessible(true);
801 Object unsafeInstance = theUnsafe.get(null);
802 Method getPackage;
803 if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
804 try {
805 getPackage = ClassLoader.class.getDeclaredMethod("getDefinedPackage", String.class);
806 } catch (NoSuchMethodException ignored) {
807 getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
808 }
809 } else {
810 getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
811 }
812 getPackage.setAccessible(true);
813 DynamicType.Builder<?> builder = new ByteBuddy()
814 .with(TypeValidation.DISABLED)
815 .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
816 .name(ClassLoader.class.getName() + "$ByteBuddyAccessor$" + RandomString.make())
817 .defineMethod("findLoadedClass", Class.class, Visibility.PUBLIC)
818 .withParameters(ClassLoader.class, String.class)
819 .intercept(MethodCall.invoke(ClassLoader.class
820 .getDeclaredMethod("findLoadedClass", String.class))
821 .onArgument(0)
822 .withArgument(1))
823 .defineMethod("defineClass", Class.class, Visibility.PUBLIC)
824 .withParameters(ClassLoader.class, String.class, byte[].class, int.class, int.class,
825 ProtectionDomain.class)
826 .intercept(MethodCall.invoke(ClassLoader.class
827 .getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class))
828 .onArgument(0)
829 .withArgument(1, 2, 3, 4, 5))
830 .defineMethod("getPackage", Package.class, Visibility.PUBLIC)
831 .withParameters(ClassLoader.class, String.class)
832 .intercept(MethodCall.invoke(getPackage)
833 .onArgument(0)
834 .withArgument(1))
835 .defineMethod("definePackage", Package.class, Visibility.PUBLIC)
836 .withParameters(ClassLoader.class, String.class, String.class, String.class, String.class,
837 String.class, String.class, String.class, URL.class)
838 .intercept(MethodCall.invoke(ClassLoader.class
839 .getDeclaredMethod("definePackage", String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class))
840 .onArgument(0)
841 .withArgument(1, 2, 3, 4, 5, 6, 7, 8));
842 try {
843 builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC)
844 .withParameters(ClassLoader.class, String.class)
845 .intercept(MethodCall.invoke(ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class))
846 .onArgument(0)
847 .withArgument(1));
848 } catch (NoSuchMethodException ignored) {
849 builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC)
850 .withParameters(ClassLoader.class, String.class)
851 .intercept(FixedValue.argument(0));
852 }
853 Class<?> type = builder.make()
854 .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, new ClassLoadingStrategy.ForUnsafeInjection())
855 .getLoaded();
856 return new UsingUnsafeInjection(
857 unsafe.getMethod("allocateInstance", Class.class).invoke(unsafeInstance, type),
858 type.getMethod("findLoadedClass", ClassLoader.class, String.class),
859 type.getMethod("defineClass", ClassLoader.class, String.class, byte[].class, int.class, int.class, ProtectionDomain.class),
860 type.getMethod("getPackage", ClassLoader.class, String.class),
861 type.getMethod("definePackage", ClassLoader.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class),
862 type.getMethod("getClassLoadingLock", ClassLoader.class, String.class));
863 }
864
865 /**
866 * {@inheritDoc}
867 */
868 public boolean isAvailable() {
869 return true;
870 }
871
872 /**
873 * {@inheritDoc}
874 */
875 public Dispatcher initialize() {
876 SecurityManager securityManager = System.getSecurityManager();
877 if (securityManager != null) {
878 try {
879 securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
880 } catch (Exception exception) {
881 return new Dispatcher.Unavailable(exception.getMessage());
882 }
883 }
884 return this;
885 }
886
887 /**
888 * {@inheritDoc}
889 */
890 public Object getClassLoadingLock(ClassLoader classLoader, String name) {
891 try {
892 return getClassLoadingLock.invoke(accessor, classLoader, name);
893 } catch (IllegalAccessException exception) {
894 throw new IllegalStateException("Could not access (accessor)::getClassLoadingLock", exception);
895 } catch (InvocationTargetException exception) {
896 throw new IllegalStateException("Error invoking (accessor)::getClassLoadingLock", exception.getCause());
897 }
898 }
899
900 /**
901 * {@inheritDoc}
902 */
903 public Class<?> findClass(ClassLoader classLoader, String name) {
904 try {
905 return (Class<?>) findLoadedClass.invoke(accessor, classLoader, name);
906 } catch (IllegalAccessException exception) {
907 throw new IllegalStateException("Could not access (accessor)::findLoadedClass", exception);
908 } catch (InvocationTargetException exception) {
909 throw new IllegalStateException("Error invoking (accessor)::findLoadedClass", exception.getCause());
910 }
911 }
912
913 /**
914 * {@inheritDoc}
915 */
916 public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
917 try {
918 return (Class<?>) defineClass.invoke(accessor, classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
919 } catch (IllegalAccessException exception) {
920 throw new IllegalStateException("Could not access (accessor)::defineClass", exception);
921 } catch (InvocationTargetException exception) {
922 throw new IllegalStateException("Error invoking (accessor)::defineClass", exception.getCause());
923 }
924 }
925
926 /**
927 * {@inheritDoc}
928 */
929 public Package getPackage(ClassLoader classLoader, String name) {
930 try {
931 return (Package) getPackage.invoke(accessor, classLoader, name);
932 } catch (IllegalAccessException exception) {
933 throw new IllegalStateException("Could not access (accessor)::getPackage", exception);
934 } catch (InvocationTargetException exception) {
935 throw new IllegalStateException("Error invoking (accessor)::getPackage", exception.getCause());
936 }
937 }
938
939 /**
940 * {@inheritDoc}
941 */
942 public Package definePackage(ClassLoader classLoader,
943 String name,
944 String specificationTitle,
945 String specificationVersion,
946 String specificationVendor,
947 String implementationTitle,
948 String implementationVersion,
949 String implementationVendor,
950 URL sealBase) {
951 try {
952 return (Package) definePackage.invoke(accessor,
953 classLoader,
954 name,
955 specificationTitle,
956 specificationVersion,
957 specificationVendor,
958 implementationTitle,
959 implementationVersion,
960 implementationVendor,
961 sealBase);
962 } catch (IllegalAccessException exception) {
963 throw new IllegalStateException("Could not access (accessor)::definePackage", exception);
964 } catch (InvocationTargetException exception) {
965 throw new IllegalStateException("Error invoking (accessor)::definePackage", exception.getCause());
966 }
967 }
968 }
969
970 /**
971 * A dispatcher implementation that uses {@code sun.misc.Unsafe#putBoolean} to set the {@link AccessibleObject} field
972 * for making methods accessible.
973 */
974 abstract class UsingUnsafeOverride implements Dispatcher, Initializable {
975
976 /**
977 * An instance of {@link ClassLoader#findLoadedClass(String)}.
978 */
979 protected final Method findLoadedClass;
980
981 /**
982 * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
983 */
984 protected final Method defineClass;
985
986 /**
987 * An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
988 */
989 protected final Method getPackage;
990
991 /**
992 * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
993 */
994 protected final Method definePackage;
995
996 /**
997 * Creates a new unsafe field injecting injection dispatcher.
998 *
999 * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
1000 * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1001 * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
1002 * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1003 */
1004 protected UsingUnsafeOverride(Method findLoadedClass,
1005 Method defineClass,
1006 Method getPackage,
1007 Method definePackage) {
1008 this.findLoadedClass = findLoadedClass;
1009 this.defineClass = defineClass;
1010 this.getPackage = getPackage;
1011 this.definePackage = definePackage;
1012 }
1013
1014 /**
1015 * Creates a new initializable class injector using an unsafe field injection.
1016 *
1017 * @return An appropriate initializable.
1018 * @throws Exception If the injector cannot be created.
1019 */
1020 @SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility")
1021 protected static Initializable make() throws Exception {
1022 if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) {
1023 return new Initializable.Unavailable("Use of Unsafe was disabled by system property");
1024 }
1025 Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
1026 Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
1027 theUnsafe.setAccessible(true);
1028 Object unsafe = theUnsafe.get(null);
1029 Field override;
1030 try {
1031 override = AccessibleObject.class.getDeclaredField("override");
1032 } catch (NoSuchFieldException ignored) {
1033 // Since Java 12, the override field is hidden from the reflection API. To circumvent this, we
1034 // create a mirror class of AccessibleObject that defines the same fields and has the same field
1035 // layout such that the override field will receive the same class offset. Doing so, we can write to
1036 // the offset location and still set a value to it, despite it being hidden from the reflection API.
1037 override = new ByteBuddy()
1038 .redefine(AccessibleObject.class)
1039 .name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName())
1040 .noNestMate()
1041 .visit(new MemberRemoval().stripInvokables(any()))
1042 .make()
1043 .load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
1044 .getLoaded()
1045 .getDeclaredField("override");
1046 }
1047 long offset = (Long) unsafeType
1048 .getMethod("objectFieldOffset", Field.class)
1049 .invoke(unsafe, override);
1050 Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class);
1051 Method getPackage;
1052 if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
1053 try {
1054 getPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class);
1055 } catch (NoSuchMethodException ignored) {
1056 getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
1057 putBoolean.invoke(unsafe, getPackage, offset, true);
1058 }
1059 } else {
1060 getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
1061 putBoolean.invoke(unsafe, getPackage, offset, true);
1062 }
1063 Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
1064 Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
1065 String.class,
1066 byte[].class,
1067 int.class,
1068 int.class,
1069 ProtectionDomain.class);
1070 Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage",
1071 String.class,
1072 String.class,
1073 String.class,
1074 String.class,
1075 String.class,
1076 String.class,
1077 String.class,
1078 URL.class);
1079 putBoolean.invoke(unsafe, defineClass, offset, true);
1080 putBoolean.invoke(unsafe, findLoadedClass, offset, true);
1081 putBoolean.invoke(unsafe, definePackage, offset, true);
1082 try {
1083 Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
1084 putBoolean.invoke(unsafe, getClassLoadingLock, offset, true);
1085 return new ForJava7CapableVm(findLoadedClass,
1086 defineClass,
1087 getPackage,
1088 definePackage,
1089 getClassLoadingLock);
1090 } catch (NoSuchMethodException ignored) {
1091 return new ForLegacyVm(findLoadedClass, defineClass, getPackage, definePackage);
1092 }
1093 }
1094
1095 /**
1096 * {@inheritDoc}
1097 */
1098 public boolean isAvailable() {
1099 return true;
1100 }
1101
1102 /**
1103 * {@inheritDoc}
1104 */
1105 public Dispatcher initialize() {
1106 SecurityManager securityManager = System.getSecurityManager();
1107 if (securityManager != null) {
1108 try {
1109 securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
1110 } catch (Exception exception) {
1111 return new Dispatcher.Unavailable(exception.getMessage());
1112 }
1113 }
1114 return this;
1115 }
1116
1117 /**
1118 * {@inheritDoc}
1119 */
1120 public Class<?> findClass(ClassLoader classLoader, String name) {
1121 try {
1122 return (Class<?>) findLoadedClass.invoke(classLoader, name);
1123 } catch (IllegalAccessException exception) {
1124 throw new IllegalStateException("Could not access java.lang.ClassLoader#findClass", exception);
1125 } catch (InvocationTargetException exception) {
1126 throw new IllegalStateException("Error invoking java.lang.ClassLoader#findClass", exception.getCause());
1127 }
1128 }
1129
1130 /**
1131 * {@inheritDoc}
1132 */
1133 public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
1134 try {
1135 return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
1136 } catch (IllegalAccessException exception) {
1137 throw new IllegalStateException("Could not access java.lang.ClassLoader#defineClass", exception);
1138 } catch (InvocationTargetException exception) {
1139 throw new IllegalStateException("Error invoking java.lang.ClassLoader#defineClass", exception.getCause());
1140 }
1141 }
1142
1143 /**
1144 * {@inheritDoc}
1145 */
1146 public Package getPackage(ClassLoader classLoader, String name) {
1147 try {
1148 return (Package) getPackage.invoke(classLoader, name);
1149 } catch (IllegalAccessException exception) {
1150 throw new IllegalStateException("Could not access java.lang.ClassLoader#getPackage", exception);
1151 } catch (InvocationTargetException exception) {
1152 throw new IllegalStateException("Error invoking java.lang.ClassLoader#getPackage", exception.getCause());
1153 }
1154 }
1155
1156 /**
1157 * {@inheritDoc}
1158 */
1159 public Package definePackage(ClassLoader classLoader,
1160 String name,
1161 String specificationTitle,
1162 String specificationVersion,
1163 String specificationVendor,
1164 String implementationTitle,
1165 String implementationVersion,
1166 String implementationVendor,
1167 URL sealBase) {
1168 try {
1169 return (Package) definePackage.invoke(classLoader,
1170 name,
1171 specificationTitle,
1172 specificationVersion,
1173 specificationVendor,
1174 implementationTitle,
1175 implementationVersion,
1176 implementationVendor,
1177 sealBase);
1178 } catch (IllegalAccessException exception) {
1179 throw new IllegalStateException("Could not access java.lang.ClassLoader#definePackage", exception);
1180 } catch (InvocationTargetException exception) {
1181 throw new IllegalStateException("Error invoking java.lang.ClassLoader#definePackage", exception.getCause());
1182 }
1183 }
1184
1185 /**
1186 * A resolved class dispatcher using unsafe field injection for a class injector on a VM running at least Java 7.
1187 */
1188 @HashCodeAndEqualsPlugin.Enhance
1189 protected static class ForJava7CapableVm extends UsingUnsafeOverride {
1190
1191 /**
1192 * An instance of {@code ClassLoader#getClassLoadingLock(String)}.
1193 */
1194 private final Method getClassLoadingLock;
1195
1196 /**
1197 * Creates a new resolved class injector using unsafe field injection for a VM running at least Java 7.
1198 *
1199 * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
1200 * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
1201 * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1202 * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
1203 * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1204 */
1205 protected ForJava7CapableVm(Method findLoadedClass,
1206 Method defineClass,
1207 Method getPackage,
1208 Method definePackage,
1209 Method getClassLoadingLock) {
1210 super(findLoadedClass, defineClass, getPackage, definePackage);
1211 this.getClassLoadingLock = getClassLoadingLock;
1212 }
1213
1214 /**
1215 * {@inheritDoc}
1216 */
1217 public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1218 try {
1219 return getClassLoadingLock.invoke(classLoader, name);
1220 } catch (IllegalAccessException exception) {
1221 throw new IllegalStateException("Could not access java.lang.ClassLoader#getClassLoadingLock", exception);
1222 } catch (InvocationTargetException exception) {
1223 throw new IllegalStateException("Error invoking java.lang.ClassLoader#getClassLoadingLock", exception.getCause());
1224 }
1225 }
1226 }
1227
1228 /**
1229 * A resolved class dispatcher using unsafe field injection for a class injector prior to Java 7.
1230 */
1231 protected static class ForLegacyVm extends UsingUnsafeOverride {
1232
1233 /**
1234 * Creates a new resolved class injector using unsafe field injection for a VM prior to Java 7.
1235 *
1236 * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
1237 * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
1238 * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
1239 * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
1240 */
1241 protected ForLegacyVm(Method findLoadedClass,
1242 Method defineClass,
1243 Method getPackage,
1244 Method definePackage) {
1245 super(findLoadedClass, defineClass, getPackage, definePackage);
1246 }
1247
1248 /**
1249 * {@inheritDoc}
1250 */
1251 public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1252 return classLoader;
1253 }
1254 }
1255 }
1256
1257 /**
1258 * Represents an unsuccessfully loaded method lookup.
1259 */
1260 @HashCodeAndEqualsPlugin.Enhance
1261 class Unavailable implements Dispatcher {
1262
1263 /**
1264 * The error message being displayed.
1265 */
1266 private final String message;
1267
1268 /**
1269 * Creates a dispatcher for a VM that does not support reflective injection.
1270 *
1271 * @param message The error message being displayed.
1272 */
1273 protected Unavailable(String message) {
1274 this.message = message;
1275 }
1276
1277 /**
1278 * {@inheritDoc}
1279 */
1280 public Object getClassLoadingLock(ClassLoader classLoader, String name) {
1281 return classLoader;
1282 }
1283
1284 /**
1285 * {@inheritDoc}
1286 */
1287 public Class<?> findClass(ClassLoader classLoader, String name) {
1288 try {
1289 return classLoader.loadClass(name);
1290 } catch (ClassNotFoundException ignored) {
1291 return UNDEFINED;
1292 }
1293 }
1294
1295 /**
1296 * {@inheritDoc}
1297 */
1298 public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
1299 throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
1300 }
1301
1302 /**
1303 * {@inheritDoc}
1304 */
1305 public Package getPackage(ClassLoader classLoader, String name) {
1306 throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
1307 }
1308
1309 /**
1310 * {@inheritDoc}
1311 */
1312 public Package definePackage(ClassLoader classLoader,
1313 String name,
1314 String specificationTitle,
1315 String specificationVersion,
1316 String specificationVendor,
1317 String implementationTitle,
1318 String implementationVersion,
1319 String implementationVendor,
1320 URL sealBase) {
1321 throw new UnsupportedOperationException("Cannot define package using injection: " + message);
1322 }
1323 }
1324 }
1325 }
1326
1327 /**
1328 * <p>
1329 * A class injector that uses a {@code java.lang.invoke.MethodHandles$Lookup} object for defining a class.
1330 * </p>
1331 * <p>
1332 * <b>Important</b>: This functionality is only available starting from Java 9.
1333 * </p>
1334 */
1335 @HashCodeAndEqualsPlugin.Enhance
1336 class UsingLookup extends AbstractBase {
1337
1338 /**
1339 * The dispatcher to interacting with method handles.
1340 */
1341 private static final Dispatcher DISPATCHER = AccessController.doPrivileged(Dispatcher.Creator.INSTANCE);
1342
1343 /**
1344 * Indicates a lookup instance's package lookup mode.
1345 */
1346 private static final int PACKAGE_LOOKUP = 0x8;
1347
1348 /**
1349 * The {@code java.lang.invoke.MethodHandles$Lookup} to use.
1350 */
1351 private final Object lookup;
1352
1353 /**
1354 * Creates a new class injector using a lookup instance.
1355 *
1356 * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
1357 */
1358 protected UsingLookup(Object lookup) {
1359 this.lookup = lookup;
1360 }
1361
1362 /**
1363 * Creates class injector that defines a class using a method handle lookup.
1364 *
1365 * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
1366 * @return An appropriate class injector.
1367 */
1368 public static UsingLookup of(Object lookup) {
1369 if (!DISPATCHER.isAlive()) {
1370 throw new IllegalStateException("The current VM does not support class definition via method handle lookups");
1371 } else if (!JavaType.METHOD_HANDLES_LOOKUP.isInstance(lookup)) {
1372 throw new IllegalArgumentException("Not a method handle lookup: " + lookup);
1373 } else if ((DISPATCHER.lookupModes(lookup) & PACKAGE_LOOKUP) == 0) {
1374 throw new IllegalArgumentException("Lookup does not imply package-access: " + lookup);
1375 }
1376 return new UsingLookup(lookup);
1377 }
1378
1379 /**
1380 * Returns the lookup type this injector is based upon.
1381 *
1382 * @return The lookup type.
1383 */
1384 public Class<?> lookupType() {
1385 return DISPATCHER.lookupType(lookup);
1386 }
1387
1388 /**
1389 * Resolves this injector to use the supplied type's scope.
1390 *
1391 * @param type The type to resolve the access scope for.
1392 * @return An new injector with the specified scope.
1393 */
1394 public UsingLookup in(Class<?> type) {
1395 return new UsingLookup(DISPATCHER.resolve(lookup, type));
1396 }
1397
1398 /**
1399 * {@inheritDoc}
1400 */
1401 public boolean isAlive() {
1402 return isAvailable();
1403 }
1404
1405 /**
1406 * {@inheritDoc}
1407 */
1408 public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
1409 String expectedPackage = TypeDescription.ForLoadedType.of(lookupType()).getPackage().getName();
1410 Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1411 for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
1412 int index = entry.getKey().lastIndexOf('.');
1413 if (!expectedPackage.equals(index == -1 ? "" : entry.getKey().substring(0, index))) {
1414 throw new IllegalArgumentException(entry.getKey() + " must be defined in the same package as " + lookup);
1415 }
1416 result.put(entry.getKey(), DISPATCHER.defineClass(lookup, entry.getValue()));
1417 }
1418 return result;
1419 }
1420
1421 /**
1422 * Checks if the current VM is capable of defining classes using a method handle lookup.
1423 *
1424 * @return {@code true} if the current VM is capable of defining classes using a lookup.
1425 */
1426 public static boolean isAvailable() {
1427 return DISPATCHER.isAlive();
1428 }
1429
1430 /**
1431 * A dispatcher for interacting with a method handle lookup.
1432 */
1433 protected interface Dispatcher {
1434
1435 /**
1436 * Indicates if this dispatcher is available on the current VM.
1437 *
1438 * @return {@code true} if this dispatcher is alive.
1439 */
1440 boolean isAlive();
1441
1442 /**
1443 * Returns the lookup type for a given method handle lookup.
1444 *
1445 * @param lookup The lookup instance.
1446 * @return The lookup type.
1447 */
1448 Class<?> lookupType(Object lookup);
1449
1450 /**
1451 * Returns a lookup objects lookup types.
1452 *
1453 * @param lookup The lookup instance.
1454 * @return The modifiers indicating the instance's lookup modes.
1455 */
1456 int lookupModes(Object lookup);
1457
1458 /**
1459 * Resolves the supplied lookup instance's access scope for the supplied type.
1460 *
1461 * @param lookup The lookup to use.
1462 * @param type The type to resolve the scope for.
1463 * @return An appropriate lookup instance.
1464 */
1465 Object resolve(Object lookup, Class<?> type);
1466
1467 /**
1468 * Defines a class.
1469 *
1470 * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
1471 * @param binaryRepresentation The defined class's binary representation.
1472 * @return The defined class.
1473 */
1474 Class<?> defineClass(Object lookup, byte[] binaryRepresentation);
1475
1476 /**
1477 * An action for defining a dispatcher.
1478 */
1479 enum Creator implements PrivilegedAction<Dispatcher> {
1480
1481 /**
1482 * The singleton instance.
1483 */
1484 INSTANCE;
1485
1486 /**
1487 * {@inheritDoc}
1488 */
1489 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
1490 public Dispatcher run() {
1491 try {
1492 Class<?> lookup = JavaType.METHOD_HANDLES_LOOKUP.load();
1493 return new Dispatcher.ForJava9CapableVm(JavaType.METHOD_HANDLES.load().getMethod("privateLookupIn", Class.class, lookup),
1494 lookup.getMethod("lookupClass"),
1495 lookup.getMethod("lookupModes"),
1496 lookup.getMethod("defineClass", byte[].class));
1497 } catch (Exception ignored) {
1498 return Dispatcher.ForLegacyVm.INSTANCE;
1499 }
1500 }
1501 }
1502
1503 /**
1504 * A dispatcher for a legacy VM that does not support class definition via method handles.
1505 */
1506 enum ForLegacyVm implements Dispatcher {
1507
1508 /**
1509 * The singleton instance.
1510 */
1511 INSTANCE;
1512
1513 /**
1514 * {@inheritDoc}
1515 */
1516 public boolean isAlive() {
1517 return false;
1518 }
1519
1520 /**
1521 * {@inheritDoc}
1522 */
1523 public Class<?> lookupType(Object lookup) {
1524 throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup");
1525 }
1526
1527 /**
1528 * {@inheritDoc}
1529 */
1530 public int lookupModes(Object lookup) {
1531 throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup");
1532 }
1533
1534 /**
1535 * {@inheritDoc}
1536 */
1537 public Object resolve(Object lookup, Class<?> type) {
1538 throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles");
1539 }
1540
1541 /**
1542 * {@inheritDoc}
1543 */
1544 public Class<?> defineClass(Object lookup, byte[] binaryRepresentation) {
1545 throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup");
1546 }
1547 }
1548
1549 /**
1550 * A dispatcher for a Java 9 capable VM that supports class definition via method handles.
1551 */
1552 @HashCodeAndEqualsPlugin.Enhance
1553 class ForJava9CapableVm implements Dispatcher {
1554
1555 /**
1556 * An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
1557 */
1558 private static final Object[] NO_ARGUMENTS = new Object[0];
1559
1560 /**
1561 * The {@code java.lang.invoke.MethodHandles$#privateLookupIn} method.
1562 */
1563 private final Method privateLookupIn;
1564
1565 /**
1566 * The {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
1567 */
1568 private final Method lookupClass;
1569
1570 /**
1571 * The {@code java.lang.invoke.MethodHandles$Lookup#lookupModes} method.
1572 */
1573 private final Method lookupModes;
1574
1575 /**
1576 * The {@code java.lang.invoke.MethodHandles$Lookup#defineClass} method.
1577 */
1578 private final Method defineClass;
1579
1580 /**
1581 * Creates a new dispatcher for a Java 9 capable VM.
1582 *
1583 * @param privateLookupIn The {@code java.lang.invoke.MethodHandles$#privateLookupIn} method.
1584 * @param lookupClass The {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
1585 * @param lookupModes The {@code java.lang.invoke.MethodHandles$Lookup#lookupModes} method.
1586 * @param defineClass The {@code java.lang.invoke.MethodHandles$Lookup#defineClass} method.
1587 */
1588 protected ForJava9CapableVm(Method privateLookupIn, Method lookupClass, Method lookupModes, Method defineClass) {
1589 this.privateLookupIn = privateLookupIn;
1590 this.lookupClass = lookupClass;
1591 this.lookupModes = lookupModes;
1592 this.defineClass = defineClass;
1593 }
1594
1595 /**
1596 * {@inheritDoc}
1597 */
1598 public boolean isAlive() {
1599 return true;
1600 }
1601
1602 /**
1603 * {@inheritDoc}
1604 */
1605 public Class<?> lookupType(Object lookup) {
1606 try {
1607 return (Class<?>) lookupClass.invoke(lookup, NO_ARGUMENTS);
1608 } catch (IllegalAccessException exception) {
1609 throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupClass", exception);
1610 } catch (InvocationTargetException exception) {
1611 throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupClass", exception.getCause());
1612 }
1613 }
1614
1615 /**
1616 * {@inheritDoc}
1617 */
1618 public int lookupModes(Object lookup) {
1619 try {
1620 return (Integer) lookupModes.invoke(lookup, NO_ARGUMENTS);
1621 } catch (IllegalAccessException exception) {
1622 throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupModes", exception);
1623 } catch (InvocationTargetException exception) {
1624 throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupModes", exception.getCause());
1625 }
1626 }
1627
1628 /**
1629 * {@inheritDoc}
1630 */
1631 public Object resolve(Object lookup, Class<?> type) {
1632 try {
1633 return privateLookupIn.invoke(null, type, lookup);
1634 } catch (IllegalAccessException exception) {
1635 throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles#privateLookupIn", exception);
1636 } catch (InvocationTargetException exception) {
1637 throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles#privateLookupIn", exception.getCause());
1638 }
1639 }
1640
1641 /**
1642 * {@inheritDoc}
1643 */
1644 public Class<?> defineClass(Object lookup, byte[] binaryRepresentation) {
1645 try {
1646 return (Class<?>) defineClass.invoke(lookup, (Object) binaryRepresentation);
1647 } catch (IllegalAccessException exception) {
1648 throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#defineClass", exception);
1649 } catch (InvocationTargetException exception) {
1650 throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#defineClass", exception.getCause());
1651 }
1652 }
1653 }
1654 }
1655 }
1656
1657 /**
1658 * A class injector that uses {@code sun.misc.Unsafe} to inject classes.
1659 */
1660 @HashCodeAndEqualsPlugin.Enhance
1661 class UsingUnsafe extends AbstractBase {
1662
1663 /**
1664 * If this property is set, Byte Buddy does not make use of any {@code Unsafe} class.
1665 */
1666 public static final String SAFE_PROPERTY = "net.bytebuddy.safe";
1667
1668 /**
1669 * The dispatcher to use.
1670 */
1671 private static final Dispatcher.Initializable DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
1672
1673 /**
1674 * A lock for the bootstrap loader when injecting code.
1675 */
1676 private static final Object BOOTSTRAP_LOADER_LOCK = new Object();
1677
1678 /**
1679 * The class loader to inject classes into or {@code null} for the bootstrap loader.
1680 */
1681 @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1682 private final ClassLoader classLoader;
1683
1684 /**
1685 * The protection domain to use or {@code null} for no protection domain.
1686 */
1687 @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
1688 private final ProtectionDomain protectionDomain;
1689
1690 /**
1691 * The dispatcher to use.
1692 */
1693 private final Dispatcher.Initializable dispatcher;
1694
1695 /**
1696 * Creates a new unsafe injector for the given class loader with a default protection domain.
1697 *
1698 * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
1699 */
1700 public UsingUnsafe(ClassLoader classLoader) {
1701 this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
1702 }
1703
1704 /**
1705 * Creates a new unsafe injector for the given class loader with a default protection domain.
1706 *
1707 * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
1708 * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
1709 */
1710 public UsingUnsafe(ClassLoader classLoader, ProtectionDomain protectionDomain) {
1711 this(classLoader, protectionDomain, DISPATCHER);
1712 }
1713
1714 /**
1715 * Creates a new unsafe injector for the given class loader with a default protection domain.
1716 *
1717 * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
1718 * @param protectionDomain The protection domain to use or {@code null} for no protection domain.
1719 * @param dispatcher The dispatcher to use.
1720 */
1721 protected UsingUnsafe(ClassLoader classLoader, ProtectionDomain protectionDomain, Dispatcher.Initializable dispatcher) {
1722 this.classLoader = classLoader;
1723 this.protectionDomain = protectionDomain;
1724 this.dispatcher = dispatcher;
1725 }
1726
1727 /**
1728 * {@inheritDoc}
1729 */
1730 public boolean isAlive() {
1731 return dispatcher.isAvailable();
1732 }
1733
1734 /**
1735 * {@inheritDoc}
1736 */
1737 public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
1738 Dispatcher dispatcher = this.dispatcher.initialize();
1739 Map<String, Class<?>> result = new HashMap<String, Class<?>>();
1740 synchronized (classLoader == null
1741 ? BOOTSTRAP_LOADER_LOCK
1742 : classLoader) {
1743 for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
1744 try {
1745 result.put(entry.getKey(), Class.forName(entry.getKey(), false, classLoader));
1746 } catch (ClassNotFoundException ignored) {
1747 result.put(entry.getKey(), dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain));
1748 }
1749 }
1750 }
1751 return result;
1752 }
1753
1754 /**
1755 * Checks if unsafe class injection is available on the current VM.
1756 *
1757 * @return {@code true} if unsafe class injection is available on the current VM.
1758 */
1759 public static boolean isAvailable() {
1760 return DISPATCHER.isAvailable();
1761 }
1762
1763 /**
1764 * Returns an unsafe class injector for the system class loader.
1765 *
1766 * @return A class injector for the system class loader.
1767 */
1768 public static ClassInjector ofSystemLoader() {
1769 return new UsingUnsafe(ClassLoader.getSystemClassLoader());
1770 }
1771
1772 /**
1773 * Returns an unsafe class injector for the platform class loader. For VMs of version 8 or older,
1774 * the extension class loader is represented instead.
1775 *
1776 * @return A class injector for the platform class loader.
1777 */
1778 public static ClassInjector ofPlatformLoader() {
1779 return new UsingUnsafe(ClassLoader.getSystemClassLoader().getParent());
1780 }
1781
1782 /**
1783 * Returns an unsafe class injector for the boot class loader.
1784 *
1785 * @return A class injector for the boot loader.
1786 */
1787 public static ClassInjector ofBootLoader() {
1788 return new UsingUnsafe(ClassLoadingStrategy.BOOTSTRAP_LOADER);
1789 }
1790
1791 /**
1792 * A dispatcher for using {@code sun.misc.Unsafe}.
1793 */
1794 protected interface Dispatcher {
1795
1796 /**
1797 * Defines a class.
1798 *
1799 * @param classLoader The class loader to inject the class into.
1800 * @param name The type's name.
1801 * @param binaryRepresentation The type's binary representation.
1802 * @param protectionDomain The type's protection domain.
1803 * @return The defined class.
1804 */
1805 Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain);
1806
1807 /**
1808 * A class injection dispatcher that is not yet initialized.
1809 */
1810 interface Initializable {
1811
1812 /**
1813 * Checks if unsafe class injection is available on the current VM.
1814 *
1815 * @return {@code true} if unsafe class injection is available.
1816 */
1817 boolean isAvailable();
1818
1819 /**
1820 * Initializes the dispatcher.
1821 *
1822 * @return The initialized dispatcher.
1823 */
1824 Dispatcher initialize();
1825 }
1826
1827 /**
1828 * A privileged action for creating a dispatcher.
1829 */
1830 enum CreationAction implements PrivilegedAction<Initializable> {
1831
1832 /**
1833 * The singleton instance.
1834 */
1835 INSTANCE;
1836
1837 /**
1838 * {@inheritDoc}
1839 */
1840 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
1841 public Initializable run() {
1842 if (Boolean.getBoolean(SAFE_PROPERTY)) {
1843 return new Unavailable("Use of Unsafe was disabled by system property");
1844 }
1845 try {
1846 Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
1847 Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
1848 theUnsafe.setAccessible(true);
1849 Object unsafe = theUnsafe.get(null);
1850 try {
1851 Method defineClass = unsafeType.getMethod("defineClass",
1852 String.class,
1853 byte[].class,
1854 int.class,
1855 int.class,
1856 ClassLoader.class,
1857 ProtectionDomain.class);
1858 defineClass.setAccessible(true);
1859 return new Enabled(unsafe, defineClass);
1860 } catch (Exception exception) {
1861 try {
1862 Field override;
1863 try {
1864 override = AccessibleObject.class.getDeclaredField("override");
1865 } catch (NoSuchFieldException ignored) {
1866 // Since Java 12, the override field is hidden from the reflection API. To circumvent this, we
1867 // create a mirror class of AccessibleObject that defines the same fields and has the same field
1868 // layout such that the override field will receive the same class offset. Doing so, we can write to
1869 // the offset location and still set a value to it, despite it being hidden from the reflection API.
1870 override = new ByteBuddy()
1871 .redefine(AccessibleObject.class)
1872 .name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName())
1873 .noNestMate()
1874 .visit(new MemberRemoval().stripInvokables(any()))
1875 .make()
1876 .load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
1877 .getLoaded()
1878 .getDeclaredField("override");
1879 }
1880 long offset = (Long) unsafeType
1881 .getMethod("objectFieldOffset", Field.class)
1882 .invoke(unsafe, override);
1883 Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class);
1884 Class<?> internalUnsafe = Class.forName("jdk.internal.misc.Unsafe");
1885 Field theUnsafeInternal = internalUnsafe.getDeclaredField("theUnsafe");
1886 putBoolean.invoke(unsafe, theUnsafeInternal, offset, true);
1887 Method defineClassInternal = internalUnsafe.getMethod("defineClass",
1888 String.class,
1889 byte[].class,
1890 int.class,
1891 int.class,
1892 ClassLoader.class,
1893 ProtectionDomain.class);
1894 putBoolean.invoke(unsafe, defineClassInternal, offset, true);
1895 return new Enabled(theUnsafeInternal.get(null), defineClassInternal);
1896 } catch (Exception ignored) {
1897 throw exception;
1898 }
1899 }
1900 } catch (Exception exception) {
1901 return new Unavailable(exception.getMessage());
1902 }
1903 }
1904 }
1905
1906 /**
1907 * An enabled dispatcher.
1908 */
1909 @HashCodeAndEqualsPlugin.Enhance
1910 class Enabled implements Dispatcher, Initializable {
1911
1912 /**
1913 * An instance of {@code sun.misc.Unsafe}.
1914 */
1915 private final Object unsafe;
1916
1917 /**
1918 * The {@code sun.misc.Unsafe#defineClass} method.
1919 */
1920 private final Method defineClass;
1921
1922 /**
1923 * Creates an enabled dispatcher.
1924 *
1925 * @param unsafe An instance of {@code sun.misc.Unsafe}.
1926 * @param defineClass The {@code sun.misc.Unsafe#defineClass} method.
1927 */
1928 protected Enabled(Object unsafe, Method defineClass) {
1929 this.unsafe = unsafe;
1930 this.defineClass = defineClass;
1931 }
1932
1933 /**
1934 * {@inheritDoc}
1935 */
1936 public boolean isAvailable() {
1937 return true;
1938 }
1939
1940 /**
1941 * {@inheritDoc}
1942 */
1943 public Dispatcher initialize() {
1944 SecurityManager securityManager = System.getSecurityManager();
1945 if (securityManager != null) {
1946 try {
1947 securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
1948 } catch (Exception exception) {
1949 return new Unavailable(exception.getMessage());
1950 }
1951 }
1952 return this;
1953 }
1954
1955 /**
1956 * {@inheritDoc}
1957 */
1958 public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
1959 try {
1960 return (Class<?>) defineClass.invoke(unsafe,
1961 name,
1962 binaryRepresentation,
1963 0,
1964 binaryRepresentation.length,
1965 classLoader,
1966 protectionDomain);
1967 } catch (IllegalAccessException exception) {
1968 throw new IllegalStateException("Could not access Unsafe::defineClass", exception);
1969 } catch (InvocationTargetException exception) {
1970 throw new IllegalStateException("Error invoking Unsafe::defineClass", exception.getCause());
1971 }
1972 }
1973 }
1974
1975 /**
1976 * A disabled dispatcher.
1977 */
1978 @HashCodeAndEqualsPlugin.Enhance
1979 class Unavailable implements Dispatcher, Initializable {
1980
1981 /**
1982 * The reason why this dispatcher is not available.
1983 */
1984 private final String message;
1985
1986 /**
1987 * Creates a disabled dispatcher.
1988 *
1989 * @param message The reason why this dispatcher is not available.
1990 */
1991 protected Unavailable(String message) {
1992 this.message = message;
1993 }
1994
1995 /**
1996 * {@inheritDoc}
1997 */
1998 public boolean isAvailable() {
1999 return false;
2000 }
2001
2002 /**
2003 * {@inheritDoc}
2004 */
2005 public Dispatcher initialize() {
2006 throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
2007 }
2008
2009 /**
2010 * {@inheritDoc}
2011 */
2012 public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
2013 throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
2014 }
2015 }
2016 }
2017
2018 /**
2019 * A factory for creating a {@link ClassInjector} that uses {@code sun.misc.Unsafe} if available but attempts a fallback
2020 * to using {@code jdk.internal.misc.Unsafe} if the {@code jdk.internal} module is not resolved or unavailable.
2021 */
2022 @HashCodeAndEqualsPlugin.Enhance
2023 public static class Factory {
2024
2025 /**
2026 * The dispatcher to use.
2027 */
2028 private final Dispatcher.Initializable dispatcher;
2029
2030 /**
2031 * Creates a new factory for an unsafe class injector that uses Byte Buddy's privileges to
2032 * accessing {@code jdk.internal.misc.Unsafe} if available.
2033 */
2034 public Factory() {
2035 this(AccessResolver.Default.INSTANCE);
2036 }
2037
2038 /**
2039 * Creates a new factory for an unsafe class injector.
2040 *
2041 * @param accessResolver The access resolver to use.
2042 */
2043 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception is captured to trigger lazy error upon use.")
2044 public Factory(AccessResolver accessResolver) {
2045 Dispatcher.Initializable dispatcher;
2046 if (DISPATCHER.isAvailable()) {
2047 dispatcher = DISPATCHER;
2048 } else {
2049 try {
2050 Class<?> unsafeType = Class.forName("jdk.internal.misc.Unsafe");
2051 Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
2052 accessResolver.apply(theUnsafe);
2053 Object unsafe = theUnsafe.get(null);
2054 Method defineClass = unsafeType.getMethod("defineClass",
2055 String.class,
2056 byte[].class,
2057 int.class,
2058 int.class,
2059 ClassLoader.class,
2060 ProtectionDomain.class);
2061 accessResolver.apply(defineClass);
2062 dispatcher = new Dispatcher.Enabled(unsafe, defineClass);
2063 } catch (Exception exception) {
2064 dispatcher = new Dispatcher.Unavailable(exception.getMessage());
2065 }
2066 }
2067 this.dispatcher = dispatcher;
2068 }
2069
2070 /**
2071 * Creates a new factory.
2072 *
2073 * @param dispatcher The dispatcher to use.
2074 */
2075 protected Factory(Dispatcher.Initializable dispatcher) {
2076 this.dispatcher = dispatcher;
2077 }
2078
2079 /**
2080 * Resolves an injection strategy that uses unsafe injection if available and also attempts to open and use
2081 * {@code jdk.internal.misc.Unsafe} as a fallback. This method generates a new class and module for opening the
2082 * internal package to avoid its exposure to any non-trusted code.
2083 *
2084 * @param instrumentation The instrumentation instance to use for opening the internal package if required.
2085 * @return An appropriate injection strategy.
2086 */
2087 public static Factory resolve(Instrumentation instrumentation) {
2088 return resolve(instrumentation, false);
2089 }
2090
2091 /**
2092 * Resolves an injection strategy that uses unsafe injection if available and also attempts to open and use
2093 * {@code jdk.internal.misc.Unsafe} as a fallback.
2094 *
2095 * @param instrumentation The instrumentation instance to use for opening the internal package if required.
2096 * @param local {@code false} if a new class should in a separated class loader and module should be created for
2097 * opening the {@code jdk.internal.misc} package. This way, the internal package is not exposed to any
2098 * other classes within this class's module.
2099 * @return An appropriate injection strategy.
2100 */
2101 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception intends to trigger disabled injection strategy.")
2102 public static Factory resolve(Instrumentation instrumentation, boolean local) {
2103 if (ClassInjector.UsingUnsafe.isAvailable() || !JavaModule.isSupported()) {
2104 return new Factory();
2105 } else {
2106 try {
2107 Class<?> type = Class.forName("jdk.internal.misc.Unsafe");
2108 PackageDescription packageDescription = new PackageDescription.ForLoadedPackage(type.getPackage());
2109 JavaModule source = JavaModule.ofType(type), target = JavaModule.ofType(ClassInjector.UsingUnsafe.class);
2110 if (source.isOpened(packageDescription, target)) {
2111 return new Factory();
2112 } else if (local) {
2113 JavaModule module = JavaModule.ofType(AccessResolver.Default.class);
2114 source.modify(instrumentation,
2115 Collections.singleton(module),
2116 Collections.<String, Set<JavaModule>>emptyMap(),
2117 Collections.singletonMap(packageDescription.getName(), Collections.singleton(module)),
2118 Collections.<Class<?>>emptySet(),
2119 Collections.<Class<?>, List<Class<?>>>emptyMap());
2120 return new Factory();
2121 } else {
2122 Class<? extends AccessResolver> resolver = new ByteBuddy()
2123 .subclass(AccessResolver.class)
2124 .method(named("apply"))
2125 .intercept(MethodCall.invoke(AccessibleObject.class.getMethod("setAccessible", boolean.class))
2126 .onArgument(0)
2127 .with(true))
2128 .make()
2129 .load(AccessResolver.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER.with(AccessResolver.class.getProtectionDomain()))
2130 .getLoaded();
2131 JavaModule module = JavaModule.ofType(resolver);
2132 source.modify(instrumentation,
2133 Collections.singleton(module),
2134 Collections.<String, Set<JavaModule>>emptyMap(),
2135 Collections.singletonMap(packageDescription.getName(), Collections.singleton(module)),
2136 Collections.<Class<?>>emptySet(),
2137 Collections.<Class<?>, List<Class<?>>>emptyMap());
2138 return new ClassInjector.UsingUnsafe.Factory(resolver.getConstructor().newInstance());
2139 }
2140 } catch (Exception exception) {
2141 return new Factory(new Dispatcher.Unavailable(exception.getMessage()));
2142 }
2143 }
2144 }
2145
2146 /**
2147 * Returns {@code true} if this factory creates a valid dispatcher.
2148 *
2149 * @return {@code true} if this factory creates a valid dispatcher.
2150 */
2151 public boolean isAvailable() {
2152 return dispatcher.isAvailable();
2153 }
2154
2155 /**
2156 * Creates a new class injector for the given class loader without a {@link ProtectionDomain}.
2157 *
2158 * @param classLoader The class loader to inject into or {@code null} to inject into the bootstrap loader.
2159 * @return An appropriate class injector.
2160 */
2161 public ClassInjector make(ClassLoader classLoader) {
2162 return make(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
2163 }
2164
2165 /**
2166 * Creates a new class injector for the given class loader and protection domain.
2167 *
2168 * @param classLoader The class loader to inject into or {@code null} to inject into the bootstrap loader.
2169 * @param protectionDomain The protection domain to apply or {@code null} if no protection domain should be used.
2170 * @return An appropriate class injector.
2171 */
2172 public ClassInjector make(ClassLoader classLoader, ProtectionDomain protectionDomain) {
2173 return new UsingUnsafe(classLoader, protectionDomain, dispatcher);
2174 }
2175
2176 /**
2177 * An access resolver that invokes {@link AccessibleObject#setAccessible(boolean)} to {@code true} in a given privilege scope.
2178 */
2179 public interface AccessResolver {
2180
2181 /**
2182 * Applies this access resolver.
2183 *
2184 * @param accessibleObject The accessible object to make accessible.
2185 */
2186 void apply(AccessibleObject accessibleObject);
2187
2188 /**
2189 * A default access resolver that uses Byte Buddy's privilege scope.
2190 */
2191 enum Default implements AccessResolver {
2192
2193 /**
2194 * The singleton instance.
2195 */
2196 INSTANCE;
2197
2198 /**
2199 * {@inheritDoc}
2200 */
2201 public void apply(AccessibleObject accessibleObject) {
2202 accessibleObject.setAccessible(true);
2203 }
2204 }
2205 }
2206 }
2207 }
2208
2209 /**
2210 * A class injector using a {@link java.lang.instrument.Instrumentation} to append to either the boot classpath
2211 * or the system class path.
2212 */
2213 @HashCodeAndEqualsPlugin.Enhance
2214 class UsingInstrumentation extends AbstractBase {
2215
2216 /**
2217 * The jar file name extension.
2218 */
2219 private static final String JAR = "jar";
2220
2221 /**
2222 * The class file extension.
2223 */
2224 private static final String CLASS_FILE_EXTENSION = ".class";
2225
2226 /**
2227 * A dispatcher for interacting with the instrumentation API.
2228 */
2229 private static final Dispatcher DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
2230
2231 /**
2232 * The instrumentation to use for appending to the class path or the boot path.
2233 */
2234 private final Instrumentation instrumentation;
2235
2236 /**
2237 * A representation of the target path to which classes are to be appended.
2238 */
2239 private final Target target;
2240
2241 /**
2242 * The folder to be used for storing jar files.
2243 */
2244 private final File folder;
2245
2246 /**
2247 * A random string generator for creating file names.
2248 */
2249 private final RandomString randomString;
2250
2251 /**
2252 * Creates an instrumentation-based class injector.
2253 *
2254 * @param folder The folder to be used for storing jar files.
2255 * @param target A representation of the target path to which classes are to be appended.
2256 * @param instrumentation The instrumentation to use for appending to the class path or the boot path.
2257 * @param randomString The random string generator to use.
2258 */
2259 protected UsingInstrumentation(File folder,
2260 Target target,
2261 Instrumentation instrumentation,
2262 RandomString randomString) {
2263 this.folder = folder;
2264 this.target = target;
2265 this.instrumentation = instrumentation;
2266 this.randomString = randomString;
2267 }
2268
2269 /**
2270 * Creates an instrumentation-based class injector.
2271 *
2272 * @param folder The folder to be used for storing jar files.
2273 * @param target A representation of the target path to which classes are to be appended.
2274 * @param instrumentation The instrumentation to use for appending to the class path or the boot path.
2275 * @return An appropriate class injector that applies instrumentation.
2276 */
2277 public static ClassInjector of(File folder, Target target, Instrumentation instrumentation) {
2278 return new UsingInstrumentation(folder, target, instrumentation, new RandomString());
2279 }
2280
2281 /**
2282 * {@inheritDoc}
2283 */
2284 public boolean isAlive() {
2285 return isAvailable();
2286 }
2287
2288 /**
2289 * {@inheritDoc}
2290 */
2291 public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
2292 File file = new File(folder, JAR + randomString.nextString() + "." + JAR);
2293 try {
2294 if (!file.createNewFile()) {
2295 throw new IllegalStateException("Cannot create file " + file);
2296 }
2297 try {
2298 JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(file));
2299 try {
2300 for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
2301 jarOutputStream.putNextEntry(new JarEntry(entry.getKey().replace('.', '/') + CLASS_FILE_EXTENSION));
2302 jarOutputStream.write(entry.getValue());
2303 }
2304 } finally {
2305 jarOutputStream.close();
2306 }
2307 JarFile jarFile = new JarFile(file, false);
2308 try {
2309 target.inject(instrumentation, jarFile);
2310 } finally {
2311 jarFile.close();
2312 }
2313 Map<String, Class<?>> result = new HashMap<String, Class<?>>();
2314 for (String name : types.keySet()) {
2315 result.put(name, Class.forName(name, false, target.getClassLoader()));
2316 }
2317 return result;
2318 } finally {
2319 if (!file.delete()) {
2320 file.deleteOnExit();
2321 }
2322 }
2323 } catch (IOException exception) {
2324 throw new IllegalStateException("Cannot write jar file to disk", exception);
2325 } catch (ClassNotFoundException exception) {
2326 throw new IllegalStateException("Cannot load injected class", exception);
2327 }
2328 }
2329
2330 /**
2331 * Returns {@code true} if this class injector is available on this VM.
2332 *
2333 * @return {@code true} if this class injector is available on this VM.
2334 */
2335 public static boolean isAvailable() {
2336 return DISPATCHER.isAlive();
2337 }
2338
2339 /**
2340 * A dispatcher to interact with the instrumentation API.
2341 */
2342 protected interface Dispatcher {
2343
2344 /**
2345 * Returns {@code true} if this dispatcher is alive.
2346 *
2347 * @return {@code true} if this dispatcher is alive.
2348 */
2349 boolean isAlive();
2350
2351 /**
2352 * Appends a jar file to the bootstrap class loader.
2353 *
2354 * @param instrumentation The instrumentation instance to interact with.
2355 * @param jarFile The jar file to append.
2356 */
2357 void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
2358
2359 /**
2360 * Appends a jar file to the system class loader.
2361 *
2362 * @param instrumentation The instrumentation instance to interact with.
2363 * @param jarFile The jar file to append.
2364 */
2365 void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
2366
2367 /**
2368 * An action to create a dispatcher for interacting with the instrumentation API.
2369 */
2370 enum CreationAction implements PrivilegedAction<Dispatcher> {
2371
2372 /**
2373 * The singleton instance.
2374 */
2375 INSTANCE;
2376
2377 /**
2378 * {@inheritDoc}
2379 */
2380 public Dispatcher run() {
2381 try {
2382 Class<?> instrumentation = Class.forName("java.lang.instrument.Instrumentation");
2383 return new ForJava6CapableVm(instrumentation.getMethod("appendToBootstrapClassLoaderSearch", JarFile.class),
2384 instrumentation.getMethod("appendToSystemClassLoaderSearch", JarFile.class));
2385 } catch (ClassNotFoundException ignored) {
2386 return ForLegacyVm.INSTANCE;
2387 } catch (NoSuchMethodException ignored) {
2388 return ForLegacyVm.INSTANCE;
2389 }
2390 }
2391 }
2392
2393 /**
2394 * A dispatcher for a legacy VM that is not capable of appending jar files using instrumentation.
2395 */
2396 enum ForLegacyVm implements Dispatcher {
2397
2398 /**
2399 * The singleton instance.
2400 */
2401 INSTANCE;
2402
2403 /**
2404 * {@inheritDoc}
2405 */
2406 public boolean isAlive() {
2407 return false;
2408 }
2409
2410 /**
2411 * {@inheritDoc}
2412 */
2413 public void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
2414 throw new UnsupportedOperationException("The current JVM does not support appending to the bootstrap loader");
2415 }
2416
2417 /**
2418 * {@inheritDoc}
2419 */
2420 public void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
2421 throw new UnsupportedOperationException("The current JVM does not support appending to the system class loader");
2422 }
2423 }
2424
2425 /**
2426 * A dispatcher for a VM that is capable of appending to the boot and system class loader.
2427 */
2428 @HashCodeAndEqualsPlugin.Enhance
2429 class ForJava6CapableVm implements Dispatcher {
2430
2431 /**
2432 * The {@code Instrumentation#appendToBootstrapClassLoaderSearch} method.
2433 */
2434 private final Method appendToBootstrapClassLoaderSearch;
2435
2436 /**
2437 * The {@code Instrumentation#appendToSystemClassLoaderSearch} method.
2438 */
2439 private final Method appendToSystemClassLoaderSearch;
2440
2441 /**
2442 * Creates a new dispatcher for a Java 6 compatible VM.
2443 *
2444 * @param appendToBootstrapClassLoaderSearch The {@code Instrumentation#appendToBootstrapClassLoaderSearch} method.
2445 * @param appendToSystemClassLoaderSearch The {@code Instrumentation#appendToSystemClassLoaderSearch} method.
2446 */
2447 protected ForJava6CapableVm(Method appendToBootstrapClassLoaderSearch, Method appendToSystemClassLoaderSearch) {
2448 this.appendToBootstrapClassLoaderSearch = appendToBootstrapClassLoaderSearch;
2449 this.appendToSystemClassLoaderSearch = appendToSystemClassLoaderSearch;
2450 }
2451
2452 /**
2453 * {@inheritDoc}
2454 */
2455 public boolean isAlive() {
2456 return true;
2457 }
2458
2459 /**
2460 * {@inheritDoc}
2461 */
2462 public void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
2463 try {
2464 appendToBootstrapClassLoaderSearch.invoke(instrumentation, jarFile);
2465 } catch (IllegalAccessException exception) {
2466 throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch", exception);
2467 } catch (InvocationTargetException exception) {
2468 throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch", exception.getCause());
2469 }
2470 }
2471
2472 /**
2473 * {@inheritDoc}
2474 */
2475 public void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
2476 try {
2477 appendToSystemClassLoaderSearch.invoke(instrumentation, jarFile);
2478 } catch (IllegalAccessException exception) {
2479 throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch", exception);
2480 } catch (InvocationTargetException exception) {
2481 throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch", exception.getCause());
2482 }
2483 }
2484 }
2485 }
2486
2487 /**
2488 * A representation of the target to which Java classes should be appended to.
2489 */
2490 public enum Target {
2491
2492 /**
2493 * Representation of the bootstrap class loader.
2494 */
2495 BOOTSTRAP(null) {
2496 @Override
2497 protected void inject(Instrumentation instrumentation, JarFile jarFile) {
2498 DISPATCHER.appendToBootstrapClassLoaderSearch(instrumentation, jarFile);
2499 }
2500 },
2501
2502 /**
2503 * Representation of the system class loader.
2504 */
2505 SYSTEM(ClassLoader.getSystemClassLoader()) {
2506 @Override
2507 protected void inject(Instrumentation instrumentation, JarFile jarFile) {
2508 DISPATCHER.appendToSystemClassLoaderSearch(instrumentation, jarFile);
2509 }
2510 };
2511
2512 /**
2513 * The class loader to load classes from.
2514 */
2515 private final ClassLoader classLoader;
2516
2517 /**
2518 * Creates a new injection target.
2519 *
2520 * @param classLoader The class loader to load classes from.
2521 */
2522 Target(ClassLoader classLoader) {
2523 this.classLoader = classLoader;
2524 }
2525
2526 /**
2527 * Returns the class loader to load classes from.
2528 *
2529 * @return The class loader to load classes from.
2530 */
2531 protected ClassLoader getClassLoader() {
2532 return classLoader;
2533 }
2534
2535 /**
2536 * Adds the given classes to the represented class loader.
2537 *
2538 * @param instrumentation The instrumentation instance to use.
2539 * @param jarFile The jar file to append.
2540 */
2541 protected abstract void inject(Instrumentation instrumentation, JarFile jarFile);
2542 }
2543 }
2544 }
2545