// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/asm/VariableTable.java
// public final class Variable {
//     private Type type;
//     private int index;
//     private boolean used;
// 
//     private Variable(Type type, int index, boolean used) {
//         this.type = type;
//         this.index = index;
//         this.used = used;
//     }
// 
//     /**
//      * Get the type of this local variable table entry.
//      * @return type of this entry
//      * @throws IllegalArgumentException if this {@link Variable} has been released
//      */
//     public Type getType() {
//         Validate.isTrue(used);
//         return type;
//     }
// 
//     /**
//      * Get the index of this entry within the local variable table.
//      * @return index of this entry
//      * @throws IllegalArgumentException if this {@link Variable} has been released
//      */
//     public int getIndex() {
//         Validate.isTrue(used);
//         return index;
//     }
// 
//     /**
//      * Returns {@code true} if this object hasn't been released.
//      * @see VariableTable#releaseExtra(com.offbynull.coroutines.instrumenter.asm.VariableTable.Variable) 
//     
//      * @return index of this entry
//      * @throws IllegalArgumentException if this {@link Variable} has been released
//      */
//     public boolean isUsed() {
//         return used;
//     }
//         
//     private VariableTable getParent() {
//         return VariableTable.this;
//     }
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/DebugGenerators.java
// public enum MarkerType {
//     /**
//      * Generate no instructions.
//      */
//     NONE,
//     /**
//      * Generate instructions to load text as string constant and immediately pop it off the stack.
//      */
//     CONSTANT,
//     /**
//      * Generate instructions to print text to standard out.
//      */
//     STDOUT
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/DebugGenerators.java
// public static InsnList debugMarker(MarkerType markerType, String text) {
//     Validate.notNull(markerType);
//     Validate.notNull(text);
//         
//     InsnList ret = new InsnList();
//         
//     switch (markerType) {
//         case NONE:
//             break;
//         case CONSTANT:
//             ret.add(new LdcInsnNode(text));
//             ret.add(new InsnNode(Opcodes.POP));
//             break;
//         case STDOUT:
//             ret.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
//             ret.add(new LdcInsnNode(text));
//             ret.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
//             break;
//         default:
//             throw new IllegalStateException();
//     }
// 
//     return ret;
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/GenericGenerators.java
// public static InsnList merge(Object... insns) {
//     Validate.notNull(insns);
//     Validate.noNullElements(insns);
// 
//     InsnList ret = new InsnList();
//     for (Object insn : insns) {
//         if (insn instanceof AbstractInsnNode) {
//             // add single instruction
//             AbstractInsnNode insnNode = (AbstractInsnNode) insn;
//             ret.add(insnNode);
//         } else if (insn instanceof InsnList) {
//             // add instruction list
//             InsnList insnList = (InsnList) insn;
//             for (int i = 0; i < insnList.size(); i++) {
//                 Validate.notNull(insnList.get(i));
//             }
//             ret.add((InsnList) insn);
//         } else if (insn instanceof InstructionGenerator) {
//             // generate conditional merger instruction list and add
//             InsnList insnList = ((InstructionGenerator) insn).generate();
//             for (int i = 0; i < insnList.size(); i++) {
//                 Validate.notNull(insnList.get(i));
//             }
//             ret.add(insnList);
//         } else {
//             // unrecognized
//             throw new IllegalArgumentException();
//         }
//     }
// 
//     return ret;
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/GenericGenerators.java
// public static ConditionalMerger mergeIf(boolean condition, Supplier<Object[]> insnsSupplier) {
//     Validate.notNull(insnsSupplier);
// 
//     ConditionalMerger merger = new ConditionalMerger();
//     return merger.mergeIf(condition, insnsSupplier);
// }


import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;
import com.offbynull.coroutines.instrumenter.asm.VariableTable.Variable;
import com.offbynull.coroutines.instrumenter.generators.DebugGenerators.MarkerType;
import static com.offbynull.coroutines.instrumenter.generators.DebugGenerators.debugMarker;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.merge;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.mergeIf;
import org.apache.commons.lang3.Validate;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
            
            switch (type.getSort()) {
                case Type.BOOLEAN:
                case Type.BYTE:
                case Type.SHORT:
                case Type.CHAR:
                case Type.INT:
                    intsCounter++;
                    break;
                case Type.FLOAT:
                    floatsCounter++;
                    break;
                case Type.LONG:
                    longsCounter++;
                    break;
                case Type.DOUBLE:
                    doublesCounter++;
                    break;
                case Type.ARRAY:
                case Type.OBJECT:
                    objectsCounter++;
                    break;
                case Type.METHOD:
                case Type.VOID:
                default:
                    throw new IllegalArgumentException();
            }
        }
        
        // Restore the stack
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/asm/VariableTable.java
// public final class Variable {
//     private Type type;
//     private int index;
//     private boolean used;
// 
//     private Variable(Type type, int index, boolean used) {
//         this.type = type;
//         this.index = index;
//         this.used = used;
//     }
// 
//     /**
//      * Get the type of this local variable table entry.
//      * @return type of this entry
//      * @throws IllegalArgumentException if this {@link Variable} has been released
//      */
//     public Type getType() {
//         Validate.isTrue(used);
//         return type;
//     }
// 
//     /**
//      * Get the index of this entry within the local variable table.
//      * @return index of this entry
//      * @throws IllegalArgumentException if this {@link Variable} has been released
//      */
//     public int getIndex() {
//         Validate.isTrue(used);
//         return index;
//     }
// 
//     /**
//      * Returns {@code true} if this object hasn't been released.
//      * @see VariableTable#releaseExtra(com.offbynull.coroutines.instrumenter.asm.VariableTable.Variable) 
//     
//      * @return index of this entry
//      * @throws IllegalArgumentException if this {@link Variable} has been released
//      */
//     public boolean isUsed() {
//         return used;
//     }
//         
//     private VariableTable getParent() {
//         return VariableTable.this;
//     }
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/DebugGenerators.java
// public enum MarkerType {
//     /**
//      * Generate no instructions.
//      */
//     NONE,
//     /**
//      * Generate instructions to load text as string constant and immediately pop it off the stack.
//      */
//     CONSTANT,
//     /**
//      * Generate instructions to print text to standard out.
//      */
//     STDOUT
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/DebugGenerators.java
// public static InsnList debugMarker(MarkerType markerType, String text) {
//     Validate.notNull(markerType);
//     Validate.notNull(text);
//         
//     InsnList ret = new InsnList();
//         
//     switch (markerType) {
//         case NONE:
//             break;
//         case CONSTANT:
//             ret.add(new LdcInsnNode(text));
//             ret.add(new InsnNode(Opcodes.POP));
//             break;
//         case STDOUT:
//             ret.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
//             ret.add(new LdcInsnNode(text));
//             ret.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false));
//             break;
//         default:
//             throw new IllegalStateException();
//     }
// 
//     return ret;
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/GenericGenerators.java
// public static InsnList merge(Object... insns) {
//     Validate.notNull(insns);
//     Validate.noNullElements(insns);
// 
//     InsnList ret = new InsnList();
//     for (Object insn : insns) {
//         if (insn instanceof AbstractInsnNode) {
//             // add single instruction
//             AbstractInsnNode insnNode = (AbstractInsnNode) insn;
//             ret.add(insnNode);
//         } else if (insn instanceof InsnList) {
//             // add instruction list
//             InsnList insnList = (InsnList) insn;
//             for (int i = 0; i < insnList.size(); i++) {
//                 Validate.notNull(insnList.get(i));
//             }
//             ret.add((InsnList) insn);
//         } else if (insn instanceof InstructionGenerator) {
//             // generate conditional merger instruction list and add
//             InsnList insnList = ((InstructionGenerator) insn).generate();
//             for (int i = 0; i < insnList.size(); i++) {
//                 Validate.notNull(insnList.get(i));
//             }
//             ret.add(insnList);
//         } else {
//             // unrecognized
//             throw new IllegalArgumentException();
//         }
//     }
// 
//     return ret;
// }
// 
// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/generators/GenericGenerators.java
// public static ConditionalMerger mergeIf(boolean condition, Supplier<Object[]> insnsSupplier) {
//     Validate.notNull(insnsSupplier);
// 
//     ConditionalMerger merger = new ConditionalMerger();
//     return merger.mergeIf(condition, insnsSupplier);
// }


// Path: instrumenter/src/main/java/com/offbynull/coroutines/instrumenter/OperandStackStateGenerators.java
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;
import com.offbynull.coroutines.instrumenter.asm.VariableTable.Variable;
import com.offbynull.coroutines.instrumenter.generators.DebugGenerators.MarkerType;
import static com.offbynull.coroutines.instrumenter.generators.DebugGenerators.debugMarker;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.merge;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.mergeIf;
import org.apache.commons.lang3.Validate;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;

            
            switch (type.getSort()) {
                case Type.BOOLEAN:
                case Type.BYTE:
                case Type.SHORT:
                case Type.CHAR:
                case Type.INT:
                    intsCounter++;
                    break;
                case Type.FLOAT:
                    floatsCounter++;
                    break;
                case Type.LONG:
                    longsCounter++;
                    break;
                case Type.DOUBLE:
                    doublesCounter++;
                    break;
                case Type.ARRAY:
                case Type.OBJECT:
                    objectsCounter++;
                    break;
                case Type.METHOD:
                case Type.VOID:
                default:
                    throw new IllegalArgumentException();
            }
        }
        
        // Restore the stack
        ret.add(debugMarker(markerType, "Loading stack items"));