blob: dc82ad67af3b63e7d254f3949c1ab12c987774fa [file] [log] [blame]
/*
* 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 putHandle(int refKind, S owner, CharSequence name, T t, boolean isInterface) {
type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
return poolHelper.putHandle(refKind, owner, name, t, isInterface);
}
@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);
}
}