org.jephyr.activeobject.instrument.ActiveObjectClassAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.jephyr.activeobject.instrument.ActiveObjectClassAdapter.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Igor Konev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.jephyr.activeobject.instrument;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.commons.LocalVariablesSorter;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.ParameterNode;
import org.objectweb.asm.tree.TypeAnnotationNode;

import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_NATIVE;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASM5;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DOUBLE;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.DUP2_X1;
import static org.objectweb.asm.Opcodes.FLOAT;
import static org.objectweb.asm.Opcodes.F_APPEND;
import static org.objectweb.asm.Opcodes.F_NEW;
import static org.objectweb.asm.Opcodes.F_SAME;
import static org.objectweb.asm.Opcodes.F_SAME1;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IF_ACMPNE;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INTEGER;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.LONG;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.POP2;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.SWAP;
import static org.objectweb.asm.Opcodes.TOP;
import static org.objectweb.asm.Opcodes.UNINITIALIZED_THIS;

public final class ActiveObjectClassAdapter extends ClassVisitor {

    public Collection<ClassEntry> classEntries;
    private Collection<MethodNode> methodNodes;
    private int version;
    private String name;
    private String superName;
    private boolean instrument = true;
    private boolean activeObject;
    private Type mailbox = Type.getType("Lorg/jephyr/activeobject/mailbox/SingleConsumerMailboxSupplier;");
    private boolean clinit;
    private int nextTaskNum;

    public ActiveObjectClassAdapter(ClassVisitor cv) {
        super(ASM5, cv);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        this.version = version;
        this.name = name;
        this.superName = superName;
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        AnnotationVisitor av = super.visitAnnotation(desc, visible);
        if (instrument) {
            if (desc.equals("Lorg/jephyr/activeobject/instrument/Instrumented;")) {
                instrument = false;
            } else if (desc.equals("Lorg/jephyr/activeobject/annotation/ActiveObject;")) {
                super.visitAnnotation("Lorg/jephyr/activeobject/instrument/Instrumented;", false);
                activeObject = true;
                classEntries = new ArrayList<>();
                methodNodes = new ArrayList<>();
                return new AnnotationVisitor(ASM5, av) {
                    @Override
                    public void visit(String name, Object value) {
                        if (name.equals("mailbox")) {
                            mailbox = (Type) value;
                        }
                        super.visit(name, value);
                    }
                };
            }
        }
        return av;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if (activeObject) {
            if ((access & ACC_STATIC) != 0 && name.equals("<clinit>") && desc.equals("()V")) {
                clinit = true;
                return new ClinitMethodAdapter(mv);
            } else if (name.charAt(0) == '<') {
                InitMethodAdapter adapter = new InitMethodAdapter(access, desc, mv);
                AnalyzerAdapter analyzerAdapter = new AnalyzerAdapter(this.name, access, name, desc, adapter);
                adapter.adapter = analyzerAdapter;
                return analyzerAdapter;
            } else {
                return new MethodAdapter(access, name, desc, signature, exceptions, mv);
            }
        }
        return mv;
    }

    @Override
    public void visitEnd() {
        if (activeObject) {
            FieldVisitor fv = visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC,
                    "activeObject$mailboxSupplier", "Ljava/util/function/Supplier;", null, null);
            fv.visitEnd();

            FieldVisitor fv1 = visitField(ACC_PRIVATE | ACC_FINAL | ACC_SYNTHETIC, "activeObject$thread",
                    "Lorg/jephyr/activeobject/support/ActiveObjectThread;", null, null);
            fv1.visitEnd();

            if (!clinit) {
                MethodVisitor mv = super.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
                mv.visitCode();
                visitClinit(mv);
                mv.visitInsn(RETURN);
                mv.visitMaxs(2, 0);
                mv.visitEnd();
            }

            MethodVisitor mv1 = super.visitMethod(ACC_PROTECTED, "activeObject$getThread",
                    "()Lorg/jephyr/activeobject/support/ActiveObjectThread;", null, null);
            mv1.visitCode();
            mv1.visitVarInsn(ALOAD, 0);
            mv1.visitFieldInsn(GETFIELD, name, "activeObject$thread",
                    "Lorg/jephyr/activeobject/support/ActiveObjectThread;");
            mv1.visitInsn(ARETURN);
            mv1.visitMaxs(1, 1);
            mv1.visitEnd();

            for (MethodNode node : methodNodes) {
                node.accept(super.visitMethod(node.access, node.name, node.desc, node.signature,
                        node.exceptions.toArray(new String[node.exceptions.size()])));
            }
        }

        super.visitEnd();
    }

    private void visitClinit(MethodVisitor mv) {
        mv.visitLdcInsn(mailbox);
        mv.visitInsn(ICONST_0);
        mv.visitTypeInsn(ANEWARRAY, "java/lang/Class");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getConstructor",
                "([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;", false);
        mv.visitInsn(ICONST_0);
        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Constructor", "newInstance",
                "([Ljava/lang/Object;)Ljava/lang/Object;", false);
        mv.visitTypeInsn(CHECKCAST, "java/util/function/Supplier");
        mv.visitFieldInsn(PUTSTATIC, name, "activeObject$mailboxSupplier", "Ljava/util/function/Supplier;");
    }

    private static void visitPush(MethodVisitor mv, int operand) {
        if (operand <= 5) {
            mv.visitInsn(ICONST_0 + operand);
        } else if (operand <= Byte.MAX_VALUE) {
            mv.visitIntInsn(BIPUSH, operand);
        } else if (operand <= Short.MAX_VALUE) {
            mv.visitIntInsn(SIPUSH, operand);
        } else {
            mv.visitLdcInsn(operand);
        }
    }

    private static void acceptAllBeforeCode(MethodNode methodNode, MethodVisitor mv) {
        if (methodNode.parameters != null) {
            for (ParameterNode node : methodNode.parameters) {
                node.accept(mv);
            }
        }

        if (methodNode.annotationDefault != null) {
            AnnotationVisitor av = mv.visitAnnotationDefault();
            if (av != null) {
                acceptAnnotation(av, null, methodNode.annotationDefault);
                av.visitEnd();
            }
        }

        if (methodNode.visibleAnnotations != null) {
            for (AnnotationNode node : methodNode.visibleAnnotations) {
                node.accept(mv.visitAnnotation(node.desc, true));
            }
        }

        if (methodNode.invisibleAnnotations != null) {
            for (AnnotationNode node : methodNode.invisibleAnnotations) {
                node.accept(mv.visitAnnotation(node.desc, false));
            }
        }

        if (methodNode.visibleTypeAnnotations != null) {
            for (TypeAnnotationNode node : methodNode.visibleTypeAnnotations) {
                node.accept(mv.visitTypeAnnotation(node.typeRef, node.typePath, node.desc, true));
            }
        }

        if (methodNode.invisibleTypeAnnotations != null) {
            for (TypeAnnotationNode node : methodNode.invisibleTypeAnnotations) {
                node.accept(mv.visitTypeAnnotation(node.typeRef, node.typePath, node.desc, false));
            }
        }

        if (methodNode.visibleParameterAnnotations != null) {
            int parameter = 0;
            for (List<AnnotationNode> nodes : methodNode.visibleParameterAnnotations) {
                if (nodes != null) {
                    for (AnnotationNode node : nodes) {
                        node.accept(mv.visitParameterAnnotation(parameter, node.desc, true));
                    }
                }
                parameter++;
            }
        }

        if (methodNode.invisibleParameterAnnotations != null) {
            int parameter = 0;
            for (List<AnnotationNode> nodes : methodNode.invisibleParameterAnnotations) {
                if (nodes != null) {
                    for (AnnotationNode node : nodes) {
                        node.accept(mv.visitParameterAnnotation(parameter, node.desc, false));
                    }
                }
                parameter++;
            }
        }

        if (methodNode.attrs != null) {
            for (Attribute attribute : methodNode.attrs) {
                mv.visitAttribute(attribute);
            }
        }
    }

    private static void acceptAnnotation(AnnotationVisitor av, String name, Object value) {
        if (value instanceof String[]) {
            String[] args = (String[]) value;
            av.visitEnum(name, args[0], args[1]);
        } else if (value instanceof AnnotationNode) {
            AnnotationNode node = (AnnotationNode) value;
            node.accept(av.visitAnnotation(name, node.desc));
        } else if (value instanceof List) {
            AnnotationVisitor av1 = av.visitArray(name);
            if (av1 != null) {
                for (Object value1 : (Iterable<?>) value) {
                    acceptAnnotation(av1, null, value1);
                }
                av1.visitEnd();
            }
        } else {
            av.visit(name, value);
        }
    }

    private static void visitBoxing(MethodVisitor mv, Type type) {
        switch (type.getSort()) {
        case Type.VOID:
            mv.visitInsn(ACONST_NULL);
            break;
        case Type.BOOLEAN:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
            break;
        case Type.CHAR:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
            break;
        case Type.BYTE:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
            break;
        case Type.SHORT:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
            break;
        case Type.INT:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
            break;
        case Type.FLOAT:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
            break;
        case Type.LONG:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
            break;
        case Type.DOUBLE:
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
            break;
        }
    }

    private static void visitUnboxing(MethodVisitor mv, Type type) {
        switch (type.getSort()) {
        case Type.VOID:
            mv.visitInsn(POP);
            break;
        case Type.BOOLEAN:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
            break;
        case Type.CHAR:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
            break;
        case Type.BYTE:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
            break;
        case Type.SHORT:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
            break;
        case Type.INT:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
            break;
        case Type.FLOAT:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
            break;
        case Type.LONG:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
            break;
        case Type.DOUBLE:
            mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "valueOf", "()D", false);
            break;
        default:
            String internalName = type.getInternalName();
            if (!internalName.equals("java/lang/Object")) {
                mv.visitTypeInsn(CHECKCAST, internalName);
            }
        }
    }

    private static void visitFrame(MethodVisitor mv, Type type) {
        switch (type.getSort()) {
        case Type.VOID:
            mv.visitFrame(F_SAME, 0, null, 0, null);
            break;
        case Type.BOOLEAN:
        case Type.CHAR:
        case Type.BYTE:
        case Type.SHORT:
        case Type.INT:
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { INTEGER });
            break;
        case Type.FLOAT:
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { FLOAT });
            break;
        case Type.LONG:
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { LONG });
            break;
        case Type.DOUBLE:
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { DOUBLE });
            break;
        default:
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { type.getInternalName() });
        }
    }

    public static final class ClassEntry {

        public final String name;
        public final byte[] bytes;

        ClassEntry(String name, byte[] bytes) {
            this.name = name;
            this.bytes = bytes;
        }
    }

    private final class ClinitMethodAdapter extends MethodVisitor {

        ClinitMethodAdapter(MethodVisitor mv) {
            super(ASM5, mv);
        }

        @Override
        public void visitCode() {
            super.visitCode();
            visitClinit(this);
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            super.visitMaxs(Math.max(maxStack, 2), maxLocals);
        }
    }

    private final class InitMethodAdapter extends LocalVariablesSorter {

        AnalyzerAdapter adapter;
        private int maxStack;
        private int maxLocals;
        private int thisVarIndex;
        private Object thisVar = UNINITIALIZED_THIS;

        InitMethodAdapter(int access, String desc, MethodVisitor mv) {
            super(ASM5, access, desc, mv);
        }

        @Override
        public void visitCode() {
            super.visitCode();
            visitVarInsn(ALOAD, 0);
            thisVarIndex = newLocal(Type.getObjectType(name));
            mv.visitVarInsn(ASTORE, thisVarIndex);
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
            super.visitMethodInsn(opcode, owner, name, desc, itf);
            if (opcode == INVOKESPECIAL && owner.equals(superName) && name.charAt(0) == '<') {
                mv.visitVarInsn(ALOAD, thisVarIndex);
                super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
                visitLdcInsn(Type.getType('L' + ActiveObjectClassAdapter.this.name + ';'));
                Label label = new Label();
                visitJumpInsn(IF_ACMPNE, label);

                visitTypeInsn(NEW, "org/jephyr/activeobject/support/ActiveObjectThread");
                visitInsn(DUP);
                visitFieldInsn(GETSTATIC, ActiveObjectClassAdapter.this.name, "activeObject$mailboxSupplier",
                        "Ljava/util/function/Supplier;");
                super.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Supplier", "get", "()Ljava/lang/Object;",
                        true);
                visitTypeInsn(CHECKCAST, "org/jephyr/activeobject/mailbox/Mailbox");
                super.visitMethodInsn(INVOKESPECIAL, "org/jephyr/activeobject/support/ActiveObjectThread", "<init>",
                        "(Lorg/jephyr/activeobject/mailbox/Mailbox;)V", false);

                int threadVarIndex = nextLocal;
                mv.visitVarInsn(ASTORE, threadVarIndex);

                mv.visitVarInsn(ALOAD, threadVarIndex);

                visitTypeInsn(NEW, "java/lang/StringBuilder");
                visitInsn(DUP);
                visitPush(this, ActiveObjectClassAdapter.this.name.length() + 9);
                super.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(I)V", false);
                visitLdcInsn(ActiveObjectClassAdapter.this.name.replace('/', '.') + '@');
                super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                        "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                mv.visitVarInsn(ALOAD, thisVarIndex);
                super.visitMethodInsn(INVOKESTATIC, "java/lang/System", "identityHashCode", "(Ljava/lang/Object;)I",
                        false);
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toHexString", "(I)Ljava/lang/String;",
                        false);
                super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                        "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                super.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;",
                        false);

                super.visitMethodInsn(INVOKEVIRTUAL, "org/jephyr/activeobject/support/ActiveObjectThread",
                        "setName", "(Ljava/lang/String;)V", false);

                mv.visitVarInsn(ALOAD, threadVarIndex);
                visitInsn(ICONST_1);
                super.visitMethodInsn(INVOKEVIRTUAL, "org/jephyr/activeobject/support/ActiveObjectThread",
                        "setDaemon", "(Z)V", false);

                super.visitMethodInsn(INVOKESTATIC, "org/jephyr/activeobject/support/Disposer", "defaultDisposer",
                        "()Lorg/jephyr/activeobject/support/Disposer;", false);
                mv.visitVarInsn(ALOAD, thisVarIndex);
                mv.visitVarInsn(ALOAD, threadVarIndex);
                super.visitMethodInsn(INVOKEVIRTUAL, "org/jephyr/activeobject/support/Disposer", "register",
                        "(Ljava/lang/Object;Lorg/jephyr/activeobject/support/Disposable;)V", false);

                mv.visitVarInsn(ALOAD, threadVarIndex);
                super.visitMethodInsn(INVOKEVIRTUAL, "org/jephyr/activeobject/support/ActiveObjectThread", "start",
                        "()V", false);

                mv.visitVarInsn(ALOAD, thisVarIndex);
                mv.visitVarInsn(ALOAD, threadVarIndex);
                visitFieldInsn(PUTFIELD, ActiveObjectClassAdapter.this.name, "activeObject$thread",
                        "Lorg/jephyr/activeobject/support/ActiveObjectThread;");

                visitLabel(label);

                thisVar = TOP;

                Object[] locals = convertValues(adapter.locals);
                Object[] stack = convertValues(adapter.stack.subList(0,
                        adapter.stack.size() - (Type.getArgumentsAndReturnSizes(desc) >> 2)));
                visitFrame(F_NEW, locals.length, locals, stack.length, stack);

                maxStack = adapter.stack.size() + 3;
                maxLocals = threadVarIndex + 1;
            }
        }

        private Object[] convertValues(Collection<?> values) {
            Collection<Object> values1 = new ArrayList<>(values.size());
            Iterator<?> iterator = values.iterator();
            while (iterator.hasNext()) {
                Object value = iterator.next();
                if (value == UNINITIALIZED_THIS) {
                    values1.add(name);
                } else {
                    values1.add(value);
                    if (value == LONG || value == DOUBLE) {
                        iterator.next();
                    }
                }
            }
            return values1.toArray();
        }

        @Override
        public void visitMaxs(int maxStack, int maxLocals) {
            mv.visitMaxs(Math.max(maxStack, this.maxStack), Math.max(nextLocal, this.maxLocals));
        }

        @Override
        protected void updateNewLocals(Object[] newLocals) {
            newLocals[thisVarIndex] = thisVar;
        }
    }

    private final class MethodAdapter extends MethodNode {

        private final MethodVisitor mv;
        private boolean exclude;
        private boolean oneway;
        private long timeout;

        MethodAdapter(int access, String name, String desc, String signature, String[] exceptions,
                MethodVisitor mv) {
            super(ASM5, access, name, desc, signature, exceptions);
            this.mv = mv;
            exclude = (access & ACC_PUBLIC) == 0 || (access & (ACC_STATIC | ACC_NATIVE | ACC_ABSTRACT)) != 0;
        }

        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            AnnotationVisitor av = super.visitAnnotation(desc, visible);
            switch (desc) {
            case "Lorg/jephyr/activeobject/annotation/ActiveMethod;":
                if ((access & (ACC_STATIC | ACC_NATIVE | ACC_ABSTRACT)) != 0) {
                    throw new IllegalStateException(
                            "@ActiveMethod is not allowed on static, native or abstract methods");
                }
                exclude = false;
                return new AnnotationVisitor(ASM5, av) {
                    @Override
                    public void visit(String name, Object value) {
                        if (name.equals("exclude")) {
                            exclude = (boolean) value;
                        }
                        super.visit(name, value);
                    }
                };
            case "Lorg/jephyr/activeobject/annotation/Oneway;":
                if ((access & (ACC_STATIC | ACC_NATIVE | ACC_ABSTRACT)) != 0) {
                    throw new IllegalStateException("@Oneway is not allowed on static, native or abstract methods");
                }
                oneway = true;
                return av;
            case "Lorg/jephyr/activeobject/annotation/Timeout;":
                if ((access & (ACC_STATIC | ACC_NATIVE | ACC_ABSTRACT)) != 0) {
                    throw new IllegalStateException(
                            "@Timeout is not allowed on static, native or abstract methods");
                }
                return new AnnotationVisitor(ASM5, av) {
                    @Override
                    public void visit(String name, Object value) {
                        if (name.equals("value")) {
                            timeout = (long) value;
                        }
                        super.visit(name, value);
                    }
                };
            default:
                return av;
            }
        }

        @Override
        public void visitEnd() {
            if (exclude) {
                accept(mv);
                return;
            }

            nextTaskNum++;
            int taskNum = nextTaskNum;
            String taskMethodName = "activeObject$" + taskNum;
            String taskClassName = ActiveObjectClassAdapter.this.name + "$$ActiveObject$" + taskNum;

            if (oneway) {
                visitOneway(taskMethodName, taskClassName);
            } else {
                visitRegular(taskMethodName, taskClassName);
            }

            // original method

            access = ACC_FINAL | ACC_SYNTHETIC;
            name = taskMethodName;
            parameters = null;
            annotationDefault = null;
            visibleAnnotations = null;
            invisibleAnnotations = null;
            visibleTypeAnnotations = null;
            invisibleTypeAnnotations = null;
            visibleParameterAnnotations = null;
            invisibleParameterAnnotations = null;
            attrs = null;
            methodNodes.add(this);
        }

        private void visitOneway(String taskMethodName, String taskClassName) {
            // task class

            ClassWriter writer = new ClassWriter(0);

            writer.visit(version, ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC, taskClassName, null, "java/lang/Object",
                    new String[] { "java/lang/Runnable" });

            String desc1 = 'L' + ActiveObjectClassAdapter.this.name + ';';

            writer.visitField(ACC_PRIVATE | ACC_FINAL, "arg$" + 1, desc1, null, null);

            Type[] types = Type.getArgumentTypes(desc);
            int fieldNum = 2;

            for (Type type : types) {
                writer.visitField(ACC_PRIVATE | ACC_FINAL, "arg$" + fieldNum, type.getDescriptor(), null, null);
                fieldNum++;
            }

            // <init>

            StringBuilder sb = new StringBuilder().append('(').append(desc1);

            for (Type type : types) {
                sb.append(type.getDescriptor());
            }

            String initDesc = sb.append(')').append('V').toString();

            MethodVisitor mv1 = writer.visitMethod(0, "<init>", initDesc, null, null);

            mv1.visitCode();

            mv1.visitVarInsn(ALOAD, 0);
            mv1.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);

            mv1.visitVarInsn(ALOAD, 0);
            mv1.visitVarInsn(ALOAD, 1);
            mv1.visitFieldInsn(PUTFIELD, taskClassName, "arg$" + 1, desc1);

            int maxStack1 = 2;
            int varIndex = 2;
            int fieldNum1 = 2;

            for (Type type : types) {
                mv1.visitVarInsn(ALOAD, 0);
                mv1.visitVarInsn(type.getOpcode(ILOAD), varIndex);
                mv1.visitFieldInsn(PUTFIELD, taskClassName, "arg$" + fieldNum1, type.getDescriptor());
                int size = type.getSize();
                int stackSize = size + 1;
                if (maxStack1 < stackSize) {
                    maxStack1 = stackSize;
                }
                varIndex += size;
                fieldNum1++;
            }

            mv1.visitInsn(RETURN);

            mv1.visitMaxs(maxStack1, varIndex);
            mv1.visitEnd();

            // run

            MethodVisitor mv2 = writer.visitMethod(ACC_PUBLIC, "run", "()V", null, null);

            mv2.visitCode();

            Label label = new Label();
            Label label1 = new Label();
            Label label2 = new Label();

            mv2.visitTryCatchBlock(label, label1, label2, "java/lang/Throwable");
            mv2.visitLabel(label);
            mv2.visitVarInsn(ALOAD, 0);

            mv2.visitVarInsn(ALOAD, 0);
            mv2.visitFieldInsn(GETFIELD, taskClassName, "arg$" + 1, desc1);

            int maxStack2 = 3;
            int fieldNum2 = 2;

            for (Type type : types) {
                mv2.visitVarInsn(ALOAD, 0);
                mv2.visitFieldInsn(GETFIELD, taskClassName, "arg$" + fieldNum2, type.getDescriptor());
                maxStack2 += type.getSize() + 1;
                fieldNum2++;
            }

            mv2.visitMethodInsn(INVOKEVIRTUAL, ActiveObjectClassAdapter.this.name, taskMethodName, desc, false);

            mv2.visitLabel(label1);
            mv2.visitInsn(RETURN);

            mv2.visitLabel(label2);
            mv2.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });
            mv2.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
            mv2.visitInsn(RETURN);

            mv2.visitMaxs(maxStack2, 1);
            mv2.visitEnd();

            classEntries.add(new ClassEntry(taskClassName, writer.toByteArray()));

            // task method

            acceptAllBeforeCode(this, mv);

            mv.visitCode();
            Label label3 = new Label();
            Label label4 = new Label();
            Label label5 = new Label();
            mv.visitTryCatchBlock(label3, label4, label5, "java/lang/InterruptedException");
            Label label6 = new Label();
            Label label7 = new Label();
            mv.visitTryCatchBlock(label3, label6, label7, null);

            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKEVIRTUAL, ActiveObjectClassAdapter.this.name, "activeObject$getThread",
                    "()Lorg/jephyr/activeobject/support/ActiveObjectThread;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jephyr/activeobject/support/ActiveObjectThread", "getMailbox",
                    "()Lorg/jephyr/activeobject/mailbox/Mailbox;", false);
            int mailboxVarIndex = Type.getArgumentsAndReturnSizes(desc) >> 2;
            mv.visitVarInsn(ASTORE, mailboxVarIndex);

            mv.visitTypeInsn(NEW, taskClassName);
            mv.visitInsn(DUP);

            mv.visitVarInsn(ALOAD, 0);

            int maxStack3 = 3;
            int varIndex2 = 1;

            for (Type type : types) {
                mv.visitVarInsn(type.getOpcode(ILOAD), varIndex2);
                int size = type.getSize();
                maxStack3 += size;
                varIndex2 += size;
            }

            mv.visitMethodInsn(INVOKESPECIAL, taskClassName, "<init>", initDesc, false);

            int taskVarIndex = mailboxVarIndex + 1;

            mv.visitVarInsn(ASTORE, taskVarIndex);

            mv.visitInsn(ICONST_0);

            int interruptedVarIndex = taskVarIndex + 1;

            mv.visitVarInsn(ISTORE, interruptedVarIndex);

            mv.visitLabel(label3);
            mv.visitFrame(F_APPEND, 3,
                    new Object[] { "org/jephyr/activeobject/mailbox/Mailbox", "java/lang/Runnable", INTEGER }, 0,
                    null);

            mv.visitVarInsn(ALOAD, mailboxVarIndex);
            mv.visitVarInsn(ALOAD, taskVarIndex);
            mv.visitMethodInsn(INVOKEINTERFACE, "org/jephyr/activeobject/mailbox/Mailbox", "enqueue",
                    "(Ljava/lang/Runnable;)V", true);

            mv.visitLabel(label4);
            mv.visitJumpInsn(GOTO, label6);

            mv.visitLabel(label5);
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/InterruptedException" });

            mv.visitInsn(POP);
            mv.visitInsn(ICONST_1);
            mv.visitVarInsn(ISTORE, interruptedVarIndex);
            mv.visitJumpInsn(GOTO, label3);

            mv.visitLabel(label6);
            mv.visitFrame(F_SAME, 0, null, 0, null);

            mv.visitVarInsn(ILOAD, interruptedVarIndex);

            Label l6 = new Label();

            mv.visitJumpInsn(IFEQ, l6);

            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);
            mv.visitJumpInsn(GOTO, l6);

            mv.visitLabel(label7);
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

            mv.visitVarInsn(ILOAD, interruptedVarIndex);

            Label l7 = new Label();

            mv.visitJumpInsn(IFEQ, l7);

            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);

            mv.visitLabel(l7);
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

            mv.visitInsn(ATHROW);

            mv.visitLabel(l6);
            mv.visitFrame(F_SAME, 0, null, 0, null);

            mv.visitInsn(RETURN);

            mv.visitMaxs(maxStack3, interruptedVarIndex + 1);
            mv.visitEnd();
        }

        private void visitRegular(String taskMethodName, String taskClassName) {
            // task class

            ClassWriter writer = new ClassWriter(0);

            writer.visit(version, ACC_FINAL | ACC_SUPER | ACC_SYNTHETIC, taskClassName, null,
                    "org/jephyr/activeobject/support/RunnableFutureTask", null);

            String desc1 = 'L' + ActiveObjectClassAdapter.this.name + ';';

            writer.visitField(ACC_PRIVATE | ACC_FINAL, "arg$" + 1, desc1, null, null);

            Type[] types = Type.getArgumentTypes(desc);
            int fieldNum = 2;

            for (Type type : types) {
                writer.visitField(ACC_PRIVATE | ACC_FINAL, "arg$" + fieldNum, type.getDescriptor(), null, null);
                fieldNum++;
            }

            // <init>

            StringBuilder sb = new StringBuilder().append('(').append(desc1);

            for (Type type : types) {
                sb.append(type.getDescriptor());
            }

            String initDesc = sb.append(')').append('V').toString();

            MethodVisitor mv1 = writer.visitMethod(0, "<init>", initDesc, null, null);

            mv1.visitCode();

            mv1.visitVarInsn(ALOAD, 0);
            mv1.visitMethodInsn(INVOKESPECIAL, "org/jephyr/activeobject/support/RunnableFutureTask", "<init>",
                    "()V", false);

            mv1.visitVarInsn(ALOAD, 0);
            mv1.visitVarInsn(ALOAD, 1);
            mv1.visitFieldInsn(PUTFIELD, taskClassName, "arg$" + 1, desc1);

            int maxStack1 = 2;
            int varIndex = 2;
            int fieldNum1 = 2;

            for (Type type : types) {
                mv1.visitVarInsn(ALOAD, 0);
                mv1.visitVarInsn(type.getOpcode(ILOAD), varIndex);
                mv1.visitFieldInsn(PUTFIELD, taskClassName, "arg$" + fieldNum1, type.getDescriptor());
                int size = type.getSize();
                int stackSize = size + 1;
                if (maxStack1 < stackSize) {
                    maxStack1 = stackSize;
                }
                varIndex += size;
                fieldNum1++;
            }

            mv1.visitInsn(RETURN);

            mv1.visitMaxs(maxStack1, varIndex);
            mv1.visitEnd();

            // run

            MethodVisitor mv2 = writer.visitMethod(ACC_PUBLIC, "run", "()V", null, null);

            mv2.visitCode();

            Label label = new Label();
            Label label1 = new Label();
            Label label2 = new Label();

            mv2.visitTryCatchBlock(label, label1, label2, "java/lang/Throwable");
            mv2.visitLabel(label);
            mv2.visitVarInsn(ALOAD, 0);

            mv2.visitVarInsn(ALOAD, 0);
            mv2.visitFieldInsn(GETFIELD, taskClassName, "arg$" + 1, desc1);

            int maxStack2 = 3;
            int fieldNum2 = 2;

            for (Type type : types) {
                mv2.visitVarInsn(ALOAD, 0);
                mv2.visitFieldInsn(GETFIELD, taskClassName, "arg$" + fieldNum2, type.getDescriptor());
                maxStack2 += type.getSize() + 1;
                fieldNum2++;
            }

            mv2.visitMethodInsn(INVOKEVIRTUAL, ActiveObjectClassAdapter.this.name, taskMethodName, desc, false);

            Type returnType = Type.getReturnType(desc);

            visitBoxing(mv2, returnType);

            mv2.visitMethodInsn(INVOKEVIRTUAL, taskClassName, "set", "(Ljava/lang/Object;)Z", false);
            mv2.visitInsn(POP);
            mv2.visitLabel(label1);
            mv2.visitInsn(RETURN);

            mv2.visitLabel(label2);
            mv2.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });
            mv2.visitVarInsn(ALOAD, 0);
            mv2.visitInsn(SWAP);
            mv2.visitMethodInsn(INVOKEVIRTUAL, taskClassName, "setException", "(Ljava/lang/Throwable;)Z", false);
            mv2.visitInsn(POP);
            mv2.visitInsn(RETURN);

            mv2.visitMaxs(maxStack2, 1);
            mv2.visitEnd();

            classEntries.add(new ClassEntry(taskClassName, writer.toByteArray()));

            // task method

            acceptAllBeforeCode(this, mv);

            mv.visitCode();

            Label label3 = new Label();
            Label label4 = new Label();
            Label label5 = new Label();
            mv.visitTryCatchBlock(label3, label4, label5, "java/lang/InterruptedException");
            Label label6 = new Label();
            Label label7 = new Label();
            mv.visitTryCatchBlock(label3, label6, label7, null);

            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKEVIRTUAL, ActiveObjectClassAdapter.this.name, "activeObject$getThread",
                    "()Lorg/jephyr/activeobject/support/ActiveObjectThread;", false);
            int threadVarIndex = Type.getArgumentsAndReturnSizes(desc) >> 2;
            mv.visitVarInsn(ASTORE, threadVarIndex);

            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);
            mv.visitVarInsn(ALOAD, threadVarIndex);
            Label label8 = new Label();
            mv.visitJumpInsn(IF_ACMPNE, label8);

            mv.visitVarInsn(ALOAD, 0);

            int varIndex1 = 1;

            for (Type type : types) {
                mv.visitVarInsn(type.getOpcode(ILOAD), varIndex1);
                varIndex1 += type.getSize();
            }

            mv.visitMethodInsn(INVOKESPECIAL, ActiveObjectClassAdapter.this.name, taskMethodName, desc, false);
            mv.visitInsn(returnType.getOpcode(IRETURN));

            mv.visitLabel(label8);
            mv.visitFrame(F_APPEND, 1, new Object[] { "org/jephyr/activeobject/support/ActiveObjectThread" }, 0,
                    null);

            mv.visitVarInsn(ALOAD, threadVarIndex);
            mv.visitMethodInsn(INVOKEVIRTUAL, "org/jephyr/activeobject/support/ActiveObjectThread", "getMailbox",
                    "()Lorg/jephyr/activeobject/mailbox/Mailbox;", false);
            int mailboxVarIndex = threadVarIndex + 1;
            mv.visitVarInsn(ASTORE, mailboxVarIndex);

            mv.visitTypeInsn(NEW, taskClassName);
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, 0);

            int maxStack3 = 3;
            int varIndex2 = 1;

            for (Type type : types) {
                mv.visitVarInsn(type.getOpcode(ILOAD), varIndex2);
                int size = type.getSize();
                maxStack3 += size;
                varIndex2 += size;
            }

            mv.visitMethodInsn(INVOKESPECIAL, taskClassName, "<init>", initDesc, false);
            int taskVarIndex = mailboxVarIndex + 1;
            mv.visitVarInsn(ASTORE, taskVarIndex);

            mv.visitInsn(ICONST_0);
            int interruptedVarIndex = taskVarIndex + 1;
            mv.visitVarInsn(ISTORE, interruptedVarIndex);

            mv.visitLabel(label3);
            mv.visitFrame(F_APPEND, 3, new Object[] { "org/jephyr/activeobject/mailbox/Mailbox",
                    "java/util/concurrent/RunnableFuture", INTEGER }, 0, null);

            mv.visitVarInsn(ALOAD, mailboxVarIndex);
            mv.visitVarInsn(ALOAD, taskVarIndex);
            mv.visitMethodInsn(INVOKEINTERFACE, "org/jephyr/activeobject/mailbox/Mailbox", "enqueue",
                    "(Ljava/lang/Runnable;)V", true);

            mv.visitLabel(label4);
            mv.visitJumpInsn(GOTO, label6);

            mv.visitLabel(label5);
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/InterruptedException" });

            mv.visitInsn(POP);
            mv.visitInsn(ICONST_1);
            mv.visitVarInsn(ISTORE, interruptedVarIndex);
            mv.visitJumpInsn(GOTO, label3);

            mv.visitLabel(label6);
            mv.visitFrame(F_SAME, 0, null, 0, null);

            mv.visitVarInsn(ILOAD, interruptedVarIndex);
            Label label9 = new Label();
            mv.visitJumpInsn(IFEQ, label9);

            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);
            mv.visitJumpInsn(GOTO, label9);

            mv.visitLabel(label7);
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

            mv.visitVarInsn(ILOAD, interruptedVarIndex);
            Label label10 = new Label();
            mv.visitJumpInsn(IFEQ, label10);

            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);

            mv.visitLabel(label10);
            mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

            mv.visitInsn(ATHROW);

            mv.visitLabel(label9);
            mv.visitFrame(F_SAME, 0, null, 0, null);

            if (timeout > 0) {
                Label label11 = new Label();
                Label label12 = new Label();
                Label label13 = new Label();
                mv.visitTryCatchBlock(label11, label12, label13, "java/lang/InterruptedException");
                Label label14 = new Label();
                mv.visitTryCatchBlock(label11, label12, label14, "java/util/concurrent/ExecutionException");
                mv.visitTryCatchBlock(label13, label14, label14, "java/util/concurrent/ExecutionException");
                Label label15 = new Label();
                mv.visitTryCatchBlock(label11, label12, label15, "java/util/concurrent/TimeoutException");
                mv.visitTryCatchBlock(label13, label14, label15, "java/util/concurrent/TimeoutException");
                Label label16 = new Label();
                mv.visitTryCatchBlock(label11, label12, label16, null);
                mv.visitTryCatchBlock(label13, label16, label16, null);

                mv.visitInsn(ICONST_0);
                mv.visitVarInsn(ISTORE, interruptedVarIndex);

                mv.visitLabel(label11);
                mv.visitFrame(F_SAME, 0, null, 0, null);

                mv.visitVarInsn(ALOAD, taskVarIndex);
                mv.visitLdcInsn(timeout);
                mv.visitFieldInsn(GETSTATIC, "java/util/concurrent/TimeUnit", "MILLISECONDS",
                        "Ljava/util/concurrent/TimeUnit;");
                mv.visitMethodInsn(INVOKEINTERFACE, "java/util/concurrent/RunnableFuture", "get",
                        "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;", true);

                visitUnboxing(mv, returnType);

                mv.visitLabel(label12);
                mv.visitVarInsn(ILOAD, interruptedVarIndex);
                Label label17 = new Label();
                mv.visitJumpInsn(IFEQ, label17);

                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;",
                        false);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);

                mv.visitLabel(label17);

                ActiveObjectClassAdapter.visitFrame(mv, returnType);

                mv.visitInsn(returnType.getOpcode(IRETURN));

                mv.visitLabel(label13);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/InterruptedException" });

                mv.visitInsn(POP);
                mv.visitInsn(ICONST_1);
                mv.visitVarInsn(ISTORE, interruptedVarIndex);
                mv.visitJumpInsn(GOTO, label11);

                mv.visitLabel(label14);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/util/concurrent/ExecutionException" });

                mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/concurrent/ExecutionException", "getCause",
                        "()Ljava/lang/Throwable;", false);
                mv.visitInsn(ATHROW);

                mv.visitLabel(label15);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/util/concurrent/TimeoutException" });

                mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
                mv.visitInsn(DUP);
                mv.visitInsn(DUP2_X1);
                mv.visitInsn(POP2);
                mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalStateException", "<init>",
                        "(Ljava/lang/Throwable;)V", false);
                mv.visitInsn(ATHROW);

                mv.visitLabel(label16);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

                mv.visitVarInsn(ILOAD, interruptedVarIndex);
                Label label18 = new Label();
                mv.visitJumpInsn(IFEQ, label18);

                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;",
                        false);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);

                mv.visitLabel(label18);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

                mv.visitInsn(ATHROW);

                mv.visitMaxs(Math.max(maxStack3, 5), interruptedVarIndex + 1);
            } else {
                Label label11 = new Label();
                Label label12 = new Label();
                Label label13 = new Label();
                mv.visitTryCatchBlock(label11, label12, label13, "java/lang/InterruptedException");
                Label label14 = new Label();
                mv.visitTryCatchBlock(label11, label12, label14, "java/util/concurrent/ExecutionException");
                mv.visitTryCatchBlock(label13, label14, label14, "java/util/concurrent/ExecutionException");
                Label label15 = new Label();
                mv.visitTryCatchBlock(label11, label12, label15, null);
                mv.visitTryCatchBlock(label13, label15, label15, null);

                mv.visitInsn(ICONST_0);
                mv.visitVarInsn(ISTORE, interruptedVarIndex);

                mv.visitLabel(label11);
                mv.visitFrame(F_SAME, 0, null, 0, null);

                mv.visitVarInsn(ALOAD, taskVarIndex);
                mv.visitMethodInsn(INVOKEINTERFACE, "java/util/concurrent/RunnableFuture", "get",
                        "()Ljava/lang/Object;", true);

                visitUnboxing(mv, returnType);

                mv.visitLabel(label12);
                mv.visitVarInsn(ILOAD, interruptedVarIndex);
                Label label16 = new Label();
                mv.visitJumpInsn(IFEQ, label16);

                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;",
                        false);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);

                mv.visitLabel(label16);

                ActiveObjectClassAdapter.visitFrame(mv, returnType);

                mv.visitInsn(returnType.getOpcode(IRETURN));

                mv.visitLabel(label13);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/InterruptedException" });

                mv.visitInsn(POP);
                mv.visitInsn(ICONST_1);
                mv.visitVarInsn(ISTORE, interruptedVarIndex);
                mv.visitJumpInsn(GOTO, label11);

                mv.visitLabel(label14);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/util/concurrent/ExecutionException" });

                mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/concurrent/ExecutionException", "getCause",
                        "()Ljava/lang/Throwable;", false);
                mv.visitInsn(ATHROW);

                mv.visitLabel(label15);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

                mv.visitVarInsn(ILOAD, interruptedVarIndex);
                Label label17 = new Label();
                mv.visitJumpInsn(IFEQ, label17);

                mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;",
                        false);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "interrupt", "()V", false);

                mv.visitLabel(label17);
                mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });

                mv.visitInsn(ATHROW);

                mv.visitMaxs(maxStack3, interruptedVarIndex + 1);
            }

            mv.visitEnd();
        }
    }
}