/***
 * ASM tests
 * Copyright (c) 2000-2011 INRIA, France Telecom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.objectweb.asm.test.cases;

import java.io.IOException;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * Generates classes with StackMap and StackMapTable attributes. Covers all
 * frame (FULL, SAME, etc.) and frame element types (TOP, NULL, INTEGER, etc.).
 * Also covers the V1_6 class version.
 * 
 * @author Eric Bruneton
 */
public class Frames extends Generator {

    final static int M = ACC_STATIC;
    final static String I1 = "Ljava/io/Serializable;";
    final static String I2 = "Ljava/lang/Comparable;";

    @Override
    public void generate(final String dir) throws IOException {
        byte[] b = dump();
        ClassWriter cw = new ClassWriter(0);
        ClassReader cr = new ClassReader(b);
        cr.accept(new RenameAdapter(cw), ClassReader.EXPAND_FRAMES);

        generate(dir, "pkg/FrameTable.class", b);
        generate(dir, "pkg/FrameMap.class", cw.toByteArray());
    }

    public byte[] dump() {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        MethodVisitor mv;
        FieldVisitor fv;

        cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "pkg/FrameTable", null,
                "java/lang/Object", null);

        fv = cw.visitField(M, "long", "Ljava/lang/Long;", null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "double", "Ljava/lang/Double;", null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "number", "Ljava/lang/Number;", null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "serializable", I1, null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "comparable", I2, null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "longArray", "[Ljava/lang/Long;", null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "intArray", "[I", null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "floatArray", "[F", null, null);
        fv.visitEnd();

        fv = cw.visitField(M, "objectArray", "[Ljava/lang/Object;", null, null);
        fv.visitEnd();

        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V",
                null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V",
                false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        uninitializedThisType(cw);
        uninitializedLocalType(cw);
        uninitializedStackType(cw);
        nullType(cw);
        topType(cw);
        arrayTypes(cw);

        mergeTypes(cw);
        mergeStackTypes(cw);
        mergeNullArray(cw);

        appendAndChopFrame(cw);
        sameLocals1stackItemFrame(cw);
        sameLocals1stackItemFrame2(cw);
        sameLocals1stackItemFrameExtended(cw);
        sameFrameExtended(cw);

        deadCode(cw);

        cw.visitEnd();

        return cw.toByteArray();
    }

    private void uninitializedThisType(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(I)V", null,
                null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitTypeInsn(NEW, "pkg/FrameTable");
        mv.visitInsn(DUP);
        mv.visitVarInsn(ILOAD, 1);
        Label l1 = new Label();
        mv.visitJumpInsn(IFNE, l1);
        mv.visitInsn(ACONST_NULL);
        Label l2 = new Label();
        mv.visitJumpInsn(GOTO, l2);
        mv.visitFrame(F_FULL, 2, new Object[] { UNINITIALIZED_THIS, INTEGER },
                3, new Object[] { UNINITIALIZED_THIS, l0, l0 });
        mv.visitLabel(l1);
        mv.visitTypeInsn(NEW, "java/lang/Object");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V",
                false);
        mv.visitFrame(F_FULL, 2, new Object[] { UNINITIALIZED_THIS, INTEGER },
                4, new Object[] { UNINITIALIZED_THIS, l0, l0,
                        "java/lang/Object" });
        mv.visitLabel(l2);
        mv.visitMethodInsn(INVOKESPECIAL, "pkg/FrameTable", "<init>",
                "(Ljava/lang/Object;)V", false);
        mv.visitMethodInsn(INVOKESPECIAL, "pkg/FrameTable", "<init>",
                "(Ljava/lang/Object;)V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void uninitializedLocalType(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "uninitializedLocalType",
                "(Z)V", null, null);
        mv.visitCode();
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitTypeInsn(NEW, "java/lang/Long");
        mv.visitVarInsn(ASTORE, 2);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitVarInsn(ILOAD, 1);
        Label l1 = new Label();
        mv.visitJumpInsn(IFEQ, l1);
        mv.visitInsn(LCONST_0);
        Label l2 = new Label();
        mv.visitJumpInsn(GOTO, l2);
        mv.visitFrame(F_FULL, 3,
                new Object[] { "pkg/FrameTable", INTEGER, l0 }, 1,
                new Object[] { l0 });
        mv.visitLabel(l1);
        mv.visitInsn(LCONST_1);
        mv.visitFrame(F_FULL, 3,
                new Object[] { "pkg/FrameTable", INTEGER, l0 }, 2,
                new Object[] { l0, LONG });
        mv.visitLabel(l2);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Long", "<init>", "(J)V",
                false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void uninitializedStackType(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "uninitializedStackType",
                "(Z)V", null, null);
        mv.visitCode();
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitTypeInsn(NEW, "java/lang/Long");
        mv.visitInsn(DUP);
        mv.visitVarInsn(ILOAD, 1);
        Label l1 = new Label();
        mv.visitJumpInsn(IFEQ, l1);
        mv.visitInsn(LCONST_0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Long", "<init>", "(J)V",
                false);
        Label l2 = new Label();
        mv.visitJumpInsn(GOTO, l2);
        mv.visitFrame(F_FULL, 1, new Object[] { "pkg/FrameTable" }, 2,
                new Object[] { l0, l0 });
        mv.visitLabel(l1);
        mv.visitInsn(LCONST_1);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Long", "<init>", "(J)V",
                false);
        mv.visitFrame(F_SAME1, 0, null, 1, new Object[] { "java/lang/Long" });
        mv.visitLabel(l2);
        mv.visitVarInsn(ASTORE, 2);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void nullType(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "nullType",
                "(Ljava/lang/String;Ljava/lang/String;)V", null, null);
        mv.visitCode();
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 2);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ALOAD, 1);
        Label l0 = new Label();
        mv.visitJumpInsn(IFNONNULL, l0);
        mv.visitVarInsn(ALOAD, 2);
        Label l1 = new Label();
        mv.visitJumpInsn(GOTO, l1);
        mv.visitFrame(F_FULL, 3, new Object[] { "pkg/FrameTable",
                "java/lang/String", NULL }, 2, new Object[] { "pkg/FrameTable",
                NULL });
        mv.visitLabel(l0);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitFrame(F_FULL, 3, new Object[] { "pkg/FrameTable",
                "java/lang/String", NULL }, 3, new Object[] { "pkg/FrameTable",
                NULL, "java/lang/String" });
        mv.visitLabel(l1);
        mv.visitMethodInsn(INVOKEVIRTUAL, "pkg/FrameTable", "nullType",
                "(Ljava/lang/String;Ljava/lang/String;)V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void topType(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "topType",
                "(ZBCSIFJDLjava/lang/Object;)V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ILOAD, 5);
        mv.visitVarInsn(ISTORE, 13);
        mv.visitVarInsn(ILOAD, 1);
        Label l0 = new Label();
        mv.visitJumpInsn(IFEQ, l0);
        mv.visitInsn(RETURN);
        mv.visitFrame(F_APPEND, 2, new Object[] { TOP, INTEGER }, 0, null);
        mv.visitLabel(l0);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void arrayTypes(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "fullFrame",
                "(Ljava/lang/String;[[Z[B[C[S[I[F[J[D[Ljava/lang/Object;)V",
                null, null);
        mv.visitCode();
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 11);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 13);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 15);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 17);
        mv.visitVarInsn(ALOAD, 1);
        Label l0 = new Label();
        mv.visitJumpInsn(IFNONNULL, l0);
        mv.visitInsn(RETURN);
        mv.visitFrame(F_FULL, 15, new Object[] { "pkg/FrameTable",
                "java/lang/String", "[[Z", "[B", "[C", "[S", "[I", "[F", "[J",
                "[D", "[Ljava/lang/Object;", LONG, LONG, LONG, LONG }, 0,
                new Object[] {});
        mv.visitLabel(l0);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void mergeTypes(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "mergeTypes", "(Z)V",
                null, null);
        mv.visitCode();
        Label l0 = new Label();
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "long",
                "Ljava/lang/Long;");
        mv.visitVarInsn(ASTORE, 2);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "number",
                "Ljava/lang/Number;");
        mv.visitVarInsn(ASTORE, 3);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "number",
                "Ljava/lang/Long;");
        mv.visitVarInsn(ASTORE, 4);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "comparable",
                "Ljava/lang/Comparable;");
        mv.visitVarInsn(ASTORE, 5);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 6);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "double",
                "Ljava/lang/Double;");
        mv.visitVarInsn(ASTORE, 7);
        mv.visitInsn(ICONST_0);
        mv.visitVarInsn(ISTORE, 8);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "intArray", "[I");
        mv.visitVarInsn(ASTORE, 9);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "double",
                "Ljava/lang/Double;");
        mv.visitVarInsn(ASTORE, 10);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 11);

        mv.visitFrame(F_FULL, 12,
                new Object[] { "pkg/FrameTable", INTEGER, "java/lang/Number",
                        "java/lang/Number", "java/lang/Number",
                        "java/lang/Object", NULL, "java/lang/Double", TOP,
                        "java/lang/Object", "java/lang/Object",
                        "[Ljava/lang/Object;" }, 0, null);
        mv.visitLabel(l0);
        mv.visitVarInsn(ALOAD, 11);
        mv.visitInsn(ICONST_0);
        mv.visitInsn(ACONST_NULL);
        mv.visitInsn(AASTORE);

        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "double",
                "Ljava/lang/Double;");
        mv.visitVarInsn(ASTORE, 2);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "double",
                "Ljava/lang/Double;");
        mv.visitVarInsn(ASTORE, 3);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "number",
                "Ljava/lang/Number;");
        mv.visitVarInsn(ASTORE, 4);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "serializable",
                "Ljava/io/Serializable;");
        mv.visitVarInsn(ASTORE, 5);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 6);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 7);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 8);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "floatArray", "[F");
        mv.visitVarInsn(ASTORE, 9);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "intArray", "[I");
        mv.visitVarInsn(ASTORE, 10);
        mv.visitInsn(ICONST_1);
        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
        mv.visitVarInsn(ASTORE, 11);

        mv.visitJumpInsn(GOTO, l0);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void mergeStackTypes(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "mergeStackTypes",
                "(Z)V", null, null);
        mv.visitCode();
        mv.visitInsn(ICONST_0);
        mv.visitInsn(ICONST_1);
        mv.visitVarInsn(ILOAD, 1);
        Label l0 = new Label();
        mv.visitJumpInsn(IFEQ, l0);
        mv.visitInsn(ICONST_0);
        Label l1 = new Label();
        mv.visitJumpInsn(GOTO, l1);
        mv.visitFrame(F_FULL, 1, new Object[] { "pkg/FrameTable" }, 2,
                new Object[] { INTEGER, INTEGER });
        mv.visitLabel(l0);
        mv.visitInsn(DUP);
        mv.visitFrame(F_FULL, 1, new Object[] { "pkg/FrameTable" }, 3,
                new Object[] { INTEGER, INTEGER, INTEGER });
        mv.visitLabel(l1);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void mergeNullArray(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "mergeNullArray", "(Z)I",
                null, null);
        mv.visitCode();
        mv.visitVarInsn(ILOAD, 1);
        Label l1 = new Label();
        mv.visitJumpInsn(IFEQ, l1);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 2);
        mv.visitVarInsn(ILOAD, 1);
        Label l2 = new Label();
        mv.visitJumpInsn(IFEQ, l2);
        mv.visitFieldInsn(GETSTATIC, "pkg/FrameTable", "longArray",
                "[Ljava/lang/Long;");
        mv.visitVarInsn(ASTORE, 2);
        mv.visitFrame(F_APPEND, 1, new Object[] { "[Ljava/lang/Long;" }, 0,
                null);
        mv.visitLabel(l2);
        mv.visitVarInsn(ALOAD, 2);
        Label l3 = new Label();
        mv.visitJumpInsn(IFNULL, l3);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitInsn(ARRAYLENGTH);
        Label l4 = new Label();
        mv.visitJumpInsn(IFNE, l4);
        mv.visitFrame(F_SAME, 0, null, 0, null);
        mv.visitLabel(l3);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 3);
        Label l5 = new Label();
        mv.visitJumpInsn(GOTO, l5);
        mv.visitFrame(F_SAME, 0, null, 0, null);
        mv.visitLabel(l4);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitInsn(ICONST_0);
        mv.visitInsn(AALOAD);
        mv.visitVarInsn(ASTORE, 3);
        mv.visitVarInsn(ILOAD, 1);
        mv.visitJumpInsn(IFNE, l5);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 3);
        mv.visitFrame(F_SAME, 0, null, 0, null);
        mv.visitLabel(l5);
        mv.visitVarInsn(ILOAD, 1);
        mv.visitInsn(IRETURN);
        mv.visitFrame(F_CHOP, 1, null, 0, null);
        mv.visitLabel(l1);
        mv.visitVarInsn(ILOAD, 1);
        mv.visitInsn(IRETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void appendAndChopFrame(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "appendAndChopFrame",
                "(I)V", null, null);
        mv.visitCode();
        mv.visitInsn(ICONST_0);
        mv.visitVarInsn(ISTORE, 2);
        mv.visitFrame(F_APPEND, 1, new Object[] { INTEGER }, 0, null);
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitVarInsn(ILOAD, 2);
        mv.visitVarInsn(ILOAD, 1);
        Label l1 = new Label();
        mv.visitJumpInsn(IF_ICMPGE, l1);
        mv.visitIincInsn(2, 1);
        mv.visitJumpInsn(GOTO, l0);
        mv.visitFrame(F_CHOP, 1, null, 0, null);
        mv.visitLabel(l1);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void sameLocals1stackItemFrame(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
                "sameLocals1stackItemFrame", "()I", null, null);
        mv.visitCode();
        Label l0 = new Label();
        Label l1 = new Label();
        mv.visitTryCatchBlock(l0, l1, l0, null);
        mv.visitInsn(ICONST_0);
        mv.visitInsn(IRETURN);
        mv.visitFrame(F_SAME1, 0, null, 1,
                new Object[] { "java/lang/Throwable" });
        mv.visitLabel(l0);
        mv.visitVarInsn(ASTORE, 1);
        mv.visitLabel(l1);
        mv.visitInsn(ICONST_0);
        mv.visitInsn(IRETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void sameLocals1stackItemFrame2(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
                "sameLocals1stackItemFrame2", "()V", null, null);
        mv.visitCode();
        Label l0 = new Label();
        Label l1 = new Label();
        Label l2 = new Label();
        mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Exception");
        Label l3 = new Label();
        mv.visitTryCatchBlock(l0, l1, l3, null);
        Label l4 = new Label();
        mv.visitTryCatchBlock(l2, l4, l3, null);
        Label l5 = new Label();
        mv.visitTryCatchBlock(l3, l5, l3, null);
        mv.visitLabel(l0);
        mv.visitTypeInsn(NEW, "java/lang/Object");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V",
                false);
        mv.visitVarInsn(ASTORE, 1);
        mv.visitLabel(l1);
        Label l6 = new Label();
        mv.visitJumpInsn(GOTO, l6);
        mv.visitFrame(F_SAME1, 0, null, 1,
                new Object[] { "java/lang/Exception" });
        mv.visitLabel(l2);
        mv.visitVarInsn(ASTORE, 2);
        mv.visitLabel(l4);
        mv.visitJumpInsn(GOTO, l6);
        mv.visitFrame(F_SAME1, 0, null, 1,
                new Object[] { "java/lang/Throwable" });
        mv.visitLabel(l3);
        mv.visitVarInsn(ASTORE, 3);
        mv.visitLabel(l5);
        mv.visitVarInsn(ALOAD, 3);
        mv.visitInsn(ATHROW);
        mv.visitFrame(F_SAME, 0, null, 0, null);
        mv.visitLabel(l6);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void sameLocals1stackItemFrameExtended(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
                "sameLocals1stackItemFrameExtended", "()I", null, null);
        mv.visitCode();
        Label l0 = new Label();
        Label l1 = new Label();
        Label l2 = new Label();
        mv.visitTryCatchBlock(l0, l1, l2, null);
        Label l3 = new Label();
        mv.visitTryCatchBlock(l2, l3, l2, null);
        mv.visitLabel(l0);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 1);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 3);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 5);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 7);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 9);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 11);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 13);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 15);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 17);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 19);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 21);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 23);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 25);
        mv.visitLabel(l1);
        mv.visitInsn(ICONST_0);
        mv.visitInsn(IRETURN);
        mv.visitFrame(F_SAME1, 0, null, 1,
                new Object[] { "java/lang/Throwable" });
        mv.visitLabel(l2);
        mv.visitVarInsn(ASTORE, 27);
        mv.visitLabel(l3);
        mv.visitInsn(ICONST_0);
        mv.visitInsn(IRETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void sameFrameExtended(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "sameFrameExtended",
                "(Z)V", null, null);
        mv.visitCode();
        mv.visitFrame(F_SAME, 0, null, 0, null);
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 2);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 4);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 6);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 8);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 10);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 12);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 14);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 16);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 18);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 20);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 22);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 24);
        mv.visitLdcInsn(new Long(11L));
        mv.visitVarInsn(LSTORE, 26);
        mv.visitVarInsn(ILOAD, 1);
        Label l1 = new Label();
        mv.visitJumpInsn(IFEQ, l1);
        mv.visitInsn(RETURN);
        mv.visitFrame(F_SAME, 0, null, 0, null);
        mv.visitLabel(l1);
        mv.visitJumpInsn(GOTO, l0);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private void deadCode(final ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "deadCode", "(Z)V", null,
                null);
        mv.visitCode();
        Label l0 = new Label();
        Label l1 = new Label();
        Label l2 = new Label();
        Label l3 = new Label();
        mv.visitTryCatchBlock(l0, l1, l1, "java/lang/Exception");
        mv.visitTryCatchBlock(l2, l3, l3, "java/lang/Exception");
        mv.visitJumpInsn(GOTO, l2);
        mv.visitFrame(F_SAME, 0, null, 0, null);
        mv.visitLabel(l0);
        mv.visitInsn(RETURN);
        mv.visitFrame(F_SAME1, 0, null, 1,
                new Object[] { "java/lang/Exception" });
        mv.visitLabel(l1);
        mv.visitVarInsn(ASTORE, 2);
        mv.visitInsn(RETURN);
        mv.visitFrame(F_FULL, 0, new Object[] { "pkg/FrameTable", INTEGER }, 0,
                null);
        mv.visitLabel(l2);
        mv.visitInsn(RETURN);
        mv.visitFrame(F_SAME1, 0, null, 1,
                new Object[] { "java/lang/Exception" });
        mv.visitLabel(l3);
        mv.visitVarInsn(ASTORE, 2);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    /**
     * Ad hoc class adapter used to rename the FrameTable class and to change
     * its class version.
     * 
     * @author Eric Bruneton
     */
    static class RenameAdapter extends ClassVisitor {

        public RenameAdapter(final ClassVisitor cv) {
            super(Opcodes.ASM5, cv);
        }

        @Override
        public void visit(final int version, final int access,
                final String name, final String signature,
                final String superName, final String[] interfaces) {
            super.visit(V1_5, access, "pkg/FrameMap", signature, superName,
                    interfaces);
        }

        @Override
        public MethodVisitor visitMethod(final int access, final String name,
                final String desc, final String signature,
                final String[] exceptions) {
            return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access,
                    name, desc, signature, exceptions)) {
                @Override
                public void visitFrame(final int type, final int nLocal,
                        final Object[] local, final int nStack,
                        final Object[] stack) {
                    Object[] clocal = new Object[local.length];
                    for (int i = 0; i < clocal.length; ++i) {
                        clocal[i] = local[i];
                        if ("pkg/FrameTable".equals(clocal[i])) {
                            clocal[i] = "pkg/FrameMap";
                        }
                    }
                    Object[] cstack = new Object[stack.length];
                    for (int i = 0; i < cstack.length; ++i) {
                        cstack[i] = stack[i];
                        if ("pkg/FrameTable".equals(cstack[i])) {
                            cstack[i] = "pkg/FrameMap";
                        }
                    }
                    super.visitFrame(type, nLocal, clocal, nStack, cstack);
                }

                @Override
                public void visitTypeInsn(final int opcode, final String desc) {
                    if (desc.equals("pkg/FrameTable")) {
                        super.visitTypeInsn(opcode, "pkg/FrameMap");
                    } else {
                        super.visitTypeInsn(opcode, desc);
                    }
                }

                @Override
                public void visitMethodInsn(final int opcode,
                        final String owner, final String name,
                        final String desc, final boolean itf) {
                    if (owner.equals("pkg/FrameTable")) {
                        super.visitMethodInsn(opcode, "pkg/FrameMap", name,
                                desc, itf);
                    } else {
                        super.visitMethodInsn(opcode, owner, name, desc, itf);
                    }
                }
            };
        }
    }
}
