ByteBufferCompat.java
package org.caffinitas.ohc.util;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import static org.objectweb.asm.Opcodes.*;
/**
* Helper class that enables compilation with any Java version (e.g. 15) and being able to access
* any {@link ByteBuffer} method.
*/
public final class ByteBufferCompat {
private ByteBufferCompat() {
}
private static final ByteBufferAccess byteBufferAccess;
static {
try {
Method m = ByteBuffer.class.getMethod("flip");
int classFileVersion = V1_8;
if (m.getReturnType() == ByteBuffer.class)
classFileVersion = V11;
// Generates a class named 'org.caffinitas.ohc.util.ByteBufferHelper' that
// implements the ByteBufferAccess interface and acts as a bridge to
// 'java.nio.ByteBuffer' using the correct signatures for BB.flip/position/clear/limit
// methods, which got their signatures changed in Java 9.
String owner = m.getReturnType().getName();
String ownerSlash = owner.replace('.', '/');
String ownerDesc = Type.getDescriptor(m.getReturnType());
String classNameDots = "org.caffinitas.ohc.util.ByteBufferHelper";
String classNameSlash = classNameDots.replace('.', '/');
ClassWriter cw = new ClassWriter(0);
cw.visit(classFileVersion,
ACC_PUBLIC + ACC_SUPER + ACC_FINAL,
classNameSlash,
null,
"java/lang/Object",
new String[]{ByteBufferAccess.class.getName().replace('.', '/')});
cw.visitSource("ByteBufferHelper.java", null);
String bbDesc = Type.getDescriptor(ByteBuffer.class);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL,
"java/lang/Object",
"<init>",
"()V",
false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "flip", "("+bbDesc+")V", null, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, ownerSlash, "flip", "()"+ownerDesc, false);
mv.visitInsn(POP);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clear", "("+bbDesc+")V", null, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, ownerSlash, "clear", "()"+ownerDesc, false);
mv.visitInsn(POP);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "limit", "("+bbDesc+"I)V", null, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, ownerSlash, "limit", "(I)"+ownerDesc, false);
mv.visitInsn(POP);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "position", "("+bbDesc+"I)V", null, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, ownerSlash, "position", "(I)"+ownerDesc, false);
mv.visitInsn(POP);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
byte[] classBytes = cw.toByteArray();
ClassLoader cl = new ClassLoader(ByteBufferCompat.class.getClassLoader()) {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (classNameDots.equals(name))
return defineClass(name, classBytes, 0, classBytes.length);
throw new ClassNotFoundException(name);
}
};
byteBufferAccess = (ByteBufferAccess) cl.loadClass(classNameDots).getDeclaredConstructor().newInstance();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public interface ByteBufferAccess {
void flip(ByteBuffer byteBuffer);
void clear(ByteBuffer byteBuffer);
void limit(ByteBuffer byteBuffer, int limit);
void position(ByteBuffer byteBuffer, int position);
}
public static void byteBufferFlip(ByteBuffer byteBuffer) {
byteBufferAccess.flip(byteBuffer);
}
public static void byteBufferClear(ByteBuffer byteBuffer) {
byteBufferAccess.clear(byteBuffer);
}
public static void byteBufferLimit(ByteBuffer byteBuffer, int limit) {
byteBufferAccess.limit(byteBuffer, limit);
}
public static void byteBufferPosition(ByteBuffer byteBuffer, int position) {
byteBufferAccess.position(byteBuffer, position);
}
}