Search in sources :

Example 1 with ACC_SUPER

use of org.objectweb.asm.Opcodes.ACC_SUPER in project LanternServer by LanternPowered.

the class FieldAccessFactory method createSetter.

/**
 * Creates a setter {@link BiConsumer} for the given {@link Field}.
 *
 * @param field The field
 * @param <T> The target object type
 * @param <V> The field value type
 * @return The bi consumer
 */
public static <T, V> BiConsumer<T, V> createSetter(Field field) {
    checkNotNull(field, "field");
    field.setAccessible(true);
    boolean isFinal = Modifier.isFinal(field.getModifiers());
    // Better check is somebody changed the final modifier already
    if (!isFinal) {
        final Field[] fields = field.getDeclaringClass().getDeclaredFields();
        boolean isFound = false;
        for (Field field1 : fields) {
            // The same signature, now check if somebody tinkered with the field
            if (field.getName().equals(field1.getName()) && field.getType().equals(field1.getType())) {
                isFinal = Modifier.isFinal(field1.getModifiers());
                isFound = true;
                break;
            }
        }
        if (!isFound) {
            throw new IllegalStateException("Something funky happened with: " + field.getName());
        }
    } else {
        try {
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }
    // Final fields don't allow direct access, so MethodHandles will do the trick.
    if (isFinal) {
        try {
            final MethodHandle methodHandle = MethodHandleMagic.trustedLookup().in(field.getDeclaringClass()).unreflectSetter(field).asType(setterMethodType);
            return (a, b) -> {
                try {
                    methodHandle.invokeExact(a, b);
                } catch (Throwable throwable) {
                    throw new IllegalStateException(throwable);
                }
            };
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }
    final ClassWriter cw = new ClassWriter(0);
    final String className = field.getName().replace('.', '/') + "$$LanternSetter$" + setterCounter.incrementAndGet();
    cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, className, "Ljava/lang/Object;Ljava/util/function/BiConsumer<Ljava/lang/Object;Ljava/lang/Object;>;", "java/lang/Object", new String[] { "java/util/function/BiConsumer" });
    // Add a empty constructor
    BytecodeUtils.visitEmptyConstructor(cw);
    // Generate the apply method
    final MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "accept", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null);
    mv.visitCode();
    final String descriptor = Type.getDescriptor(field.getType());
    final String targetName = Type.getInternalName(field.getDeclaringClass());
    final boolean isStatic = Modifier.isStatic(field.getModifiers());
    if (!isStatic) {
        // Load the target parameter
        mv.visitVarInsn(ALOAD, 1);
        // Cast it
        mv.visitTypeInsn(CHECKCAST, targetName);
    }
    // Load the value parameter
    mv.visitVarInsn(ALOAD, 2);
    // Unbox the values in case they are primitives, otherwise cast
    GeneratorUtils.visitUnboxingMethod(mv, Type.getType(field.getType()));
    // Put the value into the field
    if (isStatic) {
        mv.visitFieldInsn(PUTSTATIC, targetName, field.getName(), descriptor);
    } else {
        mv.visitFieldInsn(PUTFIELD, targetName, field.getName(), descriptor);
    }
    // Return
    mv.visitInsn(RETURN);
    mv.visitMaxs(2, 3);
    mv.visitEnd();
    // Finish class generation
    cw.visitEnd();
    // Define the class and create a function instance
    final MethodHandles.Lookup lookup = MethodHandleMagic.trustedLookup().in(field.getDeclaringClass());
    final Class<?> functionClass = MethodHandleMagic.defineNestmateClass(lookup, cw.toByteArray());
    try {
        return (BiConsumer<T, V>) functionClass.newInstance();
    } catch (Exception e) {
        throw new IllegalStateException("Something went wrong!", e);
    }
}
Also used : ClassWriter(org.objectweb.asm.ClassWriter) MethodHandle(java.lang.invoke.MethodHandle) MethodVisitor(org.objectweb.asm.MethodVisitor) ACC_SUPER(org.objectweb.asm.Opcodes.ACC_SUPER) GETSTATIC(org.objectweb.asm.Opcodes.GETSTATIC) GeneratorUtils(org.spongepowered.api.util.generator.GeneratorUtils) V1_8(org.objectweb.asm.Opcodes.V1_8) Type(org.objectweb.asm.Type) Function(java.util.function.Function) Supplier(java.util.function.Supplier) PUTFIELD(org.objectweb.asm.Opcodes.PUTFIELD) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) BiConsumer(java.util.function.BiConsumer) CHECKCAST(org.objectweb.asm.Opcodes.CHECKCAST) MethodHandles(java.lang.invoke.MethodHandles) Preconditions.checkNotNull(com.google.common.base.Preconditions.checkNotNull) Field(java.lang.reflect.Field) Preconditions.checkState(com.google.common.base.Preconditions.checkState) GETFIELD(org.objectweb.asm.Opcodes.GETFIELD) Consumer(java.util.function.Consumer) MethodType(java.lang.invoke.MethodType) RETURN(org.objectweb.asm.Opcodes.RETURN) ACC_PUBLIC(org.objectweb.asm.Opcodes.ACC_PUBLIC) PUTSTATIC(org.objectweb.asm.Opcodes.PUTSTATIC) Modifier(java.lang.reflect.Modifier) ARETURN(org.objectweb.asm.Opcodes.ARETURN) ALOAD(org.objectweb.asm.Opcodes.ALOAD) ClassWriter(org.objectweb.asm.ClassWriter) MethodVisitor(org.objectweb.asm.MethodVisitor) Field(java.lang.reflect.Field) MethodHandles(java.lang.invoke.MethodHandles) BiConsumer(java.util.function.BiConsumer) MethodHandle(java.lang.invoke.MethodHandle)

Aggregations

Preconditions.checkNotNull (com.google.common.base.Preconditions.checkNotNull)1 Preconditions.checkState (com.google.common.base.Preconditions.checkState)1 MethodHandle (java.lang.invoke.MethodHandle)1 MethodHandles (java.lang.invoke.MethodHandles)1 MethodType (java.lang.invoke.MethodType)1 Field (java.lang.reflect.Field)1 Modifier (java.lang.reflect.Modifier)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1 BiConsumer (java.util.function.BiConsumer)1 Consumer (java.util.function.Consumer)1 Function (java.util.function.Function)1 Supplier (java.util.function.Supplier)1 ClassWriter (org.objectweb.asm.ClassWriter)1 MethodVisitor (org.objectweb.asm.MethodVisitor)1 ACC_PUBLIC (org.objectweb.asm.Opcodes.ACC_PUBLIC)1 ACC_SUPER (org.objectweb.asm.Opcodes.ACC_SUPER)1 ALOAD (org.objectweb.asm.Opcodes.ALOAD)1 ARETURN (org.objectweb.asm.Opcodes.ARETURN)1 CHECKCAST (org.objectweb.asm.Opcodes.CHECKCAST)1 GETFIELD (org.objectweb.asm.Opcodes.GETFIELD)1