public static InsnList addLabel(LabelNode labelNode) {
    Validate.notNull(labelNode);

    InsnList ret = new InsnList();
    ret.add(labelNode);

    return ret;
} public static InsnList call(Method method, InsnList ... args) {
    Validate.notNull(method);
    Validate.notNull(args);
    Validate.noNullElements(args);
        
        
    InsnList ret = new InsnList();
        
    for (InsnList arg : args) {
        ret.add(arg);
    }
        
    Type clsType = Type.getType(method.getDeclaringClass());
    Type methodType = Type.getType(method);
        
    if ((method.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
        Validate.isTrue(method.getParameterCount() == args.length);
        ret.add(new MethodInsnNode(Opcodes.INVOKESTATIC, clsType.getInternalName(), method.getName(), methodType.getDescriptor(),
                false));
    } else if (method.getDeclaringClass().isInterface()) {
        Validate.isTrue(method.getParameterCount() + 1 == args.length);
        ret.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, clsType.getInternalName(), method.getName(), methodType.getDescriptor(),
                true));
    } else {
        Validate.isTrue(method.getParameterCount() + 1 == args.length);
        ret.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, clsType.getInternalName(), method.getName(), methodType.getDescriptor(),
                false));
    }
        
    return ret;
} public static InsnList construct(Constructor<?> constructor, InsnList ... args) {
    Validate.notNull(constructor);
    Validate.notNull(args);
    Validate.noNullElements(args);
    Validate.isTrue(constructor.getParameterCount() == args.length);
        
        
    InsnList ret = new InsnList();
        
    Type clsType = Type.getType(constructor.getDeclaringClass());
    Type methodType = Type.getType(constructor);
        
    ret.add(new TypeInsnNode(Opcodes.NEW, clsType.getInternalName()));
    ret.add(new InsnNode(Opcodes.DUP));
    for (InsnList arg : args) {
        ret.add(arg);
    }
    ret.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, clsType.getInternalName(), "<init>", methodType.getDescriptor(), false));
        
    return ret;
} public static InsnList jumpTo(LabelNode labelNode) {
    Validate.notNull(labelNode);

    InsnList ret = new InsnList();
    ret.add(new JumpInsnNode(Opcodes.GOTO, labelNode));

    return ret;
}     public static InsnList loadVar(Variable variable) {
        Validate.notNull(variable);

        InsnList ret = new InsnList();
        switch (variable.getType().getSort()) {
            case Type.BOOLEAN:
            case Type.BYTE:
            case Type.CHAR:
            case Type.SHORT:
            case Type.INT:
                ret.add(new VarInsnNode(Opcodes.ILOAD, variable.getIndex()));
                break;
            case Type.LONG:
                ret.add(new VarInsnNode(Opcodes.LLOAD, variable.getIndex()));
                break;
            case Type.FLOAT:
                ret.add(new VarInsnNode(Opcodes.FLOAD, variable.getIndex()));
                break;
            case Type.DOUBLE:
                ret.add(new VarInsnNode(Opcodes.DLOAD, variable.getIndex()));
                break;
            case Type.OBJECT:
            case Type.ARRAY:
                ret.add(new VarInsnNode(Opcodes.ALOAD, variable.getIndex()));
                // If required, do it outside this method
//                ret.add(new TypeInsnNode(Opcodes.CHECKCAST, variable.getType().getInternalName()));
                break;
            default:
                throw new IllegalStateException(); // should never happen, there is code in Variable/VariableTable to make sure invalid
                                                   // types aren't set
        }

        return ret;
    } 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;
} public static InsnList saveVar(Variable variable) {
    Validate.notNull(variable);

    InsnList ret = new InsnList();
    switch (variable.getType().getSort()) {
        case Type.BOOLEAN:
        case Type.BYTE:
        case Type.CHAR:
        case Type.SHORT:
        case Type.INT:
            ret.add(new VarInsnNode(Opcodes.ISTORE, variable.getIndex()));
            break;
        case Type.LONG:
            ret.add(new VarInsnNode(Opcodes.LSTORE, variable.getIndex()));
            break;
        case Type.FLOAT:
            ret.add(new VarInsnNode(Opcodes.FSTORE, variable.getIndex()));
            break;
        case Type.DOUBLE:
            ret.add(new VarInsnNode(Opcodes.DSTORE, variable.getIndex()));
            break;
        case Type.OBJECT:
        case Type.ARRAY:
            ret.add(new VarInsnNode(Opcodes.ASTORE, variable.getIndex()));
            break;
        default:
            throw new IllegalStateException(); // should never happen, there is code in Variable/VariableTable to make sure invalid
                                               // types aren't set
    }

    return ret;
} public static InsnList tableSwitch(InsnList indexInsnList, InsnList defaultInsnList, int caseStartIdx, InsnList... caseInsnLists) {
    Validate.notNull(defaultInsnList);
    Validate.notNull(indexInsnList);
    Validate.isTrue(caseStartIdx >= 0);
    Validate.notNull(caseInsnLists);
    Validate.noNullElements(caseInsnLists);
    Validate.isTrue(caseInsnLists.length > 0);
    InsnList ret = new InsnList();

    LabelNode defaultLabelNode = new LabelNode();
    LabelNode[] caseLabelNodes = new LabelNode[caseInsnLists.length];

    for (int i = 0; i < caseInsnLists.length; i++) {
        caseLabelNodes[i] = new LabelNode();
    }

    ret.add(indexInsnList);
    ret.add(new TableSwitchInsnNode(caseStartIdx, caseStartIdx + caseInsnLists.length - 1, defaultLabelNode, caseLabelNodes));

    for (int i = 0; i < caseInsnLists.length; i++) {
        LabelNode caseLabelNode = caseLabelNodes[i];
        InsnList caseInsnList = caseInsnLists[i];
        if (caseInsnList != null) {
            ret.add(caseLabelNode);
            ret.add(caseInsnList);
        }
    }

    if (defaultInsnList != null) {
        ret.add(defaultLabelNode);
        ret.add(defaultInsnList);
    }
        
    return ret;
} public final class TestUtils {

    private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
    private static final String MANIFEST_TEXT = "Manifest-Version: 1.0\r\nCreated-By: " + TestUtils.class.getName();

    private TestUtils() {
        // do nothing
    }
    
    /**
     * Equivalent to calling {@code loadClassesInZipResourceAndInstrument(path, new InstrumentationSettings(MarkerType.CONSTANT, false))}.
     * @param path path of zip resource
     * @return class loader able to access instrumented classes
     * @throws NullPointerException if any argument is {@code null}
     * @throws IOException if an IO error occurs
     */
    public static URLClassLoader loadClassesInZipResourceAndInstrument(String path) throws IOException {
        return loadClassesInZipResourceAndInstrument(path, new InstrumentationSettings(MarkerType.CONSTANT, false, true));
    }
    
    /**
     * Equivalent to calling
     * {@code loadClassesInZipResourceAndInstrument(path, new InstrumentationSettings(MarkerType.CONSTANT, false), x -> x)}.
     * @param path path of zip resource
     * @param settings instrumentation settings
     * @return class loader able to access instrumented classes
     * @throws NullPointerException if any argument is {@code null}
     * @throws IOException if an IO error occurs
     */
    public static URLClassLoader loadClassesInZipResourceAndInstrument(String path, InstrumentationSettings settings) throws IOException {
        return loadClassesInZipResourceAndInstrument(path, settings, x -> x);
    }

    /**
     * Opens up a ZIP resource, instruments the classes within, and returns a {@link URLClassLoader} object with access to those classes.
     * @param path path of zip resource
     * @param settings instrumentation settings
     * @param classModifier function to change up class as needed by tests
     * @return class loader able to access instrumented classes
     * @throws NullPointerException if any argument is {@code null}
     * @throws IOException if an IO error occurs
     */
    public static URLClassLoader loadClassesInZipResourceAndInstrument(String path, InstrumentationSettings settings,
            Function<byte[], byte[]> classModifier) throws IOException {
        Validate.notNull(path);
        
        // Load original class
        Map<String, byte[]> classContents = readZipFromResource(path);
        
        // Create JAR out of original classes so it can be found by the instrumenter
        List<JarEntry> originalJarEntries = new ArrayList<>(classContents.size());
        for (Entry<String, byte[]> entry : classContents.entrySet()) {
            originalJarEntries.add(new JarEntry(entry.getKey(), entry.getValue()));
        }
        File originalJarFile = createJar(originalJarEntries.toArray(new JarEntry[0]));
        
        // Add the jar file we created to list of classpaths (used by the instrumenter)
        List<File> classpath = new ArrayList<>();
        classpath.add(originalJarFile);
        
        // Instrument classes and write out new jar
        Instrumenter instrumenter = new Instrumenter(classpath);
        List<JarEntry> instrumentedJarEntries = new ArrayList<>(classContents.size());
        for (Entry<String, byte[]> entry : classContents.entrySet()) {
            byte[] content = entry.getValue();
            if (entry.getKey().endsWith(".class")) {
                content = instrumenter.instrument(classModifier.apply(content), settings).getInstrumentedClass();
            }
            instrumentedJarEntries.add(new JarEntry(entry.getKey(), content));
        }
        File instrumentedJarFile = createJar(instrumentedJarEntries.toArray(new JarEntry[0]));
        
        // Load up classloader with instrumented jar
        return URLClassLoader.newInstance(new URL[] { instrumentedJarFile.toURI().toURL() }, TestUtils.class.getClassLoader());
    }
    
    /**
     * Load up a ZIP resource from the classpath and generate a {@link ClassNode} for each file with a class extension in that ZIP.
     * Behaviour is ZIP or classes within are not a parseable.
     * @param path path of ZIP resource
     * @return {@link ClassNode} representation of class files in ZIP
     * @throws NullPointerException if any argument is {@code null}
     * @throws IOException if any IO error occurs
     * @throws IllegalArgumentException if {@code path} cannot be found
     */
    public static Map<String, ClassNode> readZipResourcesAsClassNodes(String path) throws IOException {
        Validate.notNull(path);
        
        Map<String, byte[]> files = readZipFromResource(path);
        Map<String, ClassNode> ret = new LinkedHashMap<>();
        for (Entry<String, byte[]> entry : files.entrySet()) {
            if (!entry.getKey().toLowerCase().endsWith(".class")) {
                continue;
            }
            
            ClassReader cr = new ClassReader(new ByteArrayInputStream(entry.getValue()));
            ClassNode classNode = new ClassNode();
            cr.accept(classNode, 0);
            
            ret.put(entry.getKey(), classNode);
        }

        return ret;
    }

    /**
     * Writes {@link ClassNode}s to a JAR and loads it up. Behaviour is unknown if class is not valid in some way.
     * @param classNodes class nodes to put in to jar
     * @return class loader with files in newly created JAR available
     * @throws IOException if any IO error occurs
     * @throws NullPointerException if any argument is {@code null} or contains {@code null}
     * @throws IllegalArgumentException if {@code classNodes} is empty
     */
    public static URLClassLoader createJarAndLoad(ClassNode ... classNodes) throws IOException {
        Validate.notNull(classNodes);
        Validate.noNullElements(classNodes);
        
        CompositeClassInformationRepository infoRepo = new CompositeClassInformationRepository(
                new ClassResourceClassInformationRepository(TestUtils.class.getClassLoader()),
                FileSystemClassInformationRepository.create(getClasspath())
        );

        SimpleClassWriter cw = new SimpleClassWriter(SimpleClassWriter.COMPUTE_MAXS | SimpleClassWriter.COMPUTE_FRAMES,infoRepo);
        
        JarEntry[] jarEntries = new JarEntry[classNodes.length];
        for (int i = 0; i < jarEntries.length; i++) {
            classNodes[i].accept(cw);

            jarEntries[i] = new JarEntry(classNodes[i].name + ".class", cw.toByteArray());
        }
        
        return createJarAndLoad(jarEntries);
    }

    /**
     * Writes entries to a JAR and loads it up.
     * @param entries class nodes to put in to jar
     * @return class loader with files in newly created JAR available
     * @throws IOException if any IO error occurs
     * @throws NullPointerException if any argument is {@code null} or contains {@code null}
     * @throws IllegalArgumentException if {@code classNodes} is empty
     */
    public static URLClassLoader createJarAndLoad(JarEntry ... entries) throws IOException {
        Validate.notNull(entries);
        Validate.noNullElements(entries);
        
        File jarFile = createJar(entries);
        return URLClassLoader.newInstance(new URL[] { jarFile.toURI().toURL() }, TestUtils.class.getClassLoader());
    }
    
    /**
     * Loads up a ZIP file that's contained in the classpath.
     * @param path path of ZIP resource
     * @return contents of files within the ZIP
     * @throws IOException if any IO error occurs
     * @throws NullPointerException if any argument is {@code null} or contains {@code null} elements
     * @throws IllegalArgumentException if {@code path} cannot be found, or if zipPaths contains duplicates
     */
    public static Map<String, byte[]> readZipFromResource(String path) throws IOException {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        URL url = cl.getResource(path);
        Validate.isTrue(url != null);
        
        Map<String, byte[]> ret = new LinkedHashMap<>();
        
        try (InputStream is = url.openStream();
                ZipArchiveInputStream zais = new ZipArchiveInputStream(is)) {
            ZipArchiveEntry entry;
            while ((entry = zais.getNextZipEntry()) != null) {
                ret.put(entry.getName(), IOUtils.toByteArray(zais));
            }
        }
        
        return ret;
    }
    
    /**
     * Loads up a resource from the classpath as a byte array.
     * @param path path of resource
     * @return contents of resource
     * @throws IOException if any IO error occurs
     * @throws IllegalArgumentException if {@code path} cannot be found
     */
    public static byte[] getResource(String path) throws IOException {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        URL url = cl.getResource(path);
        Validate.isTrue(url != null);
        try (InputStream in = url.openStream()) {
            return IOUtils.toByteArray(in);
        }
    }

    /**
     * Gets the system classpath and creates a list of {@link File} objects for each path.
     *
     * @return classpath as list of {@link File} objects
     * @throws IllegalStateException if the classpath doesn't exist or can't be split up
     */
    public static List<File> getClasspath() {
        String pathSeparator = System.getProperty("path.separator");
        Validate.validState(pathSeparator != null);
        
        String classpath = System.getProperty("java.class.path");
        Validate.validState(classpath != null);
        List<File> classPathFiles = Arrays
                .stream(classpath.split(Pattern.quote(pathSeparator)))
                .map(x -> new File(x))
                .filter(x -> x.exists())
                .collect(Collectors.toList());

        // Commented out block is no longer valid as of Java9+
//        String bootClasspath = System.getProperty("sun.boot.class.path");
//        Validate.validState(bootClasspath != null);
//        List<File> bootClassPathFiles = Arrays
//                .stream(bootClasspath.split(Pattern.quote(pathSeparator)))
//                .map(x -> new File(x))
//                .filter(x -> x.exists())
//                .collect(Collectors.toList());

        ArrayList<File> ret = new ArrayList<>();
        ret.addAll(classPathFiles);
//        ret.addAll(bootClassPathFiles);
        
        return ret;
    }

    /**
     * Creates a JAR as a temporary file. Simple manifest file will automatically be inserted.
     *
     * @param entries files to put inside of the jar
     * @return resulting jar file
     * @throws IOException if any IO errors occur
     * @throws NullPointerException if any argument is {@code null} or contains {@code null} elements
     */
    public static File createJar(JarEntry... entries) throws IOException {
        Validate.notNull(entries);
        Validate.noNullElements(entries);

        File tempFile = File.createTempFile(TestUtils.class.getSimpleName(), ".jar");
        tempFile.deleteOnExit();

        try (FileOutputStream fos = new FileOutputStream(tempFile);
                JarArchiveOutputStream jaos = new JarArchiveOutputStream(fos)) {
            writeJarEntry(jaos, MANIFEST_PATH, MANIFEST_TEXT.getBytes(Charsets.UTF_8));
            for (JarEntry entry : entries) {
                writeJarEntry(jaos, entry.name, entry.data);
            }
        }

        return tempFile;
    }

    private static void writeJarEntry(JarArchiveOutputStream jaos, String name, byte[] data) throws IOException {
        ZipArchiveEntry entry = new JarArchiveEntry(name);
        entry.setSize(data.length);
        jaos.putArchiveEntry(entry);
        jaos.write(data);
        jaos.closeArchiveEntry();
    }

    /**
     * An entry for a JAR file.
     */
    public static final class JarEntry {

        private final String name;
        private final byte[] data;

        /**
         * Constructs a {@link JarEntry} object.
         *
         * @param name path of the file within the jar
         * @param data contents of the jar
         * @throws NullPointerException if any argument is {@code null}
         * @throws IllegalArgumentException if {@code name} is empty
         */
        public JarEntry(String name, byte[] data) {
            Validate.notNull(name);
            Validate.notNull(data);
            Validate.isTrue(!name.isEmpty());
            this.name = name;
            this.data = data.clone();
        }
    }
    
    public static final class ClassSerializabler implements Function<byte[], byte[]> {

        @Override
        public byte[] apply(byte[] t) {
            ClassReader cr = new ClassReader(t);
            ClassNode cn = new SimpleClassNode();
            cr.accept(cn, 0);

            String internalSerializableType = Type.getInternalName(Serializable.class);
            if (!cn.interfaces.contains(internalSerializableType)) {
                cn.interfaces.add(internalSerializableType);
            }

            ClassWriter cw = new ClassWriter(0);
            cn.accept(cw);

            return cw.toByteArray();
        }
    }
} public static Map<String, byte[]> readZipFromResource(String path) throws IOException {
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    URL url = cl.getResource(path);
    Validate.isTrue(url != null);
        
    Map<String, byte[]> ret = new LinkedHashMap<>();
        
    try (InputStream is = url.openStream();
            ZipArchiveInputStream zais = new ZipArchiveInputStream(is)) {
        ZipArchiveEntry entry;
        while ((entry = zais.getNextZipEntry()) != null) {
            ret.put(entry.getName(), IOUtils.toByteArray(zais));
        }
    }
        
    return ret;
} public static InsnList returnVoid() {
    return returnValue(Type.VOID_TYPE, new InsnList());
} public static InsnList throwRuntimeException(String message) {
    Validate.notNull(message);

    InsnList ret = new InsnList();

    ret.add(new TypeInsnNode(Opcodes.NEW, "java/lang/RuntimeException"));
    ret.add(new InsnNode(Opcodes.DUP));
    ret.add(new LdcInsnNode(message));
    ret.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V", false));
    ret.add(new InsnNode(Opcodes.ATHROW));

    return ret;
} public static InsnList pop() {
    InsnList ret = new InsnList();
    ret.add(new InsnNode(Opcodes.POP));

    return ret;
}
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.addLabel;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.call;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.construct;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.jumpTo;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.loadVar;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.merge;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.saveVar;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.tableSwitch;
import com.offbynull.coroutines.instrumenter.testhelpers.TestUtils;
import static com.offbynull.coroutines.instrumenter.testhelpers.TestUtils.readZipFromResource;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.BasicValue;
import org.objectweb.asm.tree.analysis.Frame;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.returnVoid;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.throwRuntimeException;
import static com.offbynull.coroutines.instrumenter.generators.GenericGenerators.pop;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
package com.offbynull.coroutines.instrumenter.asm;


public class SimpleVerifierTest {

    private ClassResourceClassInformationRepository classRepo;
    private ClassNode classNode;
    private MethodNode methodNode;

    @BeforeEach
    public void setUp() throws IOException {
        classRepo = new ClassResourceClassInformationRepository(TestUtils.class.getClassLoader());

        byte[] classData = readZipFromResource("SimpleStub.zip").get("SimpleStub.class");

        ClassReader classReader = new ClassReader(classData);
        classNode = new ClassNode();
        classReader.accept(classNode, 0);

        methodNode = classNode.methods.get(1); // stub should be here
    }

    @Test
    public void testVertificationWithoutUsingClassLoader() throws Exception {

        // augment method to take in a single int argument
        methodNode.desc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE);

        // augment method instructions
        VariableTable varTable = new VariableTable(classNode, methodNode);

        Class<?> iterableClass = Iterable.class;
        Constructor arrayListConstructor = ConstructorUtils.getAccessibleConstructor(ArrayList.class);
        Constructor linkedListConstructor = ConstructorUtils.getAccessibleConstructor(LinkedList.class);
        Constructor hashSetConstructor = ConstructorUtils.getAccessibleConstructor(HashSet.class);
        Method iteratorMethod = MethodUtils.getAccessibleMethod(iterableClass, "iterator");

        Type iterableType = Type.getType(iterableClass);

        VariableTable.Variable testArg = varTable.getArgument(1);
        VariableTable.Variable listVar = varTable.acquireExtra(iterableType);

        /**
         * Collection it;
         * switch(arg1) {
         *     case 0:
         *         it = new ArrayList()
         *         break;
         *     case 1:
         *         it = new LinkedList()
         *         break;
         *     case 2:
         *         it = new HashSet()
         *         break;
         *     default: throw new RuntimeException("must be 0 or 1");
         * }
         * list.iterator();
         */
        LabelNode invokePoint = new LabelNode();
        InsnList methodInsnList
                = merge(tableSwitch(loadVar(testArg),
                                throwRuntimeException("must be 0 or 1"),
                                0,
                                merge(
                                        construct(arrayListConstructor),
                                        saveVar(listVar),
                                        jumpTo(invokePoint)
                                ),
                                merge(
                                        construct(linkedListConstructor),
                                        saveVar(listVar),
                                        jumpTo(invokePoint)
                                ),
                                merge(
                                        construct(hashSetConstructor),
                                        saveVar(listVar),
                                        jumpTo(invokePoint)
                                )
                        ),
                        addLabel(invokePoint),