/* * Copyright (c) 2014, 2016, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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 com.sun.btrace.runtime; import static com.sun.btrace.runtime.Constants.*; import com.sun.btrace.org.objectweb.asm.Handle; import com.sun.btrace.org.objectweb.asm.Label; import com.sun.btrace.org.objectweb.asm.MethodVisitor; import com.sun.btrace.org.objectweb.asm.Opcodes; import com.sun.btrace.org.objectweb.asm.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * This extended {@linkplain MethodVisitor} keeps track of the values * currently on the stack and their origin. This way it is eg. possible * to allow invocation of virtual methods only on instances obtained only * through a certain factory method. * * @author Jaroslav Bachorik */ class StackTrackingMethodVisitor extends MethodVisitor { abstract public static class StackItem { public static enum Kind { VARIABLE, CONSTANT, INSTANCE, RESULT } private Set<StackItem> parents = new HashSet<>(); public StackItem(StackItem ... parents) { this.parents.addAll(Arrays.asList(parents)); } final public Set<StackItem> getParents() { return parents; } final public void merge(StackItem sl) { parents.addAll(sl.getParents()); } abstract public Kind getKind(); } final public static class VariableItem extends StackItem { private final int var; public VariableItem(int var, StackItem... parents) { super(parents); this.var = var; } public int getVar() { return var; } @Override public Kind getKind() { return Kind.VARIABLE; } @Override public int hashCode() { int hash = 5; hash = 31 * hash + this.var; return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final VariableItem other = (VariableItem) obj; return this.var == other.var; } } public static class ConstantItem extends StackItem { private final Object val; public ConstantItem(Object val, StackItem ... parents) { super(parents); this.val = val; } public Object getValue() { return val; } @Override public Kind getKind() { return Kind.CONSTANT; } @Override public int hashCode() { int hash = 7; hash = 59 * hash + (this.val != null ? this.val.hashCode() : 0); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ConstantItem other = (ConstantItem) obj; return !(this.val != other.val && (this.val == null || !this.val.equals(other.val))); } } public static class InstanceItem extends StackItem { private final Type t; public InstanceItem(Type t, StackItem ... parents) { super(parents); this.t = t; } public Type getType() { return t; } @Override public Kind getKind() { return Kind.INSTANCE; } @Override public int hashCode() { int hash = 7; hash = 41 * hash + (this.t != null ? this.t.hashCode() : 0); return hash; } @Override @SuppressWarnings("ReferenceEquality") public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final InstanceItem other = (InstanceItem) obj; return !(this.t != other.t && (this.t == null || !this.t.equals(other.t))); } } public static class ResultItem extends StackItem { public static enum Origin { FIELD, METHOD } private final String owner, name, desc; private final Origin origin; public ResultItem(String owner, String name, String desc, Origin origin, StackItem ... parents) { super(parents); this.owner = owner; this.name = name; this.desc = desc; this.origin = origin; } public String getOwner() { return owner; } public String getName() { return name; } public String getDesc() { return desc; } public Type getType() { return Type.getReturnType(desc); } public Origin getOrigin() { return origin; } @Override public Kind getKind() { return Kind.RESULT; } @Override public int hashCode() { int hash = 7; hash = 89 * hash + (this.owner != null ? this.owner.hashCode() : 0); hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0); hash = 89 * hash + (this.desc != null ? this.desc.hashCode() : 0); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ResultItem other = (ResultItem) obj; if ((this.owner == null) ? (other.owner != null) : !this.owner.equals(other.owner)) { return false; } if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { return false; } return !((this.desc == null) ? (other.desc != null) : !this.desc.equals(other.desc)); } } private final class FrameState { private Deque<StackItem> stack; final private Map<Integer, StackItem> vars; final private Map<Integer, StackItem> args; public FrameState(Map<Integer, StackItem> args) { this(new LinkedList<StackItem>(), null, args); } private FrameState(Deque<StackItem> s, Map<Integer, StackItem> v, Map<Integer, StackItem> args) { this.stack = new LinkedList<>(s); this.vars = v != null ? new HashMap<>(v) : new HashMap<Integer, StackItem>(); this.args = new HashMap<>(args); } public StackItem peek() { return stack.peek(); } public void push(StackItem sl) { stack.push(sl); } public StackItem pop() { return stack.pop(); } public void store(StackItem sl, int index) { vars.put(index, sl); } public StackItem load(int index) { StackItem si = vars.get(index); if (si == null) { si = args.get(index); } return si; } public FrameState duplicate() { return new FrameState(stack, vars, args); } public boolean isEmpty() { return stack.isEmpty(); } public void reset() { stack.clear(); vars.clear(); } } private final class State { private final Map<Label, Set<FrameState>> stateMap = new HashMap<>(); private FrameState fState; public State(InstanceItem receiver, Type[] args) { Map<Integer, StackItem> argMap = new HashMap<>(); int index = 0; if (receiver != null) { argMap.put(index++, receiver); } for(Type t : args) { argMap.put(index++, new InstanceItem(t)); if (t.equals(Type.LONG_TYPE) || t.equals(Type.DOUBLE_TYPE)) { index++; } } this.fState = new FrameState(argMap); } public void branch(Label l) { if (visitedLabels.contains(l)) return; // back loop should preserve the stack Set<FrameState> states = stateMap.get(l); if (states == null) { states = new HashSet<>(); stateMap.put(l, states); } states.add(fState.duplicate()); } public void branch(Label l, Type throwable) { if (visitedLabels.contains(l)) return; // back loop should preserve the stack Set<FrameState> states = stateMap.get(l); if (states == null) { states = new HashSet<>(); stateMap.put(l, states); } FrameState duplicated = fState.duplicate(); duplicated.push(new InstanceItem(throwable)); states.add(duplicated); } public void join(Label l) { Set<FrameState> states = stateMap.remove(l); if (states != null) { if (fState.isEmpty() && !states.isEmpty()) { FrameState s = states.iterator().next(); if (!s.isEmpty()) { fState = s; } } for(FrameState fs : states) { Iterator<StackItem> i1 = fState.stack.iterator(); Iterator<StackItem> i2 = fs.stack.iterator(); while (i1.hasNext()) { if (i2.hasNext()) { StackItem target = i1.next(); StackItem src = i2.next(); target.merge(src); } else { throw new IllegalStateException("Error merging simulated stack"); } } if (i2.hasNext()) { throw new IllegalStateException("Error merging simulated stack"); } } } } public StackItem peek() { return fState.peek(); } public void push(StackItem sl) { fState.push(sl); } public StackItem pop() { return fState.pop(); } public void store(StackItem sl, int index) { fState.store(sl, index); } public StackItem load(int index) { return fState.load(index); } public void reset() { fState.reset(); } } private final State state; private final Map<Label, Collection<Label>> tryCatchStart = new HashMap<>(); private final Map<Label, Collection<Label>> tryCatchEnd = new HashMap<>(); private final Set<Label> effectiveHandlers = new HashSet<>(); private final Collection<Label> handlers = new ArrayList<>(); private final Set<Label> visitedLabels = new HashSet<>(); public StackTrackingMethodVisitor(MethodVisitor mv, String className, String desc, boolean isStatic) { super(Opcodes.ASM5, mv); Type[] args = Type.getArgumentTypes(desc); state = new State(isStatic ? null : new InstanceItem(Type.getObjectType(className)), args); } @Override public void visitMultiANewArrayInsn(String string, int i) { super.visitMultiANewArrayInsn(string, i); } @Override public void visitLookupSwitchInsn(Label label, int[] ints, Label[] labels) { state.pop(); super.visitLookupSwitchInsn(label, ints, labels); } @Override public void visitTableSwitchInsn(int i, int i1, Label label, Label... labels) { state.pop(); super.visitTableSwitchInsn(i, i1, label, labels); } @Override public void visitLdcInsn(Object o) { ConstantItem ci = new ConstantItem(o); state.push(ci); if (o instanceof Long || o instanceof Double) { state.push(ci); } super.visitLdcInsn(o); } @Override public void visitJumpInsn(int opcode, Label label) { super.visitJumpInsn(opcode, label); switch(opcode) { case Opcodes.IFEQ: case Opcodes.IFGE: case Opcodes.IFGT: case Opcodes.IFLE: case Opcodes.IFLT: case Opcodes.IFNE: case Opcodes.IFNONNULL: case Opcodes.IFNULL: { state.pop(); state.branch(label); break; } case Opcodes.IF_ACMPEQ: case Opcodes.IF_ACMPNE: case Opcodes.IF_ICMPEQ: case Opcodes.IF_ICMPGE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLE: case Opcodes.IF_ICMPLT: case Opcodes.IF_ICMPNE: { state.pop(); state.pop(); state.branch(label); break; } case Opcodes.GOTO: case Opcodes.JSR: { state.branch(label); state.reset(); break; } } } @Override public void visitInvokeDynamicInsn(String string, String string1, Handle handle, Object... os) { super.visitInvokeDynamicInsn(string, string1, handle, os); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itfc) { super.visitMethodInsn(opcode, owner, name, desc, itfc); List<StackItem> poppedArgs = new ArrayList<>(); Type[] args = Type.getArgumentTypes(desc); Type ret = Type.getReturnType(desc); for(int i=args.length-1;i>=0;i--) { if (!args[i].equals(Type.VOID_TYPE)) { switch(args[i].getSort()) { case Type.LONG: case Type.DOUBLE: { state.pop(); // fall through } case Type.INT: case Type.FLOAT: case Type.BOOLEAN: case Type.CHAR: case Type.SHORT: case Type.BYTE: case Type.ARRAY: case Type.METHOD: case Type.OBJECT: { poppedArgs.add(state.pop()); break; } } } } if (opcode != Opcodes.INVOKESTATIC) { poppedArgs.add(state.pop()); } if (!ret.equals(Type.VOID_TYPE)) { StackItem sl = new ResultItem(owner, name, desc, ResultItem.Origin.METHOD, poppedArgs.toArray(new StackItem[poppedArgs.size()])); switch (ret.getSort()) { case Type.LONG: case Type.DOUBLE: { state.push(sl); // fall through } case Type.INT: case Type.FLOAT: case Type.BOOLEAN: case Type.CHAR: case Type.SHORT: case Type.BYTE: case Type.ARRAY: case Type.METHOD: case Type.OBJECT: { state.push(sl); break; } } } } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { Type t = Type.getType(desc); super.visitFieldInsn(opcode, owner, name, desc); List<StackItem> parents = new ArrayList<>(); if (opcode == Opcodes.PUTFIELD || opcode == Opcodes.PUTSTATIC) { switch (t.getSort()) { case Type.LONG: case Type.DOUBLE: { state.pop(); // fall through } case Type.INT: case Type.FLOAT: case Type.BOOLEAN: case Type.CHAR: case Type.SHORT: case Type.BYTE: case Type.ARRAY: case Type.METHOD: case Type.OBJECT: { parents.add(state.pop()); break; } } } if (opcode == Opcodes.GETFIELD || opcode == Opcodes.PUTFIELD) { parents.add(state.pop()); // pop 'this' } if (opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC) { StackItem sl = new ResultItem(owner, name, desc, ResultItem.Origin.FIELD, parents.toArray(new StackItem[parents.size()])); switch (t.getSort()) { case Type.LONG: case Type.DOUBLE: { state.push(sl); // fall through } case Type.INT: case Type.FLOAT: case Type.BOOLEAN: case Type.CHAR: case Type.SHORT: case Type.BYTE: case Type.ARRAY: case Type.METHOD: case Type.OBJECT: { state.push(sl); break; } } } } @Override public void visitTypeInsn(int opcode, String type) { super.visitTypeInsn(opcode, type); switch (opcode) { case Opcodes.NEW: { state.push(new InstanceItem(Type.getType(type))); break; } case Opcodes.ANEWARRAY: { state.push(new InstanceItem(Type.getType(type), state.pop())); break; } case Opcodes.INSTANCEOF: { state.push(new InstanceItem(Type.BOOLEAN_TYPE, state.pop())); break; } } } @Override public void visitVarInsn(int opcode, int var) { super.visitVarInsn(opcode, var); switch (opcode) { case Opcodes.ILOAD: case Opcodes.FLOAD: case Opcodes.ALOAD: { state.push(state.load(var)); break; } case Opcodes.LLOAD: case Opcodes.DLOAD: { StackItem sl = state.load(var); state.push(sl); state.push(sl); break; } case Opcodes.ISTORE: case Opcodes.FSTORE: case Opcodes.ASTORE: { StackItem sl = state.pop(); state.store(sl, var); break; } case Opcodes.LSTORE: case Opcodes.DSTORE: { StackItem sl = state.pop(); state.pop(); state.store(sl, var); break; } } } @Override public void visitIntInsn(int opcode, int operand) { super.visitIntInsn(opcode, operand); switch (opcode) { case Opcodes.BIPUSH: case Opcodes.SIPUSH: { state.push(new ConstantItem(operand)); break; } case Opcodes.NEWARRAY: { StackItem sl = state.pop(); // size state.push(new InstanceItem(OBJECT_TYPE, sl)); break; } } } @Override public void visitInsn(int opcode) { super.visitInsn(opcode); switch (opcode) { case Opcodes.ACONST_NULL: { state.push(new ConstantItem(null)); break; } case Opcodes.ICONST_0: { state.push(new ConstantItem(0)); break; } case Opcodes.ICONST_1: { state.push(new ConstantItem(1)); break; } case Opcodes.ICONST_2: { state.push(new ConstantItem(2)); break; } case Opcodes.ICONST_3: { state.push(new ConstantItem(3)); break; } case Opcodes.ICONST_4: { state.push(new ConstantItem(4)); break; } case Opcodes.ICONST_5: { state.push(new ConstantItem(5)); break; } case Opcodes.ICONST_M1: { state.push(new ConstantItem(-1)); break; } case Opcodes.FCONST_0: { state.push(new ConstantItem(0f)); break; } case Opcodes.FCONST_1: { state.push(new ConstantItem(1f)); break; } case Opcodes.FCONST_2: { state.push(new ConstantItem(2f)); break; } case Opcodes.LCONST_0: { StackItem si = new ConstantItem(0L); state.push(si); state.push(si); break; } case Opcodes.LCONST_1: { StackItem si = new ConstantItem(1L); state.push(si); state.push(si); break; } case Opcodes.DCONST_0: { StackItem si = new ConstantItem(0d); state.push(si); state.push(si); break; } case Opcodes.DCONST_1: { StackItem si = new ConstantItem(1d); state.push(si); state.push(si); break; } case Opcodes.AALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); Type t = null; if (ref.getKind() == StackItem.Kind.INSTANCE) { t = ((InstanceItem)ref).getType(); } else if (ref.getKind() == StackItem.Kind.RESULT) { t = ((ResultItem)ref).getType(); } state.push(new InstanceItem(t, ref, index)); break; } case Opcodes.IALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); state.push(new InstanceItem(Type.INT_TYPE, index, ref)); break; } case Opcodes.FALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); state.push(new InstanceItem(Type.FLOAT_TYPE, index, ref)); break; } case Opcodes.BALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); state.push(new InstanceItem(Type.BYTE_TYPE, index, ref)); break; } case Opcodes.CALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); state.push(new InstanceItem(Type.CHAR_TYPE, index, ref)); break; } case Opcodes.SALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); state.push(new InstanceItem(Type.SHORT_TYPE, index, ref)); break; } case Opcodes.LALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); StackItem sl = new InstanceItem(Type.LONG_TYPE, index, ref); state.push(sl); state.push(sl); break; } case Opcodes.DALOAD: { StackItem index = state.pop(); StackItem ref = state.pop(); StackItem sl = new InstanceItem(Type.DOUBLE_TYPE, index, ref); state.push(sl); state.push(sl); break; } case Opcodes.AASTORE: case Opcodes.IASTORE: case Opcodes.FASTORE: case Opcodes.BASTORE: case Opcodes.CASTORE: case Opcodes.SASTORE: { state.pop(); // val state.pop(); // index state.pop(); // arrayref break; } case Opcodes.LASTORE: case Opcodes.DASTORE: { state.pop(); state.pop(); // var state.pop(); // index state.pop(); // arrayref break; } case Opcodes.POP: { state.pop(); break; } case Opcodes.POP2: { state.pop(); state.pop(); break; } case Opcodes.DUP: { state.push(state.peek()); break; } case Opcodes.DUP_X1: { StackItem x = state.pop(); StackItem y = state.pop(); state.push(x); state.push(y); state.push(x); break; } case Opcodes.DUP_X2: { StackItem x = state.pop(); StackItem y = state.pop(); StackItem z = state.pop(); state.push(x); state.push(z); state.push(y); state.push(x); break; } case Opcodes.DUP2: { StackItem x = state.pop(); StackItem y = state.peek(); state.push(x); state.push(y); state.push(x); break; } case Opcodes.DUP2_X1: { StackItem x2 = state.pop(); StackItem x1 = state.pop(); StackItem y = state.pop(); state.push(x1); state.push(x2); state.push(y); state.push(x1); state.push(x2); break; } case Opcodes.DUP2_X2: { StackItem x2 = state.pop(); StackItem x1 = state.pop(); StackItem y2 = state.pop(); StackItem y1 = state.pop(); state.push(x1); state.push(x2); state.push(y1); state.push(y2); state.push(x1); state.push(x2); break; } case Opcodes.SWAP: { StackItem x = state.pop(); StackItem y = state.pop(); state.push(x); state.push(y); break; } case Opcodes.IADD: case Opcodes.ISUB: case Opcodes.IMUL: case Opcodes.IDIV: case Opcodes.IREM: case Opcodes.IAND: case Opcodes.IOR: case Opcodes.IXOR: case Opcodes.ISHR: case Opcodes.ISHL: case Opcodes.IUSHR: { StackItem x = state.pop(); StackItem y = state.pop(); state.push(new InstanceItem(Type.INT_TYPE, x, y)); break; } case Opcodes.FADD: case Opcodes.FSUB: case Opcodes.FMUL: case Opcodes.FDIV: case Opcodes.FREM: { StackItem x = state.pop(); StackItem y = state.pop(); state.push(new InstanceItem(Type.FLOAT_TYPE, x, y)); break; } case Opcodes.LADD: case Opcodes.LSUB: case Opcodes.LMUL: case Opcodes.LDIV: case Opcodes.LREM: case Opcodes.LAND: case Opcodes.LOR: case Opcodes.LXOR: case Opcodes.LSHR: case Opcodes.LSHL: case Opcodes.LUSHR: { StackItem x = state.pop(); state.pop(); StackItem y = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.LONG_TYPE, x, y); state.push(rslt); state.push(rslt); break; } case Opcodes.DADD: case Opcodes.DSUB: case Opcodes.DMUL: case Opcodes.DDIV: case Opcodes.DREM: { StackItem x = state.pop(); state.pop(); StackItem y = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.DOUBLE_TYPE, x, y); state.push(rslt); state.push(rslt); break; } case Opcodes.I2L: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.LONG_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.I2F: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.FLOAT_TYPE, x); state.push(rslt); break; } case Opcodes.I2B: { Set<StackItem> parents = new HashSet<>(); StackItem x = state.pop(); parents.addAll(x.getParents()); StackItem rslt = new InstanceItem(Type.BYTE_TYPE, parents.toArray(new StackItem[parents.size()])); state.push(rslt); break; } case Opcodes.I2C: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.CHAR_TYPE, x); state.push(rslt); break; } case Opcodes.I2S: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.SHORT_TYPE, x); state.push(rslt); break; } case Opcodes.I2D: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.DOUBLE_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.L2I: { StackItem x = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.INT_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.L2F: { StackItem x = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.FLOAT_TYPE, x); state.push(rslt); break; } case Opcodes.L2D: { StackItem x = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.DOUBLE_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.F2I: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.INT_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.F2L: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.LONG_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.F2D: { StackItem x = state.pop(); StackItem rslt = new InstanceItem(Type.DOUBLE_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.D2I: { StackItem x = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.INT_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.D2F: { StackItem x = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.FLOAT_TYPE, x); state.push(rslt); break; } case Opcodes.D2L: { StackItem x = state.pop(); state.pop(); StackItem rslt = new InstanceItem(Type.LONG_TYPE, x); state.push(rslt); state.push(rslt); break; } case Opcodes.LCMP: { StackItem a = state.pop(); StackItem b = state.pop(); StackItem c = state.pop(); StackItem d = state.pop(); state.push(new InstanceItem(Type.INT_TYPE, a, b, c, d)); break; } case Opcodes.FCMPL: case Opcodes.FCMPG: { StackItem x = state.pop(); StackItem y = state.pop(); state.push(new InstanceItem(Type.INT_TYPE, x, y)); break; } case Opcodes.DCMPL: case Opcodes.DCMPG:{ StackItem a = state.pop(); StackItem b = state.pop(); StackItem c = state.pop(); StackItem d = state.pop(); state.push(new InstanceItem(Type.INT_TYPE, a, b, c, d)); break; } case Opcodes.IRETURN: case Opcodes.LRETURN: case Opcodes.FRETURN: case Opcodes.DRETURN: case Opcodes.ARETURN: case Opcodes.RETURN: case Opcodes.RET: { state.reset(); break; } case Opcodes.ATHROW: { for(Label l : effectiveHandlers) { state.branch(l); } state.reset(); break; } case Opcodes.ARRAYLENGTH: { Set<StackItem> parents = new HashSet<>(); parents.addAll(state.pop().getParents()); state.push(new InstanceItem(Type.INT_TYPE, parents.toArray(new StackItem[parents.size()]))); break; } case Opcodes.MONITORENTER: case Opcodes.MONITOREXIT: { state.pop(); break; } } } @Override public void visitIincInsn(int var, int inc) { StackItem si = state.load(var); state.store(new InstanceItem(Type.INT_TYPE, si), var); super.visitIincInsn(var, inc); } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { super.visitTryCatchBlock(start, end, handler, type); Collection<Label> labels = tryCatchStart.get(start); if (labels == null) { labels = new ArrayList<>(); tryCatchStart.put(start, labels); } labels.add(handler); addToMappedCollection(tryCatchStart, start, handler); addToMappedCollection(tryCatchEnd, end, handler); handlers.add(handler); } private void addToMappedCollection(Map<Label, Collection<Label>> map, Label l, Label handler) { Collection<Label> labels = map.get(l); if (labels == null) { labels = new ArrayList<>(); map.put(l, labels); } labels.add(handler); } @Override public void visitLabel(Label label) { super.visitLabel(label); Collection<Label> labels = tryCatchStart.get(label); if (labels != null) { effectiveHandlers.addAll(labels); } else { labels = tryCatchEnd.get(label); if (labels != null) { effectiveHandlers.removeAll(labels); } else { state.join(label); if (handlers.contains(label)) { state.push(new InstanceItem(THROWABLE_TYPE)); } } } visitedLabels.add(label); } protected List<StackItem> getMethodParams(String desc, boolean isStatic) { Type[] argTypes = Type.getArgumentTypes(desc); int idx = argTypes.length - 1; List<StackItem> items = new ArrayList<>(); Iterator<StackItem> it = state.fState.stack.iterator(); while (it.hasNext() && idx >= 0) { Type t = argTypes[idx]; items.add(0, it.next()); if (t.equals(Type.LONG_TYPE) || t.equals(Type.DOUBLE_TYPE)) { it.next(); } idx--; } if (!isStatic && it.hasNext()) { items.add(0, it.next()); } return items; } }