I need a way to access fields in a reflective nature without the performance hits from standard reflection. I have figured out how to do this with methods/constructors via LambdaMetaFactory using a privileged lookup handle, however, I can't seem to figure out how to gain field access.
I thought I could generate an inner class via something like javaassist which should theoretically have access to that field but that did not work out, throwing an IllegalAccessError.
If I could redefine the class the task would be trivial as I could generate getter/setter methods. However, for the project I am working on, I am unable to use an agent as it would need to be loaded at runtime and I would have to dynamically import the attach api from tools.
Could anyone guide me in the right direction here? I've looked into how LambdaMetaFactory generates it's interface for methods and tried to mirror that with fields with no success. Is there something internally different with fields and methods that makes this task impossible without redefinition?
In case of OpendJDK (and JDKs built atop it), the LambdaMetaFactory
generates a mostly ordinary class file (just accessing private
member(s) of another class) and uses a special method in sun.misc.Unsafe
, to create an anonymous class.
Creating a similar class file accessing a field, is straight-forward and creating an anonymous class with it, does work, as can be demonstrated with the following quick&dirty program:
public class Generator {
public static void main(String[] args) throws Throwable {
ToIntFunction<Thread> ft=generateIntFieldAccessor(Thread.class, "threadStatus");
System.out.println(ft.applyAsInt(Thread.currentThread()));
}
private static <X> ToIntFunction<X> generateIntFieldAccessor(
Class<? super X> c, String name) throws Throwable {
byte[] code = Generator.generateIntReaderCode(c.getDeclaredField(name));
Class<?> unsafe = Class.forName("sun.misc.Unsafe");
Field u = unsafe.getDeclaredField("theUnsafe");
u.setAccessible(true);
Object theUnsafe = u.get(null);
Class<ToIntFunction<X>> gen = (Class<ToIntFunction<X>>)
MethodHandles.publicLookup().bind(theUnsafe, "defineAnonymousClass",
MethodType.methodType(
Class.class, Class.class, byte[].class, Object[].class))
.invokeExact(c, code, (Object[])null);
return gen.getConstructor().newInstance();
}
private static final String HEAD = "Êþº¾\0\0\0004\0\24\7\0\21\7\0\t\7\0\n\7\0\22"
+ "\n\0\2\0\6\f\0\13\0\f\t\0\4\0\b\f\0\23\0\20\1\0\20java/lang/Object\1\0\40"
+ "java/util/function/ToIntFunction\1\0\6<init>\1\0\3()V\1\0\4Code\1\0\n"
+ "applyAsInt\1\0\25(Ljava/lang/Object;)I\1\0\1I";
private static final String TAIL = "\0001\0\1\0\2\0\1\0\3\0\0\0\2\0\1\0\13\0\f\0"
+ "\1\0\r\0\0\0\21\0\1\0\1\0\0\0\5*·\0\5±\0\0\0\0\0\21\0\16\0\17\0\1\0\r\0\0"
+ "\0\24\0\1\0\2\0\0\0\b+À\0\4´\0\7¬\0\0\0\0\0\0";
public static byte[] generateIntReaderCode(Field f) {
return new ByteArrayOutputStream(HEAD.length() + TAIL.length() + 100) {
@SuppressWarnings("deprecation") byte[] get() {
HEAD.getBytes(0, count = HEAD.length(), buf, 0);
try(DataOutputStream dos = new DataOutputStream(this)) {
String decl = f.getDeclaringClass().getName().replace('.', '/');
dos.writeByte(1); dos.writeUTF(decl+"$"+f.getName()+"$access");
dos.writeByte(1); dos.writeUTF(decl);
dos.writeByte(1); dos.writeUTF(f.getName());
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
int dynSize = count;
byte[] result = Arrays.copyOf(buf, dynSize + TAIL.length());
TAIL.getBytes(0, TAIL.length(), result, dynSize);
return result;
}
}.get();
}
}
Demo on Ideone
Of course, for production code you should better resort to one of the commonly used code generation libraries, to have maintainable factory code. E.g., OpenJDK’s LambdaMetaFactory
uses the ASM library under the hood.
If your attempt to implement a similar solution failed, you have to post what you’ve tried, so we can help identifying the problem. But perhaps, knowing that it is possible in general, does already help you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With