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);
}
}
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;
}
}
Aggregations