test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java
changeset 48826 c4d9d1b08e2e
child 48827 8772acd913e5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java	Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,1215 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Vector;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.function.ToIntFunction;
+
+public class TypedCodeBuilder<S, T, E, C extends TypedCodeBuilder<S, T, E, C>> extends MacroCodeBuilder<S, T, E, C> {
+
+    State lastStackMapState;
+    int lastStackMapPc = -1;
+    Map<CharSequence, LocalVarInfo> lvarOffsets = new HashMap<>();
+    protected State state;
+    int depth = 0;
+    int currLocalOffset = 0;
+
+    class StatefulPendingJump extends PendingJump {
+
+        State state;
+
+        StatefulPendingJump(CharSequence label, int pc, State state) {
+            super(label, pc);
+            this.state = state;
+        }
+
+        @Override
+        boolean resolve(CharSequence label, int pc) {
+            boolean b = super.resolve(label, pc);
+            if (b) {
+                TypedCodeBuilder.this.state = TypedCodeBuilder.this.state.merge(state);
+            }
+            return b;
+        }
+    }
+
+    class LocalVarInfo {
+        CharSequence name;
+        int offset;
+        int depth;
+        TypeTag type;
+
+        LocalVarInfo(CharSequence name, int offset, int depth, TypeTag type) {
+            this.name = name;
+            this.offset = offset;
+            this.depth = depth;
+            this.type = type;
+        }
+    }
+
+    public TypedCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+        super(methodBuilder);
+        T t = methodBuilder.desc;
+        state = new State();
+        if ((methodBuilder.flags & Flag.ACC_STATIC.flag) == 0) {
+            T clazz = typeHelper.type(methodBuilder.thisClass);
+            state.load(clazz, currLocalOffset++); //TODO: uninit??
+        }
+        Iterator<T> paramsIt = typeHelper.parameterTypes(t);
+        while (paramsIt.hasNext()) {
+            T p = paramsIt.next();
+            state.load(p, currLocalOffset);
+            currLocalOffset += typeHelper.tag(p).width;
+        }
+        lastStackMapState = state.dup();
+        stacksize = state.stack.size();
+        localsize = state.locals.size();
+    }
+
+    @Override
+    protected C emitOp(Opcode opcode, Object optPoolValue) {
+        updateState(opcode, optPoolValue);
+        return super.emitOp(opcode, optPoolValue);
+    }
+
+    @Override
+    protected SwitchBuilder makeSwitchBuilder() {
+        return new TypedSwitchBuilder();
+    }
+
+    class TypedSwitchBuilder extends SwitchBuilder {
+
+        @Override
+        public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
+            super.withCase(value, c -> {
+                withLocalScope(() -> {
+                    State prevState = state;
+                    state = prevState.dup();
+                    emitStackMap(c.offset());
+                    case_.accept(c);
+                    state = prevState;
+                });
+            }, fallthrough);
+            return this;
+        }
+
+        @Override
+        public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
+            super.withDefault(c -> {
+                withLocalScope(() -> {
+                    State prevState = state;
+                    state = prevState.dup();
+                    emitStackMap(c.offset());
+                    defaultCase.accept(c);
+                    state = prevState;
+                });
+            });
+            return this;
+        }
+    }
+
+    @Override
+    public StatefulTypedBuilder typed(TypeTag tag) {
+        return super.typed(tag, StatefulTypedBuilder::new);
+    }
+
+    public class StatefulTypedBuilder extends LabelledTypedBuilder {
+
+        TypeTag tag;
+
+        StatefulTypedBuilder(TypeTag tag) {
+            this.tag = tag;
+        }
+
+        @Override
+        public C astore_0() {
+            return storeAndUpdate(super::astore_0);
+        }
+
+        @Override
+        public C astore_1() {
+            return storeAndUpdate(super::astore_1);
+        }
+
+        @Override
+        public C astore_2() {
+            return storeAndUpdate(super::astore_2);
+        }
+
+        @Override
+        public C astore_3() {
+            return storeAndUpdate(super::astore_3);
+        }
+
+        @Override
+        public C astore(int n) {
+            return storeAndUpdate(() -> super.astore(n));
+        }
+
+        @Override
+        public C aastore() {
+            return storeAndUpdate(super::aastore);
+        }
+
+        @Override
+        public C areturn() {
+            state.pop(tag);
+            state.push(typeHelper.nullType());
+            return super.areturn();
+        }
+
+        @Override
+        public C anewarray(S s) {
+            super.anewarray(s);
+            state.pop();
+            state.push(typeHelper.arrayOf(typeHelper.type(s)));
+            return thisBuilder();
+        }
+
+        @Override
+        public C aconst_null() {
+            super.aconst_null();
+            state.pop();
+            state.push(tag);
+            return thisBuilder();
+        }
+
+        public C if_acmpeq(CharSequence label) {
+            return jumpAndUpdate(() -> super.if_acmpeq(label));
+        }
+
+        public C if_acmpne(CharSequence label) {
+            return jumpAndUpdate(() -> super.if_acmpne(label));
+        }
+
+        private C storeAndUpdate(Supplier<C> op) {
+            state.pop(tag);
+            state.push(typeHelper.nullType());
+            return op.get();
+        }
+
+        private C jumpAndUpdate(Supplier<C> op) {
+            state.pop(tag);
+            state.pop(tag);
+            state.push(typeHelper.nullType());
+            state.push(typeHelper.nullType());
+            return op.get();
+        }
+    }
+
+    public class State {
+        public final ArrayList<T> stack;
+        public final Vector<T> locals;
+        boolean alive;
+
+        State(ArrayList<T> stack, Vector<T> locals) {
+            this.stack = stack;
+            this.locals = locals;
+        }
+
+        State() {
+            this(new ArrayList<>(), new Vector<>());
+        }
+
+        void push(TypeTag tag) {
+            switch (tag) {
+                case A:
+                case V:
+                    throw new IllegalStateException("Bad type tag");
+                default:
+                    push(typeHelper.fromTag(tag));
+            }
+        }
+
+        void push(T t) {
+            stack.add(t);
+            if (width(t) == 2) {
+                stack.add(null);
+            }
+            if (stack.size() > stacksize) {
+                stacksize = stack.size();
+            }
+        }
+
+        T peek() {
+            return stack.get(stack.size() - 1);
+        }
+
+        T tosType() {
+            T tos = peek();
+            if (tos == null) {
+                //double slot
+                tos = stack.get(stack.size() - 2);
+            }
+            return tos;
+        }
+
+        T popInternal() {
+            return stack.remove(stack.size() - 1);
+        }
+
+        @SuppressWarnings("unchecked")
+        T pop() {
+            if (stack.size() == 0 || peek() == null) throw new IllegalStateException();
+            return popInternal();
+        }
+
+        T pop2() {
+            T o = stack.get(stack.size() - 2);
+            TypeTag t = typeHelper.tag(o);
+            if (t.width != 2) throw new IllegalStateException();
+            popInternal();
+            popInternal();
+            return o;
+        }
+
+        T pop(TypeTag t) {
+            return (t.width() == 2) ?
+                pop2() : pop();
+        }
+
+        void load(TypeTag tag, int index) {
+            if (tag == TypeTag.A) throw new IllegalStateException("Bad type tag");
+            load(typeHelper.fromTag(tag), index);
+        }
+
+        void load(T t, int index) {
+            ensureDefined(index);
+            locals.set(index, t);
+            if (width(t) == 2) {
+                locals.add(null);
+            }
+            if (locals.size() > localsize) {
+                localsize = locals.size();
+            }
+        }
+
+        void ensureDefined(int index) {
+            if (index >= locals.size()) {
+                locals.setSize(index + 1);
+            }
+        }
+
+        State dup() {
+            State newState = new State(new ArrayList<>(stack), new Vector<>(locals));
+            return newState;
+        }
+
+        State merge(State that) {
+            if (!alive) { return that; }
+            if (that.stack.size() != stack.size()) {
+                throw new IllegalStateException("Bad stack size at merge point");
+            }
+            for (int i = 0; i < stack.size(); i++) {
+                T t1 = stack.get(i);
+                T t2 = that.stack.get(i);
+                stack.set(i, merge(t1, t2, "Bad stack type at merge point"));
+            }
+            int nlocals = locals.size() > that.locals.size() ? that.locals.size() : locals.size();
+            for (int i = 0; i < nlocals; i++) {
+                T t1 = locals.get(i);
+                T t2 = that.locals.get(i);
+                locals.set(i, merge(t1, t2, "Bad local type at merge point"));
+            }
+            if (locals.size() > nlocals) {
+                for (int i = nlocals; i < locals.size(); i++) {
+                    locals.remove(i);
+                }
+            }
+            return this;
+        }
+
+        T merge(T t1, T t2, String msg) {
+            if (t1 == null && t2 == null) {
+                return t1;
+            }
+            T res;
+            TypeTag tag1 = typeHelper.tag(t1);
+            TypeTag tag2 = typeHelper.tag(t2);
+            if (tag1 != TypeTag.A && tag2 != TypeTag.A &&
+                    tag1 != TypeTag.Q && tag2 != TypeTag.Q) {
+                res = typeHelper.fromTag(TypeTag.commonSupertype(tag1, tag2));
+            } else if (t1 == typeHelper.nullType()) {
+                res = t2;
+            } else if (t2 == typeHelper.nullType()) {
+                res = t1;
+            } else {
+                res = typeHelper.commonSupertype(t1, t2);
+            }
+            if (res == null) {
+                throw new IllegalStateException(msg);
+            }
+            return res;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("[locals = %s, stack = %s]", locals, stack);
+        }
+    }
+
+    int width(T o) {
+        return o == typeHelper.nullType() ?
+                TypeTag.A.width() :
+                typeHelper.tag(o).width;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void updateState(Opcode op, Object optValue) {
+        switch (op) {
+            case VALOAD:
+            case AALOAD:
+                state.pop();
+                state.push(typeHelper.elemtype(state.pop()));
+                break;
+            case GOTO_:
+                state.alive = false;
+                break;
+            case NOP:
+            case INEG:
+            case LNEG:
+            case FNEG:
+            case DNEG:
+                break;
+            case ACONST_NULL:
+                state.push(typeHelper.nullType());
+                break;
+            case ICONST_M1:
+            case ICONST_0:
+            case ICONST_1:
+            case ICONST_2:
+            case ICONST_3:
+            case ICONST_4:
+            case ICONST_5:
+                state.push(TypeTag.I);
+                break;
+            case LCONST_0:
+            case LCONST_1:
+                state.push(TypeTag.J);
+                break;
+            case FCONST_0:
+            case FCONST_1:
+            case FCONST_2:
+                state.push(TypeTag.F);
+                break;
+            case DCONST_0:
+            case DCONST_1:
+                state.push(TypeTag.D);
+                break;
+            case ILOAD_0:
+            case FLOAD_0:
+            case ALOAD_0:
+            case LLOAD_0:
+            case DLOAD_0:
+                state.push(state.locals.get(0));
+                break;
+            case ILOAD_1:
+            case FLOAD_1:
+            case ALOAD_1:
+            case LLOAD_1:
+            case DLOAD_1:
+                state.push(state.locals.get(1));
+                break;
+            case ILOAD_2:
+            case FLOAD_2:
+            case ALOAD_2:
+            case LLOAD_2:
+            case DLOAD_2:
+                state.push(state.locals.get(2));
+                break;
+            case ILOAD_3:
+            case FLOAD_3:
+            case ALOAD_3:
+            case LLOAD_3:
+            case DLOAD_3:
+                state.push(state.locals.get(3));
+                break;
+            case ILOAD:
+            case FLOAD:
+            case ALOAD:
+            case LLOAD:
+            case DLOAD:
+            case VLOAD:
+                state.push(state.locals.get((Integer) optValue));
+                break;
+            case IALOAD:
+            case BALOAD:
+            case CALOAD:
+            case SALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case LALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case FALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case DALOAD:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case ISTORE_0:
+            case FSTORE_0:
+            case ASTORE_0:
+                state.load(state.pop(), 0);
+                break;
+            case ISTORE_1:
+            case FSTORE_1:
+            case ASTORE_1:
+                state.load(state.pop(), 1);
+                break;
+            case ISTORE_2:
+            case FSTORE_2:
+            case ASTORE_2:
+                state.load(state.pop(), 2);
+                break;
+            case ISTORE_3:
+            case FSTORE_3:
+            case ASTORE_3:
+                state.load(state.pop(), 3);
+                break;
+            case ISTORE:
+            case FSTORE:
+            case ASTORE:
+            case VSTORE:
+                state.load(state.pop(), (int) optValue);
+                break;
+            case LSTORE_0:
+            case DSTORE_0:
+                state.load(state.pop2(), 0);
+                break;
+            case LSTORE_1:
+            case DSTORE_1:
+                state.load(state.pop2(), 1);
+                break;
+            case LSTORE_2:
+            case DSTORE_2:
+                state.load(state.pop2(), 2);
+                break;
+            case LSTORE_3:
+            case DSTORE_3:
+                state.load(state.pop2(), 3);
+                break;
+            case LSTORE:
+            case DSTORE:
+                state.load(state.pop2(), (int) optValue);
+                break;
+            case POP:
+            case LSHR:
+            case LSHL:
+            case LUSHR:
+                state.pop();
+                break;
+            case VRETURN:
+            case ARETURN:
+            case IRETURN:
+            case FRETURN:
+                state.pop();
+                break;
+            case ATHROW:
+                state.pop();
+                break;
+            case POP2:
+                state.pop2();
+                break;
+            case LRETURN:
+            case DRETURN:
+                state.pop2();
+                break;
+            case DUP:
+                state.push(state.peek());
+                break;
+            case RETURN:
+                break;
+            case ARRAYLENGTH:
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case ISUB:
+            case IADD:
+            case IMUL:
+            case IDIV:
+            case IREM:
+            case ISHL:
+            case ISHR:
+            case IUSHR:
+            case IAND:
+            case IOR:
+            case IXOR:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case VASTORE:
+            case AASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case LAND:
+            case LOR:
+            case LXOR:
+            case LREM:
+            case LDIV:
+            case LMUL:
+            case LSUB:
+            case LADD:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.J);
+                break;
+            case LCMP:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case L2I:
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case I2L:
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case I2F:
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case I2D:
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case L2F:
+                state.pop2();
+                state.push(TypeTag.F);
+                break;
+            case L2D:
+                state.pop2();
+                state.push(TypeTag.D);
+                break;
+            case F2I:
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case F2L:
+                state.pop();
+                state.push(TypeTag.J);
+                break;
+            case F2D:
+                state.pop();
+                state.push(TypeTag.D);
+                break;
+            case D2I:
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case D2L:
+                state.pop2();
+                state.push(TypeTag.J);
+                break;
+            case D2F:
+                state.pop2();
+                state.push(TypeTag.F);
+                break;
+            case TABLESWITCH:
+            case LOOKUPSWITCH:
+                state.pop();
+                break;
+            case DUP_X1: {
+                T val1 = state.pop();
+                T val2 = state.pop();
+                state.push(val1);
+                state.push(val2);
+                state.push(val1);
+                break;
+            }
+            case BASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case I2B:
+            case I2C:
+            case I2S:
+                break;
+            case FMUL:
+            case FADD:
+            case FSUB:
+            case FDIV:
+            case FREM:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.F);
+                break;
+            case CASTORE:
+            case IASTORE:
+            case FASTORE:
+            case SASTORE:
+                state.pop();
+                state.pop();
+                state.pop();
+                break;
+            case LASTORE:
+            case DASTORE:
+                state.pop2();
+                state.pop();
+                state.pop();
+                break;
+            case DUP2:
+                if (state.peek() != null) {
+                    //form 1
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    //form 2
+                    T value = state.pop2();
+                    state.push(value);
+                    state.push(value);
+                }
+                break;
+            case DUP2_X1:
+                if (state.peek() != null) {
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    T value3 = state.pop();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    T value1 = state.pop2();
+                    T value2 = state.pop();
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                }
+                break;
+            case DUP2_X2:
+                if (state.peek() != null) {
+                    T value1 = state.pop();
+                    T value2 = state.pop();
+                    if (state.peek() != null) {
+                        // form 1
+                        T value3 = state.pop();
+                        T value4 = state.pop();
+                        state.push(value2);
+                        state.push(value1);
+                        state.push(value4);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    } else {
+                        // form 3
+                        T value3 = state.pop2();
+                        state.push(value2);
+                        state.push(value1);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    }
+                } else {
+                    T value1 = state.pop2();
+                    if (state.peek() != null) {
+                        // form 2
+                        T value2 = state.pop();
+                        T value3 = state.pop();
+                        state.push(value1);
+                        state.push(value3);
+                        state.push(value2);
+                        state.push(value1);
+                    } else {
+                        // form 4
+                        T value2 = state.pop2();
+                        state.push(value1);
+                        state.push(value2);
+                        state.push(value1);
+                    }
+                }
+                break;
+            case DUP_X2: {
+                T value1 = state.pop();
+                if (state.peek() != null) {
+                    // form 1
+                    T value2 = state.pop();
+                    T value3 = state.pop();
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    // form 2
+                    T value2 = state.pop2();
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                }
+            }
+            break;
+            case FCMPL:
+            case FCMPG:
+                state.pop();
+                state.pop();
+                state.push(TypeTag.I);
+                break;
+            case DCMPL:
+            case DCMPG:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.I);
+                break;
+            case SWAP: {
+                T value1 = state.pop();
+                T value2 = state.pop();
+                state.push(value1);
+                state.push(value2);
+                break;
+            }
+            case DADD:
+            case DSUB:
+            case DMUL:
+            case DDIV:
+            case DREM:
+                state.pop2();
+                state.pop2();
+                state.push(TypeTag.D);
+                break;
+            case RET:
+                break;
+            case WIDE:
+                // must be handled by the caller.
+                return;
+            case MONITORENTER:
+            case MONITOREXIT:
+                state.pop();
+                break;
+            case VNEW:
+            case NEW:
+                state.push(typeHelper.type((S) optValue));
+                break;
+            case NEWARRAY:
+                state.pop();
+                state.push(typeHelper.arrayOf(typeHelper.fromTag((TypeTag) optValue)));
+                break;
+            case ANEWARRAY:
+                state.pop();
+                state.push(typeHelper.arrayOf(typeHelper.arrayOf(typeHelper.type((S)optValue))));
+                break;
+            case VNEWARRAY:
+            case VBOX:
+            case VUNBOX:
+                state.pop();
+                state.push(typeHelper.type((S) optValue));
+                break;
+            case MULTIVNEWARRAY:
+            case MULTIANEWARRAY:
+                for (int i = 0; i < (byte) ((Object[]) optValue)[1]; i++) {
+                    state.pop();
+                }
+                state.push(typeHelper.type((S) ((Object[]) optValue)[0]));
+                break;
+            case INVOKEINTERFACE:
+            case INVOKEVIRTUAL:
+            case INVOKESPECIAL:
+            case INVOKESTATIC:
+            case INVOKEDYNAMIC:
+                processInvoke(op, (T) optValue);
+                break;
+            case GETSTATIC:
+                state.push((T) optValue);
+                break;
+            case VGETFIELD:
+            case GETFIELD:
+                state.pop();
+                state.push((T) optValue);
+                break;
+            case PUTSTATIC: {
+                TypeTag tag = typeHelper.tag((T) optValue);
+                if (tag.width == 1) {
+                    state.pop();
+                } else {
+                    state.pop2();
+                }
+                break;
+            }
+            case PUTFIELD: {
+                TypeTag tag = typeHelper.tag((T) optValue);
+                if (tag.width == 1) {
+                    state.pop();
+                } else {
+                    state.pop2();
+                }
+                state.pop();
+                break;
+            }
+            case BIPUSH:
+            case SIPUSH:
+                state.push(TypeTag.I);
+                break;
+            case LDC:
+            case LDC_W:
+            case LDC2_W:
+                state.push((T)optValue);
+                break;
+            case IF_ACMPEQ:
+            case IF_ICMPEQ:
+            case IF_ACMPNE:
+            case IF_ICMPGE:
+            case IF_ICMPGT:
+            case IF_ICMPLE:
+            case IF_ICMPLT:
+            case IF_ICMPNE:
+                state.pop();
+                state.pop();
+                break;
+            case IF_NONNULL:
+            case IF_NULL:
+            case IFEQ:
+            case IFGE:
+            case IFGT:
+            case IFLE:
+            case IFLT:
+            case IFNE:
+                state.pop();
+                break;
+            case INSTANCEOF:
+                state.pop();
+                state.push(TypeTag.Z);
+                break;
+            case TYPED:
+            case CHECKCAST:
+                break;
+
+            default:
+                throw new UnsupportedOperationException("Unsupported opcode: " + op);
+        }
+    }
+
+    void processInvoke(Opcode opcode, T invokedType) {
+        Iterator<T> paramsIt = typeHelper.parameterTypes(invokedType);
+        while (paramsIt.hasNext()) {
+            T t = paramsIt.next();
+            TypeTag tag = typeHelper.tag(t);
+            if (tag.width == 2) {
+                state.popInternal();
+                state.popInternal();
+            } else {
+                state.popInternal();
+            }
+        }
+        if (opcode != Opcode.INVOKESTATIC && opcode != Opcode.INVOKEDYNAMIC) {
+            state.pop(); //receiver
+        }
+        T retType = typeHelper.returnType(invokedType);
+        TypeTag retTag = typeHelper.tag(retType);
+        if (retTag != TypeTag.V)
+            state.push(retType);
+    }
+
+    @Override
+    protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
+        LdcPoolHelper ldcPoolHelper = new LdcPoolHelper();
+        int index = indexFunc.applyAsInt(ldcPoolHelper);
+        fat = typeHelper.tag(ldcPoolHelper.type).width() == 2;
+        return super.ldc(index, ldcPoolHelper.type, fat);
+    }
+    //where
+        class LdcPoolHelper implements PoolHelper<S, T, E> {
+
+            T type;
+
+            @Override
+            public int putClass(S symbol) {
+                type = typeHelper.type(symbol);
+                return poolHelper.putClass(symbol);
+            }
+
+            @Override
+            public int putInt(int i) {
+                type = typeHelper.fromTag(TypeTag.I);
+                return poolHelper.putInt(i);
+            }
+
+            @Override
+            public int putFloat(float f) {
+                type = typeHelper.fromTag(TypeTag.F);
+                return poolHelper.putFloat(f);
+            }
+
+            @Override
+            public int putLong(long l) {
+                type = typeHelper.fromTag(TypeTag.J);
+                return poolHelper.putLong(l);
+            }
+
+            @Override
+            public int putDouble(double d) {
+                type = typeHelper.fromTag(TypeTag.D);
+                return poolHelper.putDouble(d);
+            }
+
+            @Override
+            public int putString(String s) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/String"));
+                return poolHelper.putString(s);
+            }
+
+            @Override
+            public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+                type = constType;
+                return poolHelper.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs);
+            }
+
+            @Override
+            public int putFieldRef(S owner, CharSequence name, T type) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putUtf8(CharSequence s) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putType(T t) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int putMethodType(T t) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodType"));
+                return poolHelper.putMethodType(t);
+            }
+
+            @Override
+            public int putHandle(int refKind, S owner, CharSequence name, T t) {
+                type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
+                return poolHelper.putHandle(refKind, owner, name, t);
+            }
+
+            @Override
+            public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public int size() {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public E entries() {
+                throw new IllegalStateException();
+            }
+    }
+
+    public C load(int index) {
+        return load(typeHelper.tag(state.locals.get(index)), index);
+    }
+
+    public C store(int index) {
+        return store(typeHelper.tag(state.tosType()), index);
+    }
+
+    @Override
+    public C withLocalSize(int localsize) {
+        throw new IllegalStateException("Local size automatically computed");
+    }
+
+    @Override
+    public C withStackSize(int stacksize) {
+        throw new IllegalStateException("Stack size automatically computed");
+    }
+
+    public C withLocal(CharSequence name, T type) {
+        int offset = currLocalOffset;
+        TypeTag tag = typeHelper.tag(type);
+        lvarOffsets.put(name, new LocalVarInfo(name, offset, depth, tag));
+        state.load(type, offset);
+        currLocalOffset += tag.width;
+        return thisBuilder();
+    }
+
+    public C load(CharSequence local) {
+        return load(lvarOffsets.get(local).offset);
+    }
+
+    public C store(CharSequence local) {
+        return store(lvarOffsets.get(local).offset);
+    }
+
+    @Override
+    public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
+        return super.withTry(c -> {
+            withLocalScope(() -> {
+                tryBlock.accept(c);
+            });
+        }, catchBlocks);
+    }
+
+    @Override
+    protected CatchBuilder makeCatchBuilder(int start, int end) {
+        return new TypedCatchBuilder(start, end);
+    }
+
+    class TypedCatchBuilder extends CatchBuilder {
+
+        State initialState = state.dup();
+
+        TypedCatchBuilder(int start, int end) {
+            super(start, end);
+        }
+
+        @Override
+        protected void emitCatch(S exc, Consumer<? super C> catcher) {
+            withLocalScope(() -> {
+                state.push(typeHelper.type(exc));
+                emitStackMap(code.offset);
+                super.emitCatch(exc, catcher);
+                state = initialState;
+            });
+        }
+
+        @Override
+        protected void emitFinalizer() {
+            withLocalScope(() -> {
+                state.push(typeHelper.type(typeHelper.symbolFrom("java/lang/Throwable")));
+                emitStackMap(code.offset);
+                super.emitFinalizer();
+            });
+        }
+    }
+
+    protected void withLocalScope(Runnable runnable) {
+        int prevDepth = depth;
+        try {
+            depth++;
+            runnable.run();
+        } finally {
+            Iterator<Entry<CharSequence, LocalVarInfo>> lvarIt = lvarOffsets.entrySet().iterator();
+            while (lvarIt.hasNext()) {
+                LocalVarInfo lvi = lvarIt.next().getValue();
+                if (lvi.depth == depth) {
+                    int width = lvi.type.width;
+                    currLocalOffset -= width;
+                    lvarIt.remove();
+                }
+            }
+            depth = prevDepth;
+        }
+    }
+
+    @Override
+    void addPendingJump(CharSequence label, int pc) {
+        pendingJumps.add(new StatefulPendingJump(label, pc, state.dup()));
+    }
+
+    @Override
+    void resolveJumps(CharSequence label, int pc) {
+        super.resolveJumps(label, pc);
+        emitStackMap(pc);
+    }
+
+    //TODO: optimize stackmap generation by avoiding intermediate classes
+    protected void emitStackMap(int pc) {
+        //stack map generation
+        if (pc > lastStackMapPc) {
+            writeStackMapFrame(pc);
+            lastStackMapState = state.dup();
+            lastStackMapPc = pc;
+            nstackmaps++;
+        }
+    }
+
+    @Override
+    void build(GrowableByteBuffer buf) {
+        if (stacksize == -1) {
+            throw new IllegalStateException("Bad stack size");
+        }
+        if (localsize == -1) {
+            throw new IllegalStateException("Bad locals size");
+        }
+        if (nstackmaps > 0) {
+            GrowableByteBuffer stackmapsAttr = new GrowableByteBuffer();
+            stackmapsAttr.writeChar(nstackmaps);
+            stackmapsAttr.writeBytes(stackmaps);
+            withAttribute("StackMapTable", stackmapsAttr.bytes());
+        }
+        super.build(buf);
+    }
+
+    /**
+     * Compare this frame with the previous frame and produce
+     * an entry of compressed stack map frame.
+     */
+    void writeStackMapFrame(int pc) {
+        List<T> locals = state.locals;
+        List<T> stack = state.stack;
+        List<T> prev_locals = lastStackMapState.locals;
+        int offset_delta = lastStackMapPc == -1 ? pc : pc - lastStackMapPc - 1;
+        if (stack.size() == 1) {
+            if (locals.size() == prev_locals.size() && prev_locals.equals(locals)) {
+                sameLocals1StackItemFrame(offset_delta, stack.get(stack.size() - 1));
+                return;
+            }
+        } else if (stack.size() == 0) {
+            int diff_length = prev_locals.size() - locals.size();
+            if (diff_length == 0) {
+                sameFrame(offset_delta);
+                return;
+            } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) {
+                appendFrame(offset_delta, prev_locals.size(), locals);
+                return;
+            } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) {
+                chopFrame(offset_delta, diff_length);
+                return;
+            }
+        }
+        fullFrame(offset_delta, locals, stack);
+    }
+}