public static final String BASIC_TYPE_INVOKE_TEST = "BasicTypeInvokeTest"; public static final String DOUBLE_RETURN_INVOKE_TEST = "DoubleReturnInvokeTest"; public static final String EMPTY_CONTINUATION_POINT_INVOKE_TEST = "EmptyContinuationPointInvokeTest"; public static final String INHERITANCE_INVOKE_TEST = "InheritanceInvokeTest"; public static final String INTERFACE_INVOKE_TEST = "InterfaceInvokeTest"; public static final String LONG_RETURN_INVOKE_TEST = "LongReturnInvokeTest"; public static final String NORMAL_INVOKE_TEST = "NormalInvokeTest"; public static final String NULL_TYPE_IN_LOCAL_VARIABLE_TABLE_INVOKE_TEST = "NullTypeInLocalVariableTableInvokeTest"; public static final String RECURSIVE_INVOKE_TEST = "RecursiveInvokeTest"; public static final String RETURN_INVOKE_TEST = "ReturnInvokeTest"; public static final String STATIC_INVOKE_TEST = "StaticInvokeTest"; 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
} public static URLClassLoader loadClassesInZipResourceAndInstrument(String path) throws IOException {
    return loadClassesInZipResourceAndInstrument(path, new InstrumentationSettings(MarkerType.CONSTANT, false, true));
} public interface Coroutine extends Serializable {
    /**
     * Entry point of coroutine.
     * @param continuation used to suspend/yield the coroutine
     * @throws Exception if a problem occurs
     */
    void run(Continuation continuation) throws Exception;
} public final class CoroutineReader {
    private final CoroutineDeserializer deserializer;
    private final Map updatersMap;
    private final Map interceptersMap;

    /**
     * Construct a {@link CoroutineReader} object. Equivalent to calling
     * {@code new CoroutineReader(new DefaultCoroutineDeserializer(), new FrameUpdatePoint[0], new FrameInterceptPoint[0])}.
     */
    public CoroutineReader() {
        this(new DefaultCoroutineDeserializer(), new FrameUpdatePoint[0], new FrameInterceptPoint[0]);
    }

    /**
     * Constructs a {@link CoroutineReader}. Equivalent to calling
     * {@code new CoroutineReader(new DefaultCoroutineDeserializer(), frameUpdatePoints, new FrameInterceptPoint[0])}.
     * @param frameUpdatePoints frame update points
     * @throws IllegalArgumentException if {@code frameUpdatePoints} contains more than one entry for the same identifier
     * (className/oldMethodId/newMethodId/continuationPoint)
     * @throws NullPointerException if any argument is {@code null}
     */
    public CoroutineReader(FrameUpdatePoint[] frameUpdatePoints) {
        this(new DefaultCoroutineDeserializer(), frameUpdatePoints, new FrameInterceptPoint[0]);
    }

    /**
     * Constructs a {@link CoroutineReader}. Equivalent to calling
     * {@code new CoroutineReader(new DefaultCoroutineDeserializer(), new FrameUpdatePoint[0], frameInterceptPoints)}.
     * @param frameInterceptPoints frame intercept points
     * @throws IllegalArgumentException if {@code frameInterceptPoints} contains more than one entry for the same identifier
     * (className/methodId/continuationPoint)
     * @throws NullPointerException if any argument is {@code null}
     */
    public CoroutineReader(FrameInterceptPoint[] frameInterceptPoints) {
        this(new DefaultCoroutineDeserializer(), new FrameUpdatePoint[0], frameInterceptPoints);
    }

    /**
     * Constructs a {@link CoroutineReader}. Equivalent to calling
     * {@code new CoroutineReader(new DefaultCoroutineDeserializer(), frameUpdatePoints, frameInterceptPoints)}.
     * @param frameUpdatePoints frame update points
     * @param frameInterceptPoints frame intercept points
     * @throws IllegalArgumentException if {@code frameUpdatePoints} contains more than one entry for the same identifier
     * (className/oldMethodId/newMethodId/continuationPoint), or if {@code frameInterceptPoints} contains more than one entry for the same
     * identifier (className/methodId/continuationPoint)
     * @throws NullPointerException if any argument is {@code null}
     */
    public CoroutineReader(FrameUpdatePoint[] frameUpdatePoints, FrameInterceptPoint[] frameInterceptPoints) {
        this(new DefaultCoroutineDeserializer(), frameUpdatePoints, frameInterceptPoints);
    }

    /**
     * Constructs a {@link CoroutineReader} object.
     * @param deserializer deserializer to write out the coroutine state
     * @param frameUpdatePoints frame update points
     * @param frameInterceptPoints frame intercept points
     * @throws NullPointerException if any argument is {@code null} or contains {@code null}
     * @throws IllegalArgumentException if {@code frameUpdatePoints} contains more than one entry for the same identifier
     * (className/oldMethodId/newMethodId/continuationPoint), or if {@code frameInterceptPoints} contains more than one entry for the same
     * identifier (className/methodId/continuationPoint)
     */
    public CoroutineReader(CoroutineDeserializer deserializer,
            FrameUpdatePoint[] frameUpdatePoints,
            FrameInterceptPoint[] frameInterceptPoints) {
        if (deserializer == null || frameUpdatePoints == null || frameInterceptPoints == null) {
            throw new NullPointerException();
        }

        this.deserializer = deserializer;
        this.updatersMap = new HashMap();
        this.interceptersMap = new HashMap();

        SerializationUtils.populateUpdatesMapAndInterceptsMap(updatersMap, frameUpdatePoints, interceptersMap, frameInterceptPoints);
    }

    /**
     * Deserializes a {@link CoroutineRunner} object from a byte array.
     * <p>
     * If you're handling your own deserialization and you simply want to reconstruct the deserialized object to the
     * {@link CoroutineRunner}, use {@link #reconstruct(com.offbynull.coroutines.user.SerializedState) }.
     * @param data byte array to deserialize
     * @return {@code data} deserialized to a {@link CoroutineRunner} object
     * @throws NullPointerException if any argument is {@code null}
     * @throws IllegalArgumentException if failed to deserialize or deserialized to a state for an unrecognized method (e.g. a method that's
     * state is being deserialized for was changed but no {@link FrameUpdatePoint} was provided to this class's constructor to
     * handle the changes)
     */
    public CoroutineRunner read(byte[] data) {
        if (data == null) {
            throw new NullPointerException();
        }

        SerializedState serializedState = deserializer.deserialize(data);
        return reconstruct(serializedState);
    }

    /**
     * Reconstructs a {@link CoroutineRunner} object from a serializable state.
     * @param state serialized state to reconstruct
     * @return reconstructed {@link CoroutineRunner}
     * @throws NullPointerException if any argument is {@code null}
     * @throws IllegalArgumentException if {@code state} contains a reference / was updated to contain a reference to an
     * unrecognized method (e.g. a method that's state is being reconstructed for was changed but no {@link FrameUpdatePoint} was
     * provided to this class's constructor to handle the changes)
     */
    public CoroutineRunner reconstruct(SerializedState state) {
        if (state == null) {
            throw new NullPointerException();
        }

        try {
            state.validateState();
        } catch (IllegalStateException ise) {
            throw new IllegalArgumentException(ise);
        }

        Object context = state.getContext();

        VersionedFrame[] versionedFrames = state.getFrames();

        Coroutine coroutine = state.getCoroutine();
        Continuation cn = new Continuation();
        cn.setMode(Continuation.MODE_SAVING);
        cn.setContext(context);

        for (int i = versionedFrames.length - 1; i >= 0; i--) {
            VersionedFrame versionedFrame = versionedFrames[i];
            Frame frame = SerializationUtils.calculateCorrectFrameVersion(null, updatersMap, interceptersMap, versionedFrame);

            // Check that a workable frame actually exists
            if (frame == null) {
                throw new IllegalArgumentException("No loaded method or frame updated found for one of the supplied frames");
            }
            
            
            // Construct MethodState
            String className = frame.getClassName();
            int methodId = frame.getMethodId();
            int continuationPoint = frame.getContinuationPointId();

            LockState lockState = new LockState();
            Object[] monitors = frame.getMonitors();
            for (int j = 0; j < monitors.length; j++) {
                Object monitor = monitors[j];

                lockState.enter(monitor);
            }

            Data variables = frame.getVariables();
            Data operands = frame.getOperands();
            Object[] frameData = new Object[10];
            frameData[0] = variables.getInts();
            frameData[1] = variables.getFloats();
            frameData[2] = variables.getLongs();
            frameData[3] = variables.getDoubles();
            frameData[4] = variables.getObjects();
            frameData[5] = operands.getInts();
            frameData[6] = operands.getFloats();
            frameData[7] = operands.getLongs();
            frameData[8] = operands.getDoubles();
            frameData[9] = operands.getObjects();
            
            placeContinuationReferences(variables.getContinuationIndexes(), (Object[]) frameData[4], cn);
            placeContinuationReferences(operands.getContinuationIndexes(), (Object[]) frameData[9], cn);
            
            MethodState methodState = new MethodState(className, methodId, continuationPoint, frameData, lockState);

            
            
            // Place it in the new continuation object.
            cn.pushNewMethodState(methodState);
        }

        if (versionedFrames.length > 0) {
            // The coroutine has executed and has saved state
            cn.successExecutionCycle();
            cn.setMode(Continuation.MODE_LOADING);
        } else {
            // The coroutine hasn't executed / doesn't have saved state
            cn.reset();
        }

        return new CoroutineRunner(coroutine, cn);
    }

    private void placeContinuationReferences(int[] continuationIndexes, Object[] objects, Continuation cn) {
        for (int i = 0; i < continuationIndexes.length; i++) {
            int idx = continuationIndexes[i];
            objects[idx] = cn;
        }
    }

    /**
     * Coroutine deserializer.
     */
    public interface CoroutineDeserializer {
        /**
         * Deserializes a coroutine.
         * @param data byte array to deserialize 
         * @return deserialized state
         * @throws NullPointerException if any argument is {@code null}
         * @throws IllegalArgumentException if failed to serialize
         */
        SerializedState deserialize(byte[] data);
    }
    
    /**
     * Default implementation of {@link CoroutineDeserializer} (uses Java's built-in serialization mechanism). This implementation has the
     * the following restrictions...
     * <ol>
     * <li>Deserialization will fail if you have any synchronized blocks (monitor locks).</li>
     * <li>The classes that make up the current state of your coroutine must be serializable (must implement {@link Serializable}).</li>
     * <li>The variables/operands that make up the current state of your coroutine must be serializable (must either be primitives or
     * implement {@link Serializable}).</li>
     * </ol>
     */
    public static final class DefaultCoroutineDeserializer implements CoroutineDeserializer {

        //CHECKSTYLE.OFF:JavadocMethod - Requires @Override annotation to work, but this is designed for Java 1.4 (no annotations support)
        public SerializedState deserialize(byte[] data) {
            if (data == null) {
                throw new NullPointerException();
            }

            ByteArrayInputStream bais = null;
            ObjectInputStream ois = null;
            try {
                bais = new ByteArrayInputStream(data);
                ois = new ObjectInputStream(bais) {
                    protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
                        try {
                            return super.resolveClass(desc);
                        } catch (ClassNotFoundException cnfe) {
                            // We need this for our test, because we're loading up the coroutine classes in their own classloader and
                            // objectinputstream only uses the system classloader. See the serialization test in InstrumenterTest.
                            return Thread.currentThread().getContextClassLoader().loadClass(desc.getName());
                        }
                    }
                };

                SerializedState serializedState = (SerializedState) ois.readObject();

                VersionedFrame[] frames = serializedState.getFrames();
                for (int i = 0; i < frames.length; i++) {
                    Frame[] possibleFrames = frames[i].getFrames();
                    for (int j = 0; j < possibleFrames.length; j++) {
                        Frame frame = possibleFrames[j];
                        if (frame.getMonitors().length > 0) {
                            throw new IllegalArgumentException("Monitors not allowed in default serializer");
                        }
                    }
                }
                
                return serializedState;
            } catch (StreamCorruptedException sce) {
                throw new IllegalArgumentException(sce);
            } catch (OptionalDataException ode) {
                throw new IllegalArgumentException(ode);
            } catch (InvalidClassException ice) {
                throw new IllegalArgumentException(ice);
            } catch (ClassNotFoundException cnfe) {
                throw new IllegalArgumentException(cnfe);
            } catch (ClassCastException cce) {
                throw new IllegalArgumentException(cce);
            } catch (IOException ioe) {
                throw new IllegalStateException(ioe); // should never happen
            } finally {
                if (ois != null) {
                    try {
                        ois.close();
                    } catch (IOException ioe) {
                        // do nothing
                    }
                }
                if (bais != null) {
                    try {
                        bais.close();
                    } catch (IOException ioe) {
                        // do nothing
                    }
                }
            }
        }
        //CHECKSTYLE.ON:JavadocMethod
    }
} public final class CoroutineRunner implements Serializable {
    private static final long serialVersionUID = 6L;
    
    private Coroutine coroutine;
    private Continuation continuation;

    /**
     * Constructs a {@link CoroutineRunner} object.
     * @param coroutine coroutine to run
     * @throws NullPointerException if any argument is {@code null}
     */
    public CoroutineRunner(Coroutine coroutine) {
        if (coroutine == null) {
            throw new NullPointerException();
        }
        this.coroutine = coroutine;
        this.continuation = new Continuation();
    }
    
    CoroutineRunner(Coroutine coroutine, Continuation continuation) {
        if (coroutine == null || continuation == null) {
            throw new NullPointerException();
        }
        this.coroutine = coroutine;
        this.continuation = continuation;
    }

    /**
     * Starts/resumes execution of this coroutine. If the coroutine being executed reaches a suspension point (meaning that the method calls
     * {@link Continuation#suspend() }), this method will return {@code true}. If the coroutine has finished executing, this method will
     * return {@code false}.
     * <p>
     * Calling this method again after the coroutine has finished executing will restart the coroutine.
     * @return {@code false} if execution has completed (the method has return), {@code true} if execution was suspended.
     * @throws CoroutineException an exception occurred during execution of this coroutine, the saved execution stack and object state may
     * be out of sync at this point (meaning that unless you know what you're doing, you should not call {@link CoroutineRunner#execute() }
     * again)
     */
    public boolean execute() {
        try {
            coroutine.run(continuation);
            continuation.successExecutionCycle();
        } catch (Exception e) {
            continuation.failedExecutionCycle();
            throw new CoroutineException("Exception thrown during execution", e);
        }
        
        // if mode was not set to SAVING after return, it means the method finished executing
        if (continuation.getMode() != Continuation.MODE_SAVING) {
            continuation.reset(); // clear methodstates + set to normal
            return false;
        } else {
            continuation.setMode(Continuation.MODE_LOADING); // set to loading for next invokation
            return true;
        }
    }

    /**
     * Get the context. Accessible via the {@link Continuation} object that gets used by this coroutine.
     * @return context context
     */
    public Object getContext() {
        return continuation.getContext();
    }

    /**
     * Set the context. Accessible via the {@link Continuation} object that gets used by this coroutine.
     * @param context context
     */
    public void setContext(Object context) {
        continuation.setContext(context);
    }

    /**
     * Get the coroutine assigned to this runner.
     * @return coroutine assigned to this runner
     */
    public Coroutine getCoroutine() {
        return coroutine;
    }

    Continuation getContinuation() {
        return continuation;
    }
} public final class CoroutineWriter {
    private final CoroutineSerializer serializer;
    private final Map updatersMap;
    private final Map interceptersMap;
    
    /**
     * Construct a {@link CoroutineWriter} object. Equivalent to calling
     * {@code new CoroutineWriter(new DefaultCoroutineSerializer(), new FrameUpdatePoint[0], new FrameInterceptPoint[0])}.
     */
    public CoroutineWriter() {
        this(new DefaultCoroutineSerializer(), new FrameUpdatePoint[0], new FrameInterceptPoint[0]);
    }

    /**
     * Constructs a {@link CoroutineWriter}. Equivalent to calling
     * {@code new CoroutineWriter(new DefaultCoroutineSerializer(), frameUpdatePoints, new FrameInterceptPoint[0])}.
     * @param frameUpdatePoints frame update points
     * @throws IllegalArgumentException if {@code frameUpdatePoints} contains more than one entry for the same identifier
     * (className/oldMethodId/newMethodId/continuationPoint)
     * @throws NullPointerException if any argument is {@code null}
     */
    public CoroutineWriter(FrameUpdatePoint[] frameUpdatePoints) {
        this(new DefaultCoroutineSerializer(), frameUpdatePoints, new FrameInterceptPoint[0]);
    }

    /**
     * Constructs a {@link CoroutineWriter}. Equivalent to calling
     * {@code new CoroutineWriter(new DefaultCoroutineSerializer(), new FrameUpdatePoint[0], frameInterceptPoints)}.
     * @param frameInterceptPoints frame intercept points
     * @throws IllegalArgumentException if {@code frameInterceptPoints} contains more than one entry for the same identifier
     * (className/methodId/continuationPoint)
     * @throws NullPointerException if any argument is {@code null}
     */
    public CoroutineWriter(FrameInterceptPoint[] frameInterceptPoints) {
        this(new DefaultCoroutineSerializer(), new FrameUpdatePoint[0], frameInterceptPoints);
    }

    /**
     * Constructs a {@link CoroutineWriter}. Equivalent to calling
     * {@code new CoroutineWriter(new DefaultCoroutineSerializer(), frameUpdatePoints, frameInterceptPoints)}.
     * @param frameUpdatePoints frame update points
     * @param frameInterceptPoints frame intercept points
     * @throws IllegalArgumentException if {@code frameUpdatePoints} contains more than one entry for the same identifier
     * (className/oldMethodId/newMethodId/continuationPoint), or if {@code frameInterceptPoints} contains more than one entry for the same
     * identifier (className/methodId/continuationPoint)
     * @throws NullPointerException if any argument is {@code null}
     */
    public CoroutineWriter(FrameUpdatePoint[] frameUpdatePoints, FrameInterceptPoint[] frameInterceptPoints) {
        this(new DefaultCoroutineSerializer(), frameUpdatePoints, frameInterceptPoints);
    }

    /**
     * Constructs a {@link CoroutineWriter} object.
     * @param serializer serializer to write out the coroutine state
     * @param frameUpdatePoints frame update points
     * @param frameInterceptPoints frame intercept points
     * @throws NullPointerException if any argument is {@code null} or contains {@code null}
     * @throws IllegalArgumentException if {@code frameUpdatePoints} contains more than one entry for the same identifier
     * (className/oldMethodId/newMethodId/continuationPoint), or if {@code frameInterceptPoints} contains more than one entry for the same
     * identifier (className/methodId/continuationPoint)
     */
    public CoroutineWriter(CoroutineSerializer serializer,
            FrameUpdatePoint[] frameUpdatePoints,
            FrameInterceptPoint[] frameInterceptPoints) {
        if (serializer == null || frameUpdatePoints == null || frameInterceptPoints == null) {
            throw new NullPointerException();
        }

        this.serializer = serializer;
        this.updatersMap = new HashMap();
        this.interceptersMap = new HashMap();

        SerializationUtils.populateUpdatesMapAndInterceptsMap(
                updatersMap, frameUpdatePoints,
                interceptersMap, frameInterceptPoints);
    }
    
    /**
     * Serializes a {@link CoroutineRunner} object as a byte array.
     * @param runner coroutine runner to serialize
     * @return {@code runner} serialized to byte array
     * @throws NullPointerException if any argument is {@code null}
     * @throws IllegalArgumentException if failed to serialize
     */
    public byte[] write(CoroutineRunner runner) {
        if (runner == null) {
            throw new NullPointerException();
        }

        SerializedState serializeState = deconstruct(runner);
        return serializer.serialize(serializeState);
    }

    /**
     * Deconstructs a {@link CoroutineRunner} object to a serializable state.
     * @param runner coroutine runner to deconstruct
     * @return deconstructed representation of {@link CoroutineRunner}
     * @throws NullPointerException if any argument is {@code null}
     * @throws IllegalArgumentException if failed to deconstruct
     */
    public SerializedState deconstruct(CoroutineRunner runner) {
        if (runner == null) {
            throw new NullPointerException();
        }

        Coroutine coroutine = runner.getCoroutine();
        Continuation cn = runner.getContinuation();

        int size = cn.getSize();
        VersionedFrame[] frames = new VersionedFrame[size];

        MethodState currentMethodState = cn.getSaved(0);
        
        int idx = 0;
        while (currentMethodState != null) {
            // Pull out information from MethoState. We should never modify MethodState values, they will be copied by the Data
            // constructor before being passed to the user for further modification.
            String className = currentMethodState.getClassName();
            int methodId = currentMethodState.getMethodId();
            int continuationPoint = currentMethodState.getContinuationPoint();
            
            LockState monitors = currentMethodState.getLockState();
            
            int[] intVars = ((int[]) currentMethodState.getData()[0]);
            float[] floatVars = ((float[]) currentMethodState.getData()[1]);
            long[] longVars = ((long[]) currentMethodState.getData()[2]);
            double[] doubleVars = ((double[]) currentMethodState.getData()[3]);
            Object[] objectVars = ((Object[]) currentMethodState.getData()[4]);
            
            int[] intOperands = ((int[]) currentMethodState.getData()[5]);
            float[] floatOperands = ((float[]) currentMethodState.getData()[6]);
            long[] longOperands = ((long[]) currentMethodState.getData()[7]);
            double[] doubleOperands = ((double[]) currentMethodState.getData()[8]);
            Object[] objectOperands = ((Object[]) currentMethodState.getData()[9]);


            // Clone the object[] buffers because we need to remove references to the Continuation object for this coroutine.
            objectVars = objectVars == null ? new Object[0] : (Object[]) objectVars.clone();
            int[] continuationPositionsInObjectVars = clearContinuationReferences(objectVars, cn);

            objectOperands = objectOperands == null ? new Object[0] : (Object[]) objectOperands.clone();
            int[] continuationPositionsInObjectOperands = clearContinuationReferences(objectOperands, cn);


            // Create the frame, making sure we create empty arrays for any null references (remember that MethodState var/operand arrays
            // that don't contain any data are set to null due to an optimization).
            Frame serializedFrame = new Frame(
                    className,
                    methodId,
                    continuationPoint,
                    monitors == null ? new Object[0] : monitors.toArray(),
                    new Data(
                            intVars == null ? new int[0] : intVars,
                            floatVars == null ? new float[0] : floatVars,
                            longVars == null ? new long[0] : longVars,
                            doubleVars == null ? new double[0] : doubleVars,
                            objectVars,
                            continuationPositionsInObjectVars),
                    new Data(
                            intOperands == null ? new int[0] : intOperands,
                            floatOperands == null ? new float[0] : floatOperands,
                            longOperands == null ? new long[0] : longOperands,
                            doubleOperands == null ? new double[0] : doubleOperands,
                            objectOperands,
                            continuationPositionsInObjectOperands));

            
            // Add all possible down-versions for frame into a versionedframe object and add it
            VersionedFrame versionedFrame = SerializationUtils.calculateAllPossibleFrameVersions(
                    null,
                    updatersMap,
                    interceptersMap,
                    serializedFrame);
            frames[idx] = versionedFrame;
            idx++;

            currentMethodState = currentMethodState.getNext();
        }
        
        Object context = cn.getContext();
        
        return new SerializedState(coroutine, context, frames);
    }

    private int[] clearContinuationReferences(Object[] objects, Continuation cn) {
        int size = 0;
        for (int i = 0; i < objects.length; i++) {
            if (objects[i] == cn) {
                size++;
            }
        }
        
        int[] indexes = new int[size];
        int indexesPointer = 0;
        for (int i = 0; i < objects.length; i++) {
            if (objects[i] == cn) {
                objects[i] = null;
                indexes[indexesPointer] = i;
                indexesPointer++;
            }
        }
        
        return indexes;
    }

    /**
     * Coroutine serializer.
     */
    public interface CoroutineSerializer {
        /**
         * Serializes a coroutine.
         * @param serializedState state to serialize
         * @return serialized byte array
         * @throws NullPointerException if any argument is {@code null}
         * @throws IllegalArgumentException if failed to serialize
         */
        byte[] serialize(SerializedState serializedState);
    }

    /**
     * Default implementation of {@link CoroutineSerializer} (uses Java's built-in serialization mechanism). This implementation has the
     * following restrictions...
     * <ol>
     * <li>Serialization will fail if you have any synchronized blocks (monitor locks).</li>
     * <li>The classes that make up the current state of your coroutine must be serializable (must implement {@link Serializable}).</li>
     * <li>The variables/operands that make up the current state of your coroutine must be serializable (must either be primitives or
     * implement {@link Serializable}).</li>
     * </ol>
     */
    public static final class DefaultCoroutineSerializer implements CoroutineSerializer {
        
        //CHECKSTYLE.OFF:JavadocMethod - Requires @Override annotation to work, but this is designed for Java 1.4 (no annotations support)
        public byte[] serialize(SerializedState serializedState) {
            if (serializedState == null) {
                throw new NullPointerException();
            }

            VersionedFrame[] frames = serializedState.getFrames();
            for (int i = 0; i < frames.length; i++) {
                Frame[] possibleFrames = frames[i].getFrames();
                for (int j = 0; j < possibleFrames.length; j++) {
                    Frame frame = possibleFrames[j];
                    if (frame.getMonitors().length > 0) {
                        throw new IllegalArgumentException("Monitors not allowed in default serializer");
                    }
                }
            }

            ByteArrayOutputStream baos = null;
            ObjectOutputStream oos = null;
            try {
                baos = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(baos);

                oos.writeObject(serializedState);

                return baos.toByteArray();
            } catch (NotSerializableException nse) {
                throw new IllegalArgumentException(nse);
            } catch (InvalidClassException ice) {
                throw new IllegalArgumentException(ice);
            } catch (IOException ioe) {
                throw new IllegalStateException(ioe); // should never happen
            } finally {
                if (oos != null) {
                    try {
                        oos.close();
                    } catch (IOException ioe) {
                        // do nothing
                    }
                }
                if (baos != null) {
                    try {
                        baos.close();
                    } catch (IOException ioe) {
                        // do nothing
                    }
                }
            }
        }
        //CHECKSTYLE.ON:JavadocMethod
    }
}
import static com.offbynull.coroutines.instrumenter.SharedConstants.BASIC_TYPE_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.DOUBLE_RETURN_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.EMPTY_CONTINUATION_POINT_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.INHERITANCE_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.INTERFACE_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.LONG_RETURN_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.NORMAL_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.NULL_TYPE_IN_LOCAL_VARIABLE_TABLE_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.RECURSIVE_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.RETURN_INVOKE_TEST;
import static com.offbynull.coroutines.instrumenter.SharedConstants.STATIC_INVOKE_TEST;
import com.offbynull.coroutines.instrumenter.generators.DebugGenerators.MarkerType;
import static com.offbynull.coroutines.instrumenter.testhelpers.TestUtils.loadClassesInZipResourceAndInstrument;
import com.offbynull.coroutines.user.Coroutine;
import com.offbynull.coroutines.user.CoroutineReader;
import com.offbynull.coroutines.user.CoroutineRunner;
import com.offbynull.coroutines.user.CoroutineWriter;
import java.net.URLClassLoader;
import java.util.concurrent.ArrayBlockingQueue;
import static org.apache.commons.lang3.reflect.ConstructorUtils.invokeConstructor;
import static org.apache.commons.lang3.reflect.FieldUtils.readField;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
package com.offbynull.coroutines.instrumenter;


public final class SerializationTest {

    @Test
    public void mustProperlySuspendWithVirtualMethods() throws Exception {
        performIntCountTest(NORMAL_INVOKE_TEST, new InstrumentationSettings(MarkerType.CONSTANT, false, true));
    }
    
    @Test
    public void mustProperlySuspendWithStaticMethods() throws Exception {
        performIntCountTest(STATIC_INVOKE_TEST, new InstrumentationSettings(MarkerType.CONSTANT, false, true));
    }

    @Test
    public void mustProperlySuspendWithInterfaceMethods() throws Exception {
        performIntCountTest(INTERFACE_INVOKE_TEST, new InstrumentationSettings(MarkerType.CONSTANT, false, true));
    }

    @Test
    public void mustProperlySuspendWithRecursiveMethods() throws Exception {
        performIntCountTest(RECURSIVE_INVOKE_TEST, new InstrumentationSettings(MarkerType.CONSTANT, false, true));
    }

    @Test
    public void mustProperlySuspendWithInheritedMethods() throws Exception {
        performIntCountTest(INHERITANCE_INVOKE_TEST, new InstrumentationSettings(MarkerType.CONSTANT, false, true));