
ru.progrm_jarvis.javacommons.delegate.AsmDelegateFactory Maven / Gradle / Ivy
package ru.progrm_jarvis.javacommons.delegate;
import lombok.experimental.UtilityClass;
import lombok.val;
import lombok.var;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import ru.progrm_jarvis.javacommons.annotation.Internal;
import ru.progrm_jarvis.javacommons.bytecode.CommonBytecodeLibrary;
import ru.progrm_jarvis.javacommons.bytecode.annotation.UsesBytecodeModification;
import ru.progrm_jarvis.javacommons.bytecode.asm.AsmUtil;
import ru.progrm_jarvis.javacommons.cache.Cache;
import ru.progrm_jarvis.javacommons.cache.Caches;
import ru.progrm_jarvis.javacommons.classloading.ClassNamingStrategy;
import ru.progrm_jarvis.javacommons.classloading.GcClassDefiners;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.function.Supplier;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Type.*;
@UsesBytecodeModification(CommonBytecodeLibrary.ASM)
public final class AsmDelegateFactory extends CachingGeneratingDelegateFactory {
/**
* Lookup of this class
*/
private static final @NotNull MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
/**
* Class naming strategy used to allocate names for generated classes
*/
private static final @NotNull ClassNamingStrategy CLASS_NAMING_STRATEGY = ClassNamingStrategy.createPaginated(
AsmDelegateFactory.class.getName() + "$$Generated$$SupplierWrapper$$"
);
/**
* ASM type of {@link Supplier}
*/
private static final @NotNull Type SUPPLIER_TYPE = getType(Supplier.class);
/**
* Name of the generated field in which the lazy will be stored
*/
private static final @NotNull String GENERATED_INNER_LAZY_FIELD_NAME = "$" ,
/**
* Name of {@link Supplier#get()} method
*/
GET_METHOD_NAME = "get" ,
/**
* Internal name of {@link Supplier}
*/
SUPPLIER_INTERNAL_NAME = SUPPLIER_TYPE.getInternalName(),
/**
* Descriptor of {@link Supplier}
*/
SUPPLIER_DESCRIPTOR = SUPPLIER_TYPE.getDescriptor(),
/**
* Descriptor of {@code void(}{@link Supplier}{@code )} method
*/
VOID_SUPPLIER_METHOD_DESCRIPTOR = getMethodDescriptor(VOID_TYPE, SUPPLIER_TYPE);
private AsmDelegateFactory(final @NotNull Cache, DelegateWrapperFactory>> factories) {
super(factories);
}
/**
* Creates an {@link DelegateFactory ASM-based supplier wrapper}.
*
* @return ASM-based supplier wrapper
*
* @apiNote singleton may be used here
*/
public static DelegateFactory create() {
return Singleton.INSTANCE;
}
@Override
protected @NotNull DelegateWrapperFactory createFactory(final @NotNull Class targetType) {
final Constructor extends T> constructor;
{
final Class extends T> generatedClass = generateWrapperClass(targetType);
try {
constructor = generatedClass.getDeclaredConstructor(Supplier.class);
} catch (final NoSuchMethodException e) {
throw new Error("Could not get an empty constructor of the generated class" , e);
}
constructor.setAccessible(true);
}
return supplier -> {
try {
return constructor.newInstance(supplier);
} catch (final IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new Error("Cannot invoke constructor of the generated class" , e);
}
};
}
/**
* Creates a class implementing a delegate-wrapper for the given type.
*
* @param targetClass class for which to create a delegate-wrapper
* @param type of implemented delegate-wrapper
* @return created delegate-wrapper for the given type
*/
private static @NotNull Class extends T> generateWrapperClass(final @NotNull Class targetClass) {
final String className;
val clazz = new ClassWriter(0);
//
{
val internalName = AsmUtil.classNameToInternalName(className = CLASS_NAMING_STRATEGY.get());
{
final Type targetType;
final String
targetInternalName = (targetType = getType(targetClass)).getInternalName(),
superClassInternalName;
if (targetClass.isInterface()) clazz.visit( // implement interface
V1_8, AsmUtil.OPCODES_ACC_PUBLIC_STATIC_FINAL, internalName,
null /* no generics */, superClassInternalName = AsmUtil.OBJECT_INTERNAL_NAME,
new String[]{targetInternalName}
);
else clazz.visit( // extend class
V1_8, AsmUtil.OPCODES_ACC_PUBLIC_STATIC_FINAL, internalName,
null /* no generics */, superClassInternalName = targetInternalName, null
);
//
final String supplierSignature;
clazz.visitField(
AsmUtil.OPCODES_ACC_PUBLIC_FINAL /* safe to use public here */, GENERATED_INNER_LAZY_FIELD_NAME,
SUPPLIER_DESCRIPTOR,
supplierSignature = 'L' + SUPPLIER_INTERNAL_NAME + '<' + targetType.getDescriptor() + ">;" ,
null
).visitEnd();
//
//
{
final MethodVisitor constructor;
(constructor = clazz.visitMethod(
ACC_PUBLIC, AsmUtil.CONSTRUCTOR_METHOD_NAME, VOID_SUPPLIER_METHOD_DESCRIPTOR,
'(' + supplierSignature + ")V" , null /* no exceptions */
)).visitCode();
// push `this` onto the stack
constructor.visitVarInsn(ALOAD, 0);
// duplicate `this` for later use
constructor.visitInsn(DUP);
// invoke constructor of the super-class
constructor.visitMethodInsn(
INVOKESPECIAL, superClassInternalName,
AsmUtil.CONSTRUCTOR_METHOD_NAME, AsmUtil.VOID_METHOD_DESCRIPTOR, false
);
// push `Supplier` parameter onto the stack
constructor.visitVarInsn(ALOAD, 1);
// put parameter into the field
constructor.visitFieldInsn(
PUTFIELD, internalName, GENERATED_INNER_LAZY_FIELD_NAME, SUPPLIER_DESCRIPTOR
);
constructor.visitInsn(RETURN);
constructor.visitMaxs(2 /* this + field value */, 2 /* this + single parameter */);
constructor.visitEnd();
}
//
for (val originalMethod : targetClass.getMethods()) {
if (Modifier.isStatic(originalMethod.getModifiers())) continue;
//
final MethodVisitor method;
final String methodDescriptor, methodName;
{
final String[] exceptions;
{
final Class>[] exceptionTypes;
final int parametersLength;
exceptions = new String[parametersLength = (
exceptionTypes = originalMethod.getExceptionTypes()
).length];
for (var i = 0; i < parametersLength; i++) exceptions[i] = getDescriptor(exceptionTypes[i]);
}
(method = clazz.visitMethod(
ACC_PUBLIC, methodName = originalMethod.getName(),
methodDescriptor = getMethodDescriptor(originalMethod), null, exceptions
)).visitVarInsn(ALOAD, 0); // push `this` onto the stack
}
// get field storing the Supplier
method.visitFieldInsn(GETFIELD, internalName, GENERATED_INNER_LAZY_FIELD_NAME, SUPPLIER_DESCRIPTOR);
// invoke `Supplier#get()` on the field
method.visitMethodInsn(
INVOKEINTERFACE, SUPPLIER_INTERNAL_NAME, GET_METHOD_NAME,
AsmUtil.OBJECT_METHOD_DESCRIPTOR, true
);
{
final int parameterCount;
{
final Class>[] parameterTypes;
parameterCount = (parameterTypes = originalMethod.getParameterTypes()).length;
for (var i = 0; i < parameterCount; i++)
method
.visitVarInsn(AsmUtil.loadOpcode(parameterTypes[i]), i + 1);
}
// Invoke method on the delegate
{
final Class> methodOwner;
val ownerInternalName = getInternalName(methodOwner = originalMethod.getDeclaringClass());
if (methodOwner.isInterface()) method.visitMethodInsn(
INVOKEINTERFACE, ownerInternalName, methodName, methodDescriptor, true
);
else method.visitMethodInsn(
INVOKEVIRTUAL, ownerInternalName, methodName, methodDescriptor, false
);
}
// return from the method
method.visitInsn(AsmUtil.returnOpcode(originalMethod.getReturnType()));
{
final int max; // this + parameters == delegate + parameters
method.visitMaxs(max = 1 + parameterCount, max);
}
}
method.visitEnd();
//
}
}
clazz.visitEnd();
}
//
//noinspection unchecked
return (Class extends T>) GcClassDefiners.getDefault()
.defineClass(LOOKUP, className, clazz.toByteArray());
}
@UtilityClass
@Internal("Safe singleton implementation using class-loading rules for achieving efficient thread-safe laziness")
private static class Singleton {
/**
* Instance of {@link AsmDelegateFactory ASM-based delegate factory}
*/
private final @NotNull DelegateFactory INSTANCE = new AsmDelegateFactory(
Caches.weakKeysCache() // classes are GC-friendly loaded so they may be effectively weakly-referenced
);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy