org.jephyr.easyflow.instrument.ContinuationMethodAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.jephyr.easyflow.instrument.ContinuationMethodAdapter.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.easyflow.instrument;

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

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.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;

import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
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.DCONST_0;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DOUBLE;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DSTORE;
import static org.objectweb.asm.Opcodes.DUP_X2;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FLOAT;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.FSTORE;
import static org.objectweb.asm.Opcodes.F_NEW;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFNULL;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INTEGER;
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.LCONST_0;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LONG;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.LSTORE;
import static org.objectweb.asm.Opcodes.MONITORENTER;
import static org.objectweb.asm.Opcodes.MONITOREXIT;
import static org.objectweb.asm.Opcodes.NULL;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.POP2;
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;

final class ContinuationMethodAdapter extends AnalyzingMethodNode {

    private static final Object[] EMPTY_OBJECTS = new Object[0];

    private final String owner;
    private final MethodVisitor mv;

    private ContinuationMethodAdapter(String owner, int access, String name, String desc, String signature,
            String[] exceptions, MethodVisitor mv) {
        super(access, name, desc, signature, exceptions);
        this.owner = owner;
        this.mv = mv;
    }

    static MethodVisitor create(String owner, int access, String name, String desc, String signature,
            String[] exceptions, MethodVisitor mv) {
        ContinuationMethodAdapter adapter = new ContinuationMethodAdapter(owner, access, name, desc, signature,
                exceptions, mv);
        AnalyzerAdapter analyzerAdapter = new AnalyzerAdapter(owner, access, name, desc, adapter);
        adapter.adapter = analyzerAdapter;
        return analyzerAdapter;
    }

    @Override
    public void visitEnd() {
        List<MethodInsnNode> nodes = findNodes();

        if (nodes.isEmpty()) {
            accept(mv);
            return;
        }

        int implVarIndex = maxLocals;
        maxLocals += 1;

        Object[] initialLocals = appendValue(ensureSize(frames.get(instructions.getFirst()).locals, implVarIndex),
                "org/jephyr/continuation/easyflow/ContinuationImpl");

        updateFrames(implVarIndex);
        addMonitorHooks(implVarIndex);

        LabelNode labelNode = newLabelNode();

        instructions.insert(labelNode);

        if (!isNextFrameNode(labelNode)) {
            instructions.insert(labelNode, newFrameNode(initialLocals, EMPTY_OBJECTS));
        }

        addInvocationEndedHook(implVarIndex, labelNode);

        instructions.insertBefore(labelNode,
                new MethodInsnNode(INVOKESTATIC, "org/jephyr/continuation/easyflow/ContinuationImpl", "currentImpl",
                        "()Lorg/jephyr/continuation/easyflow/ContinuationImpl;", false));
        instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, implVarIndex));

        instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
        instructions.insertBefore(labelNode, new JumpInsnNode(IFNULL, labelNode));

        instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
        instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                "org/jephyr/continuation/easyflow/ContinuationImpl", "isSuspended", "()Z", false));

        LabelNode labelNode1 = newLabelNode();

        instructions.insertBefore(labelNode, new JumpInsnNode(IFEQ, labelNode1));

        LabelNode defaultLabelNode = newLabelNode();
        int size = nodes.size();
        int length = size - 1;
        LabelNode[] labelNodes = new LabelNode[length];

        for (int i = 0; i < length; i++) {
            labelNodes[i] = newLabelNode();
        }

        if (length > 0) {
            instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
            instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                    "org/jephyr/continuation/easyflow/ContinuationImpl", "popInt", "()I", false));
            instructions.insertBefore(labelNode,
                    new TableSwitchInsnNode(0, length - 1, defaultLabelNode, labelNodes));
        } else {
            instructions.insertBefore(labelNode, new JumpInsnNode(GOTO, defaultLabelNode));
        }

        updateMaxStack(1);

        Type returnType = Type.getReturnType(desc);

        for (int i = 0; i < size; i++) {
            MethodInsnNode node = nodes.get(i);
            Frame frame = frames.get(node);
            Object[] locals = frame.locals;
            Object[] stack = frame.stack;

            // resume

            LabelNode labelNode2 = i < length ? labelNodes[i] : defaultLabelNode;
            instructions.insertBefore(labelNode, labelNode2);
            instructions.insert(labelNode2, newFrameNode(initialLocals, EMPTY_OBJECTS));

            int intCount = length > 0 ? 1 : 0;
            int floatCount = 0;
            int longCount = 0;
            int doubleCount = 0;
            int objectCount = 0;

            for (int j = 0, n = locals.length; j < n; j++) {
                Object value = locals[j];
                if (value == INTEGER) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popInt", "()I", false));
                    instructions.insertBefore(labelNode, new VarInsnNode(ISTORE, j));
                    updateMaxStack(1);
                    intCount++;
                } else if (value == FLOAT) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popFloat", "()F", false));
                    instructions.insertBefore(labelNode, new VarInsnNode(FSTORE, j));
                    updateMaxStack(1);
                    floatCount++;
                } else if (value == LONG) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popLong", "()J", false));
                    instructions.insertBefore(labelNode, new VarInsnNode(LSTORE, j));
                    updateMaxStack(2);
                    longCount++;
                } else if (value == DOUBLE) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popDouble", "()D", false));
                    instructions.insertBefore(labelNode, new VarInsnNode(DSTORE, j));
                    updateMaxStack(2);
                    doubleCount++;
                } else if (value == NULL) {
                    instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
                    instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, j));
                    updateMaxStack(1);
                } else if (value instanceof String) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "popObject", "()Ljava/lang/Object;", false));
                    instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, (String) value));
                    instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, j));
                    updateMaxStack(1);
                    objectCount++;
                } else if (value != TOP) {
                    throw new IllegalStateException();
                }
            }

            int sizes = Type.getArgumentsAndReturnSizes(node.desc);
            int argSize = sizes >> 2;
            boolean invokeStatic = node.getOpcode() == INVOKESTATIC;
            if (invokeStatic) {
                argSize -= 1;
            }

            int stackSize = 0;

            for (int j = 0, n = stack.length - argSize; j < n; j++) {
                Object value = stack[j];
                if (value == INTEGER) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popInt", "()I", false));
                    stackSize += 1;
                    intCount++;
                } else if (value == FLOAT) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popFloat", "()F", false));
                    stackSize += 1;
                    floatCount++;
                } else if (value == LONG) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popLong", "()J", false));
                    stackSize += 2;
                    longCount++;
                } else if (value == DOUBLE) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "popDouble", "()D", false));
                    stackSize += 2;
                    doubleCount++;
                } else if (value == NULL) {
                    instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
                    stackSize += 1;
                } else if (value instanceof String) {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "popObject", "()Ljava/lang/Object;", false));
                    instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, (String) value));
                    stackSize += 1;
                    objectCount++;
                } else if (value != TOP) {
                    throw new IllegalStateException();
                }
            }

            updateMaxStack(stackSize);

            int targetVarIndex;
            int objVarIndex;
            int argsVarIndex;

            if (invokeStatic) {
                targetVarIndex = -1;
                objVarIndex = -1;
                argsVarIndex = -1;

                for (Type type : Type.getArgumentTypes(node.desc)) {
                    instructions.insertBefore(labelNode, newPushDefaultNode(type));
                    stackSize += type.getSize();
                }
            } else if (node.owner.equals("java/lang/reflect/Method") && node.name.equals("invoke")
                    && node.desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")) {
                int varIndex = implVarIndex + 1;

                if (stack[stack.length - 3] == NULL) {
                    targetVarIndex = -1;
                } else {
                    targetVarIndex = varIndex;
                    varIndex++;
                }

                if (stack[stack.length - 2] == NULL) {
                    objVarIndex = -1;
                } else {
                    objVarIndex = varIndex;
                    varIndex++;
                }

                if (stack[stack.length - 1] == NULL) {
                    argsVarIndex = -1;
                } else {
                    argsVarIndex = varIndex;
                    varIndex++;
                }

                if (maxLocals < varIndex) {
                    maxLocals = varIndex;
                }

                if (targetVarIndex == -1) {
                    instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
                } else {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "popObject", "()Ljava/lang/Object;", false));
                    instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, "java/lang/reflect/Method"));
                    instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, targetVarIndex));
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, targetVarIndex));
                    objectCount++;
                }

                if (objVarIndex == -1) {
                    instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
                } else {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "popObject", "()Ljava/lang/Object;", false));
                    instructions.insertBefore(labelNode,
                            new TypeInsnNode(CHECKCAST, (String) stack[stack.length - 2]));
                    instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, objVarIndex));
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, objVarIndex));
                    objectCount++;
                }

                if (targetVarIndex == -1) {
                    instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
                } else {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, targetVarIndex));
                }

                instructions.insertBefore(labelNode, new MethodInsnNode(INVOKEVIRTUAL, "java/lang/reflect/Method",
                        "getParameterTypes", "()[Ljava/lang/Class;", false));

                instructions.insertBefore(labelNode,
                        new MethodInsnNode(INVOKESTATIC, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                "getDefaultArguments", "([Ljava/lang/Class;)[Ljava/lang/Object;", false));
                stackSize += 3;
            } else {
                targetVarIndex = -1;
                objVarIndex = -1;
                argsVarIndex = -1;

                Object value = stack[stack.length - argSize];
                if (value == NULL) {
                    instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
                    instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, implVarIndex + 1));
                    instructions.insertBefore(labelNode, new InsnNode(ACONST_NULL));
                } else {
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "popObject", "()Ljava/lang/Object;", false));
                    instructions.insertBefore(labelNode, new VarInsnNode(ASTORE, implVarIndex + 1));
                    instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex + 1));
                    instructions.insertBefore(labelNode, new TypeInsnNode(CHECKCAST, (String) value));
                }
                stackSize += 1;

                for (Type type : Type.getArgumentTypes(node.desc)) {
                    instructions.insertBefore(labelNode, newPushDefaultNode(type));
                    stackSize += type.getSize();
                }

                objectCount++;
            }

            updateMaxStack(stackSize);

            LabelNode labelNode3 = newLabelNode();

            instructions.insertBefore(labelNode, new JumpInsnNode(GOTO, labelNode3));

            // invocation starting

            if (invokeStatic) {
                instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(node, new JumpInsnNode(IFNULL, labelNode3));

                instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(node, new LdcInsnNode(Type.getType('L' + node.owner + ';')));
                instructions.insertBefore(node, new LdcInsnNode(node.name));
                instructions.insertBefore(node, new LdcInsnNode(node.desc));
                instructions.insertBefore(node,
                        new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                "staticInvocationStarting",
                                "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V", false));

                instructions.insertBefore(node, labelNode3);
                instructions.insert(labelNode3, newFrameNode(appendValue(ensureSize(locals, implVarIndex),
                        "org/jephyr/continuation/easyflow/ContinuationImpl"), stack));
            } else if (node.owner.equals("java/lang/reflect/Method") && node.name.equals("invoke")
                    && node.desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")) {
                instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));

                LabelNode labelNode4 = newLabelNode();

                instructions.insertBefore(node, new JumpInsnNode(IFNULL, labelNode4));

                instructions.insertBefore(node,
                        argsVarIndex == -1 ? new InsnNode(POP) : new VarInsnNode(ASTORE, argsVarIndex));
                instructions.insertBefore(node,
                        objVarIndex == -1 ? new InsnNode(POP) : new VarInsnNode(ASTORE, objVarIndex));
                instructions.insertBefore(node,
                        targetVarIndex == -1 ? new InsnNode(POP) : new VarInsnNode(ASTORE, targetVarIndex));

                instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(node,
                        targetVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, targetVarIndex));
                instructions.insertBefore(node,
                        objVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, objVarIndex));
                instructions.insertBefore(node,
                        new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                "reflectiveInvocationStarting", "(Ljava/lang/reflect/Method;Ljava/lang/Object;)V",
                                false));

                instructions.insertBefore(node,
                        targetVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, targetVarIndex));
                instructions.insertBefore(node,
                        objVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, objVarIndex));
                instructions.insertBefore(node,
                        argsVarIndex == -1 ? new InsnNode(ACONST_NULL) : new VarInsnNode(ALOAD, argsVarIndex));

                instructions.insertBefore(node, new JumpInsnNode(GOTO, labelNode3));

                instructions.insertBefore(node, labelNode4);
                instructions.insert(labelNode4, newFrameNode(appendValue(ensureSize(locals, implVarIndex),
                        "org/jephyr/continuation/easyflow/ContinuationImpl"), stack));

                if (targetVarIndex != -1) {
                    instructions.insertBefore(node, new InsnNode(ACONST_NULL));
                    instructions.insertBefore(node, new VarInsnNode(ASTORE, targetVarIndex));
                }

                if (objVarIndex != -1) {
                    instructions.insertBefore(node, new InsnNode(ACONST_NULL));
                    instructions.insertBefore(node, new VarInsnNode(ASTORE, objVarIndex));
                }

                instructions.insertBefore(node, labelNode3);

                Object[] locals1 = appendValue(ensureSize(locals, implVarIndex),
                        "org/jephyr/continuation/easyflow/ContinuationImpl");

                if (targetVarIndex != -1) {
                    locals1 = appendValue(locals1, "java/lang/reflect/Method");
                }

                if (objVarIndex != -1) {
                    locals1 = appendValue(locals1, "java/lang/Object");
                }

                instructions.insert(labelNode3, newFrameNode(locals1, stack));
            } else {
                instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));

                LabelNode labelNode4 = newLabelNode();

                instructions.insertBefore(node, new JumpInsnNode(IFNULL, labelNode4));

                targetVarIndex = implVarIndex + 1;
                int varIndex = targetVarIndex + 1;

                for (int j = stack.length - 1, k = stack.length - argSize + 1; j >= k; j--) {
                    Object value = stack[j];
                    if (value == INTEGER) {
                        instructions.insertBefore(node, new VarInsnNode(ISTORE, varIndex));
                        varIndex += 1;
                    } else if (value == FLOAT) {
                        instructions.insertBefore(node, new VarInsnNode(FSTORE, varIndex));
                        varIndex += 1;
                    } else if (value == DOUBLE) {
                        instructions.insertBefore(node, new VarInsnNode(DSTORE, varIndex));
                        varIndex += 2;
                    } else if (value == LONG) {
                        instructions.insertBefore(node, new VarInsnNode(LSTORE, varIndex));
                        varIndex += 2;
                    } else if (value == NULL) {
                        instructions.insertBefore(node, new InsnNode(POP));
                    } else if (value instanceof String) {
                        instructions.insertBefore(node, new VarInsnNode(ASTORE, varIndex));
                        varIndex += 1;
                    }
                }

                if (maxLocals < varIndex) {
                    maxLocals = varIndex;
                }

                instructions.insertBefore(node, new VarInsnNode(ASTORE, targetVarIndex));
                instructions.insertBefore(node, new VarInsnNode(ALOAD, targetVarIndex));

                for (int j = stack.length - argSize + 1, n = stack.length; j < n; j++) {
                    Object value = stack[j];
                    if (value == INTEGER) {
                        varIndex -= 1;
                        instructions.insertBefore(node, new VarInsnNode(ILOAD, varIndex));
                    } else if (value == FLOAT) {
                        varIndex -= 1;
                        instructions.insertBefore(node, new VarInsnNode(FLOAD, varIndex));
                    } else if (value == DOUBLE) {
                        varIndex -= 2;
                        instructions.insertBefore(node, new VarInsnNode(DLOAD, varIndex));
                    } else if (value == LONG) {
                        varIndex -= 2;
                        instructions.insertBefore(node, new VarInsnNode(LLOAD, varIndex));
                    } else if (value == NULL) {
                        instructions.insertBefore(node, new InsnNode(ACONST_NULL));
                    } else if (value instanceof String) {
                        varIndex -= 1;
                        instructions.insertBefore(node, new VarInsnNode(ALOAD, varIndex));
                    }
                }

                instructions.insertBefore(node, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(node, new VarInsnNode(ALOAD, targetVarIndex));
                instructions.insertBefore(node, new LdcInsnNode(node.name));
                instructions.insertBefore(node, new LdcInsnNode(node.desc));
                instructions.insertBefore(node,
                        new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                "invocationStarting", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
                                false));

                instructions.insertBefore(node, new JumpInsnNode(GOTO, labelNode3));

                instructions.insertBefore(node, labelNode4);
                instructions.insert(labelNode4, newFrameNode(appendValue(ensureSize(locals, implVarIndex),
                        "org/jephyr/continuation/easyflow/ContinuationImpl"), stack));

                instructions.insertBefore(node, new InsnNode(ACONST_NULL));
                instructions.insertBefore(node, new VarInsnNode(ASTORE, targetVarIndex));

                instructions.insertBefore(node, labelNode3);
                instructions.insert(labelNode3,
                        newFrameNode(
                                appendValues(ensureSize(locals, implVarIndex),
                                        "org/jephyr/continuation/easyflow/ContinuationImpl", "java/lang/Object"),
                                stack));
            }

            updateMaxStack(stack.length + 4);

            // suspend

            Frame frame1 = findNextFrame(frames, node);
            Object[] stack1 = frame1.stack;
            int stackSize1 = stack1.length;

            LabelNode labelNode4 = newLabelNode();

            instructions.insert(node, labelNode4);

            if (!isNextFrameNode(labelNode4)) {
                instructions.insert(labelNode4, newFrameNode(appendValue(ensureSize(frame1.locals, implVarIndex),
                        "org/jephyr/continuation/easyflow/ContinuationImpl"), stack1));
            }

            instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
            instructions.insertBefore(labelNode4, new JumpInsnNode(IFNULL, labelNode4));

            instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
            instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                    "org/jephyr/continuation/easyflow/ContinuationImpl", "isSuspending", "()Z", false));
            instructions.insertBefore(labelNode4, new JumpInsnNode(IFEQ, labelNode4));

            int returnSize = sizes & 0x03;
            if (returnSize == 1) {
                instructions.insertBefore(labelNode4, new InsnNode(POP));
                stackSize1 -= 1;
            } else if (returnSize == 2) {
                instructions.insertBefore(labelNode4, new InsnNode(POP2));
                stackSize1 -= 2;
            }

            if (intCount > 0) {
                instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode4, newPushNode(intCount));
                instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                        "org/jephyr/continuation/easyflow/ContinuationImpl", "ensureIntStackSize", "(I)V", false));
                updateMaxStack(stackSize1 + 1);
            }

            if (floatCount > 0) {
                instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode4, newPushNode(floatCount));
                instructions.insertBefore(labelNode4,
                        new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                "ensureFloatStackSize", "(I)V", false));
                updateMaxStack(stackSize1 + 1);
            }

            if (longCount > 0) {
                instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode4, newPushNode(longCount));
                instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                        "org/jephyr/continuation/easyflow/ContinuationImpl", "ensureLongStackSize", "(I)V", false));
                updateMaxStack(stackSize1 + 1);
            }

            if (doubleCount > 0) {
                instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode4, newPushNode(doubleCount));
                instructions.insertBefore(labelNode4,
                        new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                "ensureDoubleStackSize", "(I)V", false));
                updateMaxStack(stackSize1 + 1);
            }

            if (objectCount > 0) {
                instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode4, newPushNode(objectCount));
                instructions.insertBefore(labelNode4,
                        new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                "ensureObjectStackSize", "(I)V", false));
                updateMaxStack(stackSize1 + 1);
            }

            if (!invokeStatic) {
                if (node.owner.equals("java/lang/reflect/Method") && node.name.equals("invoke")
                        && node.desc.equals("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")) {
                    if (objVarIndex != -1) {
                        instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                        instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, objVarIndex));
                        instructions.insertBefore(labelNode4,
                                new MethodInsnNode(INVOKEVIRTUAL,
                                        "org/jephyr/continuation/easyflow/ContinuationImpl", "pushObject",
                                        "(Ljava/lang/Object;)V", false));
                        updateMaxStack(stackSize1 + 2);
                    }
                    if (targetVarIndex != -1) {
                        instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                        instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, targetVarIndex));
                        instructions.insertBefore(labelNode4,
                                new MethodInsnNode(INVOKEVIRTUAL,
                                        "org/jephyr/continuation/easyflow/ContinuationImpl", "pushObject",
                                        "(Ljava/lang/Object;)V", false));
                        updateMaxStack(stackSize1 + 2);
                    }
                } else if (stack[stack.length - argSize] != NULL) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, targetVarIndex));
                    instructions.insertBefore(labelNode4,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "pushObject", "(Ljava/lang/Object;)V", false));
                    updateMaxStack(stackSize1 + 2);
                }
            }

            for (int j = stack.length - argSize - 1; j >= 0; j--) {
                Object value = stack[j];
                if (value == INTEGER) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new InsnNode(SWAP));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushInt", "(I)V", false));
                    updateMaxStack(stackSize1 + 1);
                    stackSize1 -= 1;
                } else if (value == FLOAT) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new InsnNode(SWAP));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushFloat", "(F)V", false));
                    updateMaxStack(stackSize1 + 1);
                    stackSize1 -= 1;
                } else if (value == LONG) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new InsnNode(DUP_X2));
                    instructions.insertBefore(labelNode4, new InsnNode(POP));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushLong", "(J)V", false));
                    updateMaxStack(stackSize1 + 2);
                    stackSize1 -= 2;
                } else if (value == DOUBLE) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new InsnNode(DUP_X2));
                    instructions.insertBefore(labelNode4, new InsnNode(POP));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushDouble", "(D)V", false));
                    updateMaxStack(stackSize1 + 2);
                    stackSize1 -= 2;
                } else if (value == NULL) {
                    instructions.insertBefore(labelNode4, new InsnNode(POP));
                    stackSize1 -= 1;
                } else if (value instanceof String) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new InsnNode(SWAP));
                    instructions.insertBefore(labelNode4,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "pushObject", "(Ljava/lang/Object;)V", false));
                    updateMaxStack(stackSize1 + 1);
                    stackSize1 -= 1;
                }
            }

            for (int j = locals.length - 1; j >= 0; j--) {
                Object value = locals[j];
                if (value == INTEGER) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new VarInsnNode(ILOAD, j));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushInt", "(I)V", false));
                    updateMaxStack(stackSize1 + 2);
                } else if (value == FLOAT) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new VarInsnNode(FLOAD, j));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushFloat", "(F)V", false));
                    updateMaxStack(stackSize1 + 2);
                } else if (value == LONG) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new VarInsnNode(LLOAD, j));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushLong", "(J)V", false));
                    updateMaxStack(stackSize1 + 3);
                } else if (value == DOUBLE) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new VarInsnNode(DLOAD, j));
                    instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                            "org/jephyr/continuation/easyflow/ContinuationImpl", "pushDouble", "(D)V", false));
                    updateMaxStack(stackSize1 + 3);
                } else if (value instanceof String) {
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                    instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, j));
                    instructions.insertBefore(labelNode4,
                            new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                    "pushObject", "(Ljava/lang/Object;)V", false));
                    updateMaxStack(stackSize1 + 2);
                }
            }

            if (length > 0) {
                instructions.insertBefore(labelNode4, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode4, newPushNode(i));
                instructions.insertBefore(labelNode4, new MethodInsnNode(INVOKEVIRTUAL,
                        "org/jephyr/continuation/easyflow/ContinuationImpl", "pushInt", "(I)V", false));
                updateMaxStack(stackSize1 + 2);
            }

            int returnSize1 = returnType.getSize();
            if (returnSize1 > 0) {
                instructions.insertBefore(labelNode4, newPushDefaultNode(returnType));
                updateMaxStack(stackSize1 + returnSize1);
            }

            instructions.insertBefore(labelNode4, new InsnNode(returnType.getOpcode(IRETURN)));
        }

        instructions.insertBefore(labelNode, labelNode1);
        instructions.insert(labelNode1, newFrameNode(initialLocals, EMPTY_OBJECTS));

        addInvocationStartedHook(implVarIndex, labelNode);

        accept(mv);
    }

    private List<MethodInsnNode> findNodes() {
        List<MethodInsnNode> nodes = new ArrayList<>();
        for (AbstractInsnNode next = instructions.getFirst(); next != null; next = next.getNext()) {
            if (next instanceof MethodInsnNode) {
                MethodInsnNode node = (MethodInsnNode) next;
                if (node.getOpcode() != INVOKESPECIAL || node.name.charAt(0) != '<') {
                    nodes.add(node);
                }
            }
        }
        return nodes;
    }

    private void updateFrames(int implVarIndex) {
        AbstractInsnNode next = instructions.getFirst();
        while (next != null) {
            AbstractInsnNode node = next;
            next = next.getNext();
            if (node instanceof FrameNode) {
                FrameNode frameNode = (FrameNode) node;
                Collection<Object> locals = new ArrayList<>();
                for (Object value : frameNode.local) {
                    locals.add(value);
                    if (isLong(value)) {
                        locals.add(TOP);
                    }
                }
                Object[] locals1 = convertValues(appendValue(ensureSize(locals.toArray(), implVarIndex),
                        "org/jephyr/continuation/easyflow/ContinuationImpl"));
                List<Object> stack = frameNode.stack;
                instructions.set(node,
                        new FrameNode(F_NEW, locals1.length, locals1, stack.size(), stack.toArray()));
            }
        }
    }

    private void addMonitorHooks(int implVarIndex) {
        AbstractInsnNode next = instructions.getFirst();
        while (next != null) {
            AbstractInsnNode node = next;
            next = next.getNext();
            int opcode = node.getOpcode();
            if (opcode == MONITORENTER || opcode == MONITOREXIT) {
                LabelNode labelNode = newLabelNode();

                instructions.insert(node, labelNode);

                Frame frame = findNextFrame(frames, node);
                Object[] stack = frame.stack;

                if (!isNextFrameNode(labelNode)) {
                    instructions.insert(labelNode, newFrameNode(appendValue(ensureSize(frame.locals, implVarIndex),
                            "org/jephyr/continuation/easyflow/ContinuationImpl"), stack));
                }

                instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode, new JumpInsnNode(IFNULL, labelNode));

                instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(labelNode,
                        new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                                opcode == MONITORENTER ? "monitorEntered" : "monitorExited", "()V", false));

                updateMaxStack(stack.length + 1);
            }
        }
    }

    private void addInvocationStartedHook(int implVarIndex, LabelNode labelNode) {
        if ((access & ACC_STATIC) == 0) {
            instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
            instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, 0));
            instructions.insertBefore(labelNode, new LdcInsnNode(name));
            instructions.insertBefore(labelNode, new LdcInsnNode(desc));
            instructions.insertBefore(labelNode,
                    new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                            "invocationStarted", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
                            false));
        } else {
            instructions.insertBefore(labelNode, new VarInsnNode(ALOAD, implVarIndex));
            instructions.insertBefore(labelNode, new LdcInsnNode(Type.getType('L' + owner + ';')));
            instructions.insertBefore(labelNode, new LdcInsnNode(name));
            instructions.insertBefore(labelNode, new LdcInsnNode(desc));
            instructions.insertBefore(labelNode,
                    new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                            "staticInvocationStarted", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V",
                            false));
        }

        updateMaxStack(4);
    }

    private void addInvocationEndedHook(int implVarIndex, LabelNode labelNode) {
        LabelNode startLabelNode = labelNode;
        LabelNode handlerLabelNode = newLabelNode();

        for (AbstractInsnNode next = labelNode.getNext(); next != null; next = next.getNext()) {
            int opcode = next.getOpcode();
            if (opcode == IRETURN || opcode == LRETURN || opcode == FRETURN || opcode == DRETURN
                    || opcode == ARETURN || opcode == RETURN) {
                LabelNode endLabelNode = newLabelNode();

                instructions.insertBefore(next, endLabelNode);
                instructions.insertBefore(next, new VarInsnNode(ALOAD, implVarIndex));

                LabelNode labelNode1 = newLabelNode();

                instructions.insertBefore(next, new JumpInsnNode(IFNULL, labelNode1));
                instructions.insertBefore(next, new VarInsnNode(ALOAD, implVarIndex));
                instructions.insertBefore(next, new MethodInsnNode(INVOKEVIRTUAL,
                        "org/jephyr/continuation/easyflow/ContinuationImpl", "invocationEnded", "()V", false));

                Frame frame = frames.get(next);
                Object[] stack = frame.stack;

                instructions.insertBefore(next, labelNode1);
                instructions.insertBefore(next, newFrameNode(appendValue(ensureSize(frame.locals, implVarIndex),
                        "org/jephyr/continuation/easyflow/ContinuationImpl"), stack));

                addTryCatchBlockNode(startLabelNode, endLabelNode, handlerLabelNode);

                startLabelNode = newLabelNode();

                instructions.insert(next, startLabelNode);

                updateMaxStack(stack.length + 1);
            }
        }

        instructions.add(handlerLabelNode);

        addTryCatchBlockNode(startLabelNode, handlerLabelNode, handlerLabelNode);

        Object[] locals = new Object[implVarIndex + 1];
        for (int i = 0; i < implVarIndex; i++) {
            locals[i] = TOP;
        }
        locals[implVarIndex] = "org/jephyr/continuation/easyflow/ContinuationImpl";
        Object[] stack = { "java/lang/Throwable" };

        instructions.add(newFrameNode(locals, stack));

        instructions.add(new VarInsnNode(ALOAD, implVarIndex));

        LabelNode labelNode1 = newLabelNode();

        instructions.add(new JumpInsnNode(IFNULL, labelNode1));

        instructions.add(new VarInsnNode(ALOAD, implVarIndex));
        instructions.add(new MethodInsnNode(INVOKEVIRTUAL, "org/jephyr/continuation/easyflow/ContinuationImpl",
                "invocationEnded", "()V", false));

        instructions.add(labelNode1);
        instructions.add(newFrameNode(locals, stack));
        instructions.add(new InsnNode(ATHROW));

        updateMaxStack(2);
    }

    private void addTryCatchBlockNode(LabelNode startLabelNode, LabelNode endLabelNode,
            LabelNode handlerLabelNode) {
        for (AbstractInsnNode next = startLabelNode.getNext(); next != endLabelNode; next = next.getNext()) {
            if (next.getOpcode() != -1) {
                tryCatchBlocks.add(new TryCatchBlockNode(startLabelNode, endLabelNode, handlerLabelNode, null));
                return;
            }
        }
    }

    private static boolean isLong(Object value) {
        return value == LONG || value == DOUBLE;
    }

    private static Object[] ensureSize(Object[] values, int size) {
        if (values.length >= size) {
            return values;
        }
        Object[] values1 = new Object[size];
        System.arraycopy(values, 0, values1, 0, values.length);
        for (int i = values.length; i < size; i++) {
            values1[i] = TOP;
        }
        return values1;
    }

    private static Object[] appendValue(Object[] values, Object value) {
        Object[] values1 = new Object[values.length + 1];
        System.arraycopy(values, 0, values1, 0, values.length);
        values1[values.length] = value;
        return values1;
    }

    private static Object[] appendValues(Object[] values, Object... valuesToSet) {
        Object[] values1 = new Object[values.length + valuesToSet.length];
        System.arraycopy(values, 0, values1, 0, values.length);
        System.arraycopy(valuesToSet, 0, values1, values.length, valuesToSet.length);
        return values1;
    }

    private static LabelNode newLabelNode() {
        Label label = new Label();
        LabelNode labelNode = new LabelNode(label);
        label.info = labelNode;
        return labelNode;
    }

    private static FrameNode newFrameNode(Object[] locals, Object[] stack) {
        Object[] locals1 = convertValues(locals);
        Object[] stack1 = convertValues(stack);
        return new FrameNode(F_NEW, locals1.length, locals1, stack1.length, stack1);
    }

    private static Object[] convertValues(Object[] values) {
        int n = values.length;
        Collection<Object> values1 = new ArrayList<>(n);
        int i = 0;
        while (i < n) {
            Object value = values[i];
            values1.add(value);
            i += isLong(value) ? 2 : 1;
        }
        return values1.toArray();
    }

    private static boolean isNextFrameNode(AbstractInsnNode node) {
        for (AbstractInsnNode next = node.getNext(); next != null
                && next.getOpcode() == -1; next = next.getNext()) {
            if (next instanceof FrameNode) {
                return true;
            }
        }
        return false;
    }

    private static AbstractInsnNode newPushDefaultNode(Type type) {
        switch (type.getSort()) {
        case Type.FLOAT:
            return new InsnNode(FCONST_0);
        case Type.LONG:
            return new InsnNode(LCONST_0);
        case Type.DOUBLE:
            return new InsnNode(DCONST_0);
        case Type.ARRAY:
        case Type.OBJECT:
            return new InsnNode(ACONST_NULL);
        default:
            return new InsnNode(ICONST_0);
        }
    }

    private static Frame findNextFrame(Map<AbstractInsnNode, Frame> frames, AbstractInsnNode node) {
        AbstractInsnNode next = node.getNext();
        while (true) {
            Frame frame = frames.get(next);
            if (frame != null) {
                return frame;
            }
            next = next.getNext();
        }
    }

    private static AbstractInsnNode newPushNode(int operand) {
        if (operand <= 5) {
            return new InsnNode(ICONST_0 + operand);
        } else if (operand <= Byte.MAX_VALUE) {
            return new IntInsnNode(BIPUSH, operand);
        } else if (operand <= Short.MAX_VALUE) {
            return new IntInsnNode(SIPUSH, operand);
        } else {
            return new LdcInsnNode(operand);
        }
    }

    private void updateMaxStack(int stackSize) {
        if (maxStack < stackSize) {
            maxStack = stackSize;
        }
    }
}