diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java --- /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> extends MacroCodeBuilder { + + State lastStackMapState; + int lastStackMapPc = -1; + Map 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 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 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 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 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 op) { + state.pop(tag); + state.push(typeHelper.nullType()); + return op.get(); + } + + private C jumpAndUpdate(Supplier op) { + state.pop(tag); + state.pop(tag); + state.push(typeHelper.nullType()); + state.push(typeHelper.nullType()); + return op.get(); + } + } + + public class State { + public final ArrayList stack; + public final Vector locals; + boolean alive; + + State(ArrayList stack, Vector 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 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> 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 { + + 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> 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> 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 tryBlock, Consumer 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 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> 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 locals = state.locals; + List stack = state.stack; + List 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); + } +}