import java.lang.classfile.ClassFile;
import java.lang.classfile.Opcode;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;

import static java.lang.classfile.ClassFile.ACC_PUBLIC;
import static java.lang.classfile.ClassFile.ACC_STATIC;
import static java.lang.constant.ConstantDescs.*;

class Scratch {
    /**
     * <pre>
     *  public static Method run:"(I)I"
     *     stack 2  locals 2
     *   {
     *          try               T0, T1;
     *          iload_0;
     *          ifge              L12;
     *          new               class java/lang/IllegalArgumentException;
     *          dup;
     *          invokespecial     Method java/lang/IllegalArgumentException."<init>":"()V";
     *          athrow;
     *   L12:   stack_frame_type  same;
     *          new               class java/lang/IndexOutOfBoundsException;
     *          dup;
     *          invokespecial     Method java/lang/IndexOutOfBoundsException."<init>":"()V";
     *          athrow;
     *          endtry            T0,T1;
     *          catch             T0 java/lang/IllegalArgumentException;
     *          stack_frame_type  stack1;
     *            stack_map       class java/lang/IllegalArgumentException;
     *          astore_1;
     *          iconst_0;
     *          ireturn;
     *          catch             T1 java/lang/Exception;
     *          stack_frame_type  stack1;
     *            stack_map       class java/lang/Exception;
     *          astore_1;
     *          sipush            1000;
     *          ireturn;
     *   }
     *   </pre>
     */
    public static void main(String[] args) {
        ClassDesc CD_IAE = ClassDesc.of(IllegalArgumentException.class.getName());
        ClassDesc CD_IOBE = ClassDesc.of(IndexOutOfBoundsException.class.getName());
        ClassDesc CD_E = ClassDesc.of(Exception.class.getName());
        try {
            byte[] bytes = ClassFile.of()
                    .build(ClassDesc.of("Test"), classBuilder -> {
                        classBuilder.withFlags(ACC_PUBLIC).withVersion(68, 0);
                        classBuilder.withMethod("<init>", MethodTypeDesc.of(CD_void), 0,
                                mb -> mb.withCode(code -> code
                                        .aload(0)
                                        .invokespecial(CD_Object, "<init>", MethodTypeDesc.of(CD_void), false)
                                        .return_()
                                ));
                        classBuilder.withMethod("run", MethodTypeDesc.of(CD_int, CD_int),
                                ACC_PUBLIC | ACC_STATIC, mb -> {
                                    mb.withCode(cb -> {
                                        cb.trying(
                                                tb -> {
                                                    tb.block(bb -> {
                                                        bb.iload(0);
                                                        bb.branch(Opcode.IFGE, bb.endLabel()); // ifge L12
                                                        bb.new_(CD_IAE);
                                                        bb.dup();
                                                        bb.invokespecial(CD_IAE, "<init>", MTD_void);
                                                        bb.athrow();
                                                    });
                                                    tb.block(bb -> {
                                                        bb.new_(CD_IOBE);
                                                        bb.dup();
                                                        bb.invokespecial(CD_IOBE, "<init>", MTD_void);
                                                        bb.athrow();
                                                    });
                                                    // 20: {opcode: GOTO, target: 31} added by the ClassFile API
                                                    // "31" exceeds the end of the Code attribute.
                                                    // can be fixed by adding tb.return_();
                                                },
                                                catchBuilder -> {
                                                    catchBuilder.catching(CD_IAE, bb -> {
                                                        bb.aload(1);           // astore_1
                                                        bb.iconst_0();         // iconst_0
                                                        bb.ireturn();          // ireturn
                                                    });
                                                    catchBuilder.catching(CD_E, bb -> {
                                                        bb.aload(1);           // astore_1
                                                        bb.sipush(1000);       // sipush 1000
                                                        bb.ireturn();          // ireturn
                                                    });
                                                }
                                        );
                                    });
                                });
                    });
        } catch (Throwable t) {
            System.err.println("Error building class: ");
            t.printStackTrace();
        }
    }
}

class test {
    public static int run(int i) {
        try {
            if (i < 0) {
                throw new IllegalArgumentException();
            } 
            throw new IndexOutOfBoundsException();
        } catch (IllegalArgumentException e) {
            return 0;
        } catch (Exception e) {
            return 1000;
        }
    }
}
