--- /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);
+ }
+}