Transformer implements ClassFileTransformer { @Override public byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException { try { if (className != null && className.startsWith("java")) { return null; } ClassReader cr = new ClassReader(classfileBuffer); if (needsInstrumentation(cr)) { return transform(loader, cr); } return null; } catch (Exception | Error e) { final RuntimeException exception = new RuntimeException("Error instrumenting: " + className, e); exception.printStackTrace(); throw exception; } }  @Override byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer); byte[] instrument(final ClassLoader classLoader, InputStream inputStream); byte[] transform(final ClassLoader classLoader, ClassReader cr); void setErrorListener(final Consumer<String> errorListener); static final String ASYNC_METHOD_NAME; static final String ASYNC_INIT_METHOD_DESC; static final String ASYNC_INIT_METHOD_NAME; static final String JOIN_METHOD_NAME; static final String JOIN_METHOD_DESC; static final String LAMBDA_DESC; static final Handle METAFACTORY_HANDLE; static WeakHashMap<URL, Boolean> futureMap; static WeakHashMap<ClassLoader, Set<URL>> classLoaderMap; }
@Test @SuppressWarnings("unchecked") public void simpleAsyncMethod() throws Exception { final ClassNode cn = createClassNode(AsyncFunction.class, cw -> { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "apply", "(Ljava/lang/Object;)Lcom/ea/async/Task;", null, new String[]{ "java/lang/Exception" }); mv.visitCode(); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, COM_EA_ASYNC_TASK); mv.visitMethodInsn(INVOKESTATIC, ASYNC_NAME, "await", "(Ljava/util/concurrent/CompletableFuture;)Ljava/lang/Object;", false); mv.visitInsn(POP); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, COM_EA_ASYNC_TASK); mv.visitInsn(ARETURN); mv.visitMaxs(1, 2); mv.visitEnd(); }); ClassWriter cw = new ClassWriter(0); cn.accept(cw); final byte[] bytes = new Transformer().transform(getClass().getClassLoader(), new ClassReader(cw.toByteArray())); DevDebug.debugSaveTrace(cn.name, bytes); assertEquals("hello", createClass(AsyncFunction.class, bytes).apply(Task.fromValue("hello")).join()); }
@Test @SuppressWarnings("unchecked") public void withLocals() throws Exception { final ClassNode cn = createClassNode(AsyncFunction.class, cw -> { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "apply", "(Ljava/lang/Object;)L"+COM_EA_ASYNC_TASK+";", null, new String[]{ "java/lang/Exception" }); mv.visitCode(); mv.visitIntInsn(SIPUSH, 1); mv.visitVarInsn(ISTORE, 2); mv.visitInsn(LCONST_0); mv.visitVarInsn(LSTORE, 3); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, COM_EA_ASYNC_TASK); mv.visitMethodInsn(INVOKESTATIC, ASYNC_NAME, "await", "(Ljava/util/concurrent/CompletableFuture;)Ljava/lang/Object;", false); mv.visitInsn(POP); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, COM_EA_ASYNC_TASK); mv.visitInsn(ARETURN); mv.visitMaxs(2, 5); mv.visitEnd(); }); ClassWriter cw = new ClassWriter(0); cn.accept(cw); final byte[] bytes = new Transformer().transform(getClass().getClassLoader(), new ClassReader(cw.toByteArray())); assertEquals("hello", createClass(AsyncFunction.class, bytes).apply(Task.fromValue("hello")).join()); }
@Test(timeout = 10_000L) @SuppressWarnings("unchecked") public void withTwoFutures() throws Exception { final ClassNode cn = createClassNode(AsyncBiFunction.class, cw -> { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "apply", "(Ljava/lang/Object;Ljava/lang/Object;)L"+COM_EA_ASYNC_TASK+";", null, new String[]{ "java/lang/Exception" }); mv.visitCode(); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, COM_EA_ASYNC_TASK); mv.visitMethodInsn(INVOKESTATIC, ASYNC_NAME, "await", "(Ljava/util/concurrent/CompletableFuture;)Ljava/lang/Object;", false); mv.visitInsn(POP); mv.visitVarInsn(ALOAD, 2); mv.visitTypeInsn(CHECKCAST, COM_EA_ASYNC_TASK); mv.visitMethodInsn(INVOKESTATIC, ASYNC_NAME, "await", "(Ljava/util/concurrent/CompletableFuture;)Ljava/lang/Object;", false); mv.visitInsn(POP); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, COM_EA_ASYNC_TASK); mv.visitInsn(ARETURN); mv.visitMaxs(2, 5); mv.visitEnd(); }); ClassWriter cw = new ClassWriter(0); cn.accept(cw); final byte[] bytes = new Transformer().transform(getClass().getClassLoader(), new ClassReader(cw.toByteArray())); DevDebug.debugSaveTrace(cn.name, bytes); assertEquals("hello", createClass(AsyncBiFunction.class, bytes).apply(Task.fromValue("hello"), Task.fromValue("world")).join()); final Task rest = createClass(AsyncBiFunction.class, bytes).apply(getBlockedTask("hello"), getBlockedTask("world")); completeFutures(); assertEquals("hello", rest.join()); }