test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java
author psandoz
Fri, 08 Sep 2017 10:46:46 -0700
changeset 48826 c4d9d1b08e2e
permissions -rw-r--r--
8186209: Tool support for ConstantDynamic 8186046: Minimal ConstantDynamic support 8190972: Ensure that AOT/Graal filters out class files containing CONSTANT_Dynamic ahead of full AOT support Reviewed-by: acorn, coleenp, kvn Contributed-by: lois.foltan@oracle.com, john.r.rose@oracle.com, paul.sandoz@oracle.com

/*
 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.experimental.bytecode;

import jdk.experimental.bytecode.PoolHelper.StaticArgListBuilder;

import java.lang.invoke.MethodHandle;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.ToIntFunction;

/**
 * Builder for class file code attributes. A code attribute is defined:
 * <pre>
 * {@code
 * Code_attribute {
 *    u2 attribute_name_index;
 *    u4 attribute_length;
 *    u2 max_stack;
 *    u2 max_locals;
 *    u4 code_length;
 *    u1 code[code_length];
 *    u2 exception_table_length;
 *    {   u2 start_pc;
 *        u2 end_pc;
 *        u2 handler_pc;
 *        u2 catch_type;
 *    } exception_table[exception_table_length];
 *    u2 attributes_count;
 *    attribute_info attributes[attributes_count];
 * } }
 * </pre>
 *
 * @param <S> the type of symbol representation
 * @param <T> the type of type descriptors representation
 * @param <E> the type of pool entries
 * @param <C> the type of this code builder
 */
public class CodeBuilder<S, T, E, C extends CodeBuilder<S, T, E, C>> extends AttributeBuilder<S, T, E, C> {

    protected GrowableByteBuffer code = new GrowableByteBuffer();
    GrowableByteBuffer catchers = new GrowableByteBuffer();
    GrowableByteBuffer stackmaps = new GrowableByteBuffer();
    MethodBuilder<S, T, E> methodBuilder;
    int ncatchers;
    int stacksize = -1;
    int localsize = -1;
    int nstackmaps = 0;

    public enum JumpMode {
        NARROW,
        WIDE;
    }

    CodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
        super(methodBuilder.poolHelper, methodBuilder.typeHelper);
        this.methodBuilder = methodBuilder;
    }

    public C getstatic(S owner, CharSequence name, T type) {
        emitOp(Opcode.GETSTATIC, type);
        code.writeChar(poolHelper.putFieldRef(owner, name, type));
        return thisBuilder();
    }

    public C putstatic(S owner, CharSequence name, T type) {
        emitOp(Opcode.PUTSTATIC, type);
        code.writeChar(poolHelper.putFieldRef(owner, name, type));
        return thisBuilder();
    }

    public C getfield(S owner, CharSequence name, T type) {
        emitOp(Opcode.GETFIELD, type);
        code.writeChar(poolHelper.putFieldRef(owner, name, type));
        return thisBuilder();
    }

    public C vgetfield(S owner, CharSequence name, T type) {
        emitOp(Opcode.VGETFIELD, type);
        code.writeChar(poolHelper.putFieldRef(owner, name, type));
        return thisBuilder();
    }

    public C putfield(S owner, CharSequence name, T type) {
        emitOp(Opcode.PUTFIELD, type);
        code.writeChar(poolHelper.putFieldRef(owner, name, type));
        return thisBuilder();
    }

    public C invokevirtual(S owner, CharSequence name, T type, boolean isInterface) {
        emitOp(Opcode.INVOKEVIRTUAL, type);
        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
        return thisBuilder();
    }

    public C invokespecial(S owner, CharSequence name, T type, boolean isInterface) {
        emitOp(Opcode.INVOKESPECIAL, type);
        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
        return thisBuilder();
    }

    public C invokestatic(S owner, CharSequence name, T type, boolean isInterface) {
        emitOp(Opcode.INVOKESTATIC, type);
        code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
        return thisBuilder();
    }

    public C invokeinterface(S owner, CharSequence name, T type) {
        emitOp(Opcode.INVOKEINTERFACE, type);
        code.writeChar(poolHelper.putMethodRef(owner, name, type, true));
        int nargs = 1;
        Iterator<T> it = typeHelper.parameterTypes(type);
        while (it.hasNext()) {
            nargs += typeHelper.tag(it.next()).width;
        }
        code.writeByte(nargs);
        code.writeByte(0);
        return thisBuilder();
    }

    public C invokedynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
        emitOp(Opcode.INVOKEDYNAMIC, invokedType);
        code.writeChar(poolHelper.putInvokeDynamic(invokedName, invokedType, bsmClass, bsmName, bsmType, staticArgs));
        code.writeChar(0); //padding
        return thisBuilder();
    }

    public C new_(S clazz) {
        emitOp(Opcode.NEW, clazz);
        code.writeChar(poolHelper.putClass(clazz));
        return thisBuilder();
    }

    public C vnew_(S clazz, CharSequence name, T desc) {
        emitOp(Opcode.VNEW, clazz);
        code.writeChar(poolHelper.putMethodRef(clazz, name, desc, false));
        return thisBuilder();
    }

    public C vnewarray(S array) {
        emitOp(Opcode.VNEWARRAY, array);
        code.writeChar(poolHelper.putClass(array));
        return thisBuilder();
    }

    public C newarray(TypeTag tag) {
        emitOp(Opcode.NEWARRAY, tag);
        int newarraycode = tag.newarraycode;
        if (newarraycode == -1) {
            throw new IllegalStateException("Bad tag " + tag);
        }
        code.writeByte(newarraycode);
        return thisBuilder();
    }

    public C anewarray(S array) {
        emitOp(Opcode.ANEWARRAY, array);
        code.writeChar(poolHelper.putClass(array));
        return thisBuilder();
    }

    public C checkcast(S target) {
        emitOp(Opcode.CHECKCAST);
        code.writeChar(poolHelper.putClass(target));
        return thisBuilder();
    }

    public C instanceof_(S target) {
        emitOp(Opcode.INSTANCEOF);
        code.writeChar(poolHelper.putClass(target));
        return thisBuilder();
    }

    public C multianewarray(S array, byte dims) {
        emitOp(Opcode.MULTIANEWARRAY, new Object[]{array, dims});
        code.writeChar(poolHelper.putClass(array)).writeByte(dims);
        return thisBuilder();
    }

    public C multivnewarray(S array, byte dims) {
        emitOp(Opcode.MULTIVNEWARRAY, new Object[]{array, dims});
        code.writeChar(poolHelper.putClass(array)).writeByte(dims);
        return thisBuilder();
    }

    public C vbox(S target) {
        emitOp(Opcode.VBOX, target);
        code.writeChar(poolHelper.putClass(target));
        return thisBuilder();
    }

    public C vunbox(S target) {
        emitOp(Opcode.VUNBOX, target);
        code.writeChar(poolHelper.putClass(target));
        return thisBuilder();
    }

    public C ldc(int i) {
        return ldc(pool -> pool.putInt(i), false);
    }

    public C ldc(long l) {
        return ldc(pool -> pool.putLong(l), true);
    }

    public C ldc(float f) {
        return ldc(pool -> pool.putFloat(f), false);
    }

    public C ldc(double d) {
        return ldc(pool -> pool.putDouble(d), true);
    }

    public C ldc(String s) {
        return ldc(pool -> pool.putString(s), false);
    }

    public C ldc(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
        boolean fat = typeHelper.tag(constType).width() == 2;
        return ldc(pool -> pool.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs), fat);
    }

    public <Z> C ldc(Z z, BiFunction<PoolHelper<S, T, E>, Z, Integer> poolFunc) {
        return ldc(pool -> poolFunc.apply(pool, z), false);
    }

    protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
        // @@@ This should probably be abstract
        int index = indexFunc.applyAsInt(poolHelper);
        return ldc(index, null, fat);
    }

    protected final C ldc(int index, T type, boolean fat) {
        if (fat) {
            emitOp(Opcode.LDC2_W, type);
            code.writeChar(index);
        } else if (index > 63) {
            emitOp(Opcode.LDC_W, type);
            code.writeChar(index);
        } else {
            emitOp(Opcode.LDC, type);
            code.writeByte(index);
        }
        return thisBuilder();
    }

    //other non-CP dependent opcodes
    public C areturn() {
        return emitOp(Opcode.ARETURN);
    }

    public C ireturn() {
        return emitOp(Opcode.IRETURN);
    }

    public C freturn() {
        return emitOp(Opcode.FRETURN);
    }

    public C lreturn() {
        return emitOp(Opcode.LRETURN);
    }

    public C dreturn() {
        return emitOp(Opcode.DRETURN);
    }

    public C return_() {
        return emitOp(Opcode.RETURN);
    }

    public C vreturn() {
        return emitOp(Opcode.VRETURN);
    }

    protected C emitWideIfNeeded(Opcode opcode, int n) {
        boolean wide = n > Byte.MAX_VALUE;
        if (wide) {
            wide();
        }
        emitOp(opcode, n);
        if (wide) {
            code.writeChar(n);
        } else {
            code.writeByte(n);
        }
        return thisBuilder();
    }

    protected C emitWideIfNeeded(Opcode opcode, int n, int v) {
        boolean wide = n > Byte.MAX_VALUE || v > Byte.MAX_VALUE;
        if (wide) {
            wide();
        }
        emitOp(opcode, n);
        if (wide) {
            code.writeChar(n).writeChar(v);
        } else {
            code.writeByte(n).writeByte(v);
        }
        return thisBuilder();
    }

    public TypedBuilder typed(TypeTag typeTag) {
        return typed(typeTag, _unused -> new TypedBuilder());
    }

    protected <TB extends TypedBuilder> TB typed(TypeTag typeTag, Function<TypeTag, TB> typedBuilderFunc) {
        emitOp(Opcode.TYPED);
        code.writeChar(poolHelper.putType(typeHelper.fromTag(typeTag)));
        return typedBuilderFunc.apply(typeTag);
    }

    public class TypedBuilder {
        public C aload_0() {
            return CodeBuilder.this.aload_0();
        }

        public C aload_1() {
            return CodeBuilder.this.aload_1();
        }

        public C aload_2() {
            return CodeBuilder.this.aload_2();
        }

        public C aload_3() {
            return CodeBuilder.this.aload_3();
        }

        public C aload(int n) {
            return CodeBuilder.this.aload(n);
        }

        public C astore_0() {
            return CodeBuilder.this.astore_0();
        }

        public C astore_1() {
            return CodeBuilder.this.astore_1();
        }

        public C astore_2() {
            return CodeBuilder.this.astore_2();
        }

        public C astore_3() {
            return CodeBuilder.this.astore_3();
        }

        public C astore(int n) {
            return CodeBuilder.this.astore(n);
        }

        public C aaload() {
            return CodeBuilder.this.aaload();
        }

        public C aastore() {
            return CodeBuilder.this.aastore();
        }

        public C areturn() {
            return CodeBuilder.this.areturn();
        }

        public C anewarray(S s) {
            return CodeBuilder.this.anewarray(s);
        }

        public C aconst_null() {
            return CodeBuilder.this.aconst_null();
        }

        public C if_acmpeq(short target) {
            return CodeBuilder.this.if_acmpeq(target);
        }

        public C if_acmpne(short target) {
            return CodeBuilder.this.if_acmpeq(target);
        }
    }

    public C vload(int i) {
        return emitWideIfNeeded(Opcode.VLOAD, i);
    }

    public C aload(int i) {
        return emitWideIfNeeded(Opcode.ALOAD, i);
    }

    public C iload(int i) {
        return emitWideIfNeeded(Opcode.ILOAD, i);
    }

    public C fload(int i) {
        return emitWideIfNeeded(Opcode.FLOAD, i);
    }

    public C lload(int i) {
        return emitWideIfNeeded(Opcode.LLOAD, i);
    }

    public C dload(int i) {
        return emitWideIfNeeded(Opcode.DLOAD, i);
    }

    public C aload_0() {
        return emitOp(Opcode.ALOAD_0);
    }

    public C iload_0() {
        return emitOp(Opcode.ILOAD_0);
    }

    public C fload_0() {
        return emitOp(Opcode.FLOAD_0);
    }

    public C lload_0() {
        return emitOp(Opcode.LLOAD_0);
    }

    public C dload_0() {
        return emitOp(Opcode.DLOAD_0);
    }

    public C aload_1() {
        return emitOp(Opcode.ALOAD_1);
    }

    public C iload_1() {
        return emitOp(Opcode.ILOAD_1);
    }

    public C fload_1() {
        return emitOp(Opcode.FLOAD_1);
    }

    public C lload_1() {
        return emitOp(Opcode.LLOAD_1);
    }

    public C dload_1() {
        return emitOp(Opcode.DLOAD_1);
    }

    public C aload_2() {
        return emitOp(Opcode.ALOAD_2);
    }

    public C iload_2() {
        return emitOp(Opcode.ILOAD_2);
    }

    public C fload_2() {
        return emitOp(Opcode.FLOAD_2);
    }

    public C lload_2() {
        return emitOp(Opcode.LLOAD_2);
    }

    public C dload_2() {
        return emitOp(Opcode.DLOAD_2);
    }

    public C aload_3() {
        return emitOp(Opcode.ALOAD_3);
    }

    public C iload_3() {
        return emitOp(Opcode.ILOAD_3);
    }

    public C fload_3() {
        return emitOp(Opcode.FLOAD_3);
    }

    public C lload_3() {
        return emitOp(Opcode.LLOAD_3);
    }

    public C dload_3() {
        return emitOp(Opcode.DLOAD_3);
    }

    public C vstore(int i) {
        return emitWideIfNeeded(Opcode.VSTORE, i);
    }

    public C astore(int i) {
        return emitWideIfNeeded(Opcode.ASTORE, i);
    }

    public C istore(int i) {
        return emitWideIfNeeded(Opcode.ISTORE, i);
    }

    public C fstore(int i) {
        return emitWideIfNeeded(Opcode.FSTORE, i);
    }

    public C lstore(int i) {
        return emitWideIfNeeded(Opcode.LSTORE, i);
    }

    public C dstore(int i) {
        return emitWideIfNeeded(Opcode.DSTORE, i);
    }

    public C astore_0() {
        return emitOp(Opcode.ASTORE_0);
    }

    public C istore_0() {
        return emitOp(Opcode.ISTORE_0);
    }

    public C fstore_0() {
        return emitOp(Opcode.FSTORE_0);
    }

    public C lstore_0() {
        return emitOp(Opcode.LSTORE_0);
    }

    public C dstore_0() {
        return emitOp(Opcode.DSTORE_0);
    }

    public C astore_1() {
        return emitOp(Opcode.ASTORE_1);
    }

    public C istore_1() {
        return emitOp(Opcode.ISTORE_1);
    }

    public C fstore_1() {
        return emitOp(Opcode.FSTORE_1);
    }

    public C lstore_1() {
        return emitOp(Opcode.LSTORE_1);
    }

    public C dstore_1() {
        return emitOp(Opcode.DSTORE_1);
    }

    public C astore_2() {
        return emitOp(Opcode.ASTORE_2);
    }

    public C istore_2() {
        return emitOp(Opcode.ISTORE_2);
    }

    public C fstore_2() {
        return emitOp(Opcode.FSTORE_2);
    }

    public C lstore_2() {
        return emitOp(Opcode.LSTORE_2);
    }

    public C dstore_2() {
        return emitOp(Opcode.DSTORE_2);
    }

    public C astore_3() {
        return emitOp(Opcode.ASTORE_3);
    }

    public C istore_3() {
        return emitOp(Opcode.ISTORE_3);
    }

    public C fstore_3() {
        return emitOp(Opcode.FSTORE_3);
    }

    public C lstore_3() {
        return emitOp(Opcode.LSTORE_3);
    }

    public C dstore_3() {
        return emitOp(Opcode.DSTORE_3);
    }

    //...

    public C iaload() {
        return emitOp(Opcode.IALOAD);
    }

    public C laload() {
        return emitOp(Opcode.LALOAD);
    }

    public C faload() {
        return emitOp(Opcode.FALOAD);
    }

    public C daload() {
        return emitOp(Opcode.DALOAD);
    }

    public C vaload() {
        return emitOp(Opcode.VALOAD);
    }

    public C aaload() {
        return emitOp(Opcode.AALOAD);
    }

    public C baload() {
        return emitOp(Opcode.BALOAD);
    }

    public C caload() {
        return emitOp(Opcode.CALOAD);
    }

    public C saload() {
        return emitOp(Opcode.SALOAD);
    }

    public C iastore() {
        return emitOp(Opcode.IASTORE);
    }

    public C lastore() {
        return emitOp(Opcode.LASTORE);
    }

    public C fastore() {
        return emitOp(Opcode.FASTORE);
    }

    public C dastore() {
        return emitOp(Opcode.DASTORE);
    }

    public C vastore() {
        return emitOp(Opcode.VASTORE);
    }

    public C aastore() {
        return emitOp(Opcode.AASTORE);
    }

    public C bastore() {
        return emitOp(Opcode.BASTORE);
    }

    public C castore() {
        return emitOp(Opcode.CASTORE);
    }

    public C sastore() {
        return emitOp(Opcode.SASTORE);
    }

    public C nop() {
        return emitOp(Opcode.NOP);
    }

    public C aconst_null() {
        return emitOp(Opcode.ACONST_NULL);
    }

    public C iconst_0() {
        return emitOp(Opcode.ICONST_0);
    }

    public C iconst_1() {
        return emitOp(Opcode.ICONST_1);
    }

    public C iconst_2() {
        return emitOp(Opcode.ICONST_2);
    }

    public C iconst_3() {
        return emitOp(Opcode.ICONST_3);
    }

    public C iconst_4() {
        return emitOp(Opcode.ICONST_4);
    }

    public C iconst_5() {
        return emitOp(Opcode.ICONST_5);
    }

    public C iconst_m1() {
        return emitOp(Opcode.ICONST_M1);
    }

    public C lconst_0() {
        return emitOp(Opcode.LCONST_0);
    }

    public C lconst_1() {
        return emitOp(Opcode.LCONST_1);
    }

    public C fconst_0() {
        return emitOp(Opcode.FCONST_0);
    }

    public C fconst_1() {
        return emitOp(Opcode.FCONST_1);
    }

    public C fconst_2() {
        return emitOp(Opcode.FCONST_2);
    }

    public C dconst_0() {
        return emitOp(Opcode.DCONST_0);
    }

    public C dconst_1() {
        return emitOp(Opcode.DCONST_1);
    }

    public C sipush(int s) {
        emitOp(Opcode.SIPUSH);
        code.writeChar(s);
        return thisBuilder();
    }

    public C bipush(int b) {
        emitOp(Opcode.BIPUSH);
        code.writeByte(b);
        return thisBuilder();
    }

    public C pop() {
        return emitOp(Opcode.POP);
    }

    public C pop2() {
        return emitOp(Opcode.POP2);
    }

    public C dup() {
        return emitOp(Opcode.DUP);
    }

    public C dup_x1() {
        return emitOp(Opcode.DUP_X1);
    }

    public C dup_x2() {
        return emitOp(Opcode.DUP_X2);
    }

    public C dup2() {
        return emitOp(Opcode.DUP2);
    }

    public C dup2_x1() {
        return emitOp(Opcode.DUP2_X1);
    }

    public C dup2_x2() {
        return emitOp(Opcode.DUP2_X2);
    }

    public C swap() {
        return emitOp(Opcode.SWAP);
    }

    public C iadd() {
        return emitOp(Opcode.IADD);
    }

    public C ladd() {
        return emitOp(Opcode.LADD);
    }

    public C fadd() {
        return emitOp(Opcode.FADD);
    }

    public C dadd() {
        return emitOp(Opcode.DADD);
    }

    public C isub() {
        return emitOp(Opcode.ISUB);
    }

    public C lsub() {
        return emitOp(Opcode.LSUB);
    }

    public C fsub() {
        return emitOp(Opcode.FSUB);
    }

    public C dsub() {
        return emitOp(Opcode.DSUB);
    }

    public C imul() {
        return emitOp(Opcode.IMUL);
    }

    public C lmul() {
        return emitOp(Opcode.LMUL);
    }

    public C fmul() {
        return emitOp(Opcode.FMUL);
    }

    public C dmul() {
        return emitOp(Opcode.DMUL);
    }

    public C idiv() {
        return emitOp(Opcode.IDIV);
    }

    public C ldiv() {
        return emitOp(Opcode.LDIV);
    }

    public C fdiv() {
        return emitOp(Opcode.FDIV);
    }

    public C ddiv() {
        return emitOp(Opcode.DDIV);
    }

    public C irem() {
        return emitOp(Opcode.IREM);
    }

    public C lrem() {
        return emitOp(Opcode.LREM);
    }

    public C frem() {
        return emitOp(Opcode.FREM);
    }

    public C drem() {
        return emitOp(Opcode.DREM);
    }

    public C ineg() {
        return emitOp(Opcode.INEG);
    }

    public C lneg() {
        return emitOp(Opcode.LNEG);
    }

    public C fneg() {
        return emitOp(Opcode.FNEG);
    }

    public C dneg() {
        return emitOp(Opcode.DNEG);
    }

    public C ishl() {
        return emitOp(Opcode.ISHL);
    }

    public C lshl() {
        return emitOp(Opcode.LSHL);
    }

    public C ishr() {
        return emitOp(Opcode.ISHR);
    }

    public C lshr() {
        return emitOp(Opcode.LSHR);
    }

    public C iushr() {
        return emitOp(Opcode.IUSHR);
    }

    public C lushr() {
        return emitOp(Opcode.LUSHR);
    }

    public C iand() {
        return emitOp(Opcode.IAND);
    }

    public C land() {
        return emitOp(Opcode.LAND);
    }

    public C ior() {
        return emitOp(Opcode.IOR);
    }

    public C lor() {
        return emitOp(Opcode.LOR);
    }

    public C ixor() {
        return emitOp(Opcode.IXOR);
    }

    public C lxor() {
        return emitOp(Opcode.LXOR);
    }

    public C iinc(int index, int val) {
        return emitWideIfNeeded(Opcode.IINC, index, val);
    }

    public C i2l() {
        return emitOp(Opcode.I2L);
    }

    public C i2f() {
        return emitOp(Opcode.I2F);
    }

    public C i2d() {
        return emitOp(Opcode.I2D);
    }

    public C l2i() {
        return emitOp(Opcode.L2I);
    }

    public C l2f() {
        return emitOp(Opcode.L2F);
    }

    public C l2d() {
        return emitOp(Opcode.L2D);
    }

    public C f2i() {
        return emitOp(Opcode.F2I);
    }

    public C f2l() {
        return emitOp(Opcode.F2L);
    }

    public C f2d() {
        return emitOp(Opcode.F2D);
    }

    public C d2i() {
        return emitOp(Opcode.D2I);
    }

    public C d2l() {
        return emitOp(Opcode.D2L);
    }

    public C d2f() {
        return emitOp(Opcode.D2F);
    }

    public C i2b() {
        return emitOp(Opcode.I2B);
    }

    public C i2c() {
        return emitOp(Opcode.I2C);
    }

    public C i2s() {
        return emitOp(Opcode.I2S);
    }

    public C lcmp() {
        return emitOp(Opcode.LCMP);
    }

    public C fcmpl() {
        return emitOp(Opcode.FCMPL);
    }

    public C fcmpg() {
        return emitOp(Opcode.FCMPG);
    }

    public C dcmpl() {
        return emitOp(Opcode.DCMPL);
    }

    public C dcmpg() {
        return emitOp(Opcode.DCMPG);
    }

    public C ifeq(short target) {
        return emitNarrowJumpOp(Opcode.IFEQ, target);
    }

    public C ifne(short target) {
        return emitNarrowJumpOp(Opcode.IFNE, target);
    }

    public C iflt(short target) {
        return emitNarrowJumpOp(Opcode.IFLT, target);
    }

    public C ifge(short target) {
        return emitNarrowJumpOp(Opcode.IFGE, target);
    }

    public C ifgt(short target) {
        return emitNarrowJumpOp(Opcode.IFGT, target);
    }

    public C ifle(short target) {
        return emitNarrowJumpOp(Opcode.IFLE, target);
    }

    public C if_icmpeq(short target) {
        return emitNarrowJumpOp(Opcode.IF_ICMPEQ, target);
    }

    public C if_icmpne(short target) {
        return emitNarrowJumpOp(Opcode.IF_ICMPNE, target);
    }

    public C if_icmplt(short target) {
        return emitNarrowJumpOp(Opcode.IF_ICMPLT, target);
    }

    public C if_icmpge(short target) {
        return emitNarrowJumpOp(Opcode.IF_ICMPGE, target);
    }

    public C if_icmpgt(short target) {
        return emitNarrowJumpOp(Opcode.IF_ICMPGT, target);
    }

    public C if_icmple(short target) {
        return emitNarrowJumpOp(Opcode.IF_ICMPLE, target);
    }

    public C if_acmpeq(short target) {
        return emitNarrowJumpOp(Opcode.IF_ACMPEQ, target);
    }

    public C if_acmpne(short target) {
        return emitNarrowJumpOp(Opcode.IF_ACMPNE, target);
    }

    public C goto_(short target) {
        return emitNarrowJumpOp(Opcode.GOTO_, target);
    }

    public C jsr(short target) {
        return emitNarrowJumpOp(Opcode.JSR, target);
    }

    public C ret(int index) {
        return emitWideIfNeeded(Opcode.RET, index);
    }

    public C tableswitch(int low, int high, int defaultTarget, int... targets) {
        if (high - low + 1 != targets.length) throw new IllegalStateException("Bad targets length");
        emitOp(Opcode.TABLESWITCH);
        //padding
        int start = code.offset;
        if ((start % 4) != 0) {
            //add padding
            for (int i = 0; i < 4 - (start % 4); i++) {
                code.writeByte(0);
            }
        }
        code.writeInt(defaultTarget)
                .writeInt(low)
                .writeInt(high);
        for (int target : targets) {
            code.writeInt(target);
        }
        return thisBuilder();
    }

    public C lookupswitch(int defaultTarget, int... npairs) {
        if (npairs.length % 2 != 0) throw new IllegalStateException("Bad npairs length");
        emitOp(Opcode.LOOKUPSWITCH);
        //padding
        int start = code.offset;
        for (int i = 0; i < (4 - (start % 4)); i++) {
            code.writeByte(0);
        }
        code.writeInt(defaultTarget)
                .writeInt(npairs.length / 2);
        for (int i = 0; i < npairs.length; i += 2) {
            code.writeInt(npairs[i]);
            code.writeInt(npairs[i + 1]);
        }
        return thisBuilder();
    }

    public C arraylength() {
        return emitOp(Opcode.ARRAYLENGTH);
    }

    public C athrow() {
        return emitOp(Opcode.ATHROW);
    }

    public C monitorenter() {
        return emitOp(Opcode.MONITORENTER);
    }

    public C monitorexit() {
        return emitOp(Opcode.MONITOREXIT);
    }

    public C wide() {
        return emitOp(Opcode.WIDE);
    }

    public C if_null(short offset) {
        return emitNarrowJumpOp(Opcode.IF_NULL, offset);
    }

    public C if_nonnull(short offset) {
        return emitNarrowJumpOp(Opcode.IF_NONNULL, offset);
    }

    public C goto_w(int target) {
        return emitWideJumpOp(Opcode.GOTO_W, target);
    }

    public C jsr_w(int target) {
        return emitWideJumpOp(Opcode.JSR_W, target);
    }

    public C withCatch(S type, int start, int end, int offset) {
        catchers.writeChar(start);
        catchers.writeChar(end);
        catchers.writeChar(offset);
        catchers.writeChar(type != null ? poolHelper.putClass(type) : 0);
        ncatchers++;
        return thisBuilder();
    }

    public C withLocalSize(int localsize) {
        this.localsize = localsize;
        return thisBuilder();
    }

    public C withStackSize(int stacksize) {
        this.stacksize = stacksize;
        return thisBuilder();
    }

    protected int localsize() {
        return localsize;
    }

    void build(GrowableByteBuffer buf) {
        buf.writeChar(stacksize); //max stack size
        buf.writeChar(localsize()); //max locals
        buf.writeInt(code.offset);
        buf.writeBytes(code);
        buf.writeChar(ncatchers);
        buf.writeBytes(catchers);
        buf.writeChar(nattrs); //attributes
        buf.writeBytes(attributes);
    }

    byte[] build() {
        GrowableByteBuffer buf = new GrowableByteBuffer();
        build(buf);
        return buf.bytes();
    }

    protected C emitNarrowJumpOp(Opcode opcode, short target) {
        emitOp(opcode);
        emitOffset(code, JumpMode.NARROW, target);
        return thisBuilder();
    }

    protected C emitWideJumpOp(Opcode opcode, int target) {
        emitOp(opcode);
        emitOffset(code, JumpMode.WIDE, target);
        return thisBuilder();
    }

    protected C emitOp(Opcode opcode) {
        return emitOp(opcode, null);
    }

    protected C emitOp(Opcode opcode, Object optPoolValue) {
        code.writeByte(opcode.code);
        return thisBuilder();
    }

    protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
        if (jumpMode == JumpMode.NARROW) {
            buf.writeChar((short) offset);
        } else {
            buf.writeInt(offset);
        }
    }

    int offset() {
        return code.offset;
    }

    /*** stackmap support ***/

    /**
     * The tags and constants used in compressed stackmap.
     */
    static final int SAME_FRAME_SIZE = 64;
    static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
    static final int SAME_FRAME_EXTENDED = 251;
    static final int FULL_FRAME = 255;
    static final int MAX_LOCAL_LENGTH_DIFF = 4;

    @SuppressWarnings("unchecked")
    private void writeStackMapType(T t) {
        if (t == null) {
            stackmaps.writeByte(0);
        } else {
            switch (typeHelper.tag(t)) {
                case B:
                case C:
                case S:
                case I:
                case Z:
                    stackmaps.writeByte(1);
                    break;
                case F:
                    stackmaps.writeByte(2);
                    break;
                case D:
                    stackmaps.writeByte(3);
                    break;
                case J:
                    stackmaps.writeByte(4);
                    break;
                case A:
                    if (t == typeHelper.nullType()) {
                        stackmaps.writeByte(5); //null
                    } else {
                        //TODO: uninit this, top?
                        stackmaps.writeByte(7);
                        stackmaps.writeChar(poolHelper.putClass(typeHelper.symbol(t)));
                    }
                    break;
                default:
                    throw new IllegalStateException("Bad type");
            }
        }
    }

    public void sameFrame(int offsetDelta) {
        int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
                offsetDelta : SAME_FRAME_EXTENDED;
        stackmaps.writeByte(frameType);
        if (frameType == SAME_FRAME_EXTENDED) {
            stackmaps.writeChar(offsetDelta);
        }
    }

    public void sameLocals1StackItemFrame(int offsetDelta, T stackItem) {
        int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
                (SAME_FRAME_SIZE + offsetDelta) : SAME_LOCALS_1_STACK_ITEM_EXTENDED;
        stackmaps.writeByte(frameType);
        if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
            stackmaps.writeChar(offsetDelta);
        }
        writeStackMapType(stackItem);
    }

    public void appendFrame(int offsetDelta, int prevLocalsSize, List<T> locals) {
        int frameType = SAME_FRAME_EXTENDED + (locals.size() - prevLocalsSize);
        stackmaps.writeByte(frameType);
        stackmaps.writeChar(offsetDelta);
        for (int i = prevLocalsSize; i < locals.size(); i++) {
            writeStackMapType(locals.get(i));
        }
    }

    public void chopFrame(int offsetDelta, int droppedVars) {
        int frameType = SAME_FRAME_EXTENDED - droppedVars;
        stackmaps.writeByte(frameType);
        stackmaps.writeChar(offsetDelta);
    }

    public void fullFrame(int offsetDelta, List<T> locals, List<T> stackItems) {
        stackmaps.writeByte(FULL_FRAME);
        stackmaps.writeChar(offsetDelta);
        stackmaps.writeChar(locals.size());
        for (T local : locals) {
            writeStackMapType(local);
        }

        stackmaps.writeChar(stackItems.size());
        for (T stackType : stackItems) {
            writeStackMapType(stackType);
        }
    }
}