/*
* Copyright 2014 - 2019 Rafael Winterhalter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bytebuddy.dynamic.loading;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.MemberRemoval;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.utility.JavaModule;
import net.bytebuddy.utility.JavaType;
import net.bytebuddy.utility.RandomString;
import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.*;
import java.net.URL;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import static net.bytebuddy.matcher.ElementMatchers.any;
A class injector is capable of injecting classes into a ClassLoader
without requiring the class loader to being able to explicitly look up these classes.
Important: Byte Buddy does not supply privileges when injecting code. When using a SecurityManager
, the user of this injector is responsible for providing access to non-public properties.
/**
* <p>
* A class injector is capable of injecting classes into a {@link java.lang.ClassLoader} without
* requiring the class loader to being able to explicitly look up these classes.
* </p>
* <p>
* <b>Important</b>: Byte Buddy does not supply privileges when injecting code. When using a {@link SecurityManager},
* the user of this injector is responsible for providing access to non-public properties.
* </p>
*/
public interface
ClassInjector {
A permission for the suppressAccessChecks
permission. /**
* A permission for the {@code suppressAccessChecks} permission.
*/
Permission SUPPRESS_ACCESS_CHECKS = new ReflectPermission("suppressAccessChecks");
Determines the default behavior for type injections when a type is already loaded.
/**
* Determines the default behavior for type injections when a type is already loaded.
*/
boolean ALLOW_EXISTING_TYPES = false;
Indicates if this class injector is available on the current VM.
Returns: true
if this injector is available on the current VM.
/**
* Indicates if this class injector is available on the current VM.
*
* @return {@code true} if this injector is available on the current VM.
*/
boolean isAlive();
Injects the given types into the represented class loader.
Params: - types – The types to load via injection.
Returns: The loaded types that were passed as arguments.
/**
* Injects the given types into the represented class loader.
*
* @param types The types to load via injection.
* @return The loaded types that were passed as arguments.
*/
Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types);
Injects the given types into the represented class loader using a mapping from name to binary representation.
Params: - types – The types to load via injection.
Returns: The loaded types that were passed as arguments.
/**
* Injects the given types into the represented class loader using a mapping from name to binary representation.
*
* @param types The types to load via injection.
* @return The loaded types that were passed as arguments.
*/
Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types);
An abstract base implementation of a class injector.
/**
* An abstract base implementation of a class injector.
*/
abstract class AbstractBase implements ClassInjector {
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types) {
Map<String, byte[]> binaryRepresentations = new LinkedHashMap<String, byte[]>();
for (Map.Entry<? extends TypeDescription, byte[]> entry : types.entrySet()) {
binaryRepresentations.put(entry.getKey().getName(), entry.getValue());
}
Map<String, Class<?>> loadedTypes = injectRaw(binaryRepresentations);
Map<TypeDescription, Class<?>> result = new LinkedHashMap<TypeDescription, Class<?>>();
for (TypeDescription typeDescription : types.keySet()) {
result.put(typeDescription, loadedTypes.get(typeDescription.getName()));
}
return result;
}
}
A class injector that uses reflective method calls.
/**
* A class injector that uses reflective method calls.
*/
@HashCodeAndEqualsPlugin.Enhance
class UsingReflection extends AbstractBase {
The dispatcher to use for accessing a class loader via reflection.
/**
* The dispatcher to use for accessing a class loader via reflection.
*/
private static final Dispatcher.Initializable DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
The class loader into which the classes are to be injected.
/**
* The class loader into which the classes are to be injected.
*/
private final ClassLoader classLoader;
The protection domain that is used when loading classes.
/**
* The protection domain that is used when loading classes.
*/
@HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
private final ProtectionDomain protectionDomain;
The package definer to be queried for package definitions.
/**
* The package definer to be queried for package definitions.
*/
private final PackageDefinitionStrategy packageDefinitionStrategy;
Determines if an exception should be thrown when attempting to load a type that already exists.
/**
* Determines if an exception should be thrown when attempting to load a type that already exists.
*/
private final boolean forbidExisting;
Creates a new injector for the given ClassLoader
and a default ProtectionDomain
and a trivial PackageDefinitionStrategy
which does not trigger an error when discovering existent classes. Params: - classLoader – The
ClassLoader
into which new class definitions are to be injected. Must not be the bootstrap loader.
/**
* Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link java.security.ProtectionDomain} and a
* trivial {@link PackageDefinitionStrategy} which does not trigger an error when discovering existent classes.
*
* @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
*/
public UsingReflection(ClassLoader classLoader) {
this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
}
Creates a new injector for the given ClassLoader
and a default PackageDefinitionStrategy
where the injection of existent classes does not trigger an error. Params: - classLoader – The
ClassLoader
into which new class definitions are to be injected. Must not be the bootstrap loader. - protectionDomain – The protection domain to apply during class definition.
/**
* Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link PackageDefinitionStrategy} where the
* injection of existent classes does not trigger an error.
*
* @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader.
* @param protectionDomain The protection domain to apply during class definition.
*/
public UsingReflection(ClassLoader classLoader, ProtectionDomain protectionDomain) {
this(classLoader,
protectionDomain,
PackageDefinitionStrategy.Trivial.INSTANCE,
ALLOW_EXISTING_TYPES);
}
Creates a new injector for the given ClassLoader
and ProtectionDomain
. Params: - classLoader – The
ClassLoader
into which new class definitions are to be injected.Must not be the bootstrap loader. - protectionDomain – The protection domain to apply during class definition.
- packageDefinitionStrategy – The package definer to be queried for package definitions.
- forbidExisting – Determines if an exception should be thrown when attempting to load a type that already exists.
/**
* Creates a new injector for the given {@link java.lang.ClassLoader} and {@link java.security.ProtectionDomain}.
*
* @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected.Must not be the bootstrap loader.
* @param protectionDomain The protection domain to apply during class definition.
* @param packageDefinitionStrategy The package definer to be queried for package definitions.
* @param forbidExisting Determines if an exception should be thrown when attempting to load a type that already exists.
*/
public UsingReflection(ClassLoader classLoader,
ProtectionDomain protectionDomain,
PackageDefinitionStrategy packageDefinitionStrategy,
boolean forbidExisting) {
if (classLoader == null) {
throw new IllegalArgumentException("Cannot inject classes into the bootstrap class loader");
}
this.classLoader = classLoader;
this.protectionDomain = protectionDomain;
this.packageDefinitionStrategy = packageDefinitionStrategy;
this.forbidExisting = forbidExisting;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return isAvailable();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
Dispatcher dispatcher = DISPATCHER.initialize();
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
synchronized (dispatcher.getClassLoadingLock(classLoader, entry.getKey())) {
Class<?> type = dispatcher.findClass(classLoader, entry.getKey());
if (type == null) {
int packageIndex = entry.getKey().lastIndexOf('.');
if (packageIndex != -1) {
String packageName = entry.getKey().substring(0, packageIndex);
PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(classLoader, packageName, entry.getKey());
if (definition.isDefined()) {
Package definedPackage = dispatcher.getPackage(classLoader, packageName);
if (definedPackage == null) {
dispatcher.definePackage(classLoader,
packageName,
definition.getSpecificationTitle(),
definition.getSpecificationVersion(),
definition.getSpecificationVendor(),
definition.getImplementationTitle(),
definition.getImplementationVersion(),
definition.getImplementationVendor(),
definition.getSealBase());
} else if (!definition.isCompatibleTo(definedPackage)) {
throw new SecurityException("Sealing violation for package " + packageName);
}
}
}
type = dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain);
} else if (forbidExisting) {
throw new IllegalStateException("Cannot inject already loaded type: " + type);
}
result.put(entry.getKey(), type);
}
}
return result;
}
Indicates if this class injection is available on the current VM.
Returns: true
if this class injection is available.
/**
* Indicates if this class injection is available on the current VM.
*
* @return {@code true} if this class injection is available.
*/
public static boolean isAvailable() {
return DISPATCHER.isAvailable();
}
Creates a class injector for the system class loader.
Returns: A class injector for the system class loader.
/**
* Creates a class injector for the system class loader.
*
* @return A class injector for the system class loader.
*/
public static ClassInjector ofSystemClassLoader() {
return new UsingReflection(ClassLoader.getSystemClassLoader());
}
A dispatcher for accessing a ClassLoader
reflectively. /**
* A dispatcher for accessing a {@link ClassLoader} reflectively.
*/
protected interface Dispatcher {
Indicates a class that is currently not defined.
/**
* Indicates a class that is currently not defined.
*/
Class<?> UNDEFINED = null;
Returns the lock for loading the specified class.
Params: - classLoader – the class loader to inject the class into.
- name – The name of the class.
Returns: The lock for loading this class.
/**
* Returns the lock for loading the specified class.
*
* @param classLoader the class loader to inject the class into.
* @param name The name of the class.
* @return The lock for loading this class.
*/
Object getClassLoadingLock(ClassLoader classLoader, String name);
Looks up a class from the given class loader.
Params: - classLoader – The class loader for which a class should be located.
- name – The binary name of the class that should be located.
Returns: The class for the binary name or null
if no such class is defined for the provided class loader.
/**
* Looks up a class from the given class loader.
*
* @param classLoader The class loader for which a class should be located.
* @param name The binary name of the class that should be located.
* @return The class for the binary name or {@code null} if no such class is defined for the provided class loader.
*/
Class<?> findClass(ClassLoader classLoader, String name);
Defines a class for the given class loader.
Params: - classLoader – The class loader for which a new class should be defined.
- name – The binary name of the class that should be defined.
- binaryRepresentation – The binary representation of the class.
- protectionDomain – The protection domain for the defined class.
Returns: The defined, loaded class.
/**
* Defines a class for the given class loader.
*
* @param classLoader The class loader for which a new class should be defined.
* @param name The binary name of the class that should be defined.
* @param binaryRepresentation The binary representation of the class.
* @param protectionDomain The protection domain for the defined class.
* @return The defined, loaded class.
*/
Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain);
Looks up a package from a class loader.
Params: - classLoader – The class loader to query.
- name – The binary name of the package.
Returns: The package for the given name as defined by the provided class loader or null
if no such package exists.
/**
* Looks up a package from a class loader.
*
* @param classLoader The class loader to query.
* @param name The binary name of the package.
* @return The package for the given name as defined by the provided class loader or {@code null} if no such package exists.
*/
Package getPackage(ClassLoader classLoader, String name);
Defines a package for the given class loader.
Params: - classLoader – The class loader for which a package is to be defined.
- name – The binary name of the package.
- specificationTitle – The specification title of the package or
null
if no specification title exists. - specificationVersion – The specification version of the package or
null
if no specification version exists. - specificationVendor – The specification vendor of the package or
null
if no specification vendor exists. - implementationTitle – The implementation title of the package or
null
if no implementation title exists. - implementationVersion – The implementation version of the package or
null
if no implementation version exists. - implementationVendor – The implementation vendor of the package or
null
if no implementation vendor exists. - sealBase – The seal base URL or
null
if the package should not be sealed.
Returns: The defined package.
/**
* Defines a package for the given class loader.
*
* @param classLoader The class loader for which a package is to be defined.
* @param name The binary name of the package.
* @param specificationTitle The specification title of the package or {@code null} if no specification title exists.
* @param specificationVersion The specification version of the package or {@code null} if no specification version exists.
* @param specificationVendor The specification vendor of the package or {@code null} if no specification vendor exists.
* @param implementationTitle The implementation title of the package or {@code null} if no implementation title exists.
* @param implementationVersion The implementation version of the package or {@code null} if no implementation version exists.
* @param implementationVendor The implementation vendor of the package or {@code null} if no implementation vendor exists.
* @param sealBase The seal base URL or {@code null} if the package should not be sealed.
* @return The defined package.
*/
Package definePackage(ClassLoader classLoader,
String name,
String specificationTitle,
String specificationVersion,
String specificationVendor,
String implementationTitle,
String implementationVersion,
String implementationVendor,
URL sealBase);
Initializes a dispatcher to make non-accessible APIs accessible.
/**
* Initializes a dispatcher to make non-accessible APIs accessible.
*/
interface Initializable {
Indicates if this dispatcher is available.
Returns: true
if this dispatcher is available.
/**
* Indicates if this dispatcher is available.
*
* @return {@code true} if this dispatcher is available.
*/
boolean isAvailable();
Initializes this dispatcher.
Returns: The initialized dispatcher.
/**
* Initializes this dispatcher.
*
* @return The initialized dispatcher.
*/
Dispatcher initialize();
Represents an unsuccessfully loaded method lookup.
/**
* Represents an unsuccessfully loaded method lookup.
*/
@HashCodeAndEqualsPlugin.Enhance
class Unavailable implements Dispatcher, Initializable {
The reason why this dispatcher is not available.
/**
* The reason why this dispatcher is not available.
*/
private final String message;
Creates a new faulty reflection store.
Params: - message – The reason why this dispatcher is not available.
/**
* Creates a new faulty reflection store.
*
* @param message The reason why this dispatcher is not available.
*/
protected Unavailable(String message) {
this.message = message;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAvailable() {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object getClassLoadingLock(ClassLoader classLoader, String name) {
return classLoader;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> findClass(ClassLoader classLoader, String name) {
try {
return classLoader.loadClass(name);
} catch (ClassNotFoundException ignored) {
return UNDEFINED;
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package getPackage(ClassLoader classLoader, String name) {
throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package definePackage(ClassLoader classLoader,
String name,
String specificationTitle,
String specificationVersion,
String specificationVendor,
String implementationTitle,
String implementationVersion,
String implementationVendor,
URL sealBase) {
throw new UnsupportedOperationException("Cannot define package using injection: " + message);
}
}
}
A creation action for a dispatcher.
/**
* A creation action for a dispatcher.
*/
enum CreationAction implements PrivilegedAction<Initializable> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
public Initializable run() {
try {
if (JavaModule.isSupported()) {
return UsingUnsafe.isAvailable()
? UsingUnsafeInjection.make()
: UsingUnsafeOverride.make();
} else {
return Direct.make();
}
} catch (InvocationTargetException exception) {
return new Initializable.Unavailable(exception.getCause().getMessage());
} catch (Exception exception) {
return new Initializable.Unavailable(exception.getMessage());
}
}
}
A class injection dispatcher that is using reflection on the ClassLoader
methods. /**
* A class injection dispatcher that is using reflection on the {@link ClassLoader} methods.
*/
@HashCodeAndEqualsPlugin.Enhance
abstract class Direct implements Dispatcher, Initializable {
An instance of ClassLoader.findLoadedClass(String)
. /**
* An instance of {@link ClassLoader#findLoadedClass(String)}.
*/
protected final Method findLoadedClass;
/**
* An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
*/
protected final Method defineClass;
An instance of ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. /**
* An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
*/
protected final Method getPackage;
/**
* An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected final Method definePackage;
Creates a new direct injection dispatcher.
Params: - findLoadedClass – An instance of
ClassLoader.findLoadedClass(String)
. - defineClass – An instance of
ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. - getPackage – An instance of
ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. - definePackage – An instance of
ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
.
/**
* Creates a new direct injection dispatcher.
*
* @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
* @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
* @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
* @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected Direct(Method findLoadedClass,
Method defineClass,
Method getPackage,
Method definePackage) {
this.findLoadedClass = findLoadedClass;
this.defineClass = defineClass;
this.getPackage = getPackage;
this.definePackage = definePackage;
}
Creates a direct dispatcher.
Throws: - Exception – If the creation is impossible.
Returns: A direct dispatcher for class injection.
/**
* Creates a direct dispatcher.
*
* @return A direct dispatcher for class injection.
* @throws Exception If the creation is impossible.
*/
@SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility")
protected static Initializable make() throws Exception {
Method getPackage;
if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
try {
getPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class);
} catch (NoSuchMethodException ignored) {
getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
getPackage.setAccessible(true);
}
} else {
getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
getPackage.setAccessible(true);
}
Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
findLoadedClass.setAccessible(true);
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
String.class,
byte[].class,
int.class,
int.class,
ProtectionDomain.class);
defineClass.setAccessible(true);
Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage",
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
URL.class);
definePackage.setAccessible(true);
try {
Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
getClassLoadingLock.setAccessible(true);
return new ForJava7CapableVm(findLoadedClass,
defineClass,
getPackage,
definePackage,
getClassLoadingLock);
} catch (NoSuchMethodException ignored) {
return new ForLegacyVm(findLoadedClass, defineClass, getPackage, definePackage);
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAvailable() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
try {
securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
} catch (Exception exception) {
return new Dispatcher.Unavailable(exception.getMessage());
}
}
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> findClass(ClassLoader classLoader, String name) {
try {
return (Class<?>) findLoadedClass.invoke(classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#findClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#findClass", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
try {
return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#defineClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#defineClass", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package getPackage(ClassLoader classLoader, String name) {
try {
return (Package) getPackage.invoke(classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#getPackage", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#getPackage", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package definePackage(ClassLoader classLoader,
String name,
String specificationTitle,
String specificationVersion,
String specificationVendor,
String implementationTitle,
String implementationVersion,
String implementationVendor,
URL sealBase) {
try {
return (Package) definePackage.invoke(classLoader,
name,
specificationTitle,
specificationVersion,
specificationVendor,
implementationTitle,
implementationVersion,
implementationVendor,
sealBase);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#definePackage", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#definePackage", exception.getCause());
}
}
A resolved class dispatcher for a class injector on a VM running at least Java 7.
/**
* A resolved class dispatcher for a class injector on a VM running at least Java 7.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class ForJava7CapableVm extends Direct {
An instance of ClassLoader#getClassLoadingLock(String)
. /**
* An instance of {@code ClassLoader#getClassLoadingLock(String)}.
*/
private final Method getClassLoadingLock;
Creates a new resolved reflection store for a VM running at least Java 7.
Params: - getClassLoadingLock – An instance of
ClassLoader#getClassLoadingLock(String)
. - findLoadedClass – An instance of
ClassLoader.findLoadedClass(String)
. - defineClass – An instance of
ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. - getPackage – An instance of
ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. - definePackage – An instance of
ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
.
/**
* Creates a new resolved reflection store for a VM running at least Java 7.
*
* @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
* @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
* @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
* @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
* @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected ForJava7CapableVm(Method findLoadedClass,
Method defineClass,
Method getPackage,
Method definePackage,
Method getClassLoadingLock) {
super(findLoadedClass, defineClass, getPackage, definePackage);
this.getClassLoadingLock = getClassLoadingLock;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object getClassLoadingLock(ClassLoader classLoader, String name) {
try {
return getClassLoadingLock.invoke(classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#getClassLoadingLock", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#getClassLoadingLock", exception.getCause());
}
}
}
A resolved class dispatcher for a class injector prior to Java 7.
/**
* A resolved class dispatcher for a class injector prior to Java 7.
*/
protected static class ForLegacyVm extends Direct {
Creates a new resolved reflection store for a VM prior to Java 8.
Params: - findLoadedClass – An instance of
ClassLoader.findLoadedClass(String)
. - defineClass – An instance of
ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. - getPackage – An instance of
ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. - definePackage – An instance of
ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
.
/**
* Creates a new resolved reflection store for a VM prior to Java 8.
*
* @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
* @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
* @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
* @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected ForLegacyVm(Method findLoadedClass,
Method defineClass,
Method getPackage,
Method definePackage) {
super(findLoadedClass, defineClass, getPackage, definePackage);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object getClassLoadingLock(ClassLoader classLoader, String name) {
return classLoader;
}
}
}
An indirect dispatcher that uses a redirection accessor class that was injected into the bootstrap class loader.
/**
* An indirect dispatcher that uses a redirection accessor class that was injected into the bootstrap class loader.
*/
@HashCodeAndEqualsPlugin.Enhance
class UsingUnsafeInjection implements Dispatcher, Initializable {
An instance of the accessor class that is required for using it's intentionally non-static methods.
/**
* An instance of the accessor class that is required for using it's intentionally non-static methods.
*/
private final Object accessor;
The accessor method for using ClassLoader.findLoadedClass(String)
. /**
* The accessor method for using {@link ClassLoader#findLoadedClass(String)}.
*/
private final Method findLoadedClass;
The accessor method for using ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. /**
* The accessor method for using {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
*/
private final Method defineClass;
The accessor method for using ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. /**
* The accessor method for using {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
*/
private final Method getPackage;
The accessor method for using ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
. /**
* The accessor method for using {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
private final Method definePackage;
The accessor method for using ClassLoader#getClassLoadingLock(String)
or returning the supplied ClassLoader
if this method does not exist on the current VM. /**
* The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the supplied {@link ClassLoader}
* if this method does not exist on the current VM.
*/
private final Method getClassLoadingLock;
Creates a new class loading injection dispatcher using an unsafe injected dispatcher.
Params: - accessor – An instance of the accessor class that is required for using it's intentionally non-static methods.
- findLoadedClass – An instance of
ClassLoader.findLoadedClass(String)
. - defineClass – An instance of
ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. - getPackage – An instance of
ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. - definePackage – An instance of
ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
. - getClassLoadingLock – The accessor method for using
ClassLoader#getClassLoadingLock(String)
or returning the supplied ClassLoader
if this method does not exist on the current VM.
/**
* Creates a new class loading injection dispatcher using an unsafe injected dispatcher.
*
* @param accessor An instance of the accessor class that is required for using it's intentionally non-static methods.
* @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
* @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
* @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
* @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
* @param getClassLoadingLock The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the
* supplied {@link ClassLoader} if this method does not exist on the current VM.
*/
protected UsingUnsafeInjection(Object accessor,
Method findLoadedClass,
Method defineClass,
Method getPackage,
Method definePackage,
Method getClassLoadingLock) {
this.accessor = accessor;
this.findLoadedClass = findLoadedClass;
this.defineClass = defineClass;
this.getPackage = getPackage;
this.definePackage = definePackage;
this.getClassLoadingLock = getClassLoadingLock;
}
Creates an indirect dispatcher.
Throws: - Exception – If the dispatcher cannot be created.
Returns: An indirect dispatcher for class creation.
/**
* Creates an indirect dispatcher.
*
* @return An indirect dispatcher for class creation.
* @throws Exception If the dispatcher cannot be created.
*/
@SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility")
protected static Initializable make() throws Exception {
if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) {
return new Initializable.Unavailable("Use of Unsafe was disabled by system property");
}
Class<?> unsafe = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafe.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Object unsafeInstance = theUnsafe.get(null);
Method getPackage;
if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
try {
getPackage = ClassLoader.class.getDeclaredMethod("getDefinedPackage", String.class);
} catch (NoSuchMethodException ignored) {
getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
}
} else {
getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
}
getPackage.setAccessible(true);
DynamicType.Builder<?> builder = new ByteBuddy()
.with(TypeValidation.DISABLED)
.subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.name(ClassLoader.class.getName() + "$ByteBuddyAccessor$" + RandomString.make())
.defineMethod("findLoadedClass", Class.class, Visibility.PUBLIC)
.withParameters(ClassLoader.class, String.class)
.intercept(MethodCall.invoke(ClassLoader.class
.getDeclaredMethod("findLoadedClass", String.class))
.onArgument(0)
.withArgument(1))
.defineMethod("defineClass", Class.class, Visibility.PUBLIC)
.withParameters(ClassLoader.class, String.class, byte[].class, int.class, int.class,
ProtectionDomain.class)
.intercept(MethodCall.invoke(ClassLoader.class
.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class))
.onArgument(0)
.withArgument(1, 2, 3, 4, 5))
.defineMethod("getPackage", Package.class, Visibility.PUBLIC)
.withParameters(ClassLoader.class, String.class)
.intercept(MethodCall.invoke(getPackage)
.onArgument(0)
.withArgument(1))
.defineMethod("definePackage", Package.class, Visibility.PUBLIC)
.withParameters(ClassLoader.class, String.class, String.class, String.class, String.class,
String.class, String.class, String.class, URL.class)
.intercept(MethodCall.invoke(ClassLoader.class
.getDeclaredMethod("definePackage", String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class))
.onArgument(0)
.withArgument(1, 2, 3, 4, 5, 6, 7, 8));
try {
builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC)
.withParameters(ClassLoader.class, String.class)
.intercept(MethodCall.invoke(ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class))
.onArgument(0)
.withArgument(1));
} catch (NoSuchMethodException ignored) {
builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC)
.withParameters(ClassLoader.class, String.class)
.intercept(FixedValue.argument(0));
}
Class<?> type = builder.make()
.load(ClassLoadingStrategy.BOOTSTRAP_LOADER, new ClassLoadingStrategy.ForUnsafeInjection())
.getLoaded();
return new UsingUnsafeInjection(
unsafe.getMethod("allocateInstance", Class.class).invoke(unsafeInstance, type),
type.getMethod("findLoadedClass", ClassLoader.class, String.class),
type.getMethod("defineClass", ClassLoader.class, String.class, byte[].class, int.class, int.class, ProtectionDomain.class),
type.getMethod("getPackage", ClassLoader.class, String.class),
type.getMethod("definePackage", ClassLoader.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class),
type.getMethod("getClassLoadingLock", ClassLoader.class, String.class));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAvailable() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
try {
securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
} catch (Exception exception) {
return new Dispatcher.Unavailable(exception.getMessage());
}
}
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object getClassLoadingLock(ClassLoader classLoader, String name) {
try {
return getClassLoadingLock.invoke(accessor, classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access (accessor)::getClassLoadingLock", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking (accessor)::getClassLoadingLock", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> findClass(ClassLoader classLoader, String name) {
try {
return (Class<?>) findLoadedClass.invoke(accessor, classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access (accessor)::findLoadedClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking (accessor)::findLoadedClass", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
try {
return (Class<?>) defineClass.invoke(accessor, classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access (accessor)::defineClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking (accessor)::defineClass", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package getPackage(ClassLoader classLoader, String name) {
try {
return (Package) getPackage.invoke(accessor, classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access (accessor)::getPackage", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking (accessor)::getPackage", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package definePackage(ClassLoader classLoader,
String name,
String specificationTitle,
String specificationVersion,
String specificationVendor,
String implementationTitle,
String implementationVersion,
String implementationVendor,
URL sealBase) {
try {
return (Package) definePackage.invoke(accessor,
classLoader,
name,
specificationTitle,
specificationVersion,
specificationVendor,
implementationTitle,
implementationVersion,
implementationVendor,
sealBase);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access (accessor)::definePackage", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking (accessor)::definePackage", exception.getCause());
}
}
}
A dispatcher implementation that uses sun.misc.Unsafe#putBoolean
to set the AccessibleObject
field for making methods accessible. /**
* A dispatcher implementation that uses {@code sun.misc.Unsafe#putBoolean} to set the {@link AccessibleObject} field
* for making methods accessible.
*/
abstract class UsingUnsafeOverride implements Dispatcher, Initializable {
An instance of ClassLoader.findLoadedClass(String)
. /**
* An instance of {@link ClassLoader#findLoadedClass(String)}.
*/
protected final Method findLoadedClass;
/**
* An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
*/
protected final Method defineClass;
An instance of ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. /**
* An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
*/
protected final Method getPackage;
/**
* An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected final Method definePackage;
Creates a new unsafe field injecting injection dispatcher.
Params: - findLoadedClass – An instance of
ClassLoader.findLoadedClass(String)
. - defineClass – An instance of
ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. - getPackage – An instance of
ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. - definePackage – An instance of
ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
.
/**
* Creates a new unsafe field injecting injection dispatcher.
*
* @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
* @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
* @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
* @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected UsingUnsafeOverride(Method findLoadedClass,
Method defineClass,
Method getPackage,
Method definePackage) {
this.findLoadedClass = findLoadedClass;
this.defineClass = defineClass;
this.getPackage = getPackage;
this.definePackage = definePackage;
}
Creates a new initializable class injector using an unsafe field injection.
Throws: - Exception – If the injector cannot be created.
Returns: An appropriate initializable.
/**
* Creates a new initializable class injector using an unsafe field injection.
*
* @return An appropriate initializable.
* @throws Exception If the injector cannot be created.
*/
@SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility")
protected static Initializable make() throws Exception {
if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) {
return new Initializable.Unavailable("Use of Unsafe was disabled by system property");
}
Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Object unsafe = theUnsafe.get(null);
Field override;
try {
override = AccessibleObject.class.getDeclaredField("override");
} catch (NoSuchFieldException ignored) {
// Since Java 12, the override field is hidden from the reflection API. To circumvent this, we
// create a mirror class of AccessibleObject that defines the same fields and has the same field
// layout such that the override field will receive the same class offset. Doing so, we can write to
// the offset location and still set a value to it, despite it being hidden from the reflection API.
override = new ByteBuddy()
.redefine(AccessibleObject.class)
.name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName())
.noNestMate()
.visit(new MemberRemoval().stripInvokables(any()))
.make()
.load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded()
.getDeclaredField("override");
}
long offset = (Long) unsafeType
.getMethod("objectFieldOffset", Field.class)
.invoke(unsafe, override);
Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class);
Method getPackage;
if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM.
try {
getPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class);
} catch (NoSuchMethodException ignored) {
getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
putBoolean.invoke(unsafe, getPackage, offset, true);
}
} else {
getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
putBoolean.invoke(unsafe, getPackage, offset, true);
}
Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
String.class,
byte[].class,
int.class,
int.class,
ProtectionDomain.class);
Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage",
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
URL.class);
putBoolean.invoke(unsafe, defineClass, offset, true);
putBoolean.invoke(unsafe, findLoadedClass, offset, true);
putBoolean.invoke(unsafe, definePackage, offset, true);
try {
Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class);
putBoolean.invoke(unsafe, getClassLoadingLock, offset, true);
return new ForJava7CapableVm(findLoadedClass,
defineClass,
getPackage,
definePackage,
getClassLoadingLock);
} catch (NoSuchMethodException ignored) {
return new ForLegacyVm(findLoadedClass, defineClass, getPackage, definePackage);
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAvailable() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
try {
securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
} catch (Exception exception) {
return new Dispatcher.Unavailable(exception.getMessage());
}
}
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> findClass(ClassLoader classLoader, String name) {
try {
return (Class<?>) findLoadedClass.invoke(classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#findClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#findClass", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
try {
return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#defineClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#defineClass", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package getPackage(ClassLoader classLoader, String name) {
try {
return (Package) getPackage.invoke(classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#getPackage", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#getPackage", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package definePackage(ClassLoader classLoader,
String name,
String specificationTitle,
String specificationVersion,
String specificationVendor,
String implementationTitle,
String implementationVersion,
String implementationVendor,
URL sealBase) {
try {
return (Package) definePackage.invoke(classLoader,
name,
specificationTitle,
specificationVersion,
specificationVendor,
implementationTitle,
implementationVersion,
implementationVendor,
sealBase);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#definePackage", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#definePackage", exception.getCause());
}
}
A resolved class dispatcher using unsafe field injection for a class injector on a VM running at least Java 7.
/**
* A resolved class dispatcher using unsafe field injection for a class injector on a VM running at least Java 7.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class ForJava7CapableVm extends UsingUnsafeOverride {
An instance of ClassLoader#getClassLoadingLock(String)
. /**
* An instance of {@code ClassLoader#getClassLoadingLock(String)}.
*/
private final Method getClassLoadingLock;
Creates a new resolved class injector using unsafe field injection for a VM running at least Java 7.
Params: - getClassLoadingLock – An instance of
ClassLoader#getClassLoadingLock(String)
. - findLoadedClass – An instance of
ClassLoader.findLoadedClass(String)
. - defineClass – An instance of
ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. - getPackage – An instance of
ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. - definePackage – An instance of
ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
.
/**
* Creates a new resolved class injector using unsafe field injection for a VM running at least Java 7.
*
* @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}.
* @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
* @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
* @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
* @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected ForJava7CapableVm(Method findLoadedClass,
Method defineClass,
Method getPackage,
Method definePackage,
Method getClassLoadingLock) {
super(findLoadedClass, defineClass, getPackage, definePackage);
this.getClassLoadingLock = getClassLoadingLock;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object getClassLoadingLock(ClassLoader classLoader, String name) {
try {
return getClassLoadingLock.invoke(classLoader, name);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access java.lang.ClassLoader#getClassLoadingLock", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.ClassLoader#getClassLoadingLock", exception.getCause());
}
}
}
A resolved class dispatcher using unsafe field injection for a class injector prior to Java 7.
/**
* A resolved class dispatcher using unsafe field injection for a class injector prior to Java 7.
*/
protected static class ForLegacyVm extends UsingUnsafeOverride {
Creates a new resolved class injector using unsafe field injection for a VM prior to Java 7.
Params: - findLoadedClass – An instance of
ClassLoader.findLoadedClass(String)
. - defineClass – An instance of
ClassLoader.defineClass(String, byte[], int, int, ProtectionDomain)
. - getPackage – An instance of
ClassLoader.getPackage(String)
or ClassLoader#getDefinedPackage(String)
. - definePackage – An instance of
ClassLoader.definePackage(String, String, String, String, String, String, String, URL)
.
/**
* Creates a new resolved class injector using unsafe field injection for a VM prior to Java 7.
*
* @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}.
* @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}.
* @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}.
* @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}.
*/
protected ForLegacyVm(Method findLoadedClass,
Method defineClass,
Method getPackage,
Method definePackage) {
super(findLoadedClass, defineClass, getPackage, definePackage);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object getClassLoadingLock(ClassLoader classLoader, String name) {
return classLoader;
}
}
}
Represents an unsuccessfully loaded method lookup.
/**
* Represents an unsuccessfully loaded method lookup.
*/
@HashCodeAndEqualsPlugin.Enhance
class Unavailable implements Dispatcher {
The error message being displayed.
/**
* The error message being displayed.
*/
private final String message;
Creates a dispatcher for a VM that does not support reflective injection.
Params: - message – The error message being displayed.
/**
* Creates a dispatcher for a VM that does not support reflective injection.
*
* @param message The error message being displayed.
*/
protected Unavailable(String message) {
this.message = message;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object getClassLoadingLock(ClassLoader classLoader, String name) {
return classLoader;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> findClass(ClassLoader classLoader, String name) {
try {
return classLoader.loadClass(name);
} catch (ClassNotFoundException ignored) {
return UNDEFINED;
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
throw new UnsupportedOperationException("Cannot define class using reflection: " + message);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package getPackage(ClassLoader classLoader, String name) {
throw new UnsupportedOperationException("Cannot get package using reflection: " + message);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Package definePackage(ClassLoader classLoader,
String name,
String specificationTitle,
String specificationVersion,
String specificationVendor,
String implementationTitle,
String implementationVersion,
String implementationVendor,
URL sealBase) {
throw new UnsupportedOperationException("Cannot define package using injection: " + message);
}
}
}
}
A class injector that uses a java.lang.invoke.MethodHandles$Lookup
object for defining a class.
Important: This functionality is only available starting from Java 9.
/**
* <p>
* A class injector that uses a {@code java.lang.invoke.MethodHandles$Lookup} object for defining a class.
* </p>
* <p>
* <b>Important</b>: This functionality is only available starting from Java 9.
* </p>
*/
@HashCodeAndEqualsPlugin.Enhance
class UsingLookup extends AbstractBase {
The dispatcher to interacting with method handles.
/**
* The dispatcher to interacting with method handles.
*/
private static final Dispatcher DISPATCHER = AccessController.doPrivileged(Dispatcher.Creator.INSTANCE);
Indicates a lookup instance's package lookup mode.
/**
* Indicates a lookup instance's package lookup mode.
*/
private static final int PACKAGE_LOOKUP = 0x8;
The java.lang.invoke.MethodHandles$Lookup
to use. /**
* The {@code java.lang.invoke.MethodHandles$Lookup} to use.
*/
private final Object lookup;
Creates a new class injector using a lookup instance.
Params: - lookup – The
java.lang.invoke.MethodHandles$Lookup
instance to use.
/**
* Creates a new class injector using a lookup instance.
*
* @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
*/
protected UsingLookup(Object lookup) {
this.lookup = lookup;
}
Creates class injector that defines a class using a method handle lookup.
Params: - lookup – The
java.lang.invoke.MethodHandles$Lookup
instance to use.
Returns: An appropriate class injector.
/**
* Creates class injector that defines a class using a method handle lookup.
*
* @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
* @return An appropriate class injector.
*/
public static UsingLookup of(Object lookup) {
if (!DISPATCHER.isAlive()) {
throw new IllegalStateException("The current VM does not support class definition via method handle lookups");
} else if (!JavaType.METHOD_HANDLES_LOOKUP.isInstance(lookup)) {
throw new IllegalArgumentException("Not a method handle lookup: " + lookup);
} else if ((DISPATCHER.lookupModes(lookup) & PACKAGE_LOOKUP) == 0) {
throw new IllegalArgumentException("Lookup does not imply package-access: " + lookup);
}
return new UsingLookup(DISPATCHER.dropLookupMode(lookup, Opcodes.ACC_PRIVATE));
}
Returns the lookup type this injector is based upon.
Returns: The lookup type.
/**
* Returns the lookup type this injector is based upon.
*
* @return The lookup type.
*/
public Class<?> lookupType() {
return DISPATCHER.lookupType(lookup);
}
Resolves this injector to use the supplied type's scope.
Params: - type – The type to resolve the access scope for.
Returns: An new injector with the specified scope.
/**
* Resolves this injector to use the supplied type's scope.
*
* @param type The type to resolve the access scope for.
* @return An new injector with the specified scope.
*/
public UsingLookup in(Class<?> type) {
return new UsingLookup(DISPATCHER.resolve(lookup, type));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return isAvailable();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
String expectedPackage = TypeDescription.ForLoadedType.of(lookupType()).getPackage().getName();
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
int index = entry.getKey().lastIndexOf('.');
if (!expectedPackage.equals(index == -1 ? "" : entry.getKey().substring(0, index))) {
throw new IllegalArgumentException(entry.getKey() + " must be defined in the same package as " + lookup);
}
result.put(entry.getKey(), DISPATCHER.defineClass(lookup, entry.getValue()));
}
return result;
}
Checks if the current VM is capable of defining classes using a method handle lookup.
Returns: true
if the current VM is capable of defining classes using a lookup.
/**
* Checks if the current VM is capable of defining classes using a method handle lookup.
*
* @return {@code true} if the current VM is capable of defining classes using a lookup.
*/
public static boolean isAvailable() {
return DISPATCHER.isAlive();
}
A dispatcher for interacting with a method handle lookup.
/**
* A dispatcher for interacting with a method handle lookup.
*/
protected interface Dispatcher {
Indicates if this dispatcher is available on the current VM.
Returns: true
if this dispatcher is alive.
/**
* Indicates if this dispatcher is available on the current VM.
*
* @return {@code true} if this dispatcher is alive.
*/
boolean isAlive();
Returns the lookup type for a given method handle lookup.
Params: - lookup – The lookup instance.
Returns: The lookup type.
/**
* Returns the lookup type for a given method handle lookup.
*
* @param lookup The lookup instance.
* @return The lookup type.
*/
Class<?> lookupType(Object lookup);
Returns a lookup objects lookup types.
Params: - lookup – The lookup instance.
Returns: The modifiers indicating the instance's lookup modes.
/**
* Returns a lookup objects lookup types.
*
* @param lookup The lookup instance.
* @return The modifiers indicating the instance's lookup modes.
*/
int lookupModes(Object lookup);
Drops a given lookup mode from a lookup instance.
Params: - lookup – The lookup instance.
- mode – The modes to drop.
Returns: A new lookup instance where the modes were dropped.
/**
* Drops a given lookup mode from a lookup instance.
*
* @param lookup The lookup instance.
* @param mode The modes to drop.
* @return A new lookup instance where the modes were dropped.
*/
Object dropLookupMode(Object lookup, int mode);
Resolves the supplied lookup instance's access scope for the supplied type.
Params: - lookup – The lookup to use.
- type – The type to resolve the scope for.
Returns: An appropriate lookup instance.
/**
* Resolves the supplied lookup instance's access scope for the supplied type.
*
* @param lookup The lookup to use.
* @param type The type to resolve the scope for.
* @return An appropriate lookup instance.
*/
Object resolve(Object lookup, Class<?> type);
Defines a class.
Params: - lookup – The
java.lang.invoke.MethodHandles$Lookup
instance to use. - binaryRepresentation – The defined class's binary representation.
Returns: The defined class.
/**
* Defines a class.
*
* @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use.
* @param binaryRepresentation The defined class's binary representation.
* @return The defined class.
*/
Class<?> defineClass(Object lookup, byte[] binaryRepresentation);
An action for defining a dispatcher.
/**
* An action for defining a dispatcher.
*/
enum Creator implements PrivilegedAction<Dispatcher> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
public Dispatcher run() {
try {
Class<?> lookup = JavaType.METHOD_HANDLES_LOOKUP.load();
return new Dispatcher.ForJava9CapableVm(JavaType.METHOD_HANDLES.load().getMethod("privateLookupIn", Class.class, lookup),
lookup.getMethod("lookupClass"),
lookup.getMethod("lookupModes"),
lookup.getMethod("dropLookupMode", int.class),
lookup.getMethod("defineClass", byte[].class));
} catch (Exception ignored) {
return Dispatcher.ForLegacyVm.INSTANCE;
}
}
}
A dispatcher for a legacy VM that does not support class definition via method handles.
/**
* A dispatcher for a legacy VM that does not support class definition via method handles.
*/
enum ForLegacyVm implements Dispatcher {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> lookupType(Object lookup) {
throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public int lookupModes(Object lookup) {
throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object dropLookupMode(Object lookup, int mode) {
throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object resolve(Object lookup, Class<?> type) {
throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(Object lookup, byte[] binaryRepresentation) {
throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup");
}
}
A dispatcher for a Java 9 capable VM that supports class definition via method handles.
/**
* A dispatcher for a Java 9 capable VM that supports class definition via method handles.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForJava9CapableVm implements Dispatcher {
An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
/**
* An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
*/
private static final Object[] NO_ARGUMENTS = new Object[0];
The java.lang.invoke.MethodHandles$#privateLookupIn
method. /**
* The {@code java.lang.invoke.MethodHandles$#privateLookupIn} method.
*/
private final Method privateLookupIn;
The java.lang.invoke.MethodHandles$Lookup#lookupClass
method. /**
* The {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
*/
private final Method lookupClass;
The java.lang.invoke.MethodHandles$Lookup#lookupModes
method. /**
* The {@code java.lang.invoke.MethodHandles$Lookup#lookupModes} method.
*/
private final Method lookupModes;
The java.lang.invoke.MethodHandles$Lookup#dropLookupMode
method. /**
* The {@code java.lang.invoke.MethodHandles$Lookup#dropLookupMode} method.
*/
private final Method dropLookupMode;
The java.lang.invoke.MethodHandles$Lookup#defineClass
method. /**
* The {@code java.lang.invoke.MethodHandles$Lookup#defineClass} method.
*/
private final Method defineClass;
Creates a new dispatcher for a Java 9 capable VM.
Params: - privateLookupIn – The
java.lang.invoke.MethodHandles$#privateLookupIn
method. - lookupClass – The
java.lang.invoke.MethodHandles$Lookup#lookupClass
method. - lookupModes – The
java.lang.invoke.MethodHandles$Lookup#lookupModes
method. - dropLookupMode – The
java.lang.invoke.MethodHandles$Lookup#dropLookupMode
method. - defineClass – The
java.lang.invoke.MethodHandles$Lookup#defineClass
method.
/**
* Creates a new dispatcher for a Java 9 capable VM.
*
* @param privateLookupIn The {@code java.lang.invoke.MethodHandles$#privateLookupIn} method.
* @param lookupClass The {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
* @param lookupModes The {@code java.lang.invoke.MethodHandles$Lookup#lookupModes} method.
* @param dropLookupMode The {@code java.lang.invoke.MethodHandles$Lookup#dropLookupMode} method.
* @param defineClass The {@code java.lang.invoke.MethodHandles$Lookup#defineClass} method.
*/
protected ForJava9CapableVm(Method privateLookupIn, Method lookupClass, Method lookupModes, Method dropLookupMode, Method defineClass) {
this.privateLookupIn = privateLookupIn;
this.lookupClass = lookupClass;
this.lookupModes = lookupModes;
this.defineClass = defineClass;
this.dropLookupMode = dropLookupMode;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> lookupType(Object lookup) {
try {
return (Class<?>) lookupClass.invoke(lookup, NO_ARGUMENTS);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupClass", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public int lookupModes(Object lookup) {
try {
return (Integer) lookupModes.invoke(lookup, NO_ARGUMENTS);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupModes", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupModes", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object dropLookupMode(Object lookup, int mode) {
try {
return dropLookupMode.invoke(lookup, mode);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupModes", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupModes", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Object resolve(Object lookup, Class<?> type) {
try {
return privateLookupIn.invoke(null, type, lookup);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles#privateLookupIn", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles#privateLookupIn", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(Object lookup, byte[] binaryRepresentation) {
try {
return (Class<?>) defineClass.invoke(lookup, (Object) binaryRepresentation);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#defineClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#defineClass", exception.getCause());
}
}
}
}
}
A class injector that uses sun.misc.Unsafe
to inject classes.
Important: This strategy is no longer available after Java 11.
/**
* <p>
* A class injector that uses {@code sun.misc.Unsafe} to inject classes.
* </p>
* <p>
* <b>Important</b>: This strategy is no longer available after Java 11.
* </p>
*/
@HashCodeAndEqualsPlugin.Enhance
class UsingUnsafe extends AbstractBase {
If this property is set, Byte Buddy does not make use of any Unsafe
class. /**
* If this property is set, Byte Buddy does not make use of any {@code Unsafe} class.
*/
public static final String SAFE_PROPERTY = "net.bytebuddy.safe";
The dispatcher to use.
/**
* The dispatcher to use.
*/
private static final Dispatcher.Initializable DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
A lock for the bootstrap loader when injecting code.
/**
* A lock for the bootstrap loader when injecting code.
*/
private static final Object BOOTSTRAP_LOADER_LOCK = new Object();
The class loader to inject classes into or null
for the bootstrap loader. /**
* The class loader to inject classes into or {@code null} for the bootstrap loader.
*/
@HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
private final ClassLoader classLoader;
The protection domain to use or null
for no protection domain. /**
* The protection domain to use or {@code null} for no protection domain.
*/
@HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
private final ProtectionDomain protectionDomain;
Creates a new unsafe injector for the given class loader with a default protection domain.
Params: - classLoader – The class loader to inject classes into or
null
for the bootstrap loader.
/**
* Creates a new unsafe injector for the given class loader with a default protection domain.
*
* @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
*/
public UsingUnsafe(ClassLoader classLoader) {
this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN);
}
Creates a new unsafe injector for the given class loader with a default protection domain.
Params: - classLoader – The class loader to inject classes into or
null
for the bootstrap loader. - protectionDomain – The protection domain to use or
null
for no protection domain.
/**
* Creates a new unsafe injector for the given class loader with a default protection domain.
*
* @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader.
* @param protectionDomain The protection domain to use or {@code null} for no protection domain.
*/
public UsingUnsafe(ClassLoader classLoader, ProtectionDomain protectionDomain) {
this.classLoader = classLoader;
this.protectionDomain = protectionDomain;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return isAvailable();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
Dispatcher dispatcher = DISPATCHER.initialize();
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
synchronized (classLoader == null
? BOOTSTRAP_LOADER_LOCK
: classLoader) {
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
try {
result.put(entry.getKey(), Class.forName(entry.getKey(), false, classLoader));
} catch (ClassNotFoundException ignored) {
result.put(entry.getKey(), dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain));
}
}
}
return result;
}
Checks if unsafe class injection is available on the current VM.
Returns: true
if unsafe class injection is available on the current VM.
/**
* Checks if unsafe class injection is available on the current VM.
*
* @return {@code true} if unsafe class injection is available on the current VM.
*/
public static boolean isAvailable() {
return DISPATCHER.isAvailable();
}
Returns an unsafe class injector for the system class loader.
Returns: A class injector for the system class loader.
/**
* Returns an unsafe class injector for the system class loader.
*
* @return A class injector for the system class loader.
*/
public static ClassInjector ofSystemLoader() {
return new UsingUnsafe(ClassLoader.getSystemClassLoader());
}
Returns an unsafe class injector for the platform class loader. For VMs of version 8 or older,
the extension class loader is represented instead.
Returns: A class injector for the platform class loader.
/**
* Returns an unsafe class injector for the platform class loader. For VMs of version 8 or older,
* the extension class loader is represented instead.
*
* @return A class injector for the platform class loader.
*/
public static ClassInjector ofPlatformLoader() {
return new UsingUnsafe(ClassLoader.getSystemClassLoader().getParent());
}
Returns an unsafe class injector for the boot class loader.
Returns: A class injector for the boot loader.
/**
* Returns an unsafe class injector for the boot class loader.
*
* @return A class injector for the boot loader.
*/
public static ClassInjector ofBootLoader() {
return new UsingUnsafe(ClassLoadingStrategy.BOOTSTRAP_LOADER);
}
A dispatcher for using sun.misc.Unsafe
. /**
* A dispatcher for using {@code sun.misc.Unsafe}.
*/
protected interface Dispatcher {
Defines a class.
Params: - classLoader – The class loader to inject the class into.
- name – The type's name.
- binaryRepresentation – The type's binary representation.
- protectionDomain – The type's protection domain.
Returns: The defined class.
/**
* Defines a class.
*
* @param classLoader The class loader to inject the class into.
* @param name The type's name.
* @param binaryRepresentation The type's binary representation.
* @param protectionDomain The type's protection domain.
* @return The defined class.
*/
Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain);
A class injection dispatcher that is not yet initialized.
/**
* A class injection dispatcher that is not yet initialized.
*/
interface Initializable {
Checks if unsafe class injection is available on the current VM.
Returns: true
if unsafe class injection is available.
/**
* Checks if unsafe class injection is available on the current VM.
*
* @return {@code true} if unsafe class injection is available.
*/
boolean isAvailable();
Initializes the dispatcher.
Returns: The initialized dispatcher.
/**
* Initializes the dispatcher.
*
* @return The initialized dispatcher.
*/
Dispatcher initialize();
}
A privileged action for creating a dispatcher.
/**
* A privileged action for creating a dispatcher.
*/
enum CreationAction implements PrivilegedAction<Initializable> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
public Initializable run() {
if (Boolean.getBoolean(SAFE_PROPERTY)) {
return new Unavailable("Use of Unsafe was disabled by system property");
}
try {
Class<?> unsafeType = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafeType.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Object unsafe = theUnsafe.get(null);
try {
Method defineClass = unsafeType.getMethod("defineClass",
String.class,
byte[].class,
int.class,
int.class,
ClassLoader.class,
ProtectionDomain.class);
defineClass.setAccessible(true);
return new Enabled(unsafe, defineClass);
} catch (Exception exception) {
try {
Field override;
try {
override = AccessibleObject.class.getDeclaredField("override");
} catch (NoSuchFieldException ignored) {
// Since Java 12, the override field is hidden from the reflection API. To circumvent this, we
// create a mirror class of AccessibleObject that defines the same fields and has the same field
// layout such that the override field will receive the same class offset. Doing so, we can write to
// the offset location and still set a value to it, despite it being hidden from the reflection API.
override = new ByteBuddy()
.redefine(AccessibleObject.class)
.name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName())
.noNestMate()
.visit(new MemberRemoval().stripInvokables(any()))
.make()
.load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded()
.getDeclaredField("override");
}
long offset = (Long) unsafeType
.getMethod("objectFieldOffset", Field.class)
.invoke(unsafe, override);
Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class);
Class<?> internalUnsafe = Class.forName("jdk.internal.misc.Unsafe");
Field theUnsafeInternal = internalUnsafe.getDeclaredField("theUnsafe");
putBoolean.invoke(unsafe, theUnsafeInternal, offset, true);
Method defineClassInternal = internalUnsafe.getMethod("defineClass",
String.class,
byte[].class,
int.class,
int.class,
ClassLoader.class,
ProtectionDomain.class);
putBoolean.invoke(unsafe, defineClassInternal, offset, true);
return new Enabled(theUnsafeInternal.get(null), defineClassInternal);
} catch (Exception ignored) {
throw exception;
}
}
} catch (Exception exception) {
return new Unavailable(exception.getMessage());
}
}
}
An enabled dispatcher.
/**
* An enabled dispatcher.
*/
@HashCodeAndEqualsPlugin.Enhance
class Enabled implements Dispatcher, Initializable {
An instance of sun.misc.Unsafe
. /**
* An instance of {@code sun.misc.Unsafe}.
*/
private final Object unsafe;
The sun.misc.Unsafe#defineClass
method. /**
* The {@code sun.misc.Unsafe#defineClass} method.
*/
private final Method defineClass;
Creates an enabled dispatcher.
Params: - unsafe – An instance of
sun.misc.Unsafe
. - defineClass – The
sun.misc.Unsafe#defineClass
method.
/**
* Creates an enabled dispatcher.
*
* @param unsafe An instance of {@code sun.misc.Unsafe}.
* @param defineClass The {@code sun.misc.Unsafe#defineClass} method.
*/
protected Enabled(Object unsafe, Method defineClass) {
this.unsafe = unsafe;
this.defineClass = defineClass;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAvailable() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
try {
securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS);
} catch (Exception exception) {
return new Unavailable(exception.getMessage());
}
}
return this;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
try {
return (Class<?>) defineClass.invoke(unsafe,
name,
binaryRepresentation,
0,
binaryRepresentation.length,
classLoader,
protectionDomain);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Could not access Unsafe::defineClass", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking Unsafe::defineClass", exception.getCause());
}
}
}
A disabled dispatcher.
/**
* A disabled dispatcher.
*/
@HashCodeAndEqualsPlugin.Enhance
class Unavailable implements Dispatcher, Initializable {
The reason why this dispatcher is not available.
/**
* The reason why this dispatcher is not available.
*/
private final String message;
Creates a disabled dispatcher.
Params: - message – The reason why this dispatcher is not available.
/**
* Creates a disabled dispatcher.
*
* @param message The reason why this dispatcher is not available.
*/
protected Unavailable(String message) {
this.message = message;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAvailable() {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) {
throw new UnsupportedOperationException("Could not access Unsafe class: " + message);
}
}
}
}
A class injector using a Instrumentation
to append to either the boot classpath or the system class path. /**
* A class injector using a {@link java.lang.instrument.Instrumentation} to append to either the boot classpath
* or the system class path.
*/
@HashCodeAndEqualsPlugin.Enhance
class UsingInstrumentation extends AbstractBase {
The jar file name extension.
/**
* The jar file name extension.
*/
private static final String JAR = "jar";
The class file extension.
/**
* The class file extension.
*/
private static final String CLASS_FILE_EXTENSION = ".class";
A dispatcher for interacting with the instrumentation API.
/**
* A dispatcher for interacting with the instrumentation API.
*/
private static final Dispatcher DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
The instrumentation to use for appending to the class path or the boot path.
/**
* The instrumentation to use for appending to the class path or the boot path.
*/
private final Instrumentation instrumentation;
A representation of the target path to which classes are to be appended.
/**
* A representation of the target path to which classes are to be appended.
*/
private final Target target;
The folder to be used for storing jar files.
/**
* The folder to be used for storing jar files.
*/
private final File folder;
A random string generator for creating file names.
/**
* A random string generator for creating file names.
*/
private final RandomString randomString;
Creates an instrumentation-based class injector.
Params: - folder – The folder to be used for storing jar files.
- target – A representation of the target path to which classes are to be appended.
- instrumentation – The instrumentation to use for appending to the class path or the boot path.
- randomString – The random string generator to use.
/**
* Creates an instrumentation-based class injector.
*
* @param folder The folder to be used for storing jar files.
* @param target A representation of the target path to which classes are to be appended.
* @param instrumentation The instrumentation to use for appending to the class path or the boot path.
* @param randomString The random string generator to use.
*/
protected UsingInstrumentation(File folder,
Target target,
Instrumentation instrumentation,
RandomString randomString) {
this.folder = folder;
this.target = target;
this.instrumentation = instrumentation;
this.randomString = randomString;
}
Creates an instrumentation-based class injector.
Params: - folder – The folder to be used for storing jar files.
- target – A representation of the target path to which classes are to be appended.
- instrumentation – The instrumentation to use for appending to the class path or the boot path.
Returns: An appropriate class injector that applies instrumentation.
/**
* Creates an instrumentation-based class injector.
*
* @param folder The folder to be used for storing jar files.
* @param target A representation of the target path to which classes are to be appended.
* @param instrumentation The instrumentation to use for appending to the class path or the boot path.
* @return An appropriate class injector that applies instrumentation.
*/
public static ClassInjector of(File folder, Target target, Instrumentation instrumentation) {
return new UsingInstrumentation(folder, target, instrumentation, new RandomString());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return isAvailable();
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) {
File jarFile = new File(folder, JAR + randomString.nextString() + "." + JAR);
try {
if (!jarFile.createNewFile()) {
throw new IllegalStateException("Cannot create file " + jarFile);
}
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile));
try {
for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) {
jarOutputStream.putNextEntry(new JarEntry(entry.getKey().replace('.', '/') + CLASS_FILE_EXTENSION));
jarOutputStream.write(entry.getValue());
}
} finally {
jarOutputStream.close();
}
target.inject(instrumentation, new JarFile(jarFile));
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
for (String name : types.keySet()) {
result.put(name, Class.forName(name, false, ClassLoader.getSystemClassLoader()));
}
return result;
} catch (IOException exception) {
throw new IllegalStateException("Cannot write jar file to disk", exception);
} catch (ClassNotFoundException exception) {
throw new IllegalStateException("Cannot load injected class", exception);
}
}
Returns true
if this class injector is available on this VM. Returns: true
if this class injector is available on this VM.
/**
* Returns {@code true} if this class injector is available on this VM.
*
* @return {@code true} if this class injector is available on this VM.
*/
public static boolean isAvailable() {
return DISPATCHER.isAlive();
}
A dispatcher to interact with the instrumentation API.
/**
* A dispatcher to interact with the instrumentation API.
*/
protected interface Dispatcher {
Returns true
if this dispatcher is alive. Returns: true
if this dispatcher is alive.
/**
* Returns {@code true} if this dispatcher is alive.
*
* @return {@code true} if this dispatcher is alive.
*/
boolean isAlive();
Appends a jar file to the bootstrap class loader.
Params: - instrumentation – The instrumentation instance to interact with.
- jarFile – The jar file to append.
/**
* Appends a jar file to the bootstrap class loader.
*
* @param instrumentation The instrumentation instance to interact with.
* @param jarFile The jar file to append.
*/
void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
Appends a jar file to the system class loader.
Params: - instrumentation – The instrumentation instance to interact with.
- jarFile – The jar file to append.
/**
* Appends a jar file to the system class loader.
*
* @param instrumentation The instrumentation instance to interact with.
* @param jarFile The jar file to append.
*/
void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
An action to create a dispatcher for interacting with the instrumentation API.
/**
* An action to create a dispatcher for interacting with the instrumentation API.
*/
enum CreationAction implements PrivilegedAction<Dispatcher> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher run() {
try {
return new ForJava6CapableVm(Instrumentation.class.getMethod("appendToBootstrapClassLoaderSearch", JarFile.class),
Instrumentation.class.getMethod("appendToSystemClassLoaderSearch", JarFile.class));
} catch (NoSuchMethodException ignored) {
return ForLegacyVm.INSTANCE;
}
}
}
A dispatcher for a legacy VM that is not capable of appending jar files.
/**
* A dispatcher for a legacy VM that is not capable of appending jar files.
*/
enum ForLegacyVm implements Dispatcher {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
throw new UnsupportedOperationException("The current JVM does not support appending to the bootstrap loader");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
throw new UnsupportedOperationException("The current JVM does not support appending to the system class loader");
}
}
A dispatcher for a VM that is capable of appending to the boot and system class loader.
/**
* A dispatcher for a VM that is capable of appending to the boot and system class loader.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForJava6CapableVm implements Dispatcher {
The Instrumentation#appendToBootstrapClassLoaderSearch
method. /**
* The {@code Instrumentation#appendToBootstrapClassLoaderSearch} method.
*/
private final Method appendToBootstrapClassLoaderSearch;
The Instrumentation#appendToSystemClassLoaderSearch
method. /**
* The {@code Instrumentation#appendToSystemClassLoaderSearch} method.
*/
private final Method appendToSystemClassLoaderSearch;
Creates a new dispatcher for a Java 6 compatible VM.
Params: - appendToBootstrapClassLoaderSearch – The
Instrumentation#appendToBootstrapClassLoaderSearch
method. - appendToSystemClassLoaderSearch – The
Instrumentation#appendToSystemClassLoaderSearch
method.
/**
* Creates a new dispatcher for a Java 6 compatible VM.
*
* @param appendToBootstrapClassLoaderSearch The {@code Instrumentation#appendToBootstrapClassLoaderSearch} method.
* @param appendToSystemClassLoaderSearch The {@code Instrumentation#appendToSystemClassLoaderSearch} method.
*/
protected ForJava6CapableVm(Method appendToBootstrapClassLoaderSearch, Method appendToSystemClassLoaderSearch) {
this.appendToBootstrapClassLoaderSearch = appendToBootstrapClassLoaderSearch;
this.appendToSystemClassLoaderSearch = appendToSystemClassLoaderSearch;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isAlive() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
try {
appendToBootstrapClassLoaderSearch.invoke(instrumentation, jarFile);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) {
try {
appendToSystemClassLoaderSearch.invoke(instrumentation, jarFile);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch", exception.getCause());
}
}
}
}
A representation of the target to which Java classes should be appended to.
/**
* A representation of the target to which Java classes should be appended to.
*/
public enum Target {
Representation of the bootstrap class loader.
/**
* Representation of the bootstrap class loader.
*/
BOOTSTRAP {
@Override
protected void inject(Instrumentation instrumentation, JarFile jarFile) {
DISPATCHER.appendToBootstrapClassLoaderSearch(instrumentation, jarFile);
}
},
Representation of the system class loader.
/**
* Representation of the system class loader.
*/
SYSTEM {
@Override
protected void inject(Instrumentation instrumentation, JarFile jarFile) {
DISPATCHER.appendToSystemClassLoaderSearch(instrumentation, jarFile);
}
};
Adds the given classes to the represented class loader.
Params: - instrumentation – The instrumentation instance to use.
- jarFile – The jar file to append.
/**
* Adds the given classes to the represented class loader.
*
* @param instrumentation The instrumentation instance to use.
* @param jarFile The jar file to append.
*/
protected abstract void inject(Instrumentation instrumentation, JarFile jarFile);
}
}
}