Search in sources :

Example 1 with MethodHandles

use of java.lang.invoke.MethodHandles 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)

Example 2 with MethodHandles

use of java.lang.invoke.MethodHandles in project hive by apache.

the class CleanerUtil method unmapHackImpl.

private static Object unmapHackImpl() {
    final MethodHandles.Lookup lookup = MethodHandles.lookup();
    try {
        try {
            // *** sun.misc.Unsafe unmapping (Java 9+) ***
            final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
            // first check if Unsafe has the right method, otherwise we can
            // give up without doing any security critical stuff:
            final MethodHandle unmapper = lookup.findVirtual(unsafeClass, "invokeCleaner", methodType(void.class, ByteBuffer.class));
            // fetch the unsafe instance and bind it to the virtual MH:
            final Field f = unsafeClass.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            final Object theUnsafe = f.get(null);
            return newBufferCleaner(ByteBuffer.class, unmapper.bindTo(theUnsafe));
        } catch (SecurityException se) {
            // as we also catch RuntimeException below!):
            throw se;
        } catch (ReflectiveOperationException | RuntimeException e) {
            // *** sun.misc.Cleaner unmapping (Java 8) ***
            final Class<?> directBufferClass = Class.forName("java.nio.DirectByteBuffer");
            final Method m = directBufferClass.getMethod("cleaner");
            m.setAccessible(true);
            final MethodHandle directBufferCleanerMethod = lookup.unreflect(m);
            final Class<?> cleanerClass = directBufferCleanerMethod.type().returnType();
            /*
         * "Compile" a MethodHandle that basically is equivalent
         * to the following code:
         *
         * void unmapper(ByteBuffer byteBuffer) {
         *   sun.misc.Cleaner cleaner =
         *       ((java.nio.DirectByteBuffer) byteBuffer).cleaner();
         *   if (Objects.nonNull(cleaner)) {
         *     cleaner.clean();
         *   } else {
         *     // the noop is needed because MethodHandles#guardWithTest
         *     // always needs ELSE
         *     noop(cleaner);
         *   }
         * }
         */
            final MethodHandle cleanMethod = lookup.findVirtual(cleanerClass, "clean", methodType(void.class));
            final MethodHandle nonNullTest = lookup.findStatic(Objects.class, "nonNull", methodType(boolean.class, Object.class)).asType(methodType(boolean.class, cleanerClass));
            final MethodHandle noop = dropArguments(constant(Void.class, null).asType(methodType(void.class)), 0, cleanerClass);
            final MethodHandle unmapper = filterReturnValue(directBufferCleanerMethod, guardWithTest(nonNullTest, cleanMethod, noop)).asType(methodType(void.class, ByteBuffer.class));
            return newBufferCleaner(directBufferClass, unmapper);
        }
    } catch (SecurityException se) {
        return "Unmapping is not supported, because not all required " + "permissions are given to the Hadoop JAR file: " + se + " [Please grant at least the following permissions: " + "RuntimePermission(\"accessClassInPackage.sun.misc\") " + " and ReflectPermission(\"suppressAccessChecks\")]";
    } catch (ReflectiveOperationException | RuntimeException e) {
        return "Unmapping is not supported on this platform, " + "because internal Java APIs are not compatible with " + "this Hadoop version: " + e;
    }
}
Also used : Method(java.lang.reflect.Method) ByteBuffer(java.nio.ByteBuffer) MethodHandles(java.lang.invoke.MethodHandles) Field(java.lang.reflect.Field) MethodHandle(java.lang.invoke.MethodHandle)

Aggregations

MethodHandle (java.lang.invoke.MethodHandle)2 MethodHandles (java.lang.invoke.MethodHandles)2 Field (java.lang.reflect.Field)2 Preconditions.checkNotNull (com.google.common.base.Preconditions.checkNotNull)1 Preconditions.checkState (com.google.common.base.Preconditions.checkState)1 MethodType (java.lang.invoke.MethodType)1 Method (java.lang.reflect.Method)1 Modifier (java.lang.reflect.Modifier)1 ByteBuffer (java.nio.ByteBuffer)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