From 557d2b39878c3cb1fad172d5e23a882a5601c6d2 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Mon, 6 Nov 2023 18:27:19 +0100 Subject: [PATCH] unify "first time" and "merge" code paths in all convert methods This removes the duplicated logic that was present in *every* convert method with any inputs/outputs. --- .../bytecode/frontend/AsmMethodSource.java | 959 +++++++----------- .../java/bytecode/frontend/Operand.java | 2 +- .../java/bytecode/frontend/StackFrame.java | 176 ++-- 3 files changed, 477 insertions(+), 660 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/AsmMethodSource.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/AsmMethodSource.java index 65385112da..7c7c99d2d5 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/AsmMethodSource.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/AsmMethodSource.java @@ -71,7 +71,6 @@ import sootup.core.jimple.common.expr.JAddExpr; import sootup.core.jimple.common.expr.JCastExpr; import sootup.core.jimple.common.expr.JDynamicInvokeExpr; -import sootup.core.jimple.common.expr.JNewArrayExpr; import sootup.core.jimple.common.expr.JNewMultiArrayExpr; import sootup.core.jimple.common.ref.*; import sootup.core.jimple.common.stmt.*; @@ -318,19 +317,7 @@ private String determineLocalName(int idx) { } void setStmt(@Nonnull AbstractInsnNode insn, @Nonnull Stmt stmt) { - Stmt overwrittenStmt = insnToStmt.put(insn, stmt); - if (overwrittenStmt != null) { - throw new IllegalArgumentException( - insn.getOpcode() + " already has an associated Stmt: " + overwrittenStmt); - } - } - - void mergeStmts(@Nonnull AbstractInsnNode insn, @Nonnull Stmt stmt) { - Stmt initiallyAssignedStmt = insnToStmt.put(insn, stmt); - if (initiallyAssignedStmt != null) { - Stmt merged = StmtContainer.getOrCreate(initiallyAssignedStmt, stmt); - insnToStmt.put(insn, merged); - } + insnToStmt.put(insn, stmt); } @Nonnull @@ -396,75 +383,47 @@ private void addReadOperandAssignments_internal(BiFunction as = - Jimple.newAssignStmt(val, rvalue.toImmediate(), getStmtPositionInfo()); - setStmt(insn, as); + JavaClassType declClass = identifierFactory.getClassType(AsmUtil.toQualifiedName(insn.owner)); + Type type = AsmUtil.toJimpleType(insn.desc); + + Operand rvalue = operandStack.pop(type); + JFieldRef val; + FieldSignature ref; + if (notInstance) { + frame.mergeInputs(rvalue); + ref = identifierFactory.getFieldSignature(insn.name, declClass, type); + val = Jimple.newStaticFieldRef(ref); } else { - opr = out[0]; - type = ((JFieldRef) opr.value).getFieldSignature().getType(); - rvalue = operandStack.pop(type); - if (notInstance) { - /* PUTSTATIC only needs one operand on the stack, the rvalue */ - frame.mergeIn(rvalue); - } else { - /* PUTFIELD has a rvalue and a base */ - frame.mergeIn(rvalue, operandStack.pop()); - } + Operand base = operandStack.pop(); + frame.mergeInputs(rvalue, base); + ref = identifierFactory.getFieldSignature(insn.name, declClass, type); + val = Jimple.newInstanceFieldRef(base.toLocal(), ref); } + JAssignStmt as = + Jimple.newAssignStmt(val, rvalue.toImmediate(), getStmtPositionInfo()); + setStmt(insn, as); /* * 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 @@ -493,28 +452,22 @@ private void convertIincInsn(@Nonnull IincInsnNode insn) { private void convertConstInsn(@Nonnull InsnNode insn) { int op = insn.getOpcode(); StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Value v; - if (op == ACONST_NULL) { - v = NullConstant.getInstance(); - } else if (op >= ICONST_M1 && op <= ICONST_5) { - v = IntConstant.getInstance(op - ICONST_0); - } else if (op == LCONST_0 || op == LCONST_1) { - v = LongConstant.getInstance(op - LCONST_0); - } else if (op >= FCONST_0 && op <= FCONST_2) { - v = FloatConstant.getInstance(op - FCONST_0); - } else if (op == DCONST_0 || op == DCONST_1) { - v = DoubleConstant.getInstance(op - DCONST_0); - } else { - throw new UnsupportedOperationException("Unknown constant opcode: " + op); - } - opr = new Operand(insn, v, this); - frame.setOut(opr); + Value v; + if (op == ACONST_NULL) { + v = NullConstant.getInstance(); + } else if (op >= ICONST_M1 && op <= ICONST_5) { + v = IntConstant.getInstance(op - ICONST_0); + } else if (op == LCONST_0 || op == LCONST_1) { + v = LongConstant.getInstance(op - LCONST_0); + } else if (op >= FCONST_0 && op <= FCONST_2) { + v = FloatConstant.getInstance(op - FCONST_0); + } else if (op == DCONST_0 || op == DCONST_1) { + v = DoubleConstant.getInstance(op - DCONST_0); } else { - opr = out[0]; + throw new UnsupportedOperationException("Unknown constant opcode: " + op); } + Operand opr = new Operand(insn, v, this); + frame.mergeOutput(opr); if (op == LCONST_0 || op == LCONST_1 || op == DCONST_0 || op == DCONST_1) { operandStack.pushDual(opr); } else { @@ -524,19 +477,12 @@ private void convertConstInsn(@Nonnull InsnNode insn) { private void convertArrayLoadInsn(@Nonnull InsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Operand indx = operandStack.pop(); - Operand base = operandStack.pop(); - JArrayRef ar = JavaJimple.getInstance().newArrayRef(base.toLocal(), indx.toImmediate()); - opr = new Operand(insn, ar, this); - frame.setIn(indx, base); - frame.setOut(opr); - } else { - opr = out[0]; - frame.mergeIn(operandStack.pop(), operandStack.pop()); - } + Operand indx = operandStack.pop(); + Operand base = operandStack.pop(); + frame.mergeInputs(indx, base); + JArrayRef ar = JavaJimple.getInstance().newArrayRef(base.toLocal(), indx.toImmediate()); + Operand opr = new Operand(insn, ar, this); + frame.mergeOutput(opr); int op = insn.getOpcode(); if (op == DALOAD || op == LALOAD) { operandStack.pushDual(opr); @@ -549,21 +495,14 @@ private void convertArrayStoreInsn(@Nonnull InsnNode insn) { int op = insn.getOpcode(); boolean dword = op == LASTORE || op == DASTORE; StackFrame frame = operandStack.getOrCreateStackframe(insn); - if (!insnToStmt.containsKey(insn)) { - Operand valueOp = dword ? operandStack.popDual() : operandStack.pop(); - Operand indexOp = operandStack.pop(); - Operand baseOp = operandStack.pop(); - JArrayRef ar = JavaJimple.getInstance().newArrayRef(baseOp.toLocal(), indexOp.toImmediate()); - JAssignStmt as = - Jimple.newAssignStmt(ar, valueOp.toImmediate(), getStmtPositionInfo()); - frame.setIn(valueOp, indexOp, baseOp); - setStmt(insn, as); - } else { - frame.mergeIn( - dword ? operandStack.popDual() : operandStack.pop(), - operandStack.pop(), - operandStack.pop()); - } + Operand valueOp = dword ? operandStack.popDual() : operandStack.pop(); + Operand indexOp = operandStack.pop(); + Operand baseOp = operandStack.pop(); + frame.mergeInputs(valueOp, indexOp, baseOp); + JArrayRef ar = JavaJimple.getInstance().newArrayRef(baseOp.toLocal(), indexOp.toImmediate()); + JAssignStmt as = + Jimple.newAssignStmt(ar, valueOp.toImmediate(), getStmtPositionInfo()); + setStmt(insn, as); } private void convertDupInsn(@Nonnull InsnNode insn) { @@ -656,65 +595,50 @@ private void convertBinopInsn(@Nonnull InsnNode insn) { || op == DCMPL || op == DCMPG; StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Operand op2 = - (dword && op != LSHL && op != LSHR && op != LUSHR) - ? operandStack.popDual() - : operandStack.pop(); - Operand op1 = dword ? operandStack.popDual() : operandStack.pop(); - Immediate v1 = op1.toImmediate(); - Immediate v2 = op2.toImmediate(); - AbstractBinopExpr binop; - if (op >= IADD && op <= DADD) { - binop = Jimple.newAddExpr(v1, v2); - } else if (op >= ISUB && op <= DSUB) { - binop = Jimple.newSubExpr(v1, v2); - } else if (op >= IMUL && op <= DMUL) { - binop = Jimple.newMulExpr(v1, v2); - } else if (op >= IDIV && op <= DDIV) { - binop = Jimple.newDivExpr(v1, v2); - } else if (op >= IREM && op <= DREM) { - binop = Jimple.newRemExpr(v1, v2); - } else if (op >= ISHL && op <= LSHL) { - binop = Jimple.newShlExpr(v1, v2); - } else if (op >= ISHR && op <= LSHR) { - binop = Jimple.newShrExpr(v1, v2); - } else if (op >= IUSHR && op <= LUSHR) { - binop = Jimple.newUshrExpr(v1, v2); - } else if (op >= IAND && op <= LAND) { - binop = Jimple.newAndExpr(v1, v2); - } else if (op >= IOR && op <= LOR) { - binop = Jimple.newOrExpr(v1, v2); - } else if (op >= IXOR && op <= LXOR) { - binop = Jimple.newXorExpr(v1, v2); - } else if (op == LCMP) { - binop = Jimple.newCmpExpr(v1, v2); - } else if (op == FCMPL || op == DCMPL) { - binop = Jimple.newCmplExpr(v1, v2); - } else if (op == FCMPG || op == DCMPG) { - binop = Jimple.newCmpgExpr(v1, v2); - } else { - throw new UnsupportedOperationException("Unknown binop: " + op); - } - opr = new Operand(insn, binop, this); - - frame.setIn(op2, op1); - frame.setOut(opr); + Operand op2 = + (dword && op != LSHL && op != LSHR && op != LUSHR) + ? operandStack.popDual() + : operandStack.pop(); + Operand op1 = dword ? operandStack.popDual() : operandStack.pop(); + frame.mergeInputs(op2, op1); + Immediate v1 = op1.toImmediate(); + Immediate v2 = op2.toImmediate(); + AbstractBinopExpr binop; + if (op >= IADD && op <= DADD) { + binop = Jimple.newAddExpr(v1, v2); + } else if (op >= ISUB && op <= DSUB) { + binop = Jimple.newSubExpr(v1, v2); + } else if (op >= IMUL && op <= DMUL) { + binop = Jimple.newMulExpr(v1, v2); + } else if (op >= IDIV && op <= DDIV) { + binop = Jimple.newDivExpr(v1, v2); + } else if (op >= IREM && op <= DREM) { + binop = Jimple.newRemExpr(v1, v2); + } else if (op >= ISHL && op <= LSHL) { + binop = Jimple.newShlExpr(v1, v2); + } else if (op >= ISHR && op <= LSHR) { + binop = Jimple.newShrExpr(v1, v2); + } else if (op >= IUSHR && op <= LUSHR) { + binop = Jimple.newUshrExpr(v1, v2); + } else if (op >= IAND && op <= LAND) { + binop = Jimple.newAndExpr(v1, v2); + } else if (op >= IOR && op <= LOR) { + binop = Jimple.newOrExpr(v1, v2); + } else if (op >= IXOR && op <= LXOR) { + binop = Jimple.newXorExpr(v1, v2); + } else if (op == LCMP) { + binop = Jimple.newCmpExpr(v1, v2); + } else if (op == FCMPL || op == DCMPL) { + binop = Jimple.newCmplExpr(v1, v2); + } else if (op == FCMPG || op == DCMPG) { + binop = Jimple.newCmpgExpr(v1, v2); } else { - opr = out[0]; - if (dword) { - if (op != LSHL && op != LSHR && op != LUSHR) { - - frame.mergeIn(operandStack.popDual(), operandStack.popDual()); - } else { - frame.mergeIn(operandStack.pop(), operandStack.popDual()); - } - } else { - frame.mergeIn(operandStack.pop(), operandStack.pop()); - } + throw new UnsupportedOperationException("Unknown binop: " + op); } + + Operand opr = new Operand(insn, binop, this); + frame.mergeOutput(opr); + if (dword && op < LCMP) { operandStack.pushDual(opr); } else { @@ -726,25 +650,18 @@ private void convertUnopInsn(@Nonnull InsnNode insn) { int op = insn.getOpcode(); boolean dword = op == LNEG || op == DNEG; StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Operand op1 = dword ? operandStack.popDual() : operandStack.pop(); - AbstractUnopExpr unop; - if (op >= INEG && op <= DNEG) { - unop = Jimple.newNegExpr(op1.toImmediate()); - } else if (op == ARRAYLENGTH) { - unop = Jimple.newLengthExpr(op1.toImmediate()); - } else { - throw new UnsupportedOperationException("Unknown unop: " + op); - } - opr = new Operand(insn, unop, this); - frame.setIn(op1); - frame.setOut(opr); + Operand op1 = dword ? operandStack.popDual() : operandStack.pop(); + frame.mergeInputs(op1); + AbstractUnopExpr unop; + if (op >= INEG && op <= DNEG) { + unop = Jimple.newNegExpr(op1.toImmediate()); + } else if (op == ARRAYLENGTH) { + unop = Jimple.newLengthExpr(op1.toImmediate()); } else { - opr = out[0]; - frame.mergeIn(dword ? operandStack.popDual() : operandStack.pop()); + throw new UnsupportedOperationException("Unknown unop: " + op); } + Operand opr = new Operand(insn, unop, this); + frame.mergeOutput(opr); if (dword) { operandStack.pushDual(opr); } else { @@ -757,52 +674,45 @@ private void convertPrimCastInsn(@Nonnull InsnNode insn) { 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 = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Type totype; - switch (op) { - case I2L: - case F2L: - case D2L: - totype = PrimitiveType.getLong(); - break; - case L2I: - case F2I: - case D2I: - totype = PrimitiveType.getInt(); - break; - case I2F: - case L2F: - case D2F: - totype = PrimitiveType.getFloat(); - break; - case I2D: - case L2D: - case F2D: - totype = PrimitiveType.getDouble(); - break; - case I2B: - totype = PrimitiveType.getByte(); - break; - case I2S: - totype = PrimitiveType.getShort(); - break; - case I2C: - totype = PrimitiveType.getChar(); - break; - default: - throw new IllegalStateException("Unknown prim cast op: " + op); - } - Operand val = fromd ? operandStack.popDual() : operandStack.pop(); - JCastExpr cast = Jimple.newCastExpr(val.toImmediate(), totype); - opr = new Operand(insn, cast, this); - frame.setIn(val); - frame.setOut(opr); - } else { - opr = out[0]; - frame.mergeIn(fromd ? operandStack.popDual() : operandStack.pop()); - } + Type totype; + switch (op) { + case I2L: + case F2L: + case D2L: + totype = PrimitiveType.getLong(); + break; + case L2I: + case F2I: + case D2I: + totype = PrimitiveType.getInt(); + break; + case I2F: + case L2F: + case D2F: + totype = PrimitiveType.getFloat(); + break; + case I2D: + case L2D: + case F2D: + totype = PrimitiveType.getDouble(); + break; + case I2B: + totype = PrimitiveType.getByte(); + break; + case I2S: + totype = PrimitiveType.getShort(); + break; + case I2C: + totype = PrimitiveType.getChar(); + break; + default: + throw new IllegalStateException("Unknown prim cast op: " + op); + } + Operand val = fromd ? operandStack.popDual() : operandStack.pop(); + frame.mergeInputs(val); + JCastExpr cast = Jimple.newCastExpr(val.toImmediate(), totype); + Operand opr = new Operand(insn, cast, this); + frame.mergeOutput(opr); if (tod) { operandStack.pushDual(opr); } else { @@ -814,15 +724,10 @@ private void convertReturnInsn(@Nonnull InsnNode insn) { int op = insn.getOpcode(); boolean dword = op == LRETURN || op == DRETURN; StackFrame frame = operandStack.getOrCreateStackframe(insn); - if (!insnToStmt.containsKey(insn)) { - Operand val = dword ? operandStack.popDual() : operandStack.pop(); - JReturnStmt ret = Jimple.newReturnStmt(val.toImmediate(), getStmtPositionInfo()); - frame.setIn(val); - setStmt(insn, ret); - } else { - final Operand operand = dword ? operandStack.popDual() : operandStack.pop(); - frame.mergeIn(operand); - } + Operand val = dword ? operandStack.popDual() : operandStack.pop(); + frame.mergeInputs(val); + JReturnStmt ret = Jimple.newReturnStmt(val.toImmediate(), getStmtPositionInfo()); + setStmt(insn, ret); } private void convertInsn(@Nonnull InsnNode insn) { @@ -869,31 +774,21 @@ private void convertInsn(@Nonnull InsnNode insn) { } } else if (op == ATHROW) { StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand opr; - if (!insnToStmt.containsKey(insn)) { - opr = operandStack.pop(); - JThrowStmt ts = Jimple.newThrowStmt(opr.toImmediate(), getStmtPositionInfo()); - frame.setIn(opr); - frame.setOut(opr); - setStmt(insn, ts); - } else { - opr = operandStack.pop(); - frame.mergeIn(opr); - } + Operand opr = operandStack.pop(); + frame.mergeInputs(opr); + JThrowStmt ts = Jimple.newThrowStmt(opr.toImmediate(), getStmtPositionInfo()); + setStmt(insn, ts); + frame.mergeOutput(opr); operandStack.push(opr); } else if (op == MONITORENTER || op == MONITOREXIT) { StackFrame frame = operandStack.getOrCreateStackframe(insn); - if (!insnToStmt.containsKey(insn)) { - Operand opr = operandStack.popStackConst(); - AbstractOpStmt ts = - op == MONITORENTER - ? Jimple.newEnterMonitorStmt(opr.toImmediate(), getStmtPositionInfo()) - : Jimple.newExitMonitorStmt(opr.toImmediate(), getStmtPositionInfo()); - frame.setIn(opr); - setStmt(insn, ts); - } else { - frame.mergeIn(operandStack.pop()); - } + Operand opr = operandStack.popStackConst(); + frame.mergeInputs(opr); + AbstractOpStmt ts = + op == MONITORENTER + ? Jimple.newEnterMonitorStmt(opr.toImmediate(), getStmtPositionInfo()) + : Jimple.newExitMonitorStmt(opr.toImmediate(), getStmtPositionInfo()); + setStmt(insn, ts); } else { throw new UnsupportedOperationException("Unknown insn op: " + op); } @@ -902,56 +797,46 @@ private void convertInsn(@Nonnull InsnNode insn) { private void convertIntInsn(@Nonnull IntInsnNode insn) { int op = insn.getOpcode(); StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Value v; - if (op == BIPUSH || op == SIPUSH) { - v = IntConstant.getInstance(insn.operand); - } else { - // assert(op == NEWARRAY) - Type type; - switch (insn.operand) { - case T_BOOLEAN: - type = PrimitiveType.getBoolean(); - break; - case T_CHAR: - type = PrimitiveType.getChar(); - break; - case T_FLOAT: - type = PrimitiveType.getFloat(); - break; - case T_DOUBLE: - type = PrimitiveType.getDouble(); - break; - case T_BYTE: - type = PrimitiveType.getByte(); - break; - case T_SHORT: - type = PrimitiveType.getShort(); - break; - case T_INT: - type = PrimitiveType.getInt(); - break; - case T_LONG: - type = PrimitiveType.getLong(); - break; - default: - throw new UnsupportedOperationException("Unknown NEWARRAY type!"); - } - Operand size = operandStack.pop(); - JNewArrayExpr anew = JavaJimple.getInstance().newNewArrayExpr(type, size.toImmediate()); - frame.setIn(size); - v = anew; - } - opr = new Operand(insn, v, this); - frame.setOut(opr); + Value v; + if (op == BIPUSH || op == SIPUSH) { + v = IntConstant.getInstance(insn.operand); } else { - opr = out[0]; - if (op == NEWARRAY) { - frame.mergeIn(operandStack.pop()); + assert op == NEWARRAY; + Type type; + switch (insn.operand) { + case T_BOOLEAN: + type = PrimitiveType.getBoolean(); + break; + case T_CHAR: + type = PrimitiveType.getChar(); + break; + case T_FLOAT: + type = PrimitiveType.getFloat(); + break; + case T_DOUBLE: + type = PrimitiveType.getDouble(); + break; + case T_BYTE: + type = PrimitiveType.getByte(); + break; + case T_SHORT: + type = PrimitiveType.getShort(); + break; + case T_INT: + type = PrimitiveType.getInt(); + break; + case T_LONG: + type = PrimitiveType.getLong(); + break; + default: + throw new UnsupportedOperationException("Unknown NEWARRAY type!"); } + Operand size = operandStack.pop(); + frame.mergeInputs(size); + v = JavaJimple.getInstance().newNewArrayExpr(type, size.toImmediate()); } + Operand opr = new Operand(insn, v, this); + frame.mergeOutput(opr); operandStack.push(opr); } @@ -971,10 +856,10 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { Operand val = operandStack.pop(); Immediate v = val.toImmediate(); AbstractConditionExpr cond; - Operand val1; if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { - val1 = operandStack.pop(); + Operand val1 = operandStack.pop(); + frame.mergeInputs(val, val1); Immediate v1 = val1.toImmediate(); switch (op) { case IF_ICMPEQ: @@ -1000,8 +885,8 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { default: throw new UnsupportedOperationException("Unknown if op: " + op); } - frame.setIn(val, val1); } else { + frame.mergeInputs(val); switch (op) { case IFEQ: cond = Jimple.newEqExpr(v, IntConstant.getInstance(0)); @@ -1030,16 +915,15 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { default: throw new UnsupportedOperationException("Unknown if op: " + op); } - frame.setIn(val); } BranchingStmt ifStmt = Jimple.newIfStmt(cond, getStmtPositionInfo()); stmtsThatBranchToLabel.put(ifStmt, insn.label); setStmt(insn, ifStmt); } else { if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { - frame.mergeIn(operandStack.pop(), operandStack.pop()); + frame.mergeInputs(operandStack.pop(), operandStack.pop()); } else { - frame.mergeIn(operandStack.pop()); + frame.mergeInputs(operandStack.pop()); } } } @@ -1048,15 +932,9 @@ private void convertLdcInsn(@Nonnull LdcInsnNode insn) { Object val = insn.cst; boolean dword = val instanceof Long || val instanceof Double; StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Value v = toSootValue(val); - opr = new Operand(insn, v, this); - frame.setOut(opr); - } else { - opr = out[0]; - } + Value v = toSootValue(val); + Operand opr = new Operand(insn, v, this); + frame.mergeOutput(opr); if (dword) { operandStack.pushDual(opr); } else { @@ -1140,10 +1018,11 @@ private MethodSignature toMethodSignature(Handle methodHandle) { private void convertLookupSwitchInsn(@Nonnull LookupSwitchInsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); if (insnToStmt.containsKey(insn)) { - frame.mergeIn(operandStack.pop()); + frame.mergeInputs(operandStack.pop()); return; } Operand key = operandStack.pop(); + frame.mergeInputs(key); List keys = new ArrayList<>(insn.keys.size()); for (Integer i : insn.keys) { @@ -1156,106 +1035,79 @@ private void convertLookupSwitchInsn(@Nonnull LookupSwitchInsnNode insn) { stmtsThatBranchToLabel.putAll(lookupSwitchStmt, insn.labels); stmtsThatBranchToLabel.put(lookupSwitchStmt, insn.dflt); - frame.setIn(key); setStmt(insn, lookupSwitchStmt); } private void convertMethodInsn(@Nonnull MethodInsnNode insn) { - int op = insn.getOpcode(); boolean isInstance = op != INVOKESTATIC; StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - Type returnType; - if (out == null) { - String clsName = AsmUtil.toQualifiedName(insn.owner); - if (clsName.charAt(0) == '[') { - clsName = "java.lang.Object"; - } - JavaClassType cls = identifierFactory.getClassType(AsmUtil.toQualifiedName(clsName)); - List sigTypes = AsmUtil.toJimpleSignatureDesc(insn.desc); - returnType = sigTypes.remove((sigTypes.size() - 1)); - MethodSignature methodSignature = - identifierFactory.getMethodSignature(cls, insn.name, returnType, sigTypes); - int nrArgs = sigTypes.size(); - final Operand[] args; - List argList = Collections.emptyList(); - if (!isInstance) { - 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] = operandStack.pop(sigTypes.get(nrArgs)); - argList.add(args[nrArgs].toImmediate()); - } - if (argList.size() > 1) { - Collections.reverse(argList); - } - if (isInstance) { - args[args.length - 1] = operandStack.pop(); - } - AbstractInvokeExpr invoke; - if (!isInstance) { - invoke = Jimple.newStaticInvokeExpr(methodSignature, argList); - } else { - Operand baseOperand = args[args.length - 1]; - Local base = baseOperand.toLocal(); - - AbstractInstanceInvokeExpr iinvoke; - switch (op) { - case INVOKESPECIAL: - iinvoke = Jimple.newSpecialInvokeExpr(base, methodSignature, argList); - break; - case INVOKEVIRTUAL: - iinvoke = Jimple.newVirtualInvokeExpr(base, methodSignature, argList); - break; - case INVOKEINTERFACE: - iinvoke = Jimple.newInterfaceInvokeExpr(base, methodSignature, argList); - break; - default: - throw new UnsupportedOperationException("Unknown invoke op:" + op); - } - - invoke = iinvoke; - } + String clsName = AsmUtil.toQualifiedName(insn.owner); + if (clsName.charAt(0) == '[') { + clsName = "java.lang.Object"; + } + JavaClassType cls = identifierFactory.getClassType(AsmUtil.toQualifiedName(clsName)); + List sigTypes = AsmUtil.toJimpleSignatureDesc(insn.desc); + Type returnType = sigTypes.remove((sigTypes.size() - 1)); + MethodSignature methodSignature = + identifierFactory.getMethodSignature(cls, insn.name, returnType, sigTypes); + int nrArgs = sigTypes.size(); + final Operand[] args; + List argList = new ArrayList<>(); + if (!isInstance) { + args = nrArgs == 0 ? null : new Operand[nrArgs]; if (args != null) { - frame.setIn(args); + argList = new ArrayList<>(nrArgs); } - opr = new Operand(insn, invoke, this); - frame.setOut(opr); } else { - opr = out[0]; - AbstractInvokeExpr expr = (AbstractInvokeExpr) opr.value; - List types = expr.getMethodSignature().getParameterTypes(); - Operand[] oprs; - int nrArgs = types.size(); - // TODO: check equivalent to isInstance? - boolean isInstanceMethod = expr instanceof AbstractInstanceInvokeExpr; - if (!isInstanceMethod) { - oprs = nrArgs == 0 ? null : new Operand[nrArgs]; - } else { - oprs = new Operand[nrArgs + 1]; + args = new Operand[nrArgs + 1]; + if (nrArgs != 0) { + argList = new ArrayList<>(nrArgs); } - if (oprs != null) { - while (nrArgs-- != 0) { - oprs[nrArgs] = operandStack.pop(types.get(nrArgs)); - } - if (isInstanceMethod) { - oprs[oprs.length - 1] = operandStack.pop(); - } + } + while (nrArgs-- != 0) { + args[nrArgs] = operandStack.pop(sigTypes.get(nrArgs)); + } + if (isInstance) { + args[args.length - 1] = operandStack.pop(); + } + if (args != null) { + frame.mergeInputs(args); + } + nrArgs = sigTypes.size(); + while (nrArgs-- != 0) { + argList.add(args[nrArgs].toImmediate()); + } + if (argList.size() > 1) { + Collections.reverse(argList); + } + AbstractInvokeExpr invoke; + if (!isInstance) { + invoke = Jimple.newStaticInvokeExpr(methodSignature, argList); + } else { + Operand baseOperand = args[args.length - 1]; + Local base = baseOperand.toLocal(); - frame.mergeIn(oprs); + AbstractInstanceInvokeExpr iinvoke; + switch (op) { + case INVOKESPECIAL: + iinvoke = Jimple.newSpecialInvokeExpr(base, methodSignature, argList); + break; + case INVOKEVIRTUAL: + iinvoke = Jimple.newVirtualInvokeExpr(base, methodSignature, argList); + break; + case INVOKEINTERFACE: + iinvoke = Jimple.newInterfaceInvokeExpr(base, methodSignature, argList); + break; + default: + throw new UnsupportedOperationException("Unknown invoke op:" + op); } - returnType = expr.getMethodSignature().getType(); + + invoke = iinvoke; } + Operand opr = new Operand(insn, invoke, this); + frame.mergeOutput(opr); + if (AsmUtil.isDWord(returnType)) { operandStack.pushDual(opr); } else if (returnType != VoidType.getInstance()) { @@ -1273,70 +1125,52 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - Type returnType; - if (out == null) { - // convert info on bootstrap method - MethodSignature bsmMethodRef = toMethodSignature(insn.bsm); - List bsmMethodArgs = new ArrayList<>(insn.bsmArgs.length); - for (Object bsmArg : insn.bsmArgs) { - bsmMethodArgs.add(toSootValue(bsmArg)); - } + // convert info on bootstrap method + MethodSignature bsmMethodRef = toMethodSignature(insn.bsm); + List bsmMethodArgs = new ArrayList<>(insn.bsmArgs.length); + for (Object bsmArg : insn.bsmArgs) { + bsmMethodArgs.add(toSootValue(bsmArg)); + } - // create ref to actual method - JavaClassType bclass = - identifierFactory.getClassType(JDynamicInvokeExpr.INVOKEDYNAMIC_DUMMY_CLASS_NAME); + // create ref to actual method + JavaClassType bclass = + identifierFactory.getClassType(JDynamicInvokeExpr.INVOKEDYNAMIC_DUMMY_CLASS_NAME); - // Generate parameters & returnType & parameterTypes - List types = AsmUtil.toJimpleSignatureDesc(insn.desc); - int nrArgs = types.size() - 1; // don't handle the return type here - List parameterTypes = new ArrayList<>(nrArgs); - List methodArgs = new ArrayList<>(nrArgs); + // Generate parameters & returnType & parameterTypes + List types = AsmUtil.toJimpleSignatureDesc(insn.desc); + int nrArgs = types.size() - 1; // don't handle the return type here + List parameterTypes = new ArrayList<>(nrArgs); + List methodArgs = new ArrayList<>(nrArgs); - Operand[] args = new Operand[nrArgs]; - // Beware: Call stack is FIFO, Jimple is linear + Operand[] args = new Operand[nrArgs]; + // Beware: Call stack is FIFO, Jimple is linear - for (int i = nrArgs - 1; i >= 0; i--) { - final Type type = types.get(i); - parameterTypes.add(type); - args[i] = operandStack.pop(type); - methodArgs.add(args[i].toImmediate()); - } - if (methodArgs.size() > 1) { - Collections.reverse(methodArgs); // Call stack is FIFO, Jimple is linear - Collections.reverse(parameterTypes); - } - returnType = types.get(types.size() - 1); + for (int i = nrArgs - 1; i >= 0; i--) { + final Type type = types.get(i); + parameterTypes.add(type); + args[i] = operandStack.pop(type); + } + frame.mergeInputs(args); + for (int i = nrArgs - 1; i >= 0; i--) { + methodArgs.add(args[i].toImmediate()); + } + if (methodArgs.size() > 1) { + Collections.reverse(methodArgs); // Call stack is FIFO, Jimple is linear + Collections.reverse(parameterTypes); + } + Type returnType = types.get(types.size() - 1); - // we always model invokeDynamic method refs as static method references - // of methods on the type SootClass.INVOKEDYNAMIC_DUMMY_CLASS_NAME - MethodSignature methodSig = - identifierFactory.getMethodSignature(bclass, insn.name, returnType, parameterTypes); + // we always model invokeDynamic method refs as static method references + // of methods on the type SootClass.INVOKEDYNAMIC_DUMMY_CLASS_NAME + MethodSignature methodSig = + identifierFactory.getMethodSignature(bclass, insn.name, returnType, parameterTypes); - JDynamicInvokeExpr indy = - Jimple.newDynamicInvokeExpr( - bsmMethodRef, bsmMethodArgs, methodSig, insn.bsm.getTag(), methodArgs); + JDynamicInvokeExpr indy = + Jimple.newDynamicInvokeExpr( + bsmMethodRef, bsmMethodArgs, methodSig, insn.bsm.getTag(), methodArgs); - frame.setIn(args); - opr = new Operand(insn, indy, this); - frame.setOut(opr); - } else { - opr = out[0]; - AbstractInvokeExpr expr = (AbstractInvokeExpr) opr.value; - List types = expr.getMethodSignature().getParameterTypes(); - Operand[] oprs; - int nrArgs = types.size(); - oprs = (nrArgs == 0) ? null : new Operand[nrArgs]; - if (oprs != null) { - while (nrArgs > 0) { - nrArgs--; - oprs[nrArgs] = operandStack.pop(types.get(nrArgs)); - } - frame.mergeIn(oprs); - } - returnType = expr.getType(); - } + Operand opr = new Operand(insn, indy, this); + frame.mergeOutput(opr); if (AsmUtil.isDWord(returnType)) { operandStack.pushDual(opr); } else if (!(returnType instanceof VoidType)) { @@ -1354,40 +1188,32 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { private void convertMultiANewArrayInsn(@Nonnull MultiANewArrayInsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - ArrayType t = (ArrayType) AsmUtil.toJimpleType(insn.desc); - int dims = insn.dims; - Operand[] sizes = new Operand[dims]; - Immediate[] sizeVals = new Immediate[dims]; - while (dims-- != 0) { - sizes[dims] = operandStack.pop(); - sizeVals[dims] = sizes[dims].toImmediate(); - } - JNewMultiArrayExpr nm = Jimple.newNewMultiArrayExpr(t, Arrays.asList(sizeVals)); - frame.setIn(sizes); - opr = new Operand(insn, nm, this); - frame.setOut(opr); - } else { - opr = out[0]; - int dims = insn.dims; - Operand[] sizes = new Operand[dims]; - while (dims-- != 0) { - sizes[dims] = operandStack.pop(); - } - frame.mergeIn(sizes); - } + ArrayType t = (ArrayType) AsmUtil.toJimpleType(insn.desc); + int dims = insn.dims; + Operand[] sizes = new Operand[dims]; + Immediate[] sizeVals = new Immediate[dims]; + while (dims-- != 0) { + sizes[dims] = operandStack.pop(); + } + frame.mergeInputs(sizes); + dims = insn.dims; + while (dims-- != 0) { + sizeVals[dims] = sizes[dims].toImmediate(); + } + JNewMultiArrayExpr nm = Jimple.newNewMultiArrayExpr(t, Arrays.asList(sizeVals)); + Operand opr = new Operand(insn, nm, this); + frame.mergeOutput(opr); operandStack.push(opr); } private void convertTableSwitchInsn(@Nonnull TableSwitchInsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); if (insnToStmt.containsKey(insn)) { - frame.mergeIn(operandStack.pop()); + frame.mergeInputs(operandStack.pop()); return; } Operand key = operandStack.pop(); + frame.mergeInputs(key); JSwitchStmt tableSwitchStmt = Jimple.newTableSwitchStmt(key.toImmediate(), insn.min, insn.max, getStmtPositionInfo()); @@ -1395,53 +1221,42 @@ private void convertTableSwitchInsn(@Nonnull TableSwitchInsnNode insn) { stmtsThatBranchToLabel.putAll(tableSwitchStmt, insn.labels); stmtsThatBranchToLabel.put(tableSwitchStmt, insn.dflt); - frame.setIn(key); setStmt(insn, tableSwitchStmt); } private void convertTypeInsn(@Nonnull TypeInsnNode insn) { int op = insn.getOpcode(); StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - Expr val; - if (op == NEW) { - val = Jimple.newNewExpr(AsmUtil.toJimpleClassType(insn.desc)); - } else { - Operand op1 = operandStack.pop(); - switch (op) { - case ANEWARRAY: - { - val = - JavaJimple.getInstance() - .newNewArrayExpr(AsmUtil.arrayTypetoJimpleType(insn.desc), op1.toImmediate()); - break; - } - case CHECKCAST: - { - val = Jimple.newCastExpr(op1.toImmediate(), AsmUtil.toJimpleClassType(insn.desc)); - break; - } - case INSTANCEOF: - { - val = - Jimple.newInstanceOfExpr(op1.toImmediate(), AsmUtil.toJimpleClassType(insn.desc)); - break; - } - default: - throw new UnsupportedOperationException("Unknown type op: " + op); - } - frame.setIn(op1); - } - opr = new Operand(insn, val, this); - frame.setOut(opr); + Expr val; + if (op == NEW) { + val = Jimple.newNewExpr(AsmUtil.toJimpleClassType(insn.desc)); } else { - opr = out[0]; - if (op != NEW) { - frame.mergeIn(operandStack.pop()); + Operand op1 = operandStack.pop(); + frame.mergeInputs(op1); + switch (op) { + case ANEWARRAY: + { + val = + JavaJimple.getInstance() + .newNewArrayExpr(AsmUtil.arrayTypetoJimpleType(insn.desc), op1.toImmediate()); + break; + } + case CHECKCAST: + { + val = Jimple.newCastExpr(op1.toImmediate(), AsmUtil.toJimpleClassType(insn.desc)); + break; + } + case INSTANCEOF: + { + val = Jimple.newInstanceOfExpr(op1.toImmediate(), AsmUtil.toJimpleClassType(insn.desc)); + break; + } + default: + throw new UnsupportedOperationException("Unknown type op: " + op); } } + Operand opr = new Operand(insn, val, this); + frame.mergeOutput(opr); operandStack.push(opr); } @@ -1449,14 +1264,8 @@ private void convertVarLoadInsn(@Nonnull VarInsnNode insn) { int op = insn.getOpcode(); boolean dword = op == LLOAD || op == DLOAD; StackFrame frame = operandStack.getOrCreateStackframe(insn); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - opr = new Operand(insn, getOrCreateLocal(insn.var), this); - frame.setOut(opr); - } else { - opr = out[0]; - } + Operand opr = new Operand(insn, getOrCreateLocal(insn.var), this); + frame.mergeOutput(opr); if (dword) { operandStack.pushDual(opr); } else { @@ -1469,24 +1278,19 @@ private void convertVarStoreInsn(@Nonnull VarInsnNode insn) { boolean dword = op == LSTORE || op == DSTORE; StackFrame frame = operandStack.getOrCreateStackframe(insn); Operand opr = dword ? operandStack.popDual() : operandStack.pop(); + frame.mergeInputs(opr); Local local = getOrCreateLocal(insn.var); - if (!insnToStmt.containsKey(insn)) { - AbstractDefinitionStmt as; - if (opr.stackLocal == null) { - // Can skip creating a new stack local for the operand - // and store the value in the local directly. - as = Jimple.newAssignStmt(local, opr.value, getStmtPositionInfo()); - // TODO check that this works correctly with the merging - opr.stackLocal = local; - setStmt(opr.insn, as); - } else if (opr.stackLocal != local) { - as = Jimple.newAssignStmt(local, opr.toImmediate(), getStmtPositionInfo()); - setStmt(insn, as); - } - - frame.setIn(opr); - } else { - frame.mergeIn(opr); + AbstractDefinitionStmt as; + if (opr.stackLocal == null) { + // Can skip creating a new stack local for the operand + // and store the value in the local directly. + as = Jimple.newAssignStmt(local, opr.value, getStmtPositionInfo()); + // TODO check that this works correctly with the merging + opr.stackLocal = local; + setStmt(opr.insn, as); + } else if (opr.stackLocal != local) { + as = Jimple.newAssignStmt(local, opr.toImmediate(), getStmtPositionInfo()); + setStmt(insn, as); } addReadOperandAssignments(local); } @@ -1525,20 +1329,15 @@ private void convertLabel(@Nonnull LabelNode ln) { } StackFrame frame = operandStack.getOrCreateStackframe(ln); - Operand[] out = frame.getOut(); - Operand opr; - if (out == null) { - JCaughtExceptionRef ref = JavaJimple.getInstance().newCaughtExceptionRef(); - Local stack = newStackLocal(); - AbstractDefinitionStmt as = - Jimple.newIdentityStmt(stack, ref, getStmtPositionInfo()); - opr = new Operand(ln, ref, this); - opr.stackLocal = stack; - frame.setOut(opr); - setStmt(ln, as); - } else { - opr = out[0]; - } + JCaughtExceptionRef ref = JavaJimple.getInstance().newCaughtExceptionRef(); + Operand opr = new Operand(ln, ref, this); + frame.mergeOutput(opr); + if (opr.stackLocal == null) { + opr.stackLocal = newStackLocal(); + } + AbstractDefinitionStmt as = + Jimple.newIdentityStmt(opr.stackLocal, ref, getStmtPositionInfo()); + setStmt(ln, as); operandStack.push(opr); } diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/Operand.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/Operand.java index a48f2e4a40..584dada50e 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/Operand.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/Operand.java @@ -108,7 +108,7 @@ void changeStackLocal(Local newStackLocal) { methodSource.setStmt( insn, Jimple.newAssignStmt(newStackLocal, value, methodSource.getStmtPositionInfo())); } else { - assert assignStmt.getLeftOp() == oldStackLocal; + assert assignStmt.getLeftOp() == oldStackLocal || assignStmt.getLeftOp() == newStackLocal; // replace `$oldStackLocal = value` with `$newStackLocal = value` methodSource.replaceStmt(assignStmt, assignStmt.withVariable(newStackLocal)); } diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/StackFrame.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/StackFrame.java index a8bb1bfa99..b64c5aeed0 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/StackFrame.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/StackFrame.java @@ -37,9 +37,15 @@ */ final class StackFrame { @Nonnull private final AbstractInsnNode insn; - @Nullable private Operand[] out; - @Nullable private Local[] inStackLocals; - @Nonnull private final ArrayList in = new ArrayList<>(1); + /** + * TODO explain + * + *

Interestingly, none of the operations that need to have their inputs/outputs tracked produce + * more than a single output, so this doesn't need to be a list. + */ + @Nullable private Operand out; + + @Nonnull final ArrayList in = new ArrayList<>(1); @Nonnull private final AsmMethodSource src; /** @@ -52,103 +58,115 @@ final class StackFrame { this.src = src; } - /** @return operands produced by this frame. */ - @Nullable - Operand[] getOut() { - return out; + void mergeOutput(@Nonnull Operand outputOperand) { + if (out == null) { + out = outputOperand; + } else { + if (out.stackLocal != null) { + assert outputOperand.stackLocal == null; + outputOperand.changeStackLocal(out.stackLocal); + } + } } /** - * Sets the operands used by this frame. + * Merges the specified operands with the operands that were previously used with this + * instruction. * - * @param oprs the operands. - */ - void setIn(@Nonnull Operand... oprs) { - in.clear(); - in.add(oprs); - inStackLocals = new Local[oprs.length]; - } - - /** - * Sets the operands produced by this frame. + *

To convert from the stack-based instructions to register-based instructions, all possible + * combinations of branches need to be walked, because the contents of the operand stack might be + * different when coming from different branches. * - * @param oprs the operands. - */ - void setOut(@Nonnull Operand... oprs) { - out = oprs; - } - - /** - * Merges the specified operands with the operands used by this frame. + *

Take the following code as an example: + * + *

System.out.println(n == 1 ? a : "two");
+ * + * If the first branch is taken `a` will be on the stack when the `println` gets invoked, if the + * second branch is taken `"two"` will be on the stack when the `println` gets invoked. This + * method will merge the two (or more) diverging operands by creating a local variable that the + * value of both operands will be assigned to in their respective branches. That local will be + * used when invoking the `println` method. + * + *

TODO somewhat easy to forget calling this after `pop`ing all required operands, maybe + * automate this in some way? * * @param oprs the new operands. * @throws IllegalArgumentException if the number of new operands is not equal to the number of * old operands. */ - void mergeIn(@Nonnull Operand... oprs) { - if (in.get(0).length != oprs.length || oprs.length == 0) { + void mergeInputs(@Nonnull Operand... oprs) { + if (in.isEmpty()) { + in.add(oprs); + // There are no other operands to merge with + return; + } + + if (in.get(0).length != oprs.length) { throw new IllegalArgumentException("Invalid in operands length!"); } + if (oprs.length == 0) { + // No operands to merge + return; + } + for (int i = 0; i < oprs.length; i++) { Operand newOp = oprs[i]; + Local stack = null; - Local stack = inStackLocals[i]; - if (stack != null) { - newOp.changeStackLocal(stack); - } else { - if (in.get(0)[i].value == newOp.value) { - // all branches have the same value, so no stack local is needed to converge the values - continue; + // Search for a stack local that was already allocated for an operand in a different branch + for (int j = 0; j != in.size(); j++) { + stack = in.get(j)[i].stackLocal; + if (stack != null) { + break; } + } - // Search for a stack local that was already allocated for an operand in a different branch - for (int j = 0; j != in.size(); j++) { - stack = in.get(j)[i].stackLocal; - if (stack != null) { - break; - } - } + // The incoming operand may already have a stack local allocated that can be re-used + if (stack == null && newOp.stackLocal != null) { + stack = newOp.stackLocal; + } - // The incoming operand may already have a stack local allocated that can be re-used - if (stack == null && newOp.stackLocal != null) { - stack = newOp.stackLocal; - } + if (stack == null && in.get(0)[i].value.equivTo(newOp.value)) { + // all branches have the same value, + // and no stack local was allocated yet, + // so no stack local is needed to converge the values + continue; + } - // Didn't find any pre-allocated stack local from any operand. - // So create a new stack local. - // TODO use a special case when the statement is an assignment to a local since in that case - // we can use the local directly instead of creating a new stack local - if (stack == null) { - stack = src.newStackLocal(); - } + // Didn't find any pre-allocated stack local from any operand. + // So create a new stack local. + // TODO use a special case when the statement is an assignment to a local since in that case + // we can use the local directly instead of creating a new stack local + if (stack == null) { + stack = src.newStackLocal(); + } - /* add assign statement for prevOp */ - for (int j = 0; j != in.size(); j++) { - Operand prevOp = in.get(j)[i]; - prevOp.changeStackLocal(stack); - } - newOp.changeStackLocal(stack); - - inStackLocals[i] = stack; - // TODO `in.get(0)` is weird because of the index? - // TODO make it more obvious that this is only run the first time (because `inStackLocals[i] - // == null`) - // replace the operand in the statement that *started* the merge - ReplaceUseStmtVisitor replaceUseStmtVisitor = - new ReplaceUseStmtVisitor(in.get(0)[i].value, stack); - // TODO how to handle the same value being in the the statement multiple times but only one - // time because of the operand? (Something like `System.out.println(operand, "hello")` with - // the operand also having the value "two") - // this might require a callback(?) to change the statement; alternative we could do the - // merging *before* constructing the statement and then replace the statement if it differs - // from an already existing one - // This actually works right now because the `ReplaceUseExprVisitor` only checks object - // equality meaning the - // two instances of the constant are different and only the correct instance is replaced - Stmt oldStatement = this.src.getStmt(this.insn); - // TODO `oldStatement` might not exist when a STORE instruction was used to set the - // stackLocal + /* add assign statement for prevOp */ + for (int j = 0; j != in.size(); j++) { + Operand prevOp = in.get(j)[i]; + prevOp.changeStackLocal(stack); + } + newOp.changeStackLocal(stack); + + // TODO `in.get(0)` is weird because of the index? + // TODO make it more obvious that this is only run the first time + // replace the operand in the statement that *started* the merge + ReplaceUseStmtVisitor replaceUseStmtVisitor = + new ReplaceUseStmtVisitor(in.get(0)[i].value, stack); + // TODO how to handle the same value being in the the statement multiple times but only one + // time because of the operand? (Something like `System.out.println(operand, "hello")` with + // the operand also having the value "two") + // this might require a callback(?) to change the statement; alternative we could do the + // merging *before* constructing the statement and then replace the statement if it differs + // from an already existing one + // This actually works right now because the `ReplaceUseExprVisitor` only checks object + // equality meaning the + // two instances of the constant are different and only the correct instance is replaced + Stmt oldStatement = this.src.getStmt(this.insn); + // TODO `oldStatement` might not exist when a STORE instruction was used to set the + // stackLocal + if (oldStatement != null) { oldStatement.accept(replaceUseStmtVisitor); this.src.replaceStmt(oldStatement, replaceUseStmtVisitor.getResult()); }