soot.asm.AsmMethodSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of soot Show documentation
Show all versions of soot Show documentation
A Java Optimization Framework
package soot.asm;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ARRAYLENGTH;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.D2F;
import static org.objectweb.asm.Opcodes.D2I;
import static org.objectweb.asm.Opcodes.D2L;
import static org.objectweb.asm.Opcodes.DADD;
import static org.objectweb.asm.Opcodes.DALOAD;
import static org.objectweb.asm.Opcodes.DASTORE;
import static org.objectweb.asm.Opcodes.DCMPG;
import static org.objectweb.asm.Opcodes.DCMPL;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DCONST_1;
import static org.objectweb.asm.Opcodes.DDIV;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DMUL;
import static org.objectweb.asm.Opcodes.DNEG;
import static org.objectweb.asm.Opcodes.DREM;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.DSTORE;
import static org.objectweb.asm.Opcodes.DSUB;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.DUP2;
import static org.objectweb.asm.Opcodes.DUP2_X1;
import static org.objectweb.asm.Opcodes.DUP2_X2;
import static org.objectweb.asm.Opcodes.DUP_X1;
import static org.objectweb.asm.Opcodes.DUP_X2;
import static org.objectweb.asm.Opcodes.F2D;
import static org.objectweb.asm.Opcodes.F2I;
import static org.objectweb.asm.Opcodes.F2L;
import static org.objectweb.asm.Opcodes.FCMPG;
import static org.objectweb.asm.Opcodes.FCMPL;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.FCONST_2;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.I2B;
import static org.objectweb.asm.Opcodes.I2C;
import static org.objectweb.asm.Opcodes.I2D;
import static org.objectweb.asm.Opcodes.I2F;
import static org.objectweb.asm.Opcodes.I2L;
import static org.objectweb.asm.Opcodes.I2S;
import static org.objectweb.asm.Opcodes.IADD;
import static org.objectweb.asm.Opcodes.IALOAD;
import static org.objectweb.asm.Opcodes.IAND;
import static org.objectweb.asm.Opcodes.IASTORE;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_5;
import static org.objectweb.asm.Opcodes.ICONST_M1;
import static org.objectweb.asm.Opcodes.IDIV;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFGE;
import static org.objectweb.asm.Opcodes.IFGT;
import static org.objectweb.asm.Opcodes.IFLE;
import static org.objectweb.asm.Opcodes.IFLT;
import static org.objectweb.asm.Opcodes.IFNE;
import static org.objectweb.asm.Opcodes.IFNONNULL;
import static org.objectweb.asm.Opcodes.IFNULL;
import static org.objectweb.asm.Opcodes.IF_ACMPEQ;
import static org.objectweb.asm.Opcodes.IF_ACMPNE;
import static org.objectweb.asm.Opcodes.IF_ICMPEQ;
import static org.objectweb.asm.Opcodes.IF_ICMPGE;
import static org.objectweb.asm.Opcodes.IF_ICMPGT;
import static org.objectweb.asm.Opcodes.IF_ICMPLE;
import static org.objectweb.asm.Opcodes.IF_ICMPLT;
import static org.objectweb.asm.Opcodes.IF_ICMPNE;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.IMUL;
import static org.objectweb.asm.Opcodes.INEG;
import static org.objectweb.asm.Opcodes.INSTANCEOF;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.IOR;
import static org.objectweb.asm.Opcodes.IREM;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.ISHL;
import static org.objectweb.asm.Opcodes.ISHR;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.ISUB;
import static org.objectweb.asm.Opcodes.IUSHR;
import static org.objectweb.asm.Opcodes.IXOR;
import static org.objectweb.asm.Opcodes.JSR;
import static org.objectweb.asm.Opcodes.L2D;
import static org.objectweb.asm.Opcodes.L2F;
import static org.objectweb.asm.Opcodes.L2I;
import static org.objectweb.asm.Opcodes.LADD;
import static org.objectweb.asm.Opcodes.LALOAD;
import static org.objectweb.asm.Opcodes.LAND;
import static org.objectweb.asm.Opcodes.LASTORE;
import static org.objectweb.asm.Opcodes.LCMP;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LCONST_1;
import static org.objectweb.asm.Opcodes.LDIV;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LMUL;
import static org.objectweb.asm.Opcodes.LNEG;
import static org.objectweb.asm.Opcodes.LOR;
import static org.objectweb.asm.Opcodes.LREM;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.LSHL;
import static org.objectweb.asm.Opcodes.LSHR;
import static org.objectweb.asm.Opcodes.LSTORE;
import static org.objectweb.asm.Opcodes.LSUB;
import static org.objectweb.asm.Opcodes.LUSHR;
import static org.objectweb.asm.Opcodes.LXOR;
import static org.objectweb.asm.Opcodes.MONITORENTER;
import static org.objectweb.asm.Opcodes.MONITOREXIT;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.NEWARRAY;
import static org.objectweb.asm.Opcodes.NOP;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.POP2;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RET;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SALOAD;
import static org.objectweb.asm.Opcodes.SASTORE;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.SWAP;
import static org.objectweb.asm.Opcodes.T_BOOLEAN;
import static org.objectweb.asm.Opcodes.T_BYTE;
import static org.objectweb.asm.Opcodes.T_CHAR;
import static org.objectweb.asm.Opcodes.T_DOUBLE;
import static org.objectweb.asm.Opcodes.T_FLOAT;
import static org.objectweb.asm.Opcodes.T_INT;
import static org.objectweb.asm.Opcodes.T_LONG;
import static org.objectweb.asm.Opcodes.T_SHORT;
import static org.objectweb.asm.tree.AbstractInsnNode.FIELD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.FRAME;
import static org.objectweb.asm.tree.AbstractInsnNode.IINC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INT_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INVOKE_DYNAMIC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.JUMP_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LABEL;
import static org.objectweb.asm.tree.AbstractInsnNode.LDC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LINE;
import static org.objectweb.asm.tree.AbstractInsnNode.LOOKUPSWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.METHOD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.MULTIANEWARRAY_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TABLESWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TYPE_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.VAR_INSN;
import com.google.common.base.Optional;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Table;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.Handle;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.ArrayType;
import soot.Body;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.LambdaMetaFactory;
import soot.Local;
import soot.LongType;
import soot.MethodSource;
import soot.Modifier;
import soot.ModuleScene;
import soot.ModuleUtil;
import soot.PackManager;
import soot.PhaseOptions;
import soot.RefType;
import soot.Scene;
import soot.ShortType;
import soot.SootClass;
import soot.SootFieldRef;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Trap;
import soot.Type;
import soot.Unit;
import soot.UnitBox;
import soot.UnknownType;
import soot.Value;
import soot.ValueBox;
import soot.VoidType;
import soot.coffi.Util;
import soot.jimple.AddExpr;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.BinopExpr;
import soot.jimple.CastExpr;
import soot.jimple.CaughtExceptionRef;
import soot.jimple.ClassConstant;
import soot.jimple.ConditionExpr;
import soot.jimple.Constant;
import soot.jimple.DefinitionStmt;
import soot.jimple.DoubleConstant;
import soot.jimple.FieldRef;
import soot.jimple.FloatConstant;
import soot.jimple.GotoStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.InstanceFieldRef;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InstanceOfExpr;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.jimple.LongConstant;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.MethodHandle;
import soot.jimple.MethodType;
import soot.jimple.MonitorStmt;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.NopStmt;
import soot.jimple.NullConstant;
import soot.jimple.ReturnStmt;
import soot.jimple.StringConstant;
import soot.jimple.TableSwitchStmt;
import soot.jimple.ThrowStmt;
import soot.jimple.UnopExpr;
import soot.options.Options;
import soot.tagkit.LineNumberTag;
import soot.tagkit.Tag;
import soot.util.Chain;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 1997 - 2014 Raja Vallee-Rai and others
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
/**
* Generates Jimple bodies from bytecode.
*
* @author Aaloan Miftah
*/
final class AsmMethodSource implements MethodSource {
private static final Logger logger = LoggerFactory.getLogger(AsmMethodSource.class);
private static final Operand DWORD_DUMMY = new Operand(null, null);
private final String module;
private static final String METAFACTORY_SIGNATURE =
"";
private static final String ALT_METAFACTORY_SIGNATURE =
"";
/* -const fields- */
private final int maxLocals;
private final InsnList instructions;
private final List localVars;
private final List tryCatchBlocks;
private final Set inlineExceptionLabels = new LinkedHashSet();
private final Map inlineExceptionHandlers = new LinkedHashMap();
private final CastAndReturnInliner castAndReturnInliner = new CastAndReturnInliner();
/* -state fields- */
private int nextLocal;
private Map locals;
private Multimap labels;
private Map units;
private ArrayList stack;
private Map frames;
private Multimap trapHandlers;
private JimpleBody body;
private int lastLineNumber = -1;
private Table edges;
private ArrayDeque conversionWorklist;
AsmMethodSource(
int maxLocals,
InsnList insns,
List localVars,
List tryCatchBlocks,
String module) {
this.maxLocals = maxLocals;
this.instructions = insns;
this.localVars = localVars;
this.tryCatchBlocks = tryCatchBlocks;
this.module = module;
}
private StackFrame getFrame(AbstractInsnNode insn) {
StackFrame frame = frames.get(insn);
if (frame == null) {
frame = new StackFrame(this);
frames.put(insn, frame);
}
return frame;
}
private SootClass getClassFromScene(String className) {
SootClass result;
if (ModuleUtil.module_mode()) {
result = ModuleScene.v().getSootClassUnsafe(className, Optional.fromNullable(this.module));
} else {
result = Scene.v().getSootClassUnsafe(className);
}
if (result == null) {
String msg = String.format("%s was not found on classpath.", className);
if (Options.v().allow_phantom_refs()) {
RefType ref = RefType.v(className);
// make sure nobody else creates the same class
synchronized (ref) {
logger.warn(msg);
result = Scene.v().makeSootClass(className, Modifier.PUBLIC);
Scene.v().addClass(result);
result.setPhantomClass();
return ref.getSootClass();
}
} else {
throw new RuntimeException(msg);
}
}
return result;
}
private Local getLocal(int idx) {
if (idx >= maxLocals) {
throw new IllegalArgumentException("Invalid local index: " + idx);
}
Integer i = idx;
Local l = locals.get(i);
if (l == null) {
String name;
if (localVars != null) {
name = null;
for (LocalVariableNode lvn : localVars) {
if (lvn.index == idx) {
name = lvn.name;
break;
}
}
/* normally for try-catch blocks */
if (name == null) {
name = "l" + idx;
}
} else {
name = "l" + idx;
}
l = Jimple.v().newLocal(name, UnknownType.v());
locals.put(i, l);
}
return l;
}
private void push(Operand opr) {
stack.add(opr);
}
private void pushDual(Operand opr) {
stack.add(DWORD_DUMMY);
stack.add(opr);
}
private Operand peek() {
return stack.get(stack.size() - 1);
}
private void push(Type t, Operand opr) {
if (AsmUtil.isDWord(t)) {
pushDual(opr);
} else {
push(opr);
}
}
private Operand pop() {
if (stack.isEmpty()) {
throw new RuntimeException("Stack underrun");
}
return stack.remove(stack.size() - 1);
}
private Operand popDual() {
Operand o = pop();
Operand o2 = pop();
if (o2 != DWORD_DUMMY && o2 != o) {
throw new AssertionError("Not dummy operand, " + o2.value + " -- " + o.value);
}
return o;
}
private Operand pop(Type t) {
return AsmUtil.isDWord(t) ? popDual() : pop();
}
private Operand popLocal(Operand o) {
Value v = o.value;
Local l = o.stack;
if (l == null && !(v instanceof Local)) {
l = o.stack = newStackLocal();
setUnit(o.insn, Jimple.v().newAssignStmt(l, v));
o.updateBoxes();
}
return o;
}
private Operand popImmediate(Operand o) {
Value v = o.value;
Local l = o.stack;
if (l == null && !(v instanceof Local) && !(v instanceof Constant)) {
l = o.stack = newStackLocal();
setUnit(o.insn, Jimple.v().newAssignStmt(l, v));
o.updateBoxes();
}
return o;
}
private Operand popStackConst(Operand o) {
Value v = o.value;
Local l = o.stack;
if (l == null && !(v instanceof Constant)) {
l = o.stack = newStackLocal();
setUnit(o.insn, Jimple.v().newAssignStmt(l, v));
o.updateBoxes();
}
return o;
}
private Operand popLocal() {
return popLocal(pop());
}
private Operand popLocalDual() {
return popLocal(popDual());
}
@SuppressWarnings("unused")
private Operand popLocal(Type t) {
return AsmUtil.isDWord(t) ? popLocalDual() : popLocal();
}
private Operand popImmediate() {
return popImmediate(pop());
}
private Operand popImmediateDual() {
return popImmediate(popDual());
}
private Operand popImmediate(Type t) {
return AsmUtil.isDWord(t) ? popImmediateDual() : popImmediate();
}
private Operand popStackConst() {
return popStackConst(pop());
}
private Operand popStackConstDual() {
return popStackConst(popDual());
}
@SuppressWarnings("unused")
private Operand popStackConst(Type t) {
return AsmUtil.isDWord(t) ? popStackConstDual() : popStackConst();
}
void setUnit(AbstractInsnNode insn, Unit u) {
if (Options.v().keep_line_number() && lastLineNumber >= 0) {
Tag lineTag = u.getTag("LineNumberTag");
if (lineTag == null) {
lineTag = new LineNumberTag(lastLineNumber);
u.addTag(lineTag);
} else if (((LineNumberTag) lineTag).getLineNumber() != lastLineNumber) {
throw new RuntimeException("Line tag mismatch");
}
}
Unit o = units.put(insn, u);
if (o != null) {
throw new AssertionError(insn.getOpcode() + " already has a unit, " + o);
}
}
void mergeUnits(AbstractInsnNode insn, Unit u) {
Unit prev = units.put(insn, u);
if (prev != null) {
Unit merged = new UnitContainer(prev, u);
units.put(insn, merged);
}
}
Local newStackLocal() {
Integer idx = nextLocal++;
Local l = Jimple.v().newLocal("$stack" + idx, UnknownType.v());
locals.put(idx, l);
return l;
}
@SuppressWarnings("unchecked")
A getUnit(AbstractInsnNode insn) {
return (A) units.get(insn);
}
private void assignReadOps(Local l) {
if (stack.isEmpty()) {
return;
}
for (Operand opr : stack) {
if (opr == DWORD_DUMMY || opr.stack != null || (l == null && opr.value instanceof Local)) {
continue;
}
if (l != null && !opr.value.equivTo(l)) {
List uses = opr.value.getUseBoxes();
boolean noref = true;
for (ValueBox use : uses) {
Value val = use.getValue();
if (val.equivTo(l)) {
noref = false;
break;
}
}
if (noref) {
continue;
}
}
int op = opr.insn.getOpcode();
if (l == null && op != GETFIELD && op != GETSTATIC && (op < IALOAD && op > SALOAD)) {
continue;
}
Local stack = newStackLocal();
opr.stack = stack;
AssignStmt as = Jimple.v().newAssignStmt(stack, opr.value);
opr.updateBoxes();
setUnit(opr.insn, as);
}
}
private void convertGetFieldInsn(FieldInsnNode insn) {
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
Type type;
if (out == null) {
SootClass declClass = this.getClassFromScene(AsmUtil.toQualifiedName(insn.owner));
type =
AsmUtil.toJimpleType(
insn.desc,
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName));
Value val;
SootFieldRef ref;
if (insn.getOpcode() == GETSTATIC) {
ref = Scene.v().makeFieldRef(declClass, insn.name, type, true);
val = Jimple.v().newStaticFieldRef(ref);
} else {
Operand base = popLocal();
ref = Scene.v().makeFieldRef(declClass, insn.name, type, false);
InstanceFieldRef ifr = Jimple.v().newInstanceFieldRef(base.stackOrValue(), ref);
val = ifr;
base.addBox(ifr.getBaseBox());
frame.in(base);
frame.boxes(ifr.getBaseBox());
}
opr = new Operand(insn, val);
frame.out(opr);
} else {
opr = out[0];
type = opr.value().getFieldRef().type();
if (insn.getOpcode() == GETFIELD) {
frame.mergeIn(pop());
}
}
push(type, opr);
}
private void convertPutFieldInsn(FieldInsnNode insn) {
boolean instance = insn.getOpcode() == PUTFIELD;
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr, rvalue;
Type type;
if (out == null) {
SootClass declClass = this.getClassFromScene(AsmUtil.toQualifiedName(insn.owner));
type =
AsmUtil.toJimpleType(
insn.desc,
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName));
Value val;
SootFieldRef ref;
rvalue = popImmediate(type);
if (!instance) {
ref = Scene.v().makeFieldRef(declClass, insn.name, type, true);
val = Jimple.v().newStaticFieldRef(ref);
frame.in(rvalue);
} else {
Operand base = popLocal();
ref = Scene.v().makeFieldRef(declClass, insn.name, type, false);
InstanceFieldRef ifr = Jimple.v().newInstanceFieldRef(base.stackOrValue(), ref);
val = ifr;
base.addBox(ifr.getBaseBox());
frame.in(rvalue, base);
}
opr = new Operand(insn, val);
frame.out(opr);
AssignStmt as = Jimple.v().newAssignStmt(val, rvalue.stackOrValue());
rvalue.addBox(as.getRightOpBox());
if (!instance) {
frame.boxes(as.getRightOpBox());
} else {
frame.boxes(as.getRightOpBox(), ((InstanceFieldRef) val).getBaseBox());
}
setUnit(insn, as);
} else {
opr = out[0];
type = opr.value().getFieldRef().type();
rvalue = pop(type);
if (!instance) {
/* PUTSTATIC only needs one operand on the stack, the rvalue */
frame.mergeIn(rvalue);
} else {
/* PUTFIELD has a rvalue and a base */
frame.mergeIn(rvalue, pop());
}
}
/*
* in case any static field or array is read from, and the static constructor or the field this instruction writes to,
* modifies that field, write out any previous read from field/array
*/
assignReadOps(null);
}
private void convertFieldInsn(FieldInsnNode insn) {
int op = insn.getOpcode();
if (op == GETSTATIC || op == GETFIELD) {
convertGetFieldInsn(insn);
} else {
convertPutFieldInsn(insn);
}
}
private void convertIincInsn(IincInsnNode insn) {
Local local = getLocal(insn.var);
assignReadOps(local);
if (!units.containsKey(insn)) {
AddExpr add = Jimple.v().newAddExpr(local, IntConstant.v(insn.incr));
setUnit(insn, Jimple.v().newAssignStmt(local, add));
}
}
private void convertConstInsn(InsnNode insn) {
int op = insn.getOpcode();
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Value v;
if (op == ACONST_NULL) {
v = NullConstant.v();
} else if (op >= ICONST_M1 && op <= ICONST_5) {
v = IntConstant.v(op - ICONST_0);
} else if (op == LCONST_0 || op == LCONST_1) {
v = LongConstant.v(op - LCONST_0);
} else if (op >= FCONST_0 && op <= FCONST_2) {
v = FloatConstant.v(op - FCONST_0);
} else if (op == DCONST_0 || op == DCONST_1) {
v = DoubleConstant.v(op - DCONST_0);
} else {
throw new AssertionError("Unknown constant opcode: " + op);
}
opr = new Operand(insn, v);
frame.out(opr);
} else {
opr = out[0];
}
if (op == LCONST_0 || op == LCONST_1 || op == DCONST_0 || op == DCONST_1) {
pushDual(opr);
} else {
push(opr);
}
}
/*
* Following version is more complex, using stack frames as opposed to simply swapping
*/
/*
* StackFrame frame = getFrame(insn); Operand[] out = frame.out(); Operand dup, dup2 = null, dupd, dupd2 = null; if (out ==
* null) { dupd = popImmediate(); dup = new Operand(insn, dupd.stackOrValue()); if (dword) { dupd2 = peek(); if (dupd2 ==
* DWORD_DUMMY) { pop(); dupd2 = dupd; } else { dupd2 = popImmediate(); } dup2 = new Operand(insn, dupd2.stackOrValue());
* frame.out(dup, dup2); frame.in(dupd, dupd2); } else { frame.out(dup); frame.in(dupd); } } else { dupd = pop(); dup =
* out[0]; if (dword) { dupd2 = pop(); if (dupd2 == DWORD_DUMMY) dupd2 = dupd; dup2 = out[1]; frame.mergeIn(dupd, dupd2); }
* else { frame.mergeIn(dupd); } }
*/
private void convertArrayLoadInsn(InsnNode insn) {
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Operand indx = popImmediate();
Operand base = popImmediate();
ArrayRef ar = Jimple.v().newArrayRef(base.stackOrValue(), indx.stackOrValue());
indx.addBox(ar.getIndexBox());
base.addBox(ar.getBaseBox());
opr = new Operand(insn, ar);
frame.in(indx, base);
frame.boxes(ar.getIndexBox(), ar.getBaseBox());
frame.out(opr);
} else {
opr = out[0];
frame.mergeIn(pop(), pop());
}
int op = insn.getOpcode();
if (op == DALOAD || op == LALOAD) {
pushDual(opr);
} else {
push(opr);
}
}
private void convertArrayStoreInsn(InsnNode insn) {
int op = insn.getOpcode();
boolean dword = op == LASTORE || op == DASTORE;
StackFrame frame = getFrame(insn);
if (!units.containsKey(insn)) {
Operand valu = dword ? popImmediateDual() : popImmediate();
Operand indx = popImmediate();
Operand base = popLocal();
ArrayRef ar = Jimple.v().newArrayRef(base.stackOrValue(), indx.stackOrValue());
indx.addBox(ar.getIndexBox());
base.addBox(ar.getBaseBox());
AssignStmt as = Jimple.v().newAssignStmt(ar, valu.stackOrValue());
valu.addBox(as.getRightOpBox());
frame.in(valu, indx, base);
frame.boxes(as.getRightOpBox(), ar.getIndexBox(), ar.getBaseBox());
setUnit(insn, as);
} else {
frame.mergeIn(dword ? popDual() : pop(), pop(), pop());
}
}
private void convertDupInsn(InsnNode insn) {
int op = insn.getOpcode();
// Get the top stack value which we need in either case
Operand dupd = popImmediate();
Operand dupd2 = null;
// Some instructions allow operands that take two registers
boolean dword = op == DUP2 || op == DUP2_X1 || op == DUP2_X2;
if (dword) {
if (peek() == DWORD_DUMMY) {
pop();
dupd2 = dupd;
} else {
dupd2 = popImmediate();
}
}
if (op == DUP) {
// val -> val, val
push(dupd);
push(dupd);
} else if (op == DUP_X1) {
// val2, val1 -> val1, val2, val1
// value1, value2 must not be of type double or long
Operand o2 = popImmediate();
push(dupd);
push(o2);
push(dupd);
} else if (op == DUP_X2) {
// value3, value2, value1 -> value1, value3, value2, value1
Operand o2 = popImmediate();
Operand o3 = peek() == DWORD_DUMMY ? pop() : popImmediate();
push(dupd);
push(o3);
push(o2);
push(dupd);
} else if (op == DUP2) {
// value2, value1 -> value2, value1, value2, value1
push(dupd2);
push(dupd);
push(dupd2);
push(dupd);
} else if (op == DUP2_X1) {
// value3, value2, value1 -> value2, value1, value3, value2, value1
// Attention: value2 may be
Operand o2 = popImmediate();
push(dupd2);
push(dupd);
push(o2);
push(dupd2);
push(dupd);
} else if (op == DUP2_X2) {
// (value4, value3), (value2, value1) -> (value2, value1), (value4, value3), (value2, value1)
Operand o2 = popImmediate();
Operand o2h = peek() == DWORD_DUMMY ? pop() : popImmediate();
push(dupd2);
push(dupd);
push(o2h);
push(o2);
push(dupd2);
push(dupd);
}
}
private void convertBinopInsn(InsnNode insn) {
int op = insn.getOpcode();
boolean dword =
op == DADD
|| op == LADD
|| op == DSUB
|| op == LSUB
|| op == DMUL
|| op == LMUL
|| op == DDIV
|| op == LDIV
|| op == DREM
|| op == LREM
|| op == LSHL
|| op == LSHR
|| op == LUSHR
|| op == LAND
|| op == LOR
|| op == LXOR
|| op == LCMP
|| op == DCMPL
|| op == DCMPG;
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Operand op2 =
(dword && op != LSHL && op != LSHR && op != LUSHR) ? popImmediateDual() : popImmediate();
Operand op1 = dword ? popImmediateDual() : popImmediate();
Value v1 = op1.stackOrValue();
Value v2 = op2.stackOrValue();
BinopExpr binop;
if (op >= IADD && op <= DADD) {
binop = Jimple.v().newAddExpr(v1, v2);
} else if (op >= ISUB && op <= DSUB) {
binop = Jimple.v().newSubExpr(v1, v2);
} else if (op >= IMUL && op <= DMUL) {
binop = Jimple.v().newMulExpr(v1, v2);
} else if (op >= IDIV && op <= DDIV) {
binop = Jimple.v().newDivExpr(v1, v2);
} else if (op >= IREM && op <= DREM) {
binop = Jimple.v().newRemExpr(v1, v2);
} else if (op >= ISHL && op <= LSHL) {
binop = Jimple.v().newShlExpr(v1, v2);
} else if (op >= ISHR && op <= LSHR) {
binop = Jimple.v().newShrExpr(v1, v2);
} else if (op >= IUSHR && op <= LUSHR) {
binop = Jimple.v().newUshrExpr(v1, v2);
} else if (op >= IAND && op <= LAND) {
binop = Jimple.v().newAndExpr(v1, v2);
} else if (op >= IOR && op <= LOR) {
binop = Jimple.v().newOrExpr(v1, v2);
} else if (op >= IXOR && op <= LXOR) {
binop = Jimple.v().newXorExpr(v1, v2);
} else if (op == LCMP) {
binop = Jimple.v().newCmpExpr(v1, v2);
} else if (op == FCMPL || op == DCMPL) {
binop = Jimple.v().newCmplExpr(v1, v2);
} else if (op == FCMPG || op == DCMPG) {
binop = Jimple.v().newCmpgExpr(v1, v2);
} else {
throw new AssertionError("Unknown binop: " + op);
}
op1.addBox(binop.getOp1Box());
op2.addBox(binop.getOp2Box());
opr = new Operand(insn, binop);
frame.in(op2, op1);
frame.boxes(binop.getOp2Box(), binop.getOp1Box());
frame.out(opr);
} else {
opr = out[0];
if (dword) {
if (op != LSHL && op != LSHR && op != LUSHR) {
frame.mergeIn(popDual(), popDual());
} else {
frame.mergeIn(pop(), popDual());
}
} else {
frame.mergeIn(pop(), pop());
}
}
if (dword && (op < LCMP || op > DCMPG)) {
pushDual(opr);
} else {
push(opr);
}
}
private void convertUnopInsn(InsnNode insn) {
int op = insn.getOpcode();
boolean dword = op == LNEG || op == DNEG;
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Operand op1 = dword ? popImmediateDual() : popImmediate();
Value v1 = op1.stackOrValue();
UnopExpr unop;
if (op >= INEG && op <= DNEG) {
unop = Jimple.v().newNegExpr(v1);
} else if (op == ARRAYLENGTH) {
unop = Jimple.v().newLengthExpr(v1);
} else {
throw new AssertionError("Unknown unop: " + op);
}
op1.addBox(unop.getOpBox());
opr = new Operand(insn, unop);
frame.in(op1);
frame.boxes(unop.getOpBox());
frame.out(opr);
} else {
opr = out[0];
frame.mergeIn(dword ? popDual() : pop());
}
if (dword) {
pushDual(opr);
} else {
push(opr);
}
}
private void convertPrimCastInsn(InsnNode insn) {
int op = insn.getOpcode();
boolean tod = op == I2L || op == I2D || op == F2L || op == F2D || op == D2L || op == L2D;
boolean fromd = op == D2L || op == L2D || op == D2I || op == L2I || op == D2F || op == L2F;
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Type totype;
if (op == I2L || op == F2L || op == D2L) {
totype = LongType.v();
} else if (op == L2I || op == F2I || op == D2I) {
totype = IntType.v();
} else if (op == I2F || op == L2F || op == D2F) {
totype = FloatType.v();
} else if (op == I2D || op == L2D || op == F2D) {
totype = DoubleType.v();
} else if (op == I2B) {
totype = ByteType.v();
} else if (op == I2S) {
totype = ShortType.v();
} else if (op == I2C) {
totype = CharType.v();
} else {
throw new AssertionError("Unknonw prim cast op: " + op);
}
Operand val = fromd ? popImmediateDual() : popImmediate();
CastExpr cast = Jimple.v().newCastExpr(val.stackOrValue(), totype);
opr = new Operand(insn, cast);
val.addBox(cast.getOpBox());
frame.in(val);
frame.boxes(cast.getOpBox());
frame.out(opr);
} else {
opr = out[0];
frame.mergeIn(fromd ? popDual() : pop());
}
if (tod) {
pushDual(opr);
} else {
push(opr);
}
}
private void convertReturnInsn(InsnNode insn) {
int op = insn.getOpcode();
boolean dword = op == LRETURN || op == DRETURN;
StackFrame frame = getFrame(insn);
if (!units.containsKey(insn)) {
Operand val = dword ? popImmediateDual() : popImmediate();
ReturnStmt ret = Jimple.v().newReturnStmt(val.stackOrValue());
val.addBox(ret.getOpBox());
frame.in(val);
frame.boxes(ret.getOpBox());
setUnit(insn, ret);
} else {
frame.mergeIn(dword ? popDual() : pop());
}
}
private void convertInsn(InsnNode insn) {
int op = insn.getOpcode();
if (op == NOP) {
/*
* We can ignore NOP instructions, but for completeness, we handle them
*/
if (!units.containsKey(insn)) {
units.put(insn, Jimple.v().newNopStmt());
}
} else if (op >= ACONST_NULL && op <= DCONST_1) {
convertConstInsn(insn);
} else if (op >= IALOAD && op <= SALOAD) {
convertArrayLoadInsn(insn);
} else if (op >= IASTORE && op <= SASTORE) {
convertArrayStoreInsn(insn);
} else if (op == POP) {
popImmediate();
} else if (op == POP2) {
popImmediate();
if (peek() == DWORD_DUMMY) {
pop();
} else {
popImmediate();
}
} else if (op >= DUP && op <= DUP2_X2) {
convertDupInsn(insn);
} else if (op == SWAP) {
Operand o1 = popImmediate();
Operand o2 = popImmediate();
push(o1);
push(o2);
} else if ((op >= IADD && op <= DREM)
|| (op >= ISHL && op <= LXOR)
|| (op >= LCMP && op <= DCMPG)) {
convertBinopInsn(insn);
} else if ((op >= INEG && op <= DNEG) || op == ARRAYLENGTH) {
convertUnopInsn(insn);
} else if (op >= I2L && op <= I2S) {
convertPrimCastInsn(insn);
} else if (op >= IRETURN && op <= ARETURN) {
convertReturnInsn(insn);
} else if (op == RETURN) {
if (!units.containsKey(insn)) {
setUnit(insn, Jimple.v().newReturnVoidStmt());
}
} else if (op == ATHROW) {
StackFrame frame = getFrame(insn);
Operand opr;
if (!units.containsKey(insn)) {
opr = popImmediate();
ThrowStmt ts = Jimple.v().newThrowStmt(opr.stackOrValue());
opr.addBox(ts.getOpBox());
frame.in(opr);
frame.out(opr);
frame.boxes(ts.getOpBox());
setUnit(insn, ts);
} else {
opr = pop();
frame.mergeIn(opr);
}
push(opr);
} else if (op == MONITORENTER || op == MONITOREXIT) {
StackFrame frame = getFrame(insn);
if (!units.containsKey(insn)) {
Operand opr = popStackConst();
MonitorStmt ts =
op == MONITORENTER
? Jimple.v().newEnterMonitorStmt(opr.stackOrValue())
: Jimple.v().newExitMonitorStmt(opr.stackOrValue());
opr.addBox(ts.getOpBox());
frame.in(opr);
frame.boxes(ts.getOpBox());
setUnit(insn, ts);
} else {
frame.mergeIn(pop());
}
} else {
throw new AssertionError("Unknown insn op: " + op);
}
}
private void convertIntInsn(IntInsnNode insn) {
int op = insn.getOpcode();
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Value v;
if (op == BIPUSH || op == SIPUSH) {
v = IntConstant.v(insn.operand);
} else {
Type type;
switch (insn.operand) {
case T_BOOLEAN:
type = BooleanType.v();
break;
case T_CHAR:
type = CharType.v();
break;
case T_FLOAT:
type = FloatType.v();
break;
case T_DOUBLE:
type = DoubleType.v();
break;
case T_BYTE:
type = ByteType.v();
break;
case T_SHORT:
type = ShortType.v();
break;
case T_INT:
type = IntType.v();
break;
case T_LONG:
type = LongType.v();
break;
default:
throw new AssertionError("Unknown NEWARRAY type!");
}
Operand size = popImmediate();
NewArrayExpr anew = Jimple.v().newNewArrayExpr(type, size.stackOrValue());
size.addBox(anew.getSizeBox());
frame.in(size);
frame.boxes(anew.getSizeBox());
v = anew;
}
opr = new Operand(insn, v);
frame.out(opr);
} else {
opr = out[0];
if (op == NEWARRAY) {
frame.mergeIn(pop());
}
}
push(opr);
}
private void convertJumpInsn(JumpInsnNode insn) {
int op = insn.getOpcode();
if (op == GOTO) {
if (!units.containsKey(insn)) {
UnitBox box = Jimple.v().newStmtBox(null);
labels.put(insn.label, box);
setUnit(insn, Jimple.v().newGotoStmt(box));
}
return;
}
/* must be ifX insn */
StackFrame frame = getFrame(insn);
if (!units.containsKey(insn)) {
Operand val = popImmediate();
Value v = val.stackOrValue();
ConditionExpr cond;
if (op >= IF_ICMPEQ && op <= IF_ACMPNE) {
Operand val1 = popImmediate();
Value v1 = val1.stackOrValue();
if (op == IF_ICMPEQ) {
cond = Jimple.v().newEqExpr(v1, v);
} else if (op == IF_ICMPNE) {
cond = Jimple.v().newNeExpr(v1, v);
} else if (op == IF_ICMPLT) {
cond = Jimple.v().newLtExpr(v1, v);
} else if (op == IF_ICMPGE) {
cond = Jimple.v().newGeExpr(v1, v);
} else if (op == IF_ICMPGT) {
cond = Jimple.v().newGtExpr(v1, v);
} else if (op == IF_ICMPLE) {
cond = Jimple.v().newLeExpr(v1, v);
} else if (op == IF_ACMPEQ) {
cond = Jimple.v().newEqExpr(v1, v);
} else if (op == IF_ACMPNE) {
cond = Jimple.v().newNeExpr(v1, v);
} else {
throw new AssertionError("Unknown if op: " + op);
}
val1.addBox(cond.getOp1Box());
val.addBox(cond.getOp2Box());
frame.boxes(cond.getOp2Box(), cond.getOp1Box());
frame.in(val, val1);
} else {
if (op == IFEQ) {
cond = Jimple.v().newEqExpr(v, IntConstant.v(0));
} else if (op == IFNE) {
cond = Jimple.v().newNeExpr(v, IntConstant.v(0));
} else if (op == IFLT) {
cond = Jimple.v().newLtExpr(v, IntConstant.v(0));
} else if (op == IFGE) {
cond = Jimple.v().newGeExpr(v, IntConstant.v(0));
} else if (op == IFGT) {
cond = Jimple.v().newGtExpr(v, IntConstant.v(0));
} else if (op == IFLE) {
cond = Jimple.v().newLeExpr(v, IntConstant.v(0));
} else if (op == IFNULL) {
cond = Jimple.v().newEqExpr(v, NullConstant.v());
} else if (op == IFNONNULL) {
cond = Jimple.v().newNeExpr(v, NullConstant.v());
} else {
throw new AssertionError("Unknown if op: " + op);
}
val.addBox(cond.getOp1Box());
frame.boxes(cond.getOp1Box());
frame.in(val);
}
UnitBox box = Jimple.v().newStmtBox(null);
labels.put(insn.label, box);
setUnit(insn, Jimple.v().newIfStmt(cond, box));
} else {
if (op >= IF_ICMPEQ && op <= IF_ACMPNE) {
frame.mergeIn(pop(), pop());
} else {
frame.mergeIn(pop());
}
}
}
private void convertLdcInsn(LdcInsnNode insn) {
Object val = insn.cst;
boolean dword = val instanceof Long || val instanceof Double;
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Value v = toSootValue(val);
opr = new Operand(insn, v);
frame.out(opr);
} else {
opr = out[0];
}
if (dword) {
pushDual(opr);
} else {
push(opr);
}
}
private Value toSootValue(Object val) throws AssertionError {
Value v;
if (val instanceof Integer) {
v = IntConstant.v((Integer) val);
} else if (val instanceof Float) {
v = FloatConstant.v((Float) val);
} else if (val instanceof Long) {
v = LongConstant.v((Long) val);
} else if (val instanceof Double) {
v = DoubleConstant.v((Double) val);
} else if (val instanceof String) {
v = StringConstant.v(val.toString());
} else if (val instanceof org.objectweb.asm.Type) {
org.objectweb.asm.Type t = (org.objectweb.asm.Type) val;
if (t.getSort() == org.objectweb.asm.Type.METHOD) {
List paramTypes =
AsmUtil.toJimpleDesc(
((org.objectweb.asm.Type) val).getDescriptor(),
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName));
Type returnType = paramTypes.remove(paramTypes.size() - 1);
v = MethodType.v(paramTypes, returnType);
} else {
v = ClassConstant.v(((org.objectweb.asm.Type) val).getDescriptor());
}
} else if (val instanceof Handle) {
Handle h = (Handle) val;
if (MethodHandle.isMethodRef(h.getTag())) {
v = MethodHandle.v(toSootMethodRef((Handle) val), ((Handle) val).getTag());
} else {
v = MethodHandle.v(toSootFieldRef((Handle) val), ((Handle) val).getTag());
}
} else {
throw new AssertionError("Unknown constant type: " + val.getClass());
}
return v;
}
private void convertLookupSwitchInsn(LookupSwitchInsnNode insn) {
StackFrame frame = getFrame(insn);
if (units.containsKey(insn)) {
frame.mergeIn(pop());
return;
}
Operand key = popImmediate();
UnitBox dflt = Jimple.v().newStmtBox(null);
List targets = new ArrayList(insn.labels.size());
labels.put(insn.dflt, dflt);
for (LabelNode ln : insn.labels) {
UnitBox box = Jimple.v().newStmtBox(null);
targets.add(box);
labels.put(ln, box);
}
List keys = new ArrayList(insn.keys.size());
for (Integer i : insn.keys) {
keys.add(IntConstant.v(i));
}
LookupSwitchStmt lss = Jimple.v().newLookupSwitchStmt(key.stackOrValue(), keys, targets, dflt);
key.addBox(lss.getKeyBox());
frame.in(key);
frame.boxes(lss.getKeyBox());
setUnit(insn, lss);
}
private void convertMethodInsn(MethodInsnNode insn) {
int op = insn.getOpcode();
boolean instance = op != INVOKESTATIC;
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
Type returnType;
if (out == null) {
String clsName = AsmUtil.toQualifiedName(insn.owner);
if (clsName.charAt(0) == '[') {
clsName = "java.lang.Object";
}
SootClass cls = this.getClassFromScene(clsName);
List sigTypes =
AsmUtil.toJimpleDesc(
insn.desc,
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName));
returnType = sigTypes.remove(sigTypes.size() - 1);
SootMethodRef ref = Scene.v().makeMethodRef(cls, insn.name, sigTypes, returnType, !instance);
int nrArgs = sigTypes.size();
final Operand[] args;
List argList = Collections.emptyList();
if (!instance) {
args = nrArgs == 0 ? null : new Operand[nrArgs];
if (args != null) {
argList = new ArrayList(nrArgs);
}
} else {
args = new Operand[nrArgs + 1];
if (nrArgs != 0) {
argList = new ArrayList(nrArgs);
}
}
while (nrArgs-- != 0) {
args[nrArgs] = popImmediate(sigTypes.get(nrArgs));
argList.add(args[nrArgs].stackOrValue());
}
if (argList.size() > 1) {
Collections.reverse(argList);
}
if (instance) {
args[args.length - 1] = popLocal();
}
ValueBox[] boxes = args == null ? null : new ValueBox[args.length];
InvokeExpr invoke;
if (!instance) {
invoke = Jimple.v().newStaticInvokeExpr(ref, argList);
} else {
Local base = (Local) args[args.length - 1].stackOrValue();
InstanceInvokeExpr iinvoke;
if (op == INVOKESPECIAL) {
iinvoke = Jimple.v().newSpecialInvokeExpr(base, ref, argList);
} else if (op == INVOKEVIRTUAL) {
iinvoke = Jimple.v().newVirtualInvokeExpr(base, ref, argList);
} else if (op == INVOKEINTERFACE) {
iinvoke = Jimple.v().newInterfaceInvokeExpr(base, ref, argList);
} else {
throw new AssertionError("Unknown invoke op:" + op);
}
boxes[boxes.length - 1] = iinvoke.getBaseBox();
args[args.length - 1].addBox(boxes[boxes.length - 1]);
invoke = iinvoke;
}
if (boxes != null) {
for (int i = 0; i != sigTypes.size(); i++) {
boxes[i] = invoke.getArgBox(i);
args[i].addBox(boxes[i]);
}
frame.boxes(boxes);
frame.in(args);
}
opr = new Operand(insn, invoke);
frame.out(opr);
} else {
opr = out[0];
InvokeExpr expr = (InvokeExpr) opr.value;
List types = expr.getMethodRef().getParameterTypes();
Operand[] oprs;
int nrArgs = types.size();
if (expr.getMethodRef().isStatic()) {
oprs = nrArgs == 0 ? null : new Operand[nrArgs];
} else {
oprs = new Operand[nrArgs + 1];
}
if (oprs != null) {
while (nrArgs-- != 0) {
oprs[nrArgs] = pop(types.get(nrArgs));
}
if (!expr.getMethodRef().isStatic()) {
oprs[oprs.length - 1] = pop();
}
frame.mergeIn(oprs);
nrArgs = types.size();
}
returnType = expr.getMethodRef().getReturnType();
}
if (AsmUtil.isDWord(returnType)) {
pushDual(opr);
} else if (!(returnType instanceof VoidType)) {
push(opr);
} else if (!units.containsKey(insn)) {
setUnit(insn, Jimple.v().newInvokeStmt(opr.value));
}
/*
* assign all read ops in case the method modifies any of the fields
*/
assignReadOps(null);
}
private void convertInvokeDynamicInsn(InvokeDynamicInsnNode insn) {
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
Type returnType;
if (out == null) {
// convert info on bootstrap method
SootMethodRef bsmMethodRef = toSootMethodRef(insn.bsm);
List bsmMethodArgs = new ArrayList(insn.bsmArgs.length);
for (Object bsmArg : insn.bsmArgs) {
bsmMethodArgs.add(toSootValue(bsmArg));
}
// create ref to actual method
SootClass bclass = Scene.v().getSootClass(SootClass.INVOKEDYNAMIC_DUMMY_CLASS_NAME);
// Generate parameters & returnType & parameterTypes
Type[] types = Util.v().jimpleTypesOfFieldOrMethodDescriptor(insn.desc);
int nrArgs = types.length - 1;
List parameterTypes = new ArrayList(nrArgs);
List methodArgs = new ArrayList(nrArgs);
Operand[] args = new Operand[nrArgs];
ValueBox[] boxes = new ValueBox[nrArgs];
// Beware: Call stack is FIFO, Jimple is linear
while (nrArgs-- != 0) {
parameterTypes.add(types[nrArgs]);
args[nrArgs] = popImmediate(types[nrArgs]);
methodArgs.add(args[nrArgs].stackOrValue());
}
if (methodArgs.size() > 1) {
Collections.reverse(methodArgs); // Call stack is FIFO, Jimple is linear
Collections.reverse(parameterTypes);
}
returnType = types[types.length - 1];
SootMethodRef bootstrap_model = null;
if (PhaseOptions.getBoolean(
PhaseOptions.v().getPhaseOptions("jb"), "model-lambdametafactory")) {
String bsmMethodRefStr = bsmMethodRef.toString();
if (bsmMethodRefStr.equals(METAFACTORY_SIGNATURE)
|| bsmMethodRefStr.equals(ALT_METAFACTORY_SIGNATURE)) {
SootClass enclosingClass = body.getMethod().getDeclaringClass();
bootstrap_model =
LambdaMetaFactory.v()
.makeLambdaHelper(
bsmMethodArgs, insn.bsm.getTag(), insn.name, types, enclosingClass);
}
}
InvokeExpr indy;
if (bootstrap_model != null) {
indy = Jimple.v().newStaticInvokeExpr(bootstrap_model, methodArgs);
} else {
// if not mimicking the LambdaMetaFactory, we model invokeDynamic method refs as static
// method references
// of methods on the type SootClass.INVOKEDYNAMIC_DUMMY_CLASS_NAME
SootMethodRef methodRef =
Scene.v().makeMethodRef(bclass, insn.name, parameterTypes, returnType, true);
indy =
Jimple.v()
.newDynamicInvokeExpr(
bsmMethodRef, bsmMethodArgs, methodRef, insn.bsm.getTag(), methodArgs);
}
if (boxes != null) {
for (int i = 0; i < types.length - 1; i++) {
boxes[i] = indy.getArgBox(i);
args[i].addBox(boxes[i]);
}
frame.boxes(boxes);
frame.in(args);
}
opr = new Operand(insn, indy);
frame.out(opr);
} else {
opr = out[0];
InvokeExpr expr = (InvokeExpr) opr.value;
List types = expr.getMethodRef().getParameterTypes();
Operand[] oprs;
int nrArgs = types.size();
if (expr.getMethodRef().isStatic()) {
oprs = nrArgs == 0 ? null : new Operand[nrArgs];
} else {
oprs = new Operand[nrArgs + 1];
}
if (oprs != null) {
while (nrArgs-- != 0) {
oprs[nrArgs] = pop(types.get(nrArgs));
}
if (!expr.getMethodRef().isStatic()) {
oprs[oprs.length - 1] = pop();
}
frame.mergeIn(oprs);
nrArgs = types.size();
}
returnType = expr.getMethodRef().getReturnType();
}
if (AsmUtil.isDWord(returnType)) {
pushDual(opr);
} else if (!(returnType instanceof VoidType)) {
push(opr);
} else if (!units.containsKey(insn)) {
setUnit(insn, Jimple.v().newInvokeStmt(opr.value));
}
/*
* assign all read ops in case the method modifies any of the fields
*/
assignReadOps(null);
}
private SootMethodRef toSootMethodRef(Handle methodHandle) {
String bsmClsName = AsmUtil.toQualifiedName(methodHandle.getOwner());
SootClass bsmCls = this.getClassFromScene(bsmClsName);
List bsmSigTypes =
AsmUtil.toJimpleDesc(
methodHandle.getDesc(),
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName));
Type returnType = bsmSigTypes.remove(bsmSigTypes.size() - 1);
return Scene.v()
.makeMethodRef(
bsmCls,
methodHandle.getName(),
bsmSigTypes,
returnType,
methodHandle.getTag() == MethodHandle.Kind.REF_INVOKE_STATIC.getValue());
}
private SootFieldRef toSootFieldRef(Handle methodHandle) {
String bsmClsName = AsmUtil.toQualifiedName(methodHandle.getOwner());
SootClass bsmCls = Scene.v().getSootClass(bsmClsName);
Type t =
AsmUtil.toJimpleDesc(
methodHandle.getDesc(),
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName))
.get(0);
int kind = methodHandle.getTag();
return Scene.v()
.makeFieldRef(
bsmCls,
methodHandle.getName(),
t,
kind == MethodHandle.Kind.REF_GET_FIELD_STATIC.getValue()
|| kind == MethodHandle.Kind.REF_PUT_FIELD_STATIC.getValue());
}
private void convertMultiANewArrayInsn(MultiANewArrayInsnNode insn) {
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
ArrayType t =
(ArrayType)
AsmUtil.toJimpleType(
insn.desc,
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName));
int dims = insn.dims;
Operand[] sizes = new Operand[dims];
Value[] sizeVals = new Value[dims];
ValueBox[] boxes = new ValueBox[dims];
while (dims-- != 0) {
sizes[dims] = popImmediate();
sizeVals[dims] = sizes[dims].stackOrValue();
}
NewMultiArrayExpr nm = Jimple.v().newNewMultiArrayExpr(t, Arrays.asList(sizeVals));
for (int i = 0; i != boxes.length; i++) {
ValueBox vb = nm.getSizeBox(i);
sizes[i].addBox(vb);
boxes[i] = vb;
}
frame.boxes(boxes);
frame.in(sizes);
opr = new Operand(insn, nm);
frame.out(opr);
} else {
opr = out[0];
int dims = insn.dims;
Operand[] sizes = new Operand[dims];
while (dims-- != 0) {
sizes[dims] = pop();
}
frame.mergeIn(sizes);
}
push(opr);
}
private void convertTableSwitchInsn(TableSwitchInsnNode insn) {
StackFrame frame = getFrame(insn);
if (units.containsKey(insn)) {
frame.mergeIn(pop());
return;
}
Operand key = popImmediate();
UnitBox dflt = Jimple.v().newStmtBox(null);
List targets = new ArrayList(insn.labels.size());
labels.put(insn.dflt, dflt);
for (LabelNode ln : insn.labels) {
UnitBox box = Jimple.v().newStmtBox(null);
targets.add(box);
labels.put(ln, box);
}
TableSwitchStmt tss =
Jimple.v().newTableSwitchStmt(key.stackOrValue(), insn.min, insn.max, targets, dflt);
key.addBox(tss.getKeyBox());
frame.in(key);
frame.boxes(tss.getKeyBox());
setUnit(insn, tss);
}
private void convertTypeInsn(TypeInsnNode insn) {
int op = insn.getOpcode();
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
Type t =
AsmUtil.toJimpleRefType(
insn.desc,
Optional.fromNullable(this.body.getMethod().getDeclaringClass().moduleName));
Value val;
if (op == NEW) {
val = Jimple.v().newNewExpr((RefType) t);
} else {
Operand op1 = popImmediate();
Value v1 = op1.stackOrValue();
ValueBox vb;
if (op == ANEWARRAY) {
NewArrayExpr expr = Jimple.v().newNewArrayExpr(t, v1);
vb = expr.getSizeBox();
val = expr;
} else if (op == CHECKCAST) {
CastExpr expr = Jimple.v().newCastExpr(v1, t);
vb = expr.getOpBox();
val = expr;
} else if (op == INSTANCEOF) {
InstanceOfExpr expr = Jimple.v().newInstanceOfExpr(v1, t);
vb = expr.getOpBox();
val = expr;
} else {
throw new AssertionError("Unknown type op: " + op);
}
op1.addBox(vb);
frame.in(op1);
frame.boxes(vb);
}
opr = new Operand(insn, val);
frame.out(opr);
} else {
opr = out[0];
if (op != NEW) {
frame.mergeIn(pop());
}
}
push(opr);
}
private void convertVarLoadInsn(VarInsnNode insn) {
int op = insn.getOpcode();
boolean dword = op == LLOAD || op == DLOAD;
StackFrame frame = getFrame(insn);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
opr = new Operand(insn, getLocal(insn.var));
frame.out(opr);
} else {
opr = out[0];
}
if (dword) {
pushDual(opr);
} else {
push(opr);
}
}
private void convertVarStoreInsn(VarInsnNode insn) {
int op = insn.getOpcode();
boolean dword = op == LSTORE || op == DSTORE;
StackFrame frame = getFrame(insn);
Operand opr = dword ? popDual() : pop();
Local local = getLocal(insn.var);
if (!units.containsKey(insn)) {
DefinitionStmt as = Jimple.v().newAssignStmt(local, opr.stackOrValue());
opr.addBox(as.getRightOpBox());
frame.boxes(as.getRightOpBox());
frame.in(opr);
setUnit(insn, as);
} else {
frame.mergeIn(opr);
}
assignReadOps(local);
}
private void convertVarInsn(VarInsnNode insn) {
int op = insn.getOpcode();
if (op >= ILOAD && op <= ALOAD) {
convertVarLoadInsn(insn);
} else if (op >= ISTORE && op <= ASTORE) {
convertVarStoreInsn(insn);
} else if (op == RET) {
/* we handle it, even thought it should be removed */
if (!units.containsKey(insn)) {
setUnit(insn, Jimple.v().newRetStmt(getLocal(insn.var)));
}
} else {
throw new AssertionError("Unknown var op: " + op);
}
}
/* Conversion */
private void convertLabel(LabelNode ln) {
if (!trapHandlers.containsKey(ln)) {
return;
}
// We create a nop statement as a placeholder so that we can jump
// somewhere from the real exception handler in case this is inline
// code
if (inlineExceptionLabels.contains(ln)) {
if (!units.containsKey(ln)) {
NopStmt nop = Jimple.v().newNopStmt();
setUnit(ln, nop);
}
return;
}
StackFrame frame = getFrame(ln);
Operand[] out = frame.out();
Operand opr;
if (out == null) {
CaughtExceptionRef ref = Jimple.v().newCaughtExceptionRef();
Local stack = newStackLocal();
DefinitionStmt as = Jimple.v().newIdentityStmt(stack, ref);
opr = new Operand(ln, ref);
opr.stack = stack;
frame.out(opr);
setUnit(ln, as);
} else {
opr = out[0];
}
push(opr);
}
private void convertLine(LineNumberNode ln) {
lastLineNumber = ln.line;
}
private void addEdges(AbstractInsnNode cur, AbstractInsnNode tgt1, List tgts) {
int lastIdx = tgts == null ? -1 : tgts.size() - 1;
Operand[] stackss = (new ArrayList(stack)).toArray(new Operand[stack.size()]);
AbstractInsnNode tgt = tgt1;
int i = 0;
tgt_loop:
do {
Edge edge = edges.get(cur, tgt);
if (edge == null) {
// make sure to store last line number to stay sound if the branch that comes later in
// bytecode is processed first
edge = new Edge(tgt, lastLineNumber);
edge.prevStacks.add(stackss);
edges.put(cur, tgt, edge);
conversionWorklist.add(edge);
continue;
}
if (edge.stack != null) {
ArrayList stackTemp = edge.stack;
if (stackTemp.size() != stackss.length) {
throw new AssertionError("Multiple un-equal stacks!");
}
for (int j = 0; j != stackss.length; j++) {
if (!stackTemp.get(j).equivTo(stackss[j])) {
throw new AssertionError("Multiple un-equal stacks!");
}
}
continue;
}
for (Operand[] ps : edge.prevStacks) {
if (Arrays.equals(ps, stackss)) {
continue tgt_loop;
}
}
edge.stack = new ArrayList(stack);
edge.prevStacks.add(stackss);
conversionWorklist.add(edge);
} while (i <= lastIdx && (tgt = tgts.get(i++)) != null);
}
private void convert() {
ArrayDeque worklist = new ArrayDeque();
for (LabelNode ln : trapHandlers.keySet()) {
if (checkInlineExceptionHandler(ln)) {
handleInlineExceptionHandler(ln, worklist);
} else {
worklist.add(new Edge(ln, new ArrayList()));
}
}
worklist.add(new Edge(instructions.getFirst(), new ArrayList()));
conversionWorklist = worklist;
edges = HashBasedTable.create(1, 1);
do {
Edge edge = worklist.pollLast();
AbstractInsnNode insn = edge.insn;
stack = edge.stack;
// restore line. this is important since we might have traversed the edge that leads to
// bytecode far away from the branch statement first and are now processing the statement
// right after the branch which should start with the lastLineNumber as it was for the branch
// statement
lastLineNumber = edge.lastLineNumber == -1 ? lastLineNumber : edge.lastLineNumber;
edge.stack = null;
do {
int type = insn.getType();
if (type == FIELD_INSN) {
convertFieldInsn((FieldInsnNode) insn);
} else if (type == IINC_INSN) {
convertIincInsn((IincInsnNode) insn);
} else if (type == INSN) {
convertInsn((InsnNode) insn);
int op = insn.getOpcode();
if ((op >= IRETURN && op <= RETURN) || op == ATHROW) {
break;
}
} else if (type == INT_INSN) {
convertIntInsn((IntInsnNode) insn);
} else if (type == LDC_INSN) {
convertLdcInsn((LdcInsnNode) insn);
} else if (type == JUMP_INSN) {
JumpInsnNode jmp = (JumpInsnNode) insn;
convertJumpInsn(jmp);
int op = jmp.getOpcode();
if (op == JSR) {
throw new UnsupportedOperationException("JSR!");
}
if (op != GOTO) {
/* ifX opcode, i.e. two successors */
AbstractInsnNode next = insn.getNext();
addEdges(insn, next, Collections.singletonList(jmp.label));
} else {
addEdges(insn, jmp.label, null);
}
break;
} else if (type == LOOKUPSWITCH_INSN) {
LookupSwitchInsnNode swtch = (LookupSwitchInsnNode) insn;
convertLookupSwitchInsn(swtch);
LabelNode dflt = swtch.dflt;
addEdges(insn, dflt, swtch.labels);
break;
} else if (type == METHOD_INSN) {
convertMethodInsn((MethodInsnNode) insn);
} else if (type == INVOKE_DYNAMIC_INSN) {
convertInvokeDynamicInsn((InvokeDynamicInsnNode) insn);
} else if (type == MULTIANEWARRAY_INSN) {
convertMultiANewArrayInsn((MultiANewArrayInsnNode) insn);
} else if (type == TABLESWITCH_INSN) {
TableSwitchInsnNode swtch = (TableSwitchInsnNode) insn;
convertTableSwitchInsn(swtch);
LabelNode dflt = swtch.dflt;
addEdges(insn, dflt, swtch.labels);
break;
} else if (type == TYPE_INSN) {
convertTypeInsn((TypeInsnNode) insn);
} else if (type == VAR_INSN) {
if (insn.getOpcode() == RET) {
throw new UnsupportedOperationException("RET!");
}
convertVarInsn((VarInsnNode) insn);
} else if (type == LABEL) {
convertLabel((LabelNode) insn);
} else if (type == LINE) {
convertLine((LineNumberNode) insn);
} else if (type == FRAME) {
// we can ignore it
} else {
throw new RuntimeException("Unknown instruction type: " + type);
}
} while ((insn = insn.getNext()) != null);
} while (!worklist.isEmpty());
conversionWorklist = null;
edges = null;
}
private void handleInlineExceptionHandler(LabelNode ln, ArrayDeque worklist) {
// Catch the exception
CaughtExceptionRef ref = Jimple.v().newCaughtExceptionRef();
Local local = newStackLocal();
DefinitionStmt as = Jimple.v().newIdentityStmt(local, ref);
Operand opr = new Operand(ln, ref);
opr.stack = local;
ArrayList stack = new ArrayList();
stack.add(opr);
worklist.add(new Edge(ln, stack));
// Save the statements
inlineExceptionHandlers.put(ln, as);
}
private boolean checkInlineExceptionHandler(LabelNode ln) {
// If this label is reachable through an exception and through normal
// code, we have to split the exceptional case (with the exception on
// the stack) from the normal fall-through case without anything on the
// stack.
for (Iterator it = instructions.iterator(); it.hasNext(); ) {
AbstractInsnNode node = it.next();
if (node instanceof JumpInsnNode) {
if (((JumpInsnNode) node).label == ln) {
inlineExceptionLabels.add(ln);
return true;
}
} else if (node instanceof LookupSwitchInsnNode) {
if (((LookupSwitchInsnNode) node).labels.contains(ln)) {
inlineExceptionLabels.add(ln);
return true;
}
} else if (node instanceof TableSwitchInsnNode) {
if (((TableSwitchInsnNode) node).labels.contains(ln)) {
inlineExceptionLabels.add(ln);
return true;
}
}
}
return false;
}
private void emitLocals() {
JimpleBody jb = body;
SootMethod m = jb.getMethod();
Collection jbl = jb.getLocals();
Collection jbu = jb.getUnits();
int iloc = 0;
if (!m.isStatic()) {
Local l = getLocal(iloc++);
jbu.add(
Jimple.v().newIdentityStmt(l, Jimple.v().newThisRef(m.getDeclaringClass().getType())));
}
int nrp = 0;
for (Object ot : m.getParameterTypes()) {
Type t = (Type) ot;
Local l = getLocal(iloc);
jbu.add(Jimple.v().newIdentityStmt(l, Jimple.v().newParameterRef(t, nrp++)));
if (AsmUtil.isDWord(t)) {
iloc += 2;
} else {
iloc++;
}
}
for (Local l : locals.values()) {
jbl.add(l);
}
}
private void emitTraps() {
Chain traps = body.getTraps();
SootClass throwable = Scene.v().getSootClass("java.lang.Throwable");
Map> handlers =
new LinkedHashMap>(tryCatchBlocks.size());
for (TryCatchBlockNode tc : tryCatchBlocks) {
UnitBox start = Jimple.v().newStmtBox(null);
UnitBox end = Jimple.v().newStmtBox(null);
Iterator hitr = handlers.get(tc.handler);
if (hitr == null) {
hitr = trapHandlers.get(tc.handler).iterator();
handlers.put(tc.handler, hitr);
}
UnitBox handler = hitr.next();
SootClass cls =
tc.type == null ? throwable : getClassFromScene(AsmUtil.toQualifiedName(tc.type));
Trap trap = Jimple.v().newTrap(cls, start, end, handler);
traps.add(trap);
labels.put(tc.start, start);
labels.put(tc.end, end);
}
}
private void emitUnits(Unit u) {
if (u instanceof UnitContainer) {
for (Unit uu : ((UnitContainer) u).units) {
emitUnits(uu);
}
} else {
body.getUnits().add(u);
}
}
private void emitUnits() {
AbstractInsnNode insn = instructions.getFirst();
ArrayDeque labls = new ArrayDeque();
while (insn != null) {
// Save the label to assign it to the next real unit
if (insn instanceof LabelNode) {
labls.add((LabelNode) insn);
}
// Get the unit associated with the current instruction
Unit u = units.get(insn);
if (u == null) {
insn = insn.getNext();
continue;
}
emitUnits(u);
// If this is an exception handler, register the starting unit for it
{
IdentityStmt caughtEx = null;
if (u instanceof IdentityStmt) {
caughtEx = (IdentityStmt) u;
} else if (u instanceof UnitContainer) {
caughtEx = getIdentityRefFromContrainer((UnitContainer) u);
}
if (insn instanceof LabelNode
&& caughtEx != null
&& caughtEx.getRightOp() instanceof CaughtExceptionRef) {
// We directly place this label
Collection traps = trapHandlers.get((LabelNode) insn);
for (UnitBox ub : traps) {
ub.setUnit(caughtEx);
}
}
}
// Register this unit for all targets of the labels ending up at it
while (!labls.isEmpty()) {
LabelNode ln = labls.poll();
Collection boxes = labels.get(ln);
if (boxes != null) {
for (UnitBox box : boxes) {
box.setUnit(u instanceof UnitContainer ? ((UnitContainer) u).getFirstUnit() : u);
}
}
}
insn = insn.getNext();
}
// Emit the inline exception handlers
for (LabelNode ln : this.inlineExceptionHandlers.keySet()) {
Unit handler = this.inlineExceptionHandlers.get(ln);
emitUnits(handler);
Collection traps = trapHandlers.get(ln);
for (UnitBox ub : traps) {
ub.setUnit(handler);
}
// We need to jump to the original implementation
Unit targetUnit = units.get(ln);
GotoStmt gotoImpl = Jimple.v().newGotoStmt(targetUnit);
body.getUnits().add(gotoImpl);
}
/* set remaining labels & boxes to last unit of chain */
if (labls.isEmpty()) {
return;
}
Unit end = Jimple.v().newNopStmt();
body.getUnits().add(end);
while (!labls.isEmpty()) {
LabelNode ln = labls.poll();
Collection boxes = labels.get(ln);
if (boxes != null) {
for (UnitBox box : boxes) {
box.setUnit(end);
}
}
}
}
private IdentityStmt getIdentityRefFromContrainer(UnitContainer u) {
for (Unit uu : u.units) {
if (uu instanceof IdentityStmt) {
return (IdentityStmt) uu;
} else if (uu instanceof UnitContainer) {
return getIdentityRefFromContrainer((UnitContainer) uu);
}
}
return null;
}
@Override
public Body getBody(SootMethod m, String phaseName) {
if (!m.isConcrete()) {
return null;
}
JimpleBody jb = Jimple.v().newBody(m);
/* initialize */
int nrInsn = instructions.size();
nextLocal = maxLocals;
locals = new LinkedHashMap(maxLocals + (maxLocals / 2));
labels = LinkedListMultimap.create(4);
units = new LinkedHashMap(nrInsn);
frames = new LinkedHashMap(nrInsn);
trapHandlers = LinkedListMultimap.create(tryCatchBlocks.size());
body = jb;
/* retrieve all trap handlers */
for (TryCatchBlockNode tc : tryCatchBlocks) {
trapHandlers.put(tc.handler, Jimple.v().newStmtBox(null));
}
/* convert instructions */
try {
convert();
} catch (Throwable t) {
throw new RuntimeException("Failed to convert " + m, t);
}
/* build body (add units, locals, traps, etc.) */
emitLocals();
emitTraps();
emitUnits();
/* clean up */
locals = null;
labels = null;
units = null;
stack = null;
frames = null;
body = null;
// Make sure to inline patterns of the form to enable proper variable
// splitting and type assignment:
// a = new A();
// goto l0;
// l0:
// b = (B) a;
// return b;
castAndReturnInliner.transform(jb);
try {
PackManager.v().getPack("jb").apply(jb);
} catch (Throwable t) {
throw new RuntimeException("Failed to apply jb to " + m, t);
}
return jb;
}
private final class Edge {
/* edge endpoint */
final AbstractInsnNode insn;
/* previous stacks at edge */
final LinkedList prevStacks;
private int lastLineNumber = -1;
/* current stack at edge */
ArrayList stack;
Edge(AbstractInsnNode insn, ArrayList stack) {
this.insn = insn;
this.prevStacks = new LinkedList();
this.stack = stack;
}
Edge(AbstractInsnNode insn, int lastLineNumber) {
this(insn, new ArrayList(AsmMethodSource.this.stack));
this.lastLineNumber = lastLineNumber;
}
}
}