From 92ae009c03f2e15cc59c89f164fa6b7eb2291d0a Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Fri, 3 Nov 2023 14:17:48 +0100 Subject: [PATCH 01/14] fix stale result in `ReplaceUseStmtVisitor` Certain assign statement would produce in no result. Because the visitor is stateful, this would result in an old result being used later. --- .../sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java b/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java index 59623ce9a66..f1986b1d82c 100644 --- a/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java +++ b/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java @@ -71,6 +71,8 @@ public void caseInvokeStmt(@Nonnull JInvokeStmt stmt) { @Override public void caseAssignStmt(@Nonnull JAssignStmt stmt) { + // fall back to the original statement when none of the cases set a result + setResult(stmt); // uses on the def side.. e.g. a base in an JArrayRef but NOT with a simple Local! final Value leftOp = stmt.getLeftOp(); @@ -106,8 +108,6 @@ public void caseAssignStmt(@Nonnull JAssignStmt stmt) { if (exprVisitor.getResult() != rValue) { setResult(stmt.withRValue(exprVisitor.getResult())); } - } else { - setResult(stmt); } } From 48579b5d3e5430ec75748a641606db633b497f45 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Fri, 3 Nov 2023 15:25:27 +0100 Subject: [PATCH 02/14] fix issues with the java bytecode frontend Moved the logic for assigning the values of operands to stack locals out of `OperandStack` and into `Operand`. That means that the assignments are created when the operands are used as a local/immediate with the `toLocal` and `toImmediate` methods instead of when they are popped of the stack with `popLocal` and `popImmediate`. This fixes some issues where the wrong `pop` method was used which caused unnecessary stack local variables to get created. This also results in improved type-safety and gets rid of a bunch of casts, because `toLocal` and `toImmediate` actually return the `Local` and `Immediate` types instead of a generic `Operand`. Significantly simplified merging logic in `StackFrame`. Factored a bunch of duplicated code into the new `changeStackLocal` in `Operand`. The old version actually incorrectly merged operands at times (See `SwitchExprWithYieldTest`). I will add a test for this in a later commit. Fixed updating usages of the `value`/`stackLocal` in `Operand`. This is now part of the `changeStackLocal` method. Simplified duplication instructions (Thanks to `Operand.DWORD_DUMMY`). Removed some commented out code. --- .../bytecode/frontend/AsmMethodSource.java | 297 +++++++----------- .../java/bytecode/frontend/Operand.java | 134 ++++---- .../java/bytecode/frontend/OperandStack.java | 72 +---- .../java/bytecode/frontend/StackFrame.java | 131 +++----- 4 files changed, 228 insertions(+), 406 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 9e5f98859b3..fa8e87a9a62 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 @@ -354,12 +354,11 @@ private void addReadOperandAssignments_internal(BiFunction val1, val2, val1 // value1, value2 must not be of type double or long - Operand o2 = operandStack.popImmediate(); + Operand o2 = operandStack.pop(); operandStack.push(dupd); operandStack.push(o2); operandStack.push(dupd); } else if (op == DUP_X2) { // value3, value2, value1 -> value1, value3, value2, value1 - Operand o2 = operandStack.popImmediate(); - Operand o3 = - operandStack.peek() == Operand.DWORD_DUMMY - ? operandStack.pop() - : operandStack.popImmediate(); + Operand o2 = operandStack.pop(); + // pops either the `Operand.DWORD_DUMMY` or the third value + Operand o3 = operandStack.pop(); operandStack.push(dupd); operandStack.push(o3); operandStack.push(o2); @@ -602,7 +579,7 @@ private void convertDupInsn(@Nonnull InsnNode insn) { } else if (op == DUP2_X1) { // value3, value2, value1 -> value2, value1, value3, value2, value1 // Attention: value2 may be - Operand o2 = operandStack.popImmediate(); + Operand o2 = operandStack.pop(); operandStack.push(dupd2); operandStack.push(dupd); operandStack.push(o2); @@ -611,11 +588,8 @@ private void convertDupInsn(@Nonnull InsnNode insn) { } else if (op == DUP2_X2) { // (value4, value3), (value2, value1) -> (value2, value1), (value4, value3), // (value2, value1) - Operand o2 = operandStack.popImmediate(); - Operand o2h = - operandStack.peek() == Operand.DWORD_DUMMY - ? operandStack.pop() - : operandStack.popImmediate(); + Operand o2 = operandStack.pop(); + Operand o2h = operandStack.pop(); operandStack.push(dupd2); operandStack.push(dupd); operandStack.push(o2h); @@ -653,11 +627,11 @@ private void convertBinopInsn(@Nonnull InsnNode insn) { if (out == null) { Operand op2 = (dword && op != LSHL && op != LSHR && op != LUSHR) - ? operandStack.popImmediateDual() - : operandStack.popImmediate(); - Operand op1 = dword ? operandStack.popImmediateDual() : operandStack.popImmediate(); - Immediate v1 = (Immediate) op1.stackOrValue(); - Immediate v2 = (Immediate) op2.stackOrValue(); + ? 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); @@ -691,8 +665,6 @@ private void convertBinopInsn(@Nonnull InsnNode insn) { throw new UnsupportedOperationException("Unknown binop: " + op); } opr = new Operand(insn, binop, this); - op1.addUsageInExpr(binop); - op2.addUsageInExpr(binop); frame.setIn(op2, op1); frame.setOut(opr); @@ -701,12 +673,12 @@ private void convertBinopInsn(@Nonnull InsnNode insn) { if (dword) { if (op != LSHL && op != LSHR && op != LUSHR) { - frame.mergeIn(currentLineNumber, operandStack.popDual(), operandStack.popDual()); + frame.mergeIn(operandStack.popDual(), operandStack.popDual()); } else { - frame.mergeIn(currentLineNumber, operandStack.pop(), operandStack.popDual()); + frame.mergeIn(operandStack.pop(), operandStack.popDual()); } } else { - frame.mergeIn(currentLineNumber, operandStack.pop(), operandStack.pop()); + frame.mergeIn(operandStack.pop(), operandStack.pop()); } } if (dword && op < LCMP) { @@ -723,23 +695,21 @@ private void convertUnopInsn(@Nonnull InsnNode insn) { Operand[] out = frame.getOut(); Operand opr; if (out == null) { - Operand op1 = dword ? operandStack.popImmediateDual() : operandStack.popImmediate(); - Value v1 = op1.stackOrValue(); + Operand op1 = dword ? operandStack.popDual() : operandStack.pop(); AbstractUnopExpr unop; if (op >= INEG && op <= DNEG) { - unop = Jimple.newNegExpr((Immediate) v1); + unop = Jimple.newNegExpr(op1.toImmediate()); } else if (op == ARRAYLENGTH) { - unop = Jimple.newLengthExpr((Immediate) v1); + unop = Jimple.newLengthExpr(op1.toImmediate()); } else { throw new UnsupportedOperationException("Unknown unop: " + op); } - op1.addUsageInExpr(unop); opr = new Operand(insn, unop, this); frame.setIn(op1); frame.setOut(opr); } else { opr = out[0]; - frame.mergeIn(currentLineNumber, dword ? operandStack.popDual() : operandStack.pop()); + frame.mergeIn(dword ? operandStack.popDual() : operandStack.pop()); } if (dword) { operandStack.pushDual(opr); @@ -790,15 +760,14 @@ private void convertPrimCastInsn(@Nonnull InsnNode insn) { default: throw new IllegalStateException("Unknown prim cast op: " + op); } - Operand val = fromd ? operandStack.popImmediateDual() : operandStack.popImmediate(); - JCastExpr cast = Jimple.newCastExpr((Immediate) val.stackOrValue(), totype); + Operand val = fromd ? operandStack.popDual() : operandStack.pop(); + JCastExpr cast = Jimple.newCastExpr(val.toImmediate(), totype); opr = new Operand(insn, cast, this); - val.addUsageInExpr(cast); frame.setIn(val); frame.setOut(opr); } else { opr = out[0]; - frame.mergeIn(currentLineNumber, fromd ? operandStack.popDual() : operandStack.pop()); + frame.mergeIn(fromd ? operandStack.popDual() : operandStack.pop()); } if (tod) { operandStack.pushDual(opr); @@ -812,14 +781,13 @@ private void convertReturnInsn(@Nonnull InsnNode insn) { boolean dword = op == LRETURN || op == DRETURN; StackFrame frame = operandStack.getOrCreateStackframe(insn); if (!insnToStmt.containsKey(insn)) { - Operand val = dword ? operandStack.popImmediateDual() : operandStack.popImmediate(); - JReturnStmt ret = Jimple.newReturnStmt((Immediate) val.stackOrValue(), getStmtPositionInfo()); + Operand val = dword ? operandStack.popDual() : operandStack.pop(); + JReturnStmt ret = Jimple.newReturnStmt(val.toImmediate(), getStmtPositionInfo()); frame.setIn(val); setStmt(insn, ret); - val.addUsageInStmt(ret); } else { final Operand operand = dword ? operandStack.popDual() : operandStack.pop(); - frame.mergeIn(currentLineNumber, operand); + frame.mergeIn(operand); } } @@ -839,19 +807,16 @@ private void convertInsn(@Nonnull InsnNode insn) { } else if (op >= IASTORE && op <= SASTORE) { convertArrayStoreInsn(insn); } else if (op == POP) { - operandStack.popImmediate(); + operandStack.pop().emitStatement(); } else if (op == POP2) { - operandStack.popImmediate(); - if (operandStack.peek() == Operand.DWORD_DUMMY) { - operandStack.pop(); - } else { - operandStack.popImmediate(); - } + operandStack.pop().emitStatement(); + // pops the `Operand.DWORD_DUMMY` or the second value + operandStack.pop().emitStatement(); } else if (op >= DUP && op <= DUP2_X2) { convertDupInsn(insn); } else if (op == SWAP) { - Operand o1 = operandStack.popImmediate(); - Operand o2 = operandStack.popImmediate(); + Operand o1 = operandStack.pop(); + Operand o2 = operandStack.pop(); operandStack.push(o1); operandStack.push(o2); } else if ((op >= IADD && op <= DREM) @@ -872,15 +837,14 @@ private void convertInsn(@Nonnull InsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); Operand opr; if (!insnToStmt.containsKey(insn)) { - opr = operandStack.popImmediate(); - JThrowStmt ts = Jimple.newThrowStmt((Immediate) opr.stackOrValue(), getStmtPositionInfo()); + opr = operandStack.pop(); + JThrowStmt ts = Jimple.newThrowStmt(opr.toImmediate(), getStmtPositionInfo()); frame.setIn(opr); frame.setOut(opr); setStmt(insn, ts); - opr.addUsageInStmt(ts); } else { opr = operandStack.pop(); - frame.mergeIn(currentLineNumber, opr); + frame.mergeIn(opr); } operandStack.push(opr); } else if (op == MONITORENTER || op == MONITOREXIT) { @@ -889,13 +853,12 @@ private void convertInsn(@Nonnull InsnNode insn) { Operand opr = operandStack.popStackConst(); Stmt ts = op == MONITORENTER - ? Jimple.newEnterMonitorStmt((Immediate) opr.stackOrValue(), getStmtPositionInfo()) - : Jimple.newExitMonitorStmt((Immediate) opr.stackOrValue(), getStmtPositionInfo()); + ? Jimple.newEnterMonitorStmt(opr.toImmediate(), getStmtPositionInfo()) + : Jimple.newExitMonitorStmt(opr.toImmediate(), getStmtPositionInfo()); frame.setIn(opr); setStmt(insn, ts); - opr.addUsageInStmt(ts); } else { - frame.mergeIn(currentLineNumber, operandStack.pop()); + frame.mergeIn(operandStack.pop()); } } else { throw new UnsupportedOperationException("Unknown insn op: " + op); @@ -942,10 +905,8 @@ private void convertIntInsn(@Nonnull IntInsnNode insn) { default: throw new UnsupportedOperationException("Unknown NEWARRAY type!"); } - Operand size = operandStack.popImmediate(); - JNewArrayExpr anew = - JavaJimple.getInstance().newNewArrayExpr(type, (Immediate) size.stackOrValue()); - size.addUsageInExpr(anew); + Operand size = operandStack.pop(); + JNewArrayExpr anew = JavaJimple.getInstance().newNewArrayExpr(type, size.toImmediate()); frame.setIn(size); v = anew; } @@ -954,7 +915,7 @@ private void convertIntInsn(@Nonnull IntInsnNode insn) { } else { opr = out[0]; if (op == NEWARRAY) { - frame.mergeIn(currentLineNumber, operandStack.pop()); + frame.mergeIn(operandStack.pop()); } } operandStack.push(opr); @@ -973,16 +934,14 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { /* must be ifX insn */ StackFrame frame = operandStack.getOrCreateStackframe(insn); if (!insnToStmt.containsKey(insn)) { - Operand val = operandStack.popImmediate(); - Immediate v = (Immediate) val.stackOrValue(); + Operand val = operandStack.pop(); + Immediate v = val.toImmediate(); AbstractConditionExpr cond; - boolean isCmp = false; - Operand val1 = null; + Operand val1; if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { - isCmp = true; - val1 = operandStack.popImmediate(); - Immediate v1 = (Immediate) val1.stackOrValue(); + val1 = operandStack.pop(); + Immediate v1 = val1.toImmediate(); switch (op) { case IF_ICMPEQ: case IF_ACMPEQ: @@ -1007,8 +966,6 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { default: throw new UnsupportedOperationException("Unknown if op: " + op); } - val1.addUsageInExpr(cond); - val.addUsageInExpr(cond); frame.setIn(val, val1); } else { switch (op) { @@ -1039,22 +996,16 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { default: throw new UnsupportedOperationException("Unknown if op: " + op); } - val.addUsageInExpr(cond); frame.setIn(val); } BranchingStmt ifStmt = Jimple.newIfStmt(cond, getStmtPositionInfo()); stmtsThatBranchToLabel.put(ifStmt, insn.label); setStmt(insn, ifStmt); - if (isCmp) { - val1.addUsageInStmt(ifStmt); - } - - val.addUsageInStmt(ifStmt); } else { if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { - frame.mergeIn(currentLineNumber, operandStack.pop(), operandStack.pop()); + frame.mergeIn(operandStack.pop(), operandStack.pop()); } else { - frame.mergeIn(currentLineNumber, operandStack.pop()); + frame.mergeIn(operandStack.pop()); } } } @@ -1131,8 +1082,8 @@ private JFieldRef toSootFieldRef(Handle methodHandle) { || kind == MethodHandle.Kind.REF_PUT_FIELD_STATIC.getValue()) { return Jimple.newStaticFieldRef(fieldSignature); } else { - Operand base = operandStack.popLocal(); - return Jimple.newInstanceFieldRef((Local) base.stackOrValue(), fieldSignature); + Operand base = operandStack.pop(); + return Jimple.newInstanceFieldRef(base.toLocal(), fieldSignature); } } @@ -1155,17 +1106,17 @@ private MethodSignature toMethodSignature(Handle methodHandle) { private void convertLookupSwitchInsn(@Nonnull LookupSwitchInsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); if (insnToStmt.containsKey(insn)) { - frame.mergeIn(currentLineNumber, operandStack.pop()); + frame.mergeIn(operandStack.pop()); return; } - Operand key = operandStack.popImmediate(); + Operand key = operandStack.pop(); List keys = new ArrayList<>(insn.keys.size()); for (Integer i : insn.keys) { keys.add(IntConstant.getInstance(i)); } JSwitchStmt lookupSwitchStmt = - Jimple.newLookupSwitchStmt((Immediate) key.stackOrValue(), keys, getStmtPositionInfo()); + Jimple.newLookupSwitchStmt(key.toImmediate(), keys, getStmtPositionInfo()); // uphold insertion order! stmtsThatBranchToLabel.putAll(lookupSwitchStmt, insn.labels); @@ -1173,7 +1124,6 @@ private void convertLookupSwitchInsn(@Nonnull LookupSwitchInsnNode insn) { frame.setIn(key); setStmt(insn, lookupSwitchStmt); - key.addUsageInStmt(lookupSwitchStmt); } private void convertMethodInsn(@Nonnull MethodInsnNode insn) { @@ -1207,15 +1157,15 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { operands = new Operand[nrArgs + 1]; while (nrArgs-- > 0) { - operands[nrArgs] = operandStack.popImmediate(sigTypes.get(nrArgs)); - argList[nrArgs] = (Immediate) operands[nrArgs].stackOrValue(); + operands[nrArgs] = operandStack.pop(sigTypes.get(nrArgs)); + argList[nrArgs] = operands[nrArgs].toImmediate(); } args = Arrays.asList(argList); } - final Operand baseOperand = operandStack.popLocal(); + final Operand baseOperand = operandStack.pop(); operands[operands.length - 1] = baseOperand; - Local base = (Local) baseOperand.stackOrValue(); + Local base = baseOperand.toLocal(); switch (op) { case INVOKESPECIAL: @@ -1230,8 +1180,6 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { default: throw new UnsupportedOperationException("Unknown invoke op:" + op); } - baseOperand.addUsageInExpr(invoke); - } else { if (nrArgs == 0) { operands = null; @@ -1244,8 +1192,8 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { do { nrArgs--; - operands[nrArgs] = operandStack.popImmediate(sigTypes.get(nrArgs)); - argList[nrArgs] = (Immediate) operands[nrArgs].stackOrValue(); + operands[nrArgs] = operandStack.pop(sigTypes.get(nrArgs)); + argList[nrArgs] = operands[nrArgs].toImmediate(); } while (nrArgs > 0); invoke = Jimple.newStaticInvokeExpr(methodSignature, Arrays.asList(argList)); @@ -1253,9 +1201,6 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { } if (operands != null) { - for (int i = 0; i < sigTypes.size(); i++) { - operands[i].addUsageInExpr(invoke); - } frame.setIn(operands); } opr = new Operand(insn, invoke, this); @@ -1274,7 +1219,7 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { oprs[nrArgs] = operandStack.pop(types.get(nrArgs)); } oprs[oprs.length - 1] = operandStack.pop(); - frame.mergeIn(currentLineNumber, oprs); + frame.mergeIn(oprs); } else { if (nrArgs > 0) { oprs = new Operand[nrArgs]; @@ -1283,7 +1228,7 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { oprs[nrArgs] = operandStack.pop(types.get(nrArgs)); } while (nrArgs > 0); - frame.mergeIn(currentLineNumber, oprs); + frame.mergeIn(oprs); } } returnType = expr.getMethodSignature().getType(); @@ -1297,7 +1242,6 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { JInvokeStmt stmt = Jimple.newInvokeStmt((AbstractInvokeExpr) opr.value, getStmtPositionInfo()); setStmt(insn, stmt); - opr.addUsageInStmt(stmt); } /* * assign all read ops in case the method modifies any of the fields @@ -1333,8 +1277,8 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { for (int i = nrArgs - 1; i >= 0; i--) { parameterTypes[i] = types.get(i); - args[i] = operandStack.popImmediate(types.get(i)); - methodArgs[i] = (Immediate) args[i].stackOrValue(); + args[i] = operandStack.pop(types.get(i)); + methodArgs[i] = args[i].toImmediate(); } returnType = types.get(types.size() - 1); @@ -1347,9 +1291,6 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { JDynamicInvokeExpr indy = Jimple.newDynamicInvokeExpr( bsmMethodRef, bsmMethodArgs, methodSig, insn.bsm.getTag(), Arrays.asList(methodArgs)); - for (int i = 0; i < types.size() - 1; i++) { - args[i].addUsageInExpr(indy); - } frame.setIn(args); opr = new Operand(insn, indy, this); @@ -1366,7 +1307,7 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { nrArgs--; oprs[nrArgs] = operandStack.pop(types.get(nrArgs)); } - frame.mergeIn(currentLineNumber, oprs); + frame.mergeIn(oprs); } returnType = expr.getType(); } @@ -1378,7 +1319,6 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { JInvokeStmt stmt = Jimple.newInvokeStmt((AbstractInvokeExpr) opr.value, getStmtPositionInfo()); setStmt(insn, stmt); - opr.addUsageInStmt(stmt); } /* * assign all read ops in case the method modifies any of the fields @@ -1396,13 +1336,10 @@ private void convertMultiANewArrayInsn(@Nonnull MultiANewArrayInsnNode insn) { Operand[] sizes = new Operand[dims]; Immediate[] sizeVals = new Immediate[dims]; while (dims-- != 0) { - sizes[dims] = operandStack.popImmediate(); - sizeVals[dims] = (Immediate) sizes[dims].stackOrValue(); + sizes[dims] = operandStack.pop(); + sizeVals[dims] = sizes[dims].toImmediate(); } JNewMultiArrayExpr nm = Jimple.newNewMultiArrayExpr(t, Arrays.asList(sizeVals)); - for (int i = 0; i < dims; i++) { - sizes[i].addUsageInExpr(nm); - } frame.setIn(sizes); opr = new Operand(insn, nm, this); frame.setOut(opr); @@ -1413,7 +1350,7 @@ private void convertMultiANewArrayInsn(@Nonnull MultiANewArrayInsnNode insn) { while (dims-- != 0) { sizes[dims] = operandStack.pop(); } - frame.mergeIn(currentLineNumber, sizes); + frame.mergeIn(sizes); } operandStack.push(opr); } @@ -1421,13 +1358,12 @@ private void convertMultiANewArrayInsn(@Nonnull MultiANewArrayInsnNode insn) { private void convertTableSwitchInsn(@Nonnull TableSwitchInsnNode insn) { StackFrame frame = operandStack.getOrCreateStackframe(insn); if (insnToStmt.containsKey(insn)) { - frame.mergeIn(currentLineNumber, operandStack.pop()); + frame.mergeIn(operandStack.pop()); return; } - Operand key = operandStack.popImmediate(); + Operand key = operandStack.pop(); JSwitchStmt tableSwitchStmt = - Jimple.newTableSwitchStmt( - (Immediate) key.stackOrValue(), insn.min, insn.max, getStmtPositionInfo()); + Jimple.newTableSwitchStmt(key.toImmediate(), insn.min, insn.max, getStmtPositionInfo()); // uphold insertion order! stmtsThatBranchToLabel.putAll(tableSwitchStmt, insn.labels); @@ -1435,7 +1371,6 @@ private void convertTableSwitchInsn(@Nonnull TableSwitchInsnNode insn) { frame.setIn(key); setStmt(insn, tableSwitchStmt); - key.addUsageInStmt(tableSwitchStmt); } private void convertTypeInsn(@Nonnull TypeInsnNode insn) { @@ -1448,38 +1383,29 @@ private void convertTypeInsn(@Nonnull TypeInsnNode insn) { if (op == NEW) { val = Jimple.newNewExpr(AsmUtil.toJimpleClassType(insn.desc)); } else { - Operand op1 = operandStack.popImmediate(); - Value v1 = op1.stackOrValue(); + Operand op1 = operandStack.pop(); switch (op) { case ANEWARRAY: { - JNewArrayExpr expr = + val = JavaJimple.getInstance() - .newNewArrayExpr(AsmUtil.arrayTypetoJimpleType(insn.desc), (Immediate) v1); - val = expr; - op1.addUsageInExpr(expr); + .newNewArrayExpr(AsmUtil.arrayTypetoJimpleType(insn.desc), op1.toImmediate()); break; } case CHECKCAST: { - JCastExpr expr = - Jimple.newCastExpr((Immediate) v1, AsmUtil.toJimpleClassType(insn.desc)); - val = expr; - op1.addUsageInExpr(expr); + val = Jimple.newCastExpr(op1.toImmediate(), AsmUtil.toJimpleClassType(insn.desc)); break; } case INSTANCEOF: { - JInstanceOfExpr expr = - Jimple.newInstanceOfExpr((Immediate) v1, AsmUtil.toJimpleClassType(insn.desc)); - val = expr; - op1.addUsageInExpr(expr); + val = + Jimple.newInstanceOfExpr(op1.toImmediate(), AsmUtil.toJimpleClassType(insn.desc)); break; } default: throw new UnsupportedOperationException("Unknown type op: " + op); } - op1.addUsageInExpr(val); frame.setIn(op1); } opr = new Operand(insn, val, this); @@ -1487,7 +1413,7 @@ private void convertTypeInsn(@Nonnull TypeInsnNode insn) { } else { opr = out[0]; if (op != NEW) { - frame.mergeIn(currentLineNumber, operandStack.pop()); + frame.mergeIn(operandStack.pop()); } } operandStack.push(opr); @@ -1520,12 +1446,14 @@ private void convertVarStoreInsn(@Nonnull VarInsnNode insn) { Local local = getOrCreateLocal(insn.var); if (!insnToStmt.containsKey(insn)) { AbstractDefinitionStmt as = - Jimple.newAssignStmt(local, opr.stackOrValue(), getStmtPositionInfo()); + Jimple.newAssignStmt(local, opr.toImmediate(), getStmtPositionInfo()); + // TODO might be able to `opr.stackLocal = local` instead of converting to an immediate + // by updating all usages of the stackLocal + // when the local isn't used anywhere else frame.setIn(opr); setStmt(insn, as); - opr.addUsageInStmt(as); } else { - frame.mergeIn(currentLineNumber, opr); + frame.mergeIn(opr); } addReadOperandAssignments(local); } @@ -1574,7 +1502,6 @@ private void convertLabel(@Nonnull LabelNode ln) { opr.stackLocal = stack; frame.setOut(opr); setStmt(ln, as); - opr.addUsageInStmt(as); } else { opr = out[0]; } @@ -2032,6 +1959,10 @@ Stmt getLatestVersionOfStmt(@Nonnull Stmt oldStmt) { } void replaceStmt(@Nonnull Stmt oldStmt, Stmt newStmt) { + if (oldStmt == newStmt) { + return; + } + AbstractInsnNode key = null; // TODO: [ms] bit expensive and called a lot? -> find better solution! @@ -2066,9 +1997,9 @@ void replaceStmt(@Nonnull Stmt oldStmt, Stmt newStmt) { /** * * returns all stmts that use this expr * - * @param expr which is used to filter associated Stmts + * @param value which is used to filter associated Stmts */ - public Stream getStmtsThatUse(@Nonnull Expr expr) { + public Stream getStmtsThatUse(@Nonnull Value value) { Stream currentUses = insnToStmt.values().stream() .flatMap( @@ -2076,11 +2007,11 @@ public Stream getStmtsThatUse(@Nonnull Expr expr) { stmt instanceof StmtContainer ? ((StmtContainer) stmt).getStmts().stream() : Stream.of(stmt)) - .filter(stmt -> stmt.getUses().contains(expr)); + .filter(stmt -> stmt.getUses().contains(value)); Stream oldMappedUses = replacedStmt.entrySet().stream() - .filter(stmt -> stmt.getKey().getUses().contains(expr)) + .filter(stmt -> stmt.getKey().getUses().contains(value)) .map(stmt -> getLatestVersionOfStmt(stmt.getValue())); return Stream.concat(currentUses, oldMappedUses); 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 fc960b91903..5cdf2c164d4 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 @@ -1,4 +1,5 @@ package sootup.java.bytecode.frontend; + /*- * #%L * Soot - a J*va Optimization Framework @@ -20,14 +21,16 @@ * . * #L% */ -import java.util.ArrayList; -import java.util.List; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.objectweb.asm.tree.AbstractInsnNode; +import sootup.core.jimple.Jimple; +import sootup.core.jimple.basic.Immediate; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.expr.Expr; +import sootup.core.jimple.common.expr.AbstractInvokeExpr; +import sootup.core.jimple.common.stmt.JAssignStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.jimple.visitor.ReplaceUseStmtVisitor; @@ -41,14 +44,11 @@ class Operand { @SuppressWarnings("ConstantConditions") static final Operand DWORD_DUMMY = new Operand(null, null, null); - @Nonnull protected final AbstractInsnNode insn; + @Nonnull protected AbstractInsnNode insn; @Nonnull protected final Value value; @Nullable protected Local stackLocal; @Nonnull private final AsmMethodSource methodSource; - @Nonnull private final List usedByStmts = new ArrayList<>(); - @Nonnull private final List usedByExpr = new ArrayList<>(); - /** * Constructs a new stack operand. * @@ -62,62 +62,81 @@ class Operand { this.methodSource = methodSource; } - /** - * Adds a usage of this operand (so whenever it is used in a stmt) - * - * @param stmt the usage - */ - void addUsageInStmt(@Nonnull Stmt stmt) { - usedByStmts.add(stmt); - } + Local getOrAssignValueToStackLocal() { + if (stackLocal == null) { + changeStackLocal(methodSource.newStackLocal()); + } - /** - * Adds a usage of this operand (so whenever it is used in a Expr) - * - * @param expr the usage - */ - void addUsageInExpr(@Nonnull Expr expr) { - usedByExpr.add(expr); + return stackLocal; } - /** Updates all statements and expressions that use this Operand. */ - void updateUsages() { - - for (Expr exprUsage : usedByExpr) { - methodSource - .getStmtsThatUse(exprUsage) - .map(methodSource::getLatestVersionOfStmt) - .filter(stmt -> !usedByStmts.contains(stmt)) - .forEach(usedByStmts::add); + void emitStatement() { + if (this == DWORD_DUMMY) { + return; } - if (value == stackOrValue()) return; + if (value instanceof AbstractInvokeExpr) { + methodSource.setStmt( + insn, + Jimple.newInvokeStmt((AbstractInvokeExpr) value, methodSource.getStmtPositionInfo())); + } else { + // create an assignment that uses the value because it might have side effects + getOrAssignValueToStackLocal(); + } + } - ReplaceUseStmtVisitor replaceStmtVisitor = new ReplaceUseStmtVisitor(value, stackOrValue()); + void changeStackLocal(Local newStackLocal) { + Local oldStackLocal = this.stackLocal; - List stmtsToDelete = new ArrayList<>(); + if (oldStackLocal == newStackLocal) { + // nothing to change + return; + } - for (int i = 0; i < usedByStmts.size(); i++) { - Stmt oldUsage = usedByStmts.get(i); + JAssignStmt assignStmt = methodSource.getStmt(insn); + if (assignStmt == null) { + // TODO the position info is the position of the *usage* (which is only mostly correct?) + // emit `$newStackLocal = value` + methodSource.setStmt( + insn, Jimple.newAssignStmt(newStackLocal, value, methodSource.getStmtPositionInfo())); + } else { + assert assignStmt.getLeftOp() == oldStackLocal; + // replace `$oldStackLocal = value` with `$newStackLocal = value` + methodSource.replaceStmt(assignStmt, assignStmt.withVariable(newStackLocal)); + } - // resolve stmt in method source, it might not exist anymore! - oldUsage = methodSource.getLatestVersionOfStmt(oldUsage); + // Replace all usages of `oldStackLocal` with `newStackLocal` + if (oldStackLocal != null) { + ReplaceUseStmtVisitor replaceStmtVisitor = + new ReplaceUseStmtVisitor(oldStackLocal, newStackLocal); + for (Stmt oldUsage : + methodSource.getStmtsThatUse(oldStackLocal).collect(Collectors.toList())) { + oldUsage.accept(replaceStmtVisitor); + Stmt newUsage = replaceStmtVisitor.getResult(); + + if (newUsage != null && oldUsage != newUsage) { + methodSource.replaceStmt(oldUsage, newUsage); + } + } + } - oldUsage.accept(replaceStmtVisitor); - Stmt newUsage = replaceStmtVisitor.getResult(); + this.stackLocal = newStackLocal; + } - if (oldUsage != newUsage) { - methodSource.replaceStmt(oldUsage, newUsage); - usedByStmts.set(i, newUsage); - } + Local toLocal() { + if (stackLocal == null && value instanceof Local) { + return (Local) value; } - usedByStmts.removeAll(stmtsToDelete); + + return getOrAssignValueToStackLocal(); } - /** @return either the stack local allocated for this operand, or its value. */ - @Nonnull - Value stackOrValue() { - return stackLocal == null ? value : stackLocal; + Immediate toImmediate() { + if (stackLocal == null && value instanceof Immediate) { + return (Immediate) value; + } + + return getOrAssignValueToStackLocal(); } /** @@ -127,11 +146,14 @@ Value stackOrValue() { * @return {@code true} if this operand is equal to another operand, {@code false} otherwise. */ boolean equivTo(@Nonnull Operand other) { - // care for DWORD comparison, as stackOrValue is null, which would result in a + Value stackOrValue = stackLocal == null ? value : stackLocal; + Value stackOrValueOther = other.stackLocal == null ? other.value : other.stackLocal; + + // care for DWORD comparison, as asValue is null, which would result in a // NullPointerException return (this == other) || ((this == Operand.DWORD_DUMMY) == (other == Operand.DWORD_DUMMY) - && stackOrValue().equivTo(other.stackOrValue())); + && stackOrValue.equivTo(stackOrValueOther)); } @Override @@ -144,16 +166,6 @@ public AbstractInsnNode getInsn() { return insn; } - @Nonnull - public Value getValue() { - return value; - } - - @Nullable - public Local getStackLocal() { - return stackLocal; - } - @Override public boolean equals(Object other) { return other instanceof Operand && equivTo((Operand) other); diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java index 5e799a3e42a..a5dfc670f9a 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java @@ -27,10 +27,6 @@ import java.util.Map; import javax.annotation.Nonnull; import org.objectweb.asm.tree.AbstractInsnNode; -import sootup.core.jimple.Jimple; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.constant.Constant; import sootup.core.types.Type; /** @@ -52,7 +48,7 @@ public OperandStack(@Nonnull AsmMethodSource methodSource, int nrInsn) { public StackFrame getOrCreateStackframe(@Nonnull AbstractInsnNode insn) { StackFrame frame = frames.get(insn); if (frame == null) { - frame = new StackFrame(methodSource); + frame = new StackFrame(insn, methodSource); frames.put(insn, frame); } return frame; @@ -106,77 +102,15 @@ public Operand pop(@Nonnull Type t) { return AsmUtil.isDWord(t) ? popDual() : pop(); } - @Nonnull - public Operand popLocal(@Nonnull Operand o) { - Value v = o.value; - Local l = o.stackLocal; - if (l == null && !(v instanceof Local)) { - l = o.stackLocal = methodSource.newStackLocal(); - methodSource.setStmt(o.insn, Jimple.newAssignStmt(l, v, methodSource.getStmtPositionInfo())); - o.updateUsages(); - } - return o; - } - - @Nonnull - public Operand popImmediate(@Nonnull Operand o) { - Value v = o.value; - Local l = o.stackLocal; - if (l == null && !(v instanceof Local) && !(v instanceof Constant)) { - l = o.stackLocal = methodSource.newStackLocal(); - methodSource.setStmt(o.insn, Jimple.newAssignStmt(l, v, methodSource.getStmtPositionInfo())); - o.updateUsages(); - } - return o; - } - - @Nonnull - public Operand popStackConst(@Nonnull Operand o) { - Value v = o.value; - Local l = o.stackLocal; - if (l == null && !(v instanceof Constant)) { - l = o.stackLocal = methodSource.newStackLocal(); - methodSource.setStmt(o.insn, Jimple.newAssignStmt(l, v, methodSource.getStmtPositionInfo())); - o.updateUsages(); - } - return o; - } - - @Nonnull - public Operand popLocal() { - return popLocal(pop()); - } - - @SuppressWarnings("unused") - @Nonnull - public Operand popLocalDual() { - return popLocal(popDual()); - } - - @Nonnull - public Operand popImmediate() { - return popImmediate(pop()); - } - - @Nonnull - public Operand popImmediateDual() { - return popImmediate(popDual()); - } - - @Nonnull - public Operand popImmediate(@Nonnull Type t) { - return AsmUtil.isDWord(t) ? popImmediateDual() : popImmediate(); - } - @Nonnull public Operand popStackConst() { - return popStackConst(pop()); + return pop(); } @SuppressWarnings("unused") @Nonnull public Operand popStackConstDual() { - return popStackConst(popDual()); + return popDual(); } @Nonnull 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 4474eef5a7e..1a7ee094eca 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 @@ -23,15 +23,10 @@ import java.util.ArrayList; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import sootup.core.jimple.Jimple; +import org.objectweb.asm.tree.AbstractInsnNode; import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.SimpleStmtPositionInfo; -import sootup.core.jimple.basic.StmtPositionInfo; -import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.stmt.AbstractDefinitionStmt; -import sootup.core.jimple.common.stmt.JAssignStmt; -import sootup.core.jimple.common.stmt.JNopStmt; import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.visitor.ReplaceUseStmtVisitor; /** * Frame of stack for an instruction. (see in = new ArrayList<>(1); @@ -51,7 +46,8 @@ final class StackFrame { * * @param src source the frame belongs to. */ - StackFrame(@Nonnull AsmMethodSource src) { + StackFrame(@Nonnull AbstractInsnNode insn, @Nonnull AsmMethodSource src) { + this.insn = insn; this.src = src; } @@ -88,111 +84,60 @@ void setOut(@Nonnull Operand... oprs) { * @throws IllegalArgumentException if the number of new operands is not equal to the number of * old operands. */ - void mergeIn(int lineNumber, @Nonnull Operand... oprs) { - if (in.get(0).length != oprs.length) { + void mergeIn(@Nonnull Operand... oprs) { + if (in.get(0).length != oprs.length || oprs.length == 0) { throw new IllegalArgumentException("Invalid in operands length!"); } - StmtPositionInfo positionInfo; - if (lineNumber > 0) { - positionInfo = new SimpleStmtPositionInfo(lineNumber); - } else { - positionInfo = StmtPositionInfo.createNoStmtPositionInfo(); - } - - final int nrIn = in.size(); for (int i = 0; i < oprs.length; i++) { Operand newOp = oprs[i]; - /* merge, since prevOp != newOp */ + // TODO skip finding/setting a common stack local when the values of the operands match? + Local stack = inStackLocals[i]; if (stack != null) { - if (newOp.stackLocal == null) { - newOp.stackLocal = stack; - JAssignStmt as = Jimple.newAssignStmt(stack, newOp.value, positionInfo); - src.setStmt(newOp.insn, as); - newOp.updateUsages(); - } else { - final Value rvalue = newOp.stackOrValue(); - // check for self/identity assignments and ignore them - if (stack != rvalue) { - JAssignStmt as = Jimple.newAssignStmt(stack, rvalue, positionInfo); - src.mergeStmts(newOp.insn, as); - } - } + newOp.changeStackLocal(stack); } else { - for (int j = 0; j != nrIn; j++) { + // 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; } } - if (stack == null) { + + // 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) { - 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 != nrIn; j++) { + for (int j = 0; j != in.size(); j++) { Operand prevOp = in.get(j)[i]; - if (prevOp.stackLocal == stack) { - continue; - } - if (prevOp.stackLocal == null) { - prevOp.stackLocal = stack; - JAssignStmt as = Jimple.newAssignStmt(stack, prevOp.value, positionInfo); - src.setStmt(prevOp.insn, as); - } else { - Stmt u = src.getStmt(prevOp.insn); - AbstractDefinitionStmt as = - (AbstractDefinitionStmt) - (u instanceof StmtContainer ? ((StmtContainer) u).getFirstStmt() : u); - Value lvb = as.getLeftOp(); - assert lvb == prevOp.stackLocal : "Invalid stack local!"; - prevOp.stackLocal = stack; - } - prevOp.updateUsages(); - } - if (newOp.stackLocal != stack) { - if (newOp.stackLocal == null) { - newOp.stackLocal = stack; - JAssignStmt as = Jimple.newAssignStmt(stack, newOp.value, positionInfo); - src.setStmt(newOp.insn, as); - } else { - Stmt u = src.getStmt(newOp.insn); - if (!(u instanceof JNopStmt)) { - AbstractDefinitionStmt as = - (AbstractDefinitionStmt) - (u instanceof StmtContainer ? ((StmtContainer) u).getFirstStmt() : u); - Value lvb = as.getLeftOp(); - assert lvb == newOp.stackLocal : "Invalid stack local!"; - } - newOp.stackLocal = stack; - } - newOp.updateUsages(); + 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`) + ReplaceUseStmtVisitor replaceUseStmtVisitor = + new ReplaceUseStmtVisitor(in.get(0)[i].value, stack); + Stmt oldStatement = this.src.getStmt(this.insn); + oldStatement.accept(replaceUseStmtVisitor); + this.src.replaceStmt(oldStatement, replaceUseStmtVisitor.getResult()); } - - /* - * this version uses allocated locals if it finds both operands have stack locals allocated already - */ - /* - * if (stack == null) { if (in.size() != 1) throw new AssertionError("Local h " + in.size()); stack = - * src.newStackLocal(); inStackLocals[i] = stack; ValueBox box = boxes == null ? null : boxes[i]; /* add assign - * statement for prevOp * for (int j = 0; j != nrIn; j++) { Operand prevOp = in.get(j)[i]; prevOp.removeBox(box); if - * (prevOp.stack == null) { prevOp.stack = stack; as = Jimple.v().newAssignStmt(stack, prevOp.value); - * src.setUnit(prevOp.insn, as); prevOp.updateBoxes(); } else { as = Jimple.v().newAssignStmt(stack, - * prevOp.stackOrValue()); src.mergeUnits(prevOp.insn, as); } prevOp.addBox(as.getRightOpBox()); } if (box != null) - * box.setValue(stack); } if (newOp.stack == null) { newOp.stack = stack; as = Jimple.v().newAssignStmt(stack, - * newOp.value); src.setUnit(newOp.insn, as); newOp.updateBoxes(); } else { as = Jimple.v().newAssignStmt(stack, - * newOp.stackOrValue()); src.mergeUnits(newOp.insn, as); } newOp.addBox(as.getRightOpBox()); - */ - } - // add if there is a difference - if (0 < oprs.length) { - in.add(oprs); } + + in.add(oprs); } } From 75ebad47be58a350a62b643c0379207e774c3cf3 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Sat, 4 Nov 2023 17:01:23 +0100 Subject: [PATCH 03/14] don't merge operands when their values match --- .../java/sootup/java/bytecode/frontend/StackFrame.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 1a7ee094eca..be99368b32f 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 @@ -92,12 +92,15 @@ void mergeIn(@Nonnull Operand... oprs) { for (int i = 0; i < oprs.length; i++) { Operand newOp = oprs[i]; - // TODO skip finding/setting a common stack local when the values of the operands match? - 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; From 147c1c5844f0b1a43b9e9bbbd70ac27a62856ec9 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Sat, 4 Nov 2023 17:53:22 +0100 Subject: [PATCH 04/14] start inlining STORE instructions --- .../java6/binary/LocalMerging.class | Bin 0 -> 684 bytes .../java6/source/LocalMerging.java | 18 ++++ .../bytecode/frontend/AsmMethodSource.java | 19 ++-- .../java/bytecode/frontend/Operand.java | 3 + .../java/bytecode/frontend/StackFrame.java | 9 ++ .../java6/LocalMergingTest.java | 97 ++++++++++++++++++ 6 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 shared-test-resources/miniTestSuite/java6/binary/LocalMerging.class create mode 100644 shared-test-resources/miniTestSuite/java6/source/LocalMerging.java create mode 100644 sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java diff --git a/shared-test-resources/miniTestSuite/java6/binary/LocalMerging.class b/shared-test-resources/miniTestSuite/java6/binary/LocalMerging.class new file mode 100644 index 0000000000000000000000000000000000000000..bd06387c6a5fb46f48ba0fc6ece4070c56fae7f2 GIT binary patch literal 684 zcmZvZ%}xR_6opTj!6J+Tqlo{%3&E&7fJPT4CMKXJ7+e=@aCGRHv1oi7cdm7zi3=aV zhf;5=K`^qo?cAPozT5Kt@%jc}7b|Hb;BdGek{DvhU5Fb|359A`j+z&;r5T2{0~P2U z2B%bRq>!Rbq~tKt$Z&WbMld?ytKVH~*)ggvxeHaB7(|uhI8eH-V<|dYDdb3?Z=*DP zjB$*6C}4tN>>n`$zcSWPQCjIc&JIXffogA+8yu4iUNvfouqNX+H5uH!=u9$XtAUb- z-A+@+C!!frXEN;5od)`RFH+Z9D4NwR9h4gkBXuoWmo;%^9UKdT*RxcH>AyKg`dr3# z2!?b$>c%a(9~ff3f3i(;LoA_4?{0n$n0a}sQ}i`QE2A~J_5$`u8{$wk$7atkgITIZ z+9az(Yr*HWCk)TH_u%UfNc!1l0W943&r)D1(W!yoD|7z4du_L9BC@)y!q Bel-99 literal 0 HcmV?d00001 diff --git a/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java b/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java new file mode 100644 index 00000000000..42d0a02d0c7 --- /dev/null +++ b/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java @@ -0,0 +1,18 @@ +public class LocalMerging { + public void localMergingWithConstant(int n) { + String a = "one"; + // The branch returns either a local or a constant. + // Because of the divergence neither `a` nor `"two"` should be inlined, + // but a stack local variable should be created for holding the result of the branch. + System.out.println(n == 1 ? a : "two"); + } + + public void localMergingWithOtherLocal(int n) { + String a = "one"; + String b = "two"; + // The branch returns either a local or a different local. + // Because of the divergence neither `a` nor `b` should be inlined, + // but a stack local variable should be created for holding the result of the branch. + System.out.println(n == 1 ? a : b); + } +} 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 fa8e87a9a62..e30569c662a 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 @@ -1445,13 +1445,20 @@ private void convertVarStoreInsn(@Nonnull VarInsnNode insn) { Operand opr = dword ? operandStack.popDual() : operandStack.pop(); Local local = getOrCreateLocal(insn.var); if (!insnToStmt.containsKey(insn)) { - AbstractDefinitionStmt as = - Jimple.newAssignStmt(local, opr.toImmediate(), getStmtPositionInfo()); - // TODO might be able to `opr.stackLocal = local` instead of converting to an immediate - // by updating all usages of the stackLocal - // when the local isn't used anywhere else + 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); - setStmt(insn, as); } else { frame.mergeIn(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 5cdf2c164d4..9fe1390ac5a 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 @@ -46,6 +46,9 @@ class Operand { @Nonnull protected AbstractInsnNode insn; @Nonnull protected final Value value; + // TODO probably need to store the `insn` for the STORE instruction when a *real* + // local is used here and use that when merging, + // or more specifically when changing to a different stack local in `changeStackLocal` @Nullable protected Local stackLocal; @Nonnull private final AsmMethodSource methodSource; 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 be99368b32f..0bf124855fa 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 @@ -133,9 +133,18 @@ void mergeIn(@Nonnull Operand... oprs) { // 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 Stmt oldStatement = this.src.getStmt(this.insn); + // TODO `oldStatement` might not exist when a STORE instruction was used to set the + // stackLocal oldStatement.accept(replaceUseStmtVisitor); this.src.replaceStmt(oldStatement, replaceUseStmtVisitor.getResult()); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java new file mode 100644 index 00000000000..f94dacffe6a --- /dev/null +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java @@ -0,0 +1,97 @@ +package sootup.java.bytecode.minimaltestsuite.java6; + +import categories.Java8Test; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import sootup.core.model.SootMethod; +import sootup.java.bytecode.minimaltestsuite.MinimalBytecodeTestSuiteBase; + +@Category(Java8Test.class) +public class LocalMergingTest extends MinimalBytecodeTestSuiteBase { + @Test + public void test() { + SootMethod methodConstant = + loadMethod( + identifierFactory.getMethodSignature( + getDeclaredClassSignature(), + "localMergingWithConstant", + "void", + Collections.singletonList("int"))); + assertJimpleStmts(methodConstant, expectedBodyStmtsConstant()); + + SootMethod methodOtherLocal = + loadMethod( + identifierFactory.getMethodSignature( + getDeclaredClassSignature(), + "localMergingWithOtherLocal", + "void", + Collections.singletonList("int"))); + assertJimpleStmts(methodOtherLocal, expectedBodyStmtsOtherLocal()); + } + + /** + * + * + *
+   * public void localMergingWithConstant(int n) {
+   *     String a = "one";
+   *     // The branch returns either a local or a constant.
+   *     // Because of the divergence neither `a` nor `"two"` should be inlined,
+   *     // but a stack local variable should be created for holding the result of the branch.
+   *     System.out.println(n == 1 ? a : "two");
+   * }
+   * 
+ */ + public List expectedBodyStmtsConstant() { + return Stream.of( + "$l0 := @this: LocalMerging", + "$l1 := @parameter0: int", + "$l2 = \"one\"", + "$stack3 = ", + "if $l1 != 1 goto label1", + "$stack4 = $l2", + "goto label2", + "label1:", + "$stack4 = \"two\"", + "label2:", + "virtualinvoke $stack3.($stack4)", + "return") + .collect(Collectors.toList()); + } + + /** + * + * + *
+   * public void localMergingWithOtherLocal(int n) {
+   *     String a = "one";
+   *     String b = "two";
+   *     // The branch returns either a local or a different local.
+   *     // Because of the divergence neither `a` nor `b` should be inlined,
+   *     // but a stack local variable should be created for holding the result of the branch.
+   *     System.out.println(n == 1 ? a : b);
+   * }
+   * 
+ */ + public List expectedBodyStmtsOtherLocal() { + return Stream.of( + "$l0 := @this: LocalMerging", + "$l1 := @parameter0: int", + "$l2 = \"one\"", + "$l3 = \"two\"", + "$stack4 = ", + "if $l1 != 1 goto label1", + "$stack5 = $l2", + "goto label2", + "label1:", + "$stack5 = $l3", + "label2:", + "virtualinvoke $stack4.($stack5)", + "return") + .collect(Collectors.toList()); + } +} From 5d440658894c78fc9834eca57f38da7334380984 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Sat, 4 Nov 2023 21:42:23 +0100 Subject: [PATCH 05/14] add test for merging with duplicate values --- .../java6/binary/LocalMerging.class | Bin 684 -> 885 bytes .../java6/source/LocalMerging.java | 9 +++++++++ .../sootup/java/bytecode/frontend/Operand.java | 5 +++++ .../java/bytecode/frontend/StackFrame.java | 3 +++ 4 files changed, 17 insertions(+) diff --git a/shared-test-resources/miniTestSuite/java6/binary/LocalMerging.class b/shared-test-resources/miniTestSuite/java6/binary/LocalMerging.class index bd06387c6a5fb46f48ba0fc6ece4070c56fae7f2..eb3b9b9edb71d248d9422dbcfa430f78e4fc8c0b 100644 GIT binary patch delta 467 zcmZXPO-lk%7=_Or9Vf;?Yp@SBwGUI1%4ih?MGyp$5Cpo3V35I#L1h|)R{aL?543C3 zrd81*TC{4{k7*a#xuawt7tX!+y!SrudmhxUkpA)W@(N%Ng|7Zy(V=6`KpY8y@RjYI zxUIJBdN%^wh2u->+A29#vtBy&+(xs$Gw!U8TN>uOpJ!sZw;{GNX{S}QoU-lK`M=um zE)F_vr%|&!yJ9&VyLT?KyGbY<2na$zMTnZ>EyZ1MSBp;&k30yZsj@830vRk&Q@uBl zJCcc7ply4a9wm%p*=ReQ?0ycL>?EnA( delta 249 zcmY+7Jqp555QJwpCXo~hL5PX}QB(ppVkz2t1~20g;1cS}Y z?hO0w%R9Qk{e8ItSYx;kZjO(p8Oc_9W+!v=lyntASpyCPE<9??twR^Oo1Gyi4uXVg zYdZ^CXj5ae(DBALH3BTefsY2N1OO9gjym`>5+7Yq!kkyXKpA`s^b6cUr;?b5MoSBr TSwM(N0Vo;x%Sz0EM~d_ViBTD{ diff --git a/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java b/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java index 42d0a02d0c7..3a5e64e0431 100644 --- a/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java +++ b/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java @@ -15,4 +15,13 @@ public void localMergingWithOtherLocal(int n) { // but a stack local variable should be created for holding the result of the branch. System.out.println(n == 1 ? a : b); } + + public void localMergingWithDuplicateValue(int n) { + String a = "one"; + // One of the branches for the first argument contains the constant "two" + // and the second argument also contains the constant "two". + // This test ensures that when the first argument gets replaced by a stack local, + // the second argument isn't replaced as well. + System.setProperty(n == 1 ? a : "two", "two"); + } } 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 9fe1390ac5a..ce1d463a388 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 @@ -78,6 +78,11 @@ void emitStatement() { return; } + if (methodSource.getStmt(insn) != null) { + // the operand is already used, which means side effects already happen as well + return; + } + if (value instanceof AbstractInvokeExpr) { methodSource.setStmt( insn, 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 0bf124855fa..2f352876506 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 @@ -142,6 +142,9 @@ void mergeIn(@Nonnull Operand... oprs) { // 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 From ce285415ca772d8bfc561f7cc31d1bddd93c8d8e Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Sat, 4 Nov 2023 21:44:28 +0100 Subject: [PATCH 06/14] fix tests for bytecode frontend changes --- .../java10/TypeInferenceTest.java | 10 +- .../java14/SwitchExprWithYieldTest.java | 43 ++- .../java6/AccessArraysTest.java | 36 +- .../java6/AssertStatementTest.java | 27 +- .../java6/BooleanOperatorsTest.java | 87 ++--- .../java6/BreakInWhileLoopTest.java | 3 +- .../java6/ContinueInWhileLoopTest.java | 4 +- .../java6/DeclareEnumTest.java | 4 +- .../java6/DoWhileLoopTest.java | 4 +- .../java6/ForEachLoopTest.java | 4 +- .../minimaltestsuite/java6/ForLoopTest.java | 4 +- .../java6/IfElseStatementTest.java | 21 +- .../Initialize3DimensionalArraysTest.java | 324 +++++++++--------- .../InitializeMultidimensionalArraysTest.java | 139 ++++---- .../java6/LabelStatementTest.java | 4 +- .../java6/LabelledLoopBreakTest.java | 8 +- .../java6/LocalMergingTest.java | 39 +++ .../java6/ReferencingThisTest.java | 10 +- .../java6/StatementEvalTest.java | 2 +- .../java6/SynchronizedBlockTest.java | 21 +- .../java6/TryCatchFinallyTest.java | 47 ++- .../java6/UnaryOpIntTest.java | 6 +- .../minimaltestsuite/java6/WhileLoopTest.java | 4 +- .../java7/MultiTryCatchTest.java | 51 ++- .../SwitchCaseStatementWithStringTest.java | 11 +- .../java7/TryWithResourcesTest.java | 39 +-- .../java9/TryWithResourcesConciseTest.java | 31 +- 27 files changed, 459 insertions(+), 524 deletions(-) diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java10/TypeInferenceTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java10/TypeInferenceTest.java index a826432df6d..60ded75e68b 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java10/TypeInferenceTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java10/TypeInferenceTest.java @@ -57,12 +57,10 @@ public List expectedBodyStmts() { "specialinvoke $stack6.(java.io.Reader)>($l3)", "$l4 = $stack6", "label1:", - "$stack9 = $l4", - "$stack7 = virtualinvoke $stack9.()", - "$l2 = $stack7", - "if $stack7 == null goto label2", - "$stack8 = ", - "virtualinvoke $stack8.($l2)", + "$l2 = virtualinvoke $l4.()", + "if $l2 == null goto label2", + "$stack7 = ", + "virtualinvoke $stack7.($l2)", "goto label1", "label2:", "virtualinvoke $l4.()", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/SwitchExprWithYieldTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/SwitchExprWithYieldTest.java index 3389ac236e4..82994ee2dea 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/SwitchExprWithYieldTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/SwitchExprWithYieldTest.java @@ -64,6 +64,13 @@ public MethodSignature getMethodSignature() { * } * }; * + * switch (k) { + * case 1: + * s += "single"; + * default: + * s += "somethingElse"; + * }; + * * System.out.println(s); * } * @@ -80,59 +87,51 @@ public List expectedBodyStmts() { "case 3: goto label02", "default: goto label03", "label01:", - "$stack11 = \"single\"", + "$l2 = \"single\"", "goto label04", "label02:", - "$stack11 = \"double\"", + "$l2 = \"double\"", "goto label04", "label03:", - "$stack11 = \"somethingElse\"", + "$l2 = \"somethingElse\"", "label04:", - "$l2 = $stack11", - "$stack12 = $l1", - "switch($stack12)", + "switch($l1)", "case 1: goto label05", "case 2: goto label06", "case 3: goto label06", "default: goto label07", "label05:", - "$stack9 = \"single\"", + "$l2 = \"single\"", "goto label08", "label06:", - "$stack9 = \"double\"", + "$l2 = \"double\"", "goto label08", "label07:", - "$stack9 = \"somethingElse\"", + "$l2 = \"somethingElse\"", "label08:", - "$l2 = $stack9", - "$stack10 = $l1", - "switch($stack10)", + "switch($l1)", "case 1: goto label09", "case 2: goto label10", "case 3: goto label10", "default: goto label11", "label09:", - "$stack7 = \"no fall through\"", + "$l2 = \"no fall through\"", "goto label12", "label10:", - "$stack7 = \"still no fall through\"", + "$l2 = \"still no fall through\"", "goto label12", "label11:", - "$stack7 = \"we will not fall through\"", + "$l2 = \"we will not fall through\"", "label12:", - "$l2 = $stack7", - "$stack8 = $l1", - "switch($stack8)", + "switch($l1)", "case 1: goto label13", "default: goto label14", "label13:", "$l2 = dynamicinvoke \"makeConcatWithConstants\" ($l2) (\"\\u0001single\")", "label14:", - "$stack4 = $l2", - "$stack5 = dynamicinvoke \"makeConcatWithConstants\" ($l2) (\"\\u0001somethingElse\")", + "$l2 = dynamicinvoke \"makeConcatWithConstants\" ($l2) (\"\\u0001somethingElse\")", "$stack3 = ", - "$stack6 = $l2", - "virtualinvoke $stack3.($stack6)", + "virtualinvoke $stack3.($l2)", "return") .collect(Collectors.toCollection(ArrayList::new)); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AccessArraysTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AccessArraysTest.java index 90711f384ea..dc23780ac18 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AccessArraysTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AccessArraysTest.java @@ -76,9 +76,7 @@ public List expectedBodyStmtsIntArrays() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l6", "$l5 = $l5 + 1", @@ -114,9 +112,7 @@ public List expectedBodyStmtsByteArrays() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l6", "$l5 = $l5 + 1", @@ -152,9 +148,7 @@ public List expectedBodyStmtsShortArrays() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l6", "$l5 = $l5 + 1", @@ -190,9 +184,7 @@ public List expectedBodyStmtsLongArrays() { "$l5 = lengthof $l4", "$l6 = 0", "label1:", - "$stack11 = $l6", - "$stack10 = $l5", - "if $stack11 >= $stack10 goto label2", + "if $l6 >= $l5 goto label2", "$l7 = $l4[$l6]", "$l2 = $l7", "$l6 = $l6 + 1", @@ -229,9 +221,7 @@ public List expectedBodyStmtsFloatArrays() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l6", "$l5 = $l5 + 1", @@ -267,9 +257,7 @@ public List expectedBodyStmtsDoubleArrays() { "$l5 = lengthof $l4", "$l6 = 0", "label1:", - "$stack11 = $l6", - "$stack10 = $l5", - "if $stack11 >= $stack10 goto label2", + "if $l6 >= $l5 goto label2", "$l7 = $l4[$l6]", "$l2 = $l7", "$l6 = $l6 + 1", @@ -304,9 +292,7 @@ public List expectedBodyStmtsBooleanArrays() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l6", "$l5 = $l5 + 1", @@ -342,9 +328,7 @@ public List expectedBodyStmtsCharArrays() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l6", "$l5 = $l5 + 1", @@ -378,9 +362,7 @@ public List expectedBodyStmtsStringArrays() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l6", "$l5 = $l5 + 1", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AssertStatementTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AssertStatementTest.java index 283726bc4ec..df0b9f96639 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AssertStatementTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/AssertStatementTest.java @@ -70,12 +70,11 @@ public List expectedBodyStmtsExtend() { "$stack2 = ", "if $stack2 != 0 goto label1", "if \"\" != null goto label1", - "$stack4 = new java.lang.AssertionError", - "specialinvoke $stack4.()>()", - "throw $stack4", + "$stack3 = new java.lang.AssertionError", + "specialinvoke $stack3.()>()", + "throw $stack3", "label1:", - "$stack3 = 4", - "$l1 = $stack3", + "$l1 = 4", "return") .collect(Collectors.toList()); } @@ -98,21 +97,19 @@ public List expectedBodyStmtsExtend2() { "$stack2 = ", "if $stack2 != 0 goto label1", "if \"first\" != null goto label1", - "$stack7 = new java.lang.AssertionError", - "specialinvoke $stack7.()>()", - "throw $stack7", + "$stack5 = new java.lang.AssertionError", + "specialinvoke $stack5.()>()", + "throw $stack5", "label1:", - "$stack6 = 1", - "$l1 = $stack6", + "$l1 = 1", "$stack3 = ", "if $stack3 != 0 goto label2", "if \"second\" != null goto label2", - "$stack5 = new java.lang.AssertionError", - "specialinvoke $stack5.()>()", - "throw $stack5", + "$stack4 = new java.lang.AssertionError", + "specialinvoke $stack4.()>()", + "throw $stack4", "label2:", - "$stack4 = 2", - "$l1 = $stack4", + "$l1 = 2", "return") .collect(Collectors.toList()); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BooleanOperatorsTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BooleanOperatorsTest.java index 870446f17bc..9124029e66e 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BooleanOperatorsTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BooleanOperatorsTest.java @@ -96,9 +96,7 @@ public List expectedBodyStmtsRelationalOpEqual() { "$l0 := @this: BooleanOperators", "$l1 = 0", "label1:", - "$stack3 = $l1", - "$stack2 = 10", - "if $stack3 > $stack2 goto label2", + "if $l1 > 10 goto label2", "$l1 = $l1 + 1", "if $l1 != 5 goto label1", "goto label2", @@ -129,9 +127,7 @@ public List expectedBodyStmtsRelationalOpNotEqual() { "$l1 = 0", "$l2 = \"\"", "label1:", - "$stack4 = $l1", - "$stack3 = 10", - "if $stack4 >= $stack3 goto label2", + "if $l1 >= 10 goto label2", "$l1 = $l1 + 1", "if $l1 == 5 goto label1", "$l2 = \"i != 5\"", @@ -157,15 +153,13 @@ public List expectedBodyStmtsComplementOp() { return Stream.of( "$l0 := @this: BooleanOperators", "$l1 = 1", - "if $l1 == 0 goto label3", + "if $l1 == 0 goto label2", "if $l1 != 0 goto label1", - "$stack2 = 1", + "$l1 = 1", "goto label2", "label1:", - "$stack2 = 0", + "$l1 = 0", "label2:", - "$l1 = $stack2", - "label3:", "return") .collect(Collectors.toList()); } @@ -211,21 +205,15 @@ public List expectedBodyStmtsLogicalOpAnd() { "if $stack6 == 0 goto label1", "$l5 = \"A\"", "label1:", - "$stack15 = $l3", - "$stack14 = $l4", - "$stack7 = $stack15 & $stack14", + "$stack7 = $l3 & $l4", "if $stack7 == 0 goto label2", "$l5 = \"B\"", "label2:", - "$stack13 = $l1", - "$stack12 = $l3", - "$stack8 = $stack13 & $stack12", + "$stack8 = $l1 & $l3", "if $stack8 == 0 goto label3", "$l5 = \"C\"", "label3:", - "$stack11 = $l4", - "$stack10 = $l2", - "$stack9 = $stack11 & $stack10", + "$stack9 = $l4 & $l2", "if $stack9 == 0 goto label4", "$l5 = \"D\"", "label4:", @@ -274,21 +262,15 @@ public List expectedBodyStmtsLogicalOpOr() { "if $stack6 == 0 goto label1", "$l5 = \"A\"", "label1:", - "$stack15 = $l3", - "$stack14 = $l4", - "$stack7 = $stack15 | $stack14", + "$stack7 = $l3 | $l4", "if $stack7 == 0 goto label2", "$l5 = \"B\"", "label2:", - "$stack13 = $l1", - "$stack12 = $l3", - "$stack8 = $stack13 | $stack12", + "$stack8 = $l1 | $l3", "if $stack8 == 0 goto label3", "$l5 = \"C\"", "label3:", - "$stack11 = $l4", - "$stack10 = $l2", - "$stack9 = $stack11 | $stack10", + "$stack9 = $l4 | $l2", "if $stack9 == 0 goto label4", "$l5 = \"D\"", "label4:", @@ -337,21 +319,15 @@ public List expectedBodyStmtsLogicalOpXor() { "if $stack6 == 0 goto label1", "$l5 = \"A\"", "label1:", - "$stack15 = $l3", - "$stack14 = $l4", - "$stack7 = $stack15 ^ $stack14", + "$stack7 = $l3 ^ $l4", "if $stack7 == 0 goto label2", "$l5 = \"B\"", "label2:", - "$stack13 = $l1", - "$stack12 = $l3", - "$stack8 = $stack13 ^ $stack12", + "$stack8 = $l1 ^ $l3", "if $stack8 == 0 goto label3", "$l5 = \"C\"", "label3:", - "$stack11 = $l4", - "$stack10 = $l2", - "$stack9 = $stack11 ^ $stack10", + "$stack9 = $l4 ^ $l2", "if $stack9 == 0 goto label4", "$l5 = \"D\"", "label4:", @@ -400,18 +376,15 @@ public List expectedBodyStmtsConditionalOpAnd() { "if $l2 == 0 goto label1", "$l5 = \"A\"", "label1:", - "$stack8 = $l3", - "if $stack8 == 0 goto label2", + "if $l3 == 0 goto label2", "if $l4 == 0 goto label2", "$l5 = \"B\"", "label2:", - "$stack7 = $l1", - "if $stack7 == 0 goto label3", + "if $l1 == 0 goto label3", "if $l3 == 0 goto label3", "$l5 = \"C\"", "label3:", - "$stack6 = $l4", - "if $stack6 == 0 goto label4", + "if $l4 == 0 goto label4", "if $l2 == 0 goto label4", "$l5 = \"D\"", "label4:", @@ -459,29 +432,22 @@ public List expectedBodyStmtsConditionalOpOr() { "if $l1 != 0 goto label1", "if $l2 == 0 goto label2", "label1:", - "$stack12 = \"A\"", - "$l5 = $stack12", + "$l5 = \"A\"", "label2:", - "$stack11 = $l3", - "if $stack11 != 0 goto label3", + "if $l3 != 0 goto label3", "if $l4 == 0 goto label4", "label3:", - "$stack10 = \"B\"", - "$l5 = $stack10", + "$l5 = \"B\"", "label4:", - "$stack9 = $l1", - "if $stack9 != 0 goto label5", + "if $l1 != 0 goto label5", "if $l3 == 0 goto label6", "label5:", - "$stack8 = \"C\"", - "$l5 = $stack8", + "$l5 = \"C\"", "label6:", - "$stack7 = $l4", - "if $stack7 != 0 goto label7", + "if $l4 != 0 goto label7", "if $l2 == 0 goto label8", "label7:", - "$stack6 = \"D\"", - "$l5 = $stack6", + "$l5 = \"D\"", "label8:", "return") .collect(Collectors.toList()); @@ -504,12 +470,11 @@ public List expectedBodyStmtsConditionalOp() { "$l1 = 5", "$l2 = \"\"", "if $l1 >= 10 goto label1", - "$stack3 = \"i less than 10\"", + "$l2 = \"i less than 10\"", "goto label2", "label1:", - "$stack3 = \"i greater than 10\"", + "$l2 = \"i greater than 10\"", "label2:", - "$l2 = $stack3", "return") .collect(Collectors.toList()); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BreakInWhileLoopTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BreakInWhileLoopTest.java index 13f8090bb5b..0b6cefa0d31 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BreakInWhileLoopTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/BreakInWhileLoopTest.java @@ -41,8 +41,7 @@ public List expectedBodyStmts() { "$l1 = 10", "$l2 = 5", "label1:", - "$stack3 = $l1", - "if $stack3 <= 0 goto label2", + "if $l1 <= 0 goto label2", "$l1 = $l1 + -1", "if $l1 != $l2 goto label1", "goto label2", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ContinueInWhileLoopTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ContinueInWhileLoopTest.java index d7857098ed9..fb8ae4f437b 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ContinueInWhileLoopTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ContinueInWhileLoopTest.java @@ -40,9 +40,7 @@ public List expectedBodyStmts() { "$l0 := @this: ContinueInWhileLoop", "$l1 = 0", "label1:", - "$stack3 = $l1", - "$stack2 = 10", - "if $stack3 >= $stack2 goto label3", + "if $l1 >= 10 goto label3", "if $l1 != 5 goto label2", "$l1 = $l1 + 1", "goto label1", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DeclareEnumTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DeclareEnumTest.java index 3808d68147c..a1b42b6366b 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DeclareEnumTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DeclareEnumTest.java @@ -50,9 +50,7 @@ public List expectedBodyStmts() { "$l2 = lengthof $l1", "$l3 = 0", "label1:", - "$stack7 = $l3", - "$stack6 = $l2", - "if $stack7 >= $stack6 goto label2", + "if $l3 >= $l2 goto label2", "$l4 = $l1[$l3]", "$stack5 = ", "virtualinvoke $stack5.($l4)", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DoWhileLoopTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DoWhileLoopTest.java index 1017b47f52f..20a6a89b107 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DoWhileLoopTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/DoWhileLoopTest.java @@ -38,9 +38,7 @@ public List expectedBodyStmts() { "$l2 = 0", "label1:", "$l2 = $l2 + 1", - "$stack4 = $l1", - "$stack3 = $l2", - "if $stack4 > $stack3 goto label1", + "if $l1 > $l2 goto label1", "return") .collect(Collectors.toList()); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForEachLoopTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForEachLoopTest.java index e8ebbfcbc48..703f3f041ab 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForEachLoopTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForEachLoopTest.java @@ -51,9 +51,7 @@ public List expectedBodyStmts() { "$l4 = lengthof $l3", "$l5 = 0", "label1:", - "$stack9 = $l5", - "$stack8 = $l4", - "if $stack9 >= $stack8 goto label2", + "if $l5 >= $l4 goto label2", "$l6 = $l3[$l5]", "$l2 = $l2 + 1", "$l5 = $l5 + 1", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForLoopTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForLoopTest.java index ea8ceca6ccb..7560b20443b 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForLoopTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ForLoopTest.java @@ -39,9 +39,7 @@ public List expectedBodyStmts() { "$l2 = 0", "$l3 = 0", "label1:", - "$stack5 = $l3", - "$stack4 = $l1", - "if $stack5 >= $stack4 goto label2", + "if $l3 >= $l1 goto label2", "$l2 = $l2 + 1", "$l3 = $l3 + 1", "goto label1", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/IfElseStatementTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/IfElseStatementTest.java index 96bcd00bf53..2f4ec88f70e 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/IfElseStatementTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/IfElseStatementTest.java @@ -83,8 +83,7 @@ public List expectedBodyStmtsIfStatement() { "if $l1 >= 42 goto label1", "$l2 = 1", "label1:", - "$stack3 = $l2", - "return $stack3") + "return $l2") .collect(Collectors.toList()); } @@ -114,8 +113,7 @@ public List expectedBodyStmtsIfElseStatement() { "label1:", "$l2 = 2", "label2:", - "$stack3 = $l2", - "return $stack3") + "return $l2") .collect(Collectors.toList()); } @@ -151,8 +149,7 @@ public List expectedBodyStmtsIfElseIfStatement() { "label2:", "$l2 = 3", "label3:", - "$stack3 = $l2", - "return $stack3") + "return $l2") .collect(Collectors.toList()); } @@ -190,8 +187,7 @@ public List expectedBodyStmtsIfElseCascadingStatement() { "label2:", "$l2 = 3", "label3:", - "$stack3 = $l2", - "return $stack3") + "return $l2") .collect(Collectors.toList()); } @@ -229,8 +225,7 @@ public List expectedBodyStmtsIfElseCascadingInElseStatement() { "label2:", "$l2 = 22", "label3:", - "$stack3 = $l2", - "return $stack3") + "return $l2") .collect(Collectors.toList()); } @@ -274,8 +269,7 @@ public List expectedBodyStmtsIfElseCascadingElseIfStatement() { "label3:", "$l2 = 2", "label4:", - "$stack3 = $l2", - "return $stack3") + "return $l2") .collect(Collectors.toList()); } @@ -319,8 +313,7 @@ public List expectedBodyStmtsIfElseCascadingElseIfInElseStatement() { "label3:", "$l2 = 23", "label4:", - "$stack3 = $l2", - "return $stack3") + "return $l2") .collect(Collectors.toList()); } } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/Initialize3DimensionalArraysTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/Initialize3DimensionalArraysTest.java index 7d25ab2b337..de7ae0dbdae 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/Initialize3DimensionalArraysTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/Initialize3DimensionalArraysTest.java @@ -63,30 +63,30 @@ public MethodSignature getMethodSignature(String methodName) { public List expectedBodyStmtsIntArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (int[][])[2]", + "$stack5 = newarray (int[][])[2]", "$stack3 = newarray (int[])[2]", - "$stack4 = newarray (int)[3]", - "$stack4[0] = 1", - "$stack4[1] = 2", - "$stack4[2] = 3", - "$stack3[0] = $stack4", - "$stack5 = newarray (int)[2]", - "$stack5[0] = 5", - "$stack5[1] = 6", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (int[])[2]", - "$stack7 = newarray (int)[3]", - "$stack7[0] = 7", - "$stack7[1] = 8", - "$stack7[2] = 9", - "$stack6[0] = $stack7", + "$stack2 = newarray (int)[3]", + "$stack2[0] = 1", + "$stack2[1] = 2", + "$stack2[2] = 3", + "$stack3[0] = $stack2", + "$stack4 = newarray (int)[2]", + "$stack4[0] = 5", + "$stack4[1] = 6", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (int[])[2]", + "$stack6 = newarray (int)[3]", + "$stack6[0] = 7", + "$stack6[1] = 8", + "$stack6[2] = 9", + "$stack7[0] = $stack6", "$stack8 = newarray (int)[2]", "$stack8[0] = 10", "$stack8[1] = 11", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -104,30 +104,30 @@ public List expectedBodyStmtsIntArrays() { public List expectedBodyStmtsByteArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (byte[][])[2]", + "$stack5 = newarray (byte[][])[2]", "$stack3 = newarray (byte[])[2]", - "$stack4 = newarray (byte)[3]", - "$stack4[0] = 7", - "$stack4[1] = 8", - "$stack4[2] = 9", - "$stack3[0] = $stack4", - "$stack5 = newarray (byte)[2]", - "$stack5[0] = 10", - "$stack5[1] = 11", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (byte[])[2]", - "$stack7 = newarray (byte)[3]", - "$stack7[0] = 1", - "$stack7[1] = 2", - "$stack7[2] = 3", - "$stack6[0] = $stack7", + "$stack2 = newarray (byte)[3]", + "$stack2[0] = 7", + "$stack2[1] = 8", + "$stack2[2] = 9", + "$stack3[0] = $stack2", + "$stack4 = newarray (byte)[2]", + "$stack4[0] = 10", + "$stack4[1] = 11", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (byte[])[2]", + "$stack6 = newarray (byte)[3]", + "$stack6[0] = 1", + "$stack6[1] = 2", + "$stack6[2] = 3", + "$stack7[0] = $stack6", "$stack8 = newarray (byte)[2]", "$stack8[0] = 5", "$stack8[1] = 6", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -145,28 +145,28 @@ public List expectedBodyStmtsByteArrays() { public List expectedBodyStmtsShortArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (short[][])[2]", + "$stack5 = newarray (short[][])[2]", "$stack3 = newarray (short[])[2]", + "$stack2 = newarray (short)[2]", + "$stack2[0] = 10", + "$stack2[1] = 20", + "$stack3[0] = $stack2", "$stack4 = newarray (short)[2]", - "$stack4[0] = 10", - "$stack4[1] = 20", - "$stack3[0] = $stack4", - "$stack5 = newarray (short)[2]", - "$stack5[0] = 40", - "$stack5[1] = 85", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (short[])[2]", - "$stack7 = newarray (short)[2]", - "$stack7[0] = 56", - "$stack7[1] = 59", - "$stack6[0] = $stack7", + "$stack4[0] = 40", + "$stack4[1] = 85", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (short[])[2]", + "$stack6 = newarray (short)[2]", + "$stack6[0] = 56", + "$stack6[1] = 59", + "$stack7[0] = $stack6", "$stack8 = newarray (short)[2]", "$stack8[0] = 95", "$stack8[1] = 35", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -184,29 +184,29 @@ public List expectedBodyStmtsShortArrays() { public List expectedBodyStmtsLongArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (long[][])[2]", + "$stack5 = newarray (long[][])[2]", "$stack3 = newarray (long[])[2]", - "$stack4 = newarray (long)[2]", - "$stack4[0] = 547087L", - "$stack4[1] = 654786L", - "$stack3[0] = $stack4", - "$stack5 = newarray (long)[3]", - "$stack5[0] = 547287L", - "$stack5[1] = 864645L", - "$stack5[2] = 6533786L", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (long[])[2]", - "$stack7 = newarray (long)[2]", - "$stack7[0] = 34565L", - "$stack7[1] = 234L", - "$stack6[0] = $stack7", + "$stack2 = newarray (long)[2]", + "$stack2[0] = 547087L", + "$stack2[1] = 654786L", + "$stack3[0] = $stack2", + "$stack4 = newarray (long)[3]", + "$stack4[0] = 547287L", + "$stack4[1] = 864645L", + "$stack4[2] = 6533786L", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (long[])[2]", + "$stack6 = newarray (long)[2]", + "$stack6[0] = 34565L", + "$stack6[1] = 234L", + "$stack7[0] = $stack6", "$stack8 = newarray (long)[2]", "$stack8[0] = 9851L", "$stack8[1] = 63543L", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -224,28 +224,28 @@ public List expectedBodyStmtsLongArrays() { public List expectedBodyStmtsFloatArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (float[][])[2]", + "$stack5 = newarray (float[][])[2]", "$stack3 = newarray (float[])[2]", + "$stack2 = newarray (float)[2]", + "$stack2[0] = 3.14F", + "$stack2[1] = 5.46F", + "$stack3[0] = $stack2", "$stack4 = newarray (float)[2]", - "$stack4[0] = 3.14F", - "$stack4[1] = 5.46F", - "$stack3[0] = $stack4", - "$stack5 = newarray (float)[2]", - "$stack5[0] = 2.987F", - "$stack5[1] = 4.87F", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (float[])[2]", - "$stack7 = newarray (float)[2]", - "$stack7[0] = 65.15F", - "$stack7[1] = 854.18F", - "$stack6[0] = $stack7", + "$stack4[0] = 2.987F", + "$stack4[1] = 4.87F", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (float[])[2]", + "$stack6 = newarray (float)[2]", + "$stack6[0] = 65.15F", + "$stack6[1] = 854.18F", + "$stack7[0] = $stack6", "$stack8 = newarray (float)[2]", "$stack8[0] = 16.51F", "$stack8[1] = 58.14F", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -263,27 +263,27 @@ public List expectedBodyStmtsFloatArrays() { public List expectedBodyStmtsDoubleArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (double[][])[2]", + "$stack5 = newarray (double[][])[2]", "$stack3 = newarray (double[])[2]", - "$stack4 = newarray (double)[2]", - "$stack4[0] = 6.765414", - "$stack4[1] = 9.676565646", - "$stack3[0] = $stack4", - "$stack5 = newarray (double)[1]", - "$stack5[0] = 45.345435", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (double[])[2]", - "$stack7 = newarray (double)[2]", - "$stack7[0] = 3.5656", - "$stack7[1] = 68.234234", - "$stack6[0] = $stack7", + "$stack2 = newarray (double)[2]", + "$stack2[0] = 6.765414", + "$stack2[1] = 9.676565646", + "$stack3[0] = $stack2", + "$stack4 = newarray (double)[1]", + "$stack4[0] = 45.345435", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (double[])[2]", + "$stack6 = newarray (double)[2]", + "$stack6[0] = 3.5656", + "$stack6[1] = 68.234234", + "$stack7[0] = $stack6", "$stack8 = newarray (double)[2]", "$stack8[0] = 68416.651", "$stack8[1] = 65416.5", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -301,26 +301,26 @@ public List expectedBodyStmtsDoubleArrays() { public List expectedBodyStmtsBooleanArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (boolean[][])[2]", + "$stack5 = newarray (boolean[][])[2]", "$stack3 = newarray (boolean[])[2]", - "$stack4 = newarray (boolean)[2]", + "$stack2 = newarray (boolean)[2]", + "$stack2[0] = 1", + "$stack2[1] = 0", + "$stack3[0] = $stack2", + "$stack4 = newarray (boolean)[1]", "$stack4[0] = 1", - "$stack4[1] = 0", - "$stack3[0] = $stack4", - "$stack5 = newarray (boolean)[1]", - "$stack5[0] = 1", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (boolean[])[2]", - "$stack7 = newarray (boolean)[2]", - "$stack7[0] = 0", - "$stack7[1] = 0", - "$stack6[0] = $stack7", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (boolean[])[2]", + "$stack6 = newarray (boolean)[2]", + "$stack6[0] = 0", + "$stack6[1] = 0", + "$stack7[0] = $stack6", "$stack8 = newarray (boolean)[1]", "$stack8[0] = 1", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -338,29 +338,29 @@ public List expectedBodyStmtsBooleanArrays() { public List expectedBodyStmtsCharArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (char[][])[2]", + "$stack5 = newarray (char[][])[2]", "$stack3 = newarray (char[])[2]", - "$stack4 = newarray (char)[3]", - "$stack4[0] = 65", - "$stack4[1] = 98", - "$stack4[2] = 38", - "$stack3[0] = $stack4", - "$stack5 = newarray (char)[2]", - "$stack5[0] = 99", - "$stack5[1] = 36", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (char[])[2]", - "$stack7 = newarray (char)[2]", - "$stack7[0] = 50", - "$stack7[1] = 71", - "$stack6[0] = $stack7", + "$stack2 = newarray (char)[3]", + "$stack2[0] = 65", + "$stack2[1] = 98", + "$stack2[2] = 38", + "$stack3[0] = $stack2", + "$stack4 = newarray (char)[2]", + "$stack4[0] = 99", + "$stack4[1] = 36", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (char[])[2]", + "$stack6 = newarray (char)[2]", + "$stack6[0] = 50", + "$stack6[1] = 71", + "$stack7[0] = $stack6", "$stack8 = newarray (char)[2]", "$stack8[0] = 97", "$stack8[1] = 37", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } @@ -377,27 +377,27 @@ public List expectedBodyStmtsCharArrays() { public List expectedBodyStmtsStringArrays() { return Stream.of( "$l0 := @this: Initialize3DimensionalArrays", - "$stack2 = newarray (java.lang.String[][])[2]", + "$stack5 = newarray (java.lang.String[][])[2]", "$stack3 = newarray (java.lang.String[])[2]", - "$stack4 = newarray (java.lang.String)[1]", - "$stack4[0] = \"Hello World\"", - "$stack3[0] = $stack4", - "$stack5 = newarray (java.lang.String)[2]", - "$stack5[0] = \"Greetings\"", - "$stack5[1] = \"Welcome\"", - "$stack3[1] = $stack5", - "$stack2[0] = $stack3", - "$stack6 = newarray (java.lang.String[])[2]", - "$stack7 = newarray (java.lang.String)[2]", - "$stack7[0] = \"Future\"", - "$stack7[1] = \"Soot\"", - "$stack6[0] = $stack7", + "$stack2 = newarray (java.lang.String)[1]", + "$stack2[0] = \"Hello World\"", + "$stack3[0] = $stack2", + "$stack4 = newarray (java.lang.String)[2]", + "$stack4[0] = \"Greetings\"", + "$stack4[1] = \"Welcome\"", + "$stack3[1] = $stack4", + "$stack5[0] = $stack3", + "$stack7 = newarray (java.lang.String[])[2]", + "$stack6 = newarray (java.lang.String)[2]", + "$stack6[0] = \"Future\"", + "$stack6[1] = \"Soot\"", + "$stack7[0] = $stack6", "$stack8 = newarray (java.lang.String)[2]", "$stack8[0] = \"UPB\"", "$stack8[1] = \"HNI\"", - "$stack6[1] = $stack8", - "$stack2[1] = $stack6", - "$l1 = $stack2", + "$stack7[1] = $stack8", + "$stack5[1] = $stack7", + "$l1 = $stack5", "return") .collect(Collectors.toList()); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/InitializeMultidimensionalArraysTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/InitializeMultidimensionalArraysTest.java index b194ca63c78..0875236c178 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/InitializeMultidimensionalArraysTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/InitializeMultidimensionalArraysTest.java @@ -32,7 +32,6 @@ public void test() { assertJimpleStmts(method, expectedBodyStmtsLongArrays()); method = loadMethod(getMethodSignature("floatArrays")); - assertJimpleStmts(method, expectedBodyStmtsFloatArrays()); method = loadMethod(getMethodSignature("doubleArrays")); @@ -65,22 +64,22 @@ public MethodSignature getMethodSignature(String methodName) { public List expectedBodyStmtsIntArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (int[])[3]", - "$stack3 = newarray (int)[3]", - "$stack3[0] = 1", - "$stack3[1] = 2", - "$stack3[2] = 3", - "$stack2[0] = $stack3", + "$stack3 = newarray (int[])[3]", + "$stack2 = newarray (int)[3]", + "$stack2[0] = 1", + "$stack2[1] = 2", + "$stack2[2] = 3", + "$stack3[0] = $stack2", "$stack4 = newarray (int)[2]", "$stack4[0] = 5", "$stack4[1] = 6", - "$stack2[1] = $stack4", + "$stack3[1] = $stack4", "$stack5 = newarray (int)[3]", "$stack5[0] = 7", "$stack5[1] = 8", "$stack5[2] = 9", - "$stack2[2] = $stack5", - "$l1 = $stack2", + "$stack3[2] = $stack5", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -97,15 +96,15 @@ public List expectedBodyStmtsIntArrays() { public List expectedBodyStmtsByteArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (byte[])[2]", - "$stack3 = newarray (byte)[2]", - "$stack3[0] = 4", - "$stack3[1] = 5", - "$stack2[0] = $stack3", + "$stack3 = newarray (byte[])[2]", + "$stack2 = newarray (byte)[2]", + "$stack2[0] = 4", + "$stack2[1] = 5", + "$stack3[0] = $stack2", "$stack4 = newarray (byte)[1]", "$stack4[0] = 2", - "$stack2[1] = $stack4", - "$l1 = $stack2", + "$stack3[1] = $stack4", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -121,16 +120,16 @@ public List expectedBodyStmtsByteArrays() { public List expectedBodyStmtsShortArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (short[])[2]", - "$stack3 = newarray (short)[3]", - "$stack3[0] = 10", - "$stack3[1] = 20", - "$stack3[2] = 30", - "$stack2[0] = $stack3", + "$stack3 = newarray (short[])[2]", + "$stack2 = newarray (short)[3]", + "$stack2[0] = 10", + "$stack2[1] = 20", + "$stack2[2] = 30", + "$stack3[0] = $stack2", "$stack4 = newarray (short)[1]", "$stack4[0] = 40", - "$stack2[1] = $stack4", - "$l1 = $stack2", + "$stack3[1] = $stack4", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -146,21 +145,21 @@ public List expectedBodyStmtsShortArrays() { public List expectedBodyStmtsLongArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (long[])[3]", - "$stack3 = newarray (long)[2]", - "$stack3[0] = 547087L", - "$stack3[1] = 654786L", - "$stack2[0] = $stack3", + "$stack3 = newarray (long[])[3]", + "$stack2 = newarray (long)[2]", + "$stack2[0] = 547087L", + "$stack2[1] = 654786L", + "$stack3[0] = $stack2", "$stack4 = newarray (long)[3]", "$stack4[0] = 547287L", "$stack4[1] = 864645L", "$stack4[2] = 6533786L", - "$stack2[1] = $stack4", + "$stack3[1] = $stack4", "$stack5 = newarray (long)[2]", "$stack5[0] = 34565L", "$stack5[1] = 234L", - "$stack2[2] = $stack5", - "$l1 = $stack2", + "$stack3[2] = $stack5", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -178,16 +177,16 @@ public List expectedBodyStmtsLongArrays() { public List expectedBodyStmtsFloatArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (float[])[2]", - "$stack3 = newarray (float)[2]", - "$stack3[0] = 3.14F", - "$stack3[1] = 5.46F", - "$stack2[0] = $stack3", + "$stack3 = newarray (float[])[2]", + "$stack2 = newarray (float)[2]", + "$stack2[0] = 3.14F", + "$stack2[1] = 5.46F", + "$stack3[0] = $stack2", "$stack4 = newarray (float)[2]", "$stack4[0] = 2.987F", "$stack4[1] = 4.87F", - "$stack2[1] = $stack4", - "$l1 = $stack2", + "$stack3[1] = $stack4", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -206,19 +205,19 @@ public List expectedBodyStmtsFloatArrays() { public List expectedBodyStmtsDoubleArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (double[])[3]", - "$stack3 = newarray (double)[2]", - "$stack3[0] = 6.765414", - "$stack3[1] = 9.676565646", - "$stack2[0] = $stack3", + "$stack3 = newarray (double[])[3]", + "$stack2 = newarray (double)[2]", + "$stack2[0] = 6.765414", + "$stack2[1] = 9.676565646", + "$stack3[0] = $stack2", "$stack4 = newarray (double)[1]", "$stack4[0] = 45.345435", - "$stack2[1] = $stack4", + "$stack3[1] = $stack4", "$stack5 = newarray (double)[2]", "$stack5[0] = 3.5656", "$stack5[1] = 68.234234", - "$stack2[2] = $stack5", - "$l1 = $stack2", + "$stack3[2] = $stack5", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -235,15 +234,15 @@ public List expectedBodyStmtsDoubleArrays() { public List expectedBodyStmtsBooleanArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (boolean[])[2]", - "$stack3 = newarray (boolean)[2]", - "$stack3[0] = 1", - "$stack3[1] = 0", - "$stack2[0] = $stack3", + "$stack3 = newarray (boolean[])[2]", + "$stack2 = newarray (boolean)[2]", + "$stack2[0] = 1", + "$stack2[1] = 0", + "$stack3[0] = $stack2", "$stack4 = newarray (boolean)[1]", "$stack4[0] = 1", - "$stack2[1] = $stack4", - "$l1 = $stack2", + "$stack3[1] = $stack4", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -261,21 +260,21 @@ public List expectedBodyStmtsBooleanArrays() { public List expectedBodyStmtsCharArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (char[])[3]", - "$stack3 = newarray (char)[3]", - "$stack3[0] = 65", - "$stack3[1] = 98", - "$stack3[2] = 38", - "$stack2[0] = $stack3", + "$stack3 = newarray (char[])[3]", + "$stack2 = newarray (char)[3]", + "$stack2[0] = 65", + "$stack2[1] = 98", + "$stack2[2] = 38", + "$stack3[0] = $stack2", "$stack4 = newarray (char)[2]", "$stack4[0] = 99", "$stack4[1] = 36", - "$stack2[1] = $stack4", + "$stack3[1] = $stack4", "$stack5 = newarray (char)[2]", "$stack5[0] = 50", "$stack5[1] = 71", - "$stack2[2] = $stack5", - "$l1 = $stack2", + "$stack3[2] = $stack5", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } @@ -292,15 +291,15 @@ public List expectedBodyStmtsCharArrays() { public List expectedBodyStmtsStringArrays() { return Stream.of( "$l0 := @this: InitializeMultidimensionalArrays", - "$stack2 = newarray (java.lang.String[])[2]", - "$stack3 = newarray (java.lang.String)[1]", - "$stack3[0] = \"Hello World\"", - "$stack2[0] = $stack3", + "$stack3 = newarray (java.lang.String[])[2]", + "$stack2 = newarray (java.lang.String)[1]", + "$stack2[0] = \"Hello World\"", + "$stack3[0] = $stack2", "$stack4 = newarray (java.lang.String)[2]", "$stack4[0] = \"Greetings\"", "$stack4[1] = \"Welcome\"", - "$stack2[1] = $stack4", - "$l1 = $stack2", + "$stack3[1] = $stack4", + "$l1 = $stack3", "return") .collect(Collectors.toList()); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelStatementTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelStatementTest.java index 6743f3fa4ff..199e0b6e159 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelStatementTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelStatementTest.java @@ -42,9 +42,7 @@ public List expectedBodyStmts() { "$l1 = 20", "$l2 = 1", "label1:", - "$stack5 = $l2", - "$stack4 = $l1", - "if $stack5 >= $stack4 goto label3", + "if $l2 >= $l1 goto label3", "$stack3 = $l2 % 10", "if $stack3 != 0 goto label2", "goto label3", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelledLoopBreakTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelledLoopBreakTest.java index c121ab06dbb..46e3ff35430 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelledLoopBreakTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LabelledLoopBreakTest.java @@ -40,14 +40,10 @@ public List expectedBodyStmts() { "$l0 := @this: LabelledLoopBreak", "$l1 = 0", "label1:", - "$stack4 = $l1", - "$stack3 = 5", - "if $stack4 >= $stack3 goto label5", + "if $l1 >= 5 goto label5", "$l2 = 0", "label2:", - "$stack6 = $l2", - "$stack5 = 5", - "if $stack6 >= $stack5 goto label4", + "if $l2 >= 5 goto label4", "if $l1 != 1 goto label3", "goto label5", "label3:", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java index f94dacffe6a..346f88e6847 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java @@ -31,6 +31,15 @@ public void test() { "void", Collections.singletonList("int"))); assertJimpleStmts(methodOtherLocal, expectedBodyStmtsOtherLocal()); + + SootMethod methodDuplicateValue = + loadMethod( + identifierFactory.getMethodSignature( + getDeclaredClassSignature(), + "localMergingWithDuplicateValue", + "void", + Collections.singletonList("int"))); + assertJimpleStmts(methodDuplicateValue, expectedBodyStmtsDuplicateValue()); } /** @@ -94,4 +103,34 @@ public List expectedBodyStmtsOtherLocal() { "return") .collect(Collectors.toList()); } + + /** + * + * + *
+   * public void localMergingWithDuplicateValue(int n) {
+   *     String a = "one";
+   *     // One of the branches for the first argument contains the constant "two"
+   *     // and the second argument also contains the constant "two".
+   *     // This test ensures that when the first argument gets replaced by a stack local,
+   *     // the second argument isn't replaced as well.
+   *     System.setProperty(n == 1 ? a : "two", "two");
+   * }
+   * 
+ */ + public List expectedBodyStmtsDuplicateValue() { + return Stream.of( + "$l0 := @this: LocalMerging", + "$l1 := @parameter0: int", + "$l2 = \"one\"", + "if $l1 != 1 goto label1", + "$stack3 = $l2", + "goto label2", + "label1:", + "$stack3 = \"two\"", + "label2:", + "staticinvoke ($stack3, \"two\")", + "return") + .collect(Collectors.toList()); + } } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ReferencingThisTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ReferencingThisTest.java index cbfa91c7475..92d0ac01cdd 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ReferencingThisTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/ReferencingThisTest.java @@ -51,11 +51,11 @@ public List expectedBodyStmts() { "$l0 := @this: ReferencingThis", "$stack2 = ", "virtualinvoke $stack2.(\" this keyword as an argument in the constructor call\")", - "$stack3 = new ReferencingThis", - "$stack5 = $l0.", - "$stack4 = $l0.", - "specialinvoke $stack3.(int,int)>($stack5, $stack4)", - "$l1 = $stack3", + "$stack5 = new ReferencingThis", + "$stack4 = $l0.", + "$stack3 = $l0.", + "specialinvoke $stack5.(int,int)>($stack4, $stack3)", + "$l1 = $stack5", "virtualinvoke $l1.()", "return") .collect(Collectors.toCollection(ArrayList::new)); diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/StatementEvalTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/StatementEvalTest.java index b723d5b1d22..42bfd7f83f8 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/StatementEvalTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/StatementEvalTest.java @@ -38,7 +38,7 @@ public List expectedBodyStmts() { "$l1 = 1", "$stack2 = $l1", "$l1 = 3", - "$l1 = $stack2 + 3", + "$l1 = $stack2 + $l1", "return") .collect(Collectors.toList()); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/SynchronizedBlockTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/SynchronizedBlockTest.java index a6dcc79ed37..678121f7c21 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/SynchronizedBlockTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/SynchronizedBlockTest.java @@ -41,22 +41,19 @@ public void test() { public List expectedBodyStmts() { return Stream.of( "$l0 := @this: SynchronizedBlock", - "$stack3 = $l0.", - "$l1 = $stack3", - "entermonitor $stack3", + "$l1 = $l0.", + "entermonitor $l1", "label1:", - "$stack5 = ", - "$stack4 = $l0.", - "virtualinvoke $stack5.($stack4)", - "$stack6 = $l1", - "exitmonitor $stack6", + "$stack4 = ", + "$stack3 = $l0.", + "virtualinvoke $stack4.($stack3)", + "exitmonitor $l1", "label2:", "goto label5", "label3:", - "$stack7 := @caughtexception", - "$l2 = $stack7", - "$stack8 = $l1", - "exitmonitor $stack8", + "$stack5 := @caughtexception", + "$l2 = $stack5", + "exitmonitor $l1", "label4:", "throw $l2", "label5:", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/TryCatchFinallyTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/TryCatchFinallyTest.java index 267a068fa17..57364db356c 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/TryCatchFinallyTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/TryCatchFinallyTest.java @@ -394,11 +394,9 @@ public List expectedBodyStmtsTryCatchFinallyNested() { "$stack13 = ", "virtualinvoke $stack13.($l1)", "label8:", - "$stack14 = \"1finally\"", - "$l1 = $stack14", + "$l1 = \"1finally\"", "$stack6 = ", - "$stack15 = $l1", - "virtualinvoke $stack6.($stack15)", + "virtualinvoke $stack6.($l1)", "goto label9", "label9:", "return", @@ -535,11 +533,9 @@ public List expectedBodyStmtsTryCatchFinallyNestedInCatch() { "$stack10 = ", "virtualinvoke $stack10.($l1)", "label09:", - "$stack15 = \"1finally\"", - "$l1 = $stack15", + "$l1 = \"1finally\"", "$stack11 = ", - "$stack16 = $l1", - "virtualinvoke $stack11.($stack16)", + "virtualinvoke $stack11.($l1)", "goto label10", "label10:", "return", @@ -596,34 +592,34 @@ public List expectedBodyStmtsTryCatchFinallyNestedInFinally() { "label04:", "goto label16", "label05:", - "$stack20 := @caughtexception", - "$l2 = $stack20", + "$stack19 := @caughtexception", + "$l2 = $stack19", "$l1 = \"2catch\"", - "$stack21 = ", - "virtualinvoke $stack21.($l1)", + "$stack20 = ", + "virtualinvoke $stack20.($l1)", "goto label16", "label06:", - "$stack16 := @caughtexception", - "$l2 = $stack16", + "$stack15 := @caughtexception", + "$l2 = $stack15", "$l1 = \"1catch\"", - "$stack17 = ", - "virtualinvoke $stack17.($l1)", + "$stack16 = ", + "virtualinvoke $stack16.($l1)", "label07:", "$l1 = \"1finally\"", - "$stack18 = ", - "virtualinvoke $stack18.($l1)", + "$stack17 = ", + "virtualinvoke $stack17.($l1)", "label08:", "$l1 = \"2try\"", - "$stack19 = ", - "virtualinvoke $stack19.($l1)", + "$stack18 = ", + "virtualinvoke $stack18.($l1)", "label09:", "goto label16", "label10:", - "$stack14 := @caughtexception", - "$l2 = $stack14", + "$stack13 := @caughtexception", + "$l2 = $stack13", "$l1 = \"2catch\"", - "$stack15 = ", - "virtualinvoke $stack15.($l1)", + "$stack14 = ", + "virtualinvoke $stack14.($l1)", "goto label16", "label11:", "$stack10 := @caughtexception", @@ -644,8 +640,7 @@ public List expectedBodyStmtsTryCatchFinallyNestedInFinally() { "$stack9 = ", "virtualinvoke $stack9.($l1)", "label15:", - "$stack13 = $l3", - "throw $stack13", + "throw $l3", "label16:", "return", "catch java.lang.Exception from label01 to label02 with label06", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/UnaryOpIntTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/UnaryOpIntTest.java index 018bc87823d..66e0d6ff947 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/UnaryOpIntTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/UnaryOpIntTest.java @@ -41,9 +41,9 @@ public void test() { public List expectedBodyStmts() { return Stream.of( "$l0 := @this: UnaryOpInt", - "$stack3 = $l0.", - "$stack2 = $l0.", - "$l1 = $stack3 + $stack2", + "$stack2 = $l0.", + "$stack3 = $l0.", + "$l1 = $stack2 + $stack3", "return") .collect(Collectors.toCollection(ArrayList::new)); } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/WhileLoopTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/WhileLoopTest.java index f02f0a7979f..6914d442760 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/WhileLoopTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/WhileLoopTest.java @@ -38,9 +38,7 @@ public List expectedBodyStmts() { "$l1 = 10", "$l2 = 0", "label1:", - "$stack4 = $l1", - "$stack3 = $l2", - "if $stack4 <= $stack3 goto label2", + "if $l1 <= $l2 goto label2", "$l1 = $l1 + -1", "goto label1", "label2:", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/MultiTryCatchTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/MultiTryCatchTest.java index 87ce538510a..78eec33e664 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/MultiTryCatchTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/MultiTryCatchTest.java @@ -60,66 +60,63 @@ public MethodSignature getMethodSignature() { public List expectedBodyStmts() { return Stream.of( "$l0 := @this: MultiTryCatch", - "$stack6 = new java.io.BufferedReader", - "$stack7 = new java.io.FileReader", - "specialinvoke $stack7.(java.lang.String)>(\"file.txt\")", - "specialinvoke $stack6.(java.io.Reader)>($stack7)", - "$l1 = $stack6", + "$stack7 = new java.io.BufferedReader", + "$stack6 = new java.io.FileReader", + "specialinvoke $stack6.(java.lang.String)>(\"file.txt\")", + "specialinvoke $stack7.(java.io.Reader)>($stack6)", + "$l1 = $stack7", "label01:", "$l2 = \"\"", "$l3 = 2", "$stack8 = ", "virtualinvoke $stack8.($l3)", "label02:", - "$stack11 = $l1", - "$stack9 = virtualinvoke $stack11.()", - "$l2 = $stack9", - "if $stack9 == null goto label16", - "$stack10 = ", - "virtualinvoke $stack10.($l2)", + "$l2 = virtualinvoke $l1.()", + "if $l2 == null goto label16", + "$stack9 = ", + "virtualinvoke $stack9.($l2)", "goto label02", "label03:", - "$stack18 := @caughtexception", - "$l2 = $stack18", + "$stack15 := @caughtexception", + "$l2 = $stack15", "label04:", "virtualinvoke $l1.()", "label05:", "goto label19", "label06:", - "$stack17 := @caughtexception", - "$l2 = $stack17", + "$stack14 := @caughtexception", + "$l2 = $stack14", "goto label19", "label07:", - "$stack16 := @caughtexception", - "$l2 = $stack16", + "$stack13 := @caughtexception", + "$l2 = $stack13", "label08:", "virtualinvoke $l1.()", "label09:", "goto label19", "label10:", - "$stack15 := @caughtexception", - "$l2 = $stack15", + "$stack12 := @caughtexception", + "$l2 = $stack12", "goto label19", "label11:", - "$stack13 := @caughtexception", - "$l4 = $stack13", + "$stack11 := @caughtexception", + "$l4 = $stack11", "label12:", "virtualinvoke $l1.()", "label13:", "goto label15", "label14:", - "$stack12 := @caughtexception", - "$l5 = $stack12", + "$stack10 := @caughtexception", + "$l5 = $stack10", "label15:", - "$stack14 = $l4", - "throw $stack14", + "throw $l4", "label16:", "virtualinvoke $l1.()", "label17:", "goto label19", "label18:", - "$stack19 := @caughtexception", - "$l2 = $stack19", + "$stack16 := @caughtexception", + "$l2 = $stack16", "goto label19", "label19:", "return", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/SwitchCaseStatementWithStringTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/SwitchCaseStatementWithStringTest.java index d8dc068d1bf..d3de128c445 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/SwitchCaseStatementWithStringTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/SwitchCaseStatementWithStringTest.java @@ -68,13 +68,13 @@ public List expectedBodyStmts() { "case 110339486: goto label3", "default: goto label4", "label1:", - "$stack9 = virtualinvoke $l3.(\"one\")", - "if $stack9 == 0 goto label4", + "$stack8 = virtualinvoke $l3.(\"one\")", + "if $stack8 == 0 goto label4", "$l4 = 0", "goto label4", "label2:", - "$stack8 = virtualinvoke $l3.(\"two\")", - "if $stack8 == 0 goto label4", + "$stack7 = virtualinvoke $l3.(\"two\")", + "if $stack7 == 0 goto label4", "$l4 = 1", "goto label4", "label3:", @@ -82,8 +82,7 @@ public List expectedBodyStmts() { "if $stack6 == 0 goto label4", "$l4 = 2", "label4:", - "$stack7 = $l4", - "switch($stack7)", + "switch($l4)", "case 0: goto label5", "case 1: goto label6", "case 2: goto label7", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/TryWithResourcesTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/TryWithResourcesTest.java index 1c65fcba61b..bcc41246968 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/TryWithResourcesTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java7/TryWithResourcesTest.java @@ -47,30 +47,28 @@ public void test() { public List expectedBodyStmts() { return Stream.of( "$l0 := @this: TryWithResources", - "$stack6 = new java.io.BufferedReader", - "$stack7 = new java.io.FileReader", - "specialinvoke $stack7.(java.lang.String)>(\"file.txt\")", - "specialinvoke $stack6.(java.io.Reader)>($stack7)", - "$l1 = $stack6", + "$stack7 = new java.io.BufferedReader", + "$stack6 = new java.io.FileReader", + "specialinvoke $stack6.(java.lang.String)>(\"file.txt\")", + "specialinvoke $stack7.(java.io.Reader)>($stack6)", + "$l1 = $stack7", "$l2 = null", "label01:", "$l3 = \"\"", "label02:", - "$stack10 = $l1", - "$stack8 = virtualinvoke $stack10.()", - "$l3 = $stack8", - "if $stack8 == null goto label11", - "$stack9 = ", - "virtualinvoke $stack9.($l3)", + "$l3 = virtualinvoke $l1.()", + "if $l3 == null goto label11", + "$stack8 = ", + "virtualinvoke $stack8.($l3)", "goto label02", "label03:", - "$stack14 := @caughtexception", - "$l3 = $stack14", + "$stack11 := @caughtexception", + "$l3 = $stack11", "$l2 = $l3", "throw $l3", "label04:", - "$stack12 := @caughtexception", - "$l4 = $stack12", + "$stack10 := @caughtexception", + "$l4 = $stack10", "label05:", "if $l1 == null goto label10", "if $l2 == null goto label09", @@ -79,15 +77,14 @@ public List expectedBodyStmts() { "label07:", "goto label10", "label08:", - "$stack11 := @caughtexception", - "$l5 = $stack11", + "$stack9 := @caughtexception", + "$l5 = $stack9", "virtualinvoke $l2.($l5)", "goto label10", "label09:", "virtualinvoke $l1.()", "label10:", - "$stack13 = $l4", - "throw $stack13", + "throw $l4", "label11:", "if $l1 == null goto label16", "if $l2 == null goto label15", @@ -96,8 +93,8 @@ public List expectedBodyStmts() { "label13:", "goto label16", "label14:", - "$stack15 := @caughtexception", - "$l3 = $stack15", + "$stack12 := @caughtexception", + "$l3 = $stack12", "virtualinvoke $l2.($l3)", "goto label16", "label15:", diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java9/TryWithResourcesConciseTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java9/TryWithResourcesConciseTest.java index 6a861a22c78..93fda577191 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java9/TryWithResourcesConciseTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java9/TryWithResourcesConciseTest.java @@ -68,37 +68,34 @@ public void test() { public List expectedBodyStmts() { return Stream.of( "$l0 := @this: TryWithResourcesConcise", - "$stack5 = new java.io.BufferedReader", - "$stack6 = new java.io.FileReader", - "specialinvoke $stack6.(java.lang.String)>(\"file.txt\")", - "specialinvoke $stack5.(java.io.Reader)>($stack6)", - "$l1 = $stack5", + "$stack6 = new java.io.BufferedReader", + "$stack5 = new java.io.FileReader", + "specialinvoke $stack5.(java.lang.String)>(\"file.txt\")", + "specialinvoke $stack6.(java.io.Reader)>($stack5)", + "$l1 = $stack6", "$l2 = $l1", "label1:", "$l3 = \"\"", "label2:", - "$stack9 = $l1", - "$stack7 = virtualinvoke $stack9.()", - "$l3 = $stack7", - "if $stack7 == null goto label8", - "$stack8 = ", - "virtualinvoke $stack8.($l3)", + "$l3 = virtualinvoke $l1.()", + "if $l3 == null goto label8", + "$stack7 = ", + "virtualinvoke $stack7.($l3)", "goto label2", "label3:", - "$stack11 := @caughtexception", - "$l3 = $stack11", + "$stack9 := @caughtexception", + "$l3 = $stack9", "if $l2 == null goto label7", "label4:", "virtualinvoke $l2.()", "label5:", "goto label7", "label6:", - "$stack10 := @caughtexception", - "$l4 = $stack10", + "$stack8 := @caughtexception", + "$l4 = $stack8", "virtualinvoke $l3.($l4)", "label7:", - "$stack12 = $l3", - "throw $stack12", + "throw $l3", "label8:", "if $l2 == null goto label9", "virtualinvoke $l2.()", From 8aa2a08ff48d13b140faa5ed1e5822644d7f80d0 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Mon, 6 Nov 2023 18:27:19 +0100 Subject: [PATCH 07/14] 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 | 953 +++++++----------- .../java/bytecode/frontend/Operand.java | 2 +- .../java/bytecode/frontend/StackFrame.java | 174 ++-- 3 files changed, 463 insertions(+), 666 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 e30569c662a..e817ec0030b 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 @@ -286,19 +286,7 @@ private String determineLocalName(int idx, boolean isField) { } 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 @@ -364,74 +352,46 @@ private void addReadOperandAssignments_internal(BiFunction= 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 { @@ -491,19 +445,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); @@ -516,20 +463,13 @@ 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) { @@ -622,65 +562,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 { @@ -692,25 +617,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 { @@ -723,52 +641,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 { @@ -780,15 +691,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) { @@ -835,31 +741,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(); - Stmt 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); + Stmt ts = + op == MONITORENTER + ? Jimple.newEnterMonitorStmt(opr.toImmediate(), getStmtPositionInfo()) + : Jimple.newExitMonitorStmt(opr.toImmediate(), getStmtPositionInfo()); + setStmt(insn, ts); } else { throw new UnsupportedOperationException("Unknown insn op: " + op); } @@ -868,56 +764,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); } @@ -937,10 +823,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: @@ -966,8 +852,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)); @@ -996,16 +882,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()); } } } @@ -1014,15 +899,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 { @@ -1106,10 +985,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) { @@ -1122,117 +1002,68 @@ 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[] operands; - AbstractInvokeExpr invoke; - if (isInstance) { - final List args; - if (nrArgs == 0) { - operands = new Operand[1]; - args = Collections.emptyList(); - } else { - Immediate[] argList = new Immediate[nrArgs]; - operands = new Operand[nrArgs + 1]; - - while (nrArgs-- > 0) { - operands[nrArgs] = operandStack.pop(sigTypes.get(nrArgs)); - argList[nrArgs] = operands[nrArgs].toImmediate(); - } - args = Arrays.asList(argList); - } - - final Operand baseOperand = operandStack.pop(); - operands[operands.length - 1] = baseOperand; - Local base = baseOperand.toLocal(); - - switch (op) { - case INVOKESPECIAL: - invoke = Jimple.newSpecialInvokeExpr(base, methodSignature, args); - break; - case INVOKEVIRTUAL: - invoke = Jimple.newVirtualInvokeExpr(base, methodSignature, args); - break; - case INVOKEINTERFACE: - invoke = Jimple.newInterfaceInvokeExpr(base, methodSignature, args); - break; - default: - throw new UnsupportedOperationException("Unknown invoke op:" + op); - } - } else { - if (nrArgs == 0) { - operands = null; - invoke = Jimple.newStaticInvokeExpr(methodSignature, Collections.emptyList()); - - } else { - - operands = new Operand[nrArgs]; - Immediate[] argList = new Immediate[nrArgs]; - - do { - nrArgs--; - operands[nrArgs] = operandStack.pop(sigTypes.get(nrArgs)); - argList[nrArgs] = operands[nrArgs].toImmediate(); - } while (nrArgs > 0); - - invoke = Jimple.newStaticInvokeExpr(methodSignature, Arrays.asList(argList)); - } - } - - if (operands != null) { - frame.setIn(operands); - } - opr = new Operand(insn, invoke, this); - frame.setOut(opr); + 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[] operands; + Immediate[] argList = new Immediate[nrArgs]; + final List args; + if (!isInstance) { + operands = nrArgs == 0 ? null : new Operand[nrArgs]; } else { - opr = out[0]; - AbstractInvokeExpr expr = (AbstractInvokeExpr) opr.value; - List types = expr.getMethodSignature().getParameterTypes(); - Operand[] oprs; - int nrArgs = types.size(); - assert (isInstance); // TODO: check equivalent to isInstance? - boolean isInstanceMethod = expr instanceof AbstractInstanceInvokeExpr; - if (isInstanceMethod) { - oprs = new Operand[nrArgs + 1]; - while (nrArgs-- != 0) { - oprs[nrArgs] = operandStack.pop(types.get(nrArgs)); - } - oprs[oprs.length - 1] = operandStack.pop(); - frame.mergeIn(oprs); - } else { - if (nrArgs > 0) { - oprs = new Operand[nrArgs]; - do { - nrArgs--; - oprs[nrArgs] = operandStack.pop(types.get(nrArgs)); - } while (nrArgs > 0); - - frame.mergeIn(oprs); - } + operands = new Operand[nrArgs + 1]; + } + while (nrArgs-- != 0) { + operands[nrArgs] = operandStack.pop(sigTypes.get(nrArgs)); + } + if (isInstance) { + operands[operands.length - 1] = operandStack.pop(); + } + if (operands != null) { + frame.mergeInputs(operands); + } + nrArgs = sigTypes.size(); + while (nrArgs-- != 0) { + argList[nrArgs] = operands[nrArgs].toImmediate(); + } + args = Arrays.asList(argList); + AbstractInvokeExpr invoke; + if (!isInstance) { + invoke = Jimple.newStaticInvokeExpr(methodSignature, args); + } else { + Operand baseOperand = operands[operands.length - 1]; + Local base = baseOperand.toLocal(); + + switch (op) { + case INVOKESPECIAL: + invoke = Jimple.newSpecialInvokeExpr(base, methodSignature, args); + break; + case INVOKEVIRTUAL: + invoke = Jimple.newVirtualInvokeExpr(base, methodSignature, args); + break; + case INVOKEINTERFACE: + invoke = Jimple.newInterfaceInvokeExpr(base, methodSignature, args); + break; + default: + throw new UnsupportedOperationException("Unknown invoke op:" + op); } - returnType = expr.getMethodSignature().getType(); } + Operand opr = new Operand(insn, invoke, this); + frame.mergeOutput(opr); if (AsmUtil.isDWord(returnType)) { operandStack.pushDual(opr); @@ -1251,66 +1082,49 @@ 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; - Type[] parameterTypes = new Type[nrArgs]; - Immediate[] methodArgs = new Immediate[nrArgs]; + // Generate parameters & returnType & parameterTypes + List types = AsmUtil.toJimpleSignatureDesc(insn.desc); + int nrArgs = types.size() - 1; + Type[] parameterTypes = new Type[nrArgs]; + Immediate[] methodArgs = new Immediate[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--) { - parameterTypes[i] = types.get(i); - args[i] = operandStack.pop(types.get(i)); - methodArgs[i] = args[i].toImmediate(); - } - returnType = types.get(types.size() - 1); + for (int i = nrArgs - 1; i >= 0; i--) { + parameterTypes[i] = types.get(i); - // 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, Arrays.asList(parameterTypes)); + args[i] = operandStack.pop(types.get(i)); + } + frame.mergeInputs(args); + for (int i = nrArgs - 1; i >= 0; i--) { + methodArgs[i] = args[i].toImmediate(); + } + Type returnType = types.get(types.size() - 1); - JDynamicInvokeExpr indy = - Jimple.newDynamicInvokeExpr( - bsmMethodRef, bsmMethodArgs, methodSig, insn.bsm.getTag(), Arrays.asList(methodArgs)); + // 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, Arrays.asList(parameterTypes)); - 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(); - } + JDynamicInvokeExpr indy = + Jimple.newDynamicInvokeExpr( + bsmMethodRef, bsmMethodArgs, methodSig, insn.bsm.getTag(), Arrays.asList(methodArgs)); + + Operand opr = new Operand(insn, indy, this); + frame.mergeOutput(opr); if (AsmUtil.isDWord(returnType)) { operandStack.pushDual(opr); } else if (!(returnType instanceof VoidType)) { @@ -1328,40 +1142,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()); @@ -1369,53 +1175,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); } @@ -1423,14 +1218,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 { @@ -1443,24 +1232,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); } @@ -1499,19 +1283,14 @@ 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(); - JIdentityStmt 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(); + } + JIdentityStmt 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 ce1d463a388..c8f9df5f8c2 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 2f352876506..f673399921b 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 @@ -36,9 +36,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; /** @@ -51,103 +57,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); + /* 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 + // 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()); } From 39ee51847a066ad10c10130ad69155b93a390f14 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 7 Nov 2023 16:13:28 +0100 Subject: [PATCH 08/14] document `addReadOperandAssignments` --- .../sootup/java/bytecode/frontend/AsmMethodSource.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 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 e817ec0030b..65d74de4067 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 @@ -342,10 +342,6 @@ private void addReadOperandAssignments_internal(BiFunction Date: Tue, 7 Nov 2023 16:46:22 +0100 Subject: [PATCH 09/14] rename `StackFrame` to `OperandMerging` The class had nothing to do with stack frames --- .../bytecode/frontend/AsmMethodSource.java | 126 +++++++++--------- .../{StackFrame.java => OperandMerging.java} | 53 ++++---- .../java/bytecode/frontend/OperandStack.java | 16 +-- 3 files changed, 94 insertions(+), 101 deletions(-) rename sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/{StackFrame.java => OperandMerging.java} (81%) 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 65d74de4067..1b266ff6ad5 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 @@ -347,7 +347,7 @@ private void addReadOperandAssignments_internal(BiFunction= INEG && op <= DNEG) { unop = Jimple.newNegExpr(op1.toImmediate()); @@ -624,7 +624,7 @@ private void convertUnopInsn(@Nonnull InsnNode insn) { throw new UnsupportedOperationException("Unknown unop: " + op); } Operand opr = new Operand(insn, unop, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); if (dword) { operandStack.pushDual(opr); } else { @@ -636,7 +636,7 @@ private void convertPrimCastInsn(@Nonnull 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 = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Type totype; switch (op) { case I2L: @@ -672,10 +672,10 @@ private void convertPrimCastInsn(@Nonnull InsnNode insn) { throw new IllegalStateException("Unknown prim cast op: " + op); } Operand val = fromd ? operandStack.popDual() : operandStack.pop(); - frame.mergeInputs(val); + merging.mergeInputs(val); JCastExpr cast = Jimple.newCastExpr(val.toImmediate(), totype); Operand opr = new Operand(insn, cast, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); if (tod) { operandStack.pushDual(opr); } else { @@ -686,9 +686,9 @@ private void convertPrimCastInsn(@Nonnull InsnNode insn) { private void convertReturnInsn(@Nonnull InsnNode insn) { int op = insn.getOpcode(); boolean dword = op == LRETURN || op == DRETURN; - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Operand val = dword ? operandStack.popDual() : operandStack.pop(); - frame.mergeInputs(val); + merging.mergeInputs(val); JReturnStmt ret = Jimple.newReturnStmt(val.toImmediate(), getStmtPositionInfo()); setStmt(insn, ret); } @@ -736,17 +736,17 @@ private void convertInsn(@Nonnull InsnNode insn) { setStmt(insn, Jimple.newReturnVoidStmt(getStmtPositionInfo())); } } else if (op == ATHROW) { - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Operand opr = operandStack.pop(); - frame.mergeInputs(opr); + merging.mergeInputs(opr); JThrowStmt ts = Jimple.newThrowStmt(opr.toImmediate(), getStmtPositionInfo()); setStmt(insn, ts); - frame.mergeOutput(opr); + merging.mergeOutput(opr); operandStack.push(opr); } else if (op == MONITORENTER || op == MONITOREXIT) { - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Operand opr = operandStack.popStackConst(); - frame.mergeInputs(opr); + merging.mergeInputs(opr); Stmt ts = op == MONITORENTER ? Jimple.newEnterMonitorStmt(opr.toImmediate(), getStmtPositionInfo()) @@ -759,7 +759,7 @@ private void convertInsn(@Nonnull InsnNode insn) { private void convertIntInsn(@Nonnull IntInsnNode insn) { int op = insn.getOpcode(); - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Value v; if (op == BIPUSH || op == SIPUSH) { v = IntConstant.getInstance(insn.operand); @@ -795,11 +795,11 @@ private void convertIntInsn(@Nonnull IntInsnNode insn) { throw new UnsupportedOperationException("Unknown NEWARRAY type!"); } Operand size = operandStack.pop(); - frame.mergeInputs(size); + merging.mergeInputs(size); v = JavaJimple.getInstance().newNewArrayExpr(type, size.toImmediate()); } Operand opr = new Operand(insn, v, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); operandStack.push(opr); } @@ -814,7 +814,7 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { return; } /* must be ifX insn */ - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); if (!insnToStmt.containsKey(insn)) { Operand val = operandStack.pop(); Immediate v = val.toImmediate(); @@ -822,7 +822,7 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { Operand val1 = operandStack.pop(); - frame.mergeInputs(val, val1); + merging.mergeInputs(val, val1); Immediate v1 = val1.toImmediate(); switch (op) { case IF_ICMPEQ: @@ -849,7 +849,7 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { throw new UnsupportedOperationException("Unknown if op: " + op); } } else { - frame.mergeInputs(val); + merging.mergeInputs(val); switch (op) { case IFEQ: cond = Jimple.newEqExpr(v, IntConstant.getInstance(0)); @@ -884,9 +884,9 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { setStmt(insn, ifStmt); } else { if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { - frame.mergeInputs(operandStack.pop(), operandStack.pop()); + merging.mergeInputs(operandStack.pop(), operandStack.pop()); } else { - frame.mergeInputs(operandStack.pop()); + merging.mergeInputs(operandStack.pop()); } } } @@ -894,10 +894,10 @@ private void convertJumpInsn(@Nonnull JumpInsnNode insn) { private void convertLdcInsn(@Nonnull LdcInsnNode insn) { Object val = insn.cst; boolean dword = val instanceof Long || val instanceof Double; - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Value v = toSootValue(val); Operand opr = new Operand(insn, v, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); if (dword) { operandStack.pushDual(opr); } else { @@ -979,13 +979,13 @@ private MethodSignature toMethodSignature(Handle methodHandle) { } private void convertLookupSwitchInsn(@Nonnull LookupSwitchInsnNode insn) { - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); if (insnToStmt.containsKey(insn)) { - frame.mergeInputs(operandStack.pop()); + merging.mergeInputs(operandStack.pop()); return; } Operand key = operandStack.pop(); - frame.mergeInputs(key); + merging.mergeInputs(key); List keys = new ArrayList<>(insn.keys.size()); for (Integer i : insn.keys) { @@ -1004,7 +1004,7 @@ private void convertLookupSwitchInsn(@Nonnull LookupSwitchInsnNode insn) { private void convertMethodInsn(@Nonnull MethodInsnNode insn) { int op = insn.getOpcode(); boolean isInstance = op != INVOKESTATIC; - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); String clsName = AsmUtil.toQualifiedName(insn.owner); if (clsName.charAt(0) == '[') { clsName = "java.lang.Object"; @@ -1030,7 +1030,7 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { operands[operands.length - 1] = operandStack.pop(); } if (operands != null) { - frame.mergeInputs(operands); + merging.mergeInputs(operands); } nrArgs = sigTypes.size(); while (nrArgs-- != 0) { @@ -1059,7 +1059,7 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { } } Operand opr = new Operand(insn, invoke, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); if (AsmUtil.isDWord(returnType)) { operandStack.pushDual(opr); @@ -1077,7 +1077,7 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { } private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); // convert info on bootstrap method MethodSignature bsmMethodRef = toMethodSignature(insn.bsm); List bsmMethodArgs = new ArrayList<>(insn.bsmArgs.length); @@ -1103,7 +1103,7 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { args[i] = operandStack.pop(types.get(i)); } - frame.mergeInputs(args); + merging.mergeInputs(args); for (int i = nrArgs - 1; i >= 0; i--) { methodArgs[i] = args[i].toImmediate(); } @@ -1120,7 +1120,7 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { bsmMethodRef, bsmMethodArgs, methodSig, insn.bsm.getTag(), Arrays.asList(methodArgs)); Operand opr = new Operand(insn, indy, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); if (AsmUtil.isDWord(returnType)) { operandStack.pushDual(opr); } else if (!(returnType instanceof VoidType)) { @@ -1137,7 +1137,7 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { } private void convertMultiANewArrayInsn(@Nonnull MultiANewArrayInsnNode insn) { - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); ArrayType t = (ArrayType) AsmUtil.toJimpleType(insn.desc); int dims = insn.dims; Operand[] sizes = new Operand[dims]; @@ -1145,25 +1145,25 @@ private void convertMultiANewArrayInsn(@Nonnull MultiANewArrayInsnNode insn) { while (dims-- != 0) { sizes[dims] = operandStack.pop(); } - frame.mergeInputs(sizes); + merging.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); + merging.mergeOutput(opr); operandStack.push(opr); } private void convertTableSwitchInsn(@Nonnull TableSwitchInsnNode insn) { - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); if (insnToStmt.containsKey(insn)) { - frame.mergeInputs(operandStack.pop()); + merging.mergeInputs(operandStack.pop()); return; } Operand key = operandStack.pop(); - frame.mergeInputs(key); + merging.mergeInputs(key); JSwitchStmt tableSwitchStmt = Jimple.newTableSwitchStmt(key.toImmediate(), insn.min, insn.max, getStmtPositionInfo()); @@ -1176,13 +1176,13 @@ private void convertTableSwitchInsn(@Nonnull TableSwitchInsnNode insn) { private void convertTypeInsn(@Nonnull TypeInsnNode insn) { int op = insn.getOpcode(); - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Expr val; if (op == NEW) { val = Jimple.newNewExpr(AsmUtil.toJimpleClassType(insn.desc)); } else { Operand op1 = operandStack.pop(); - frame.mergeInputs(op1); + merging.mergeInputs(op1); switch (op) { case ANEWARRAY: { @@ -1206,16 +1206,16 @@ private void convertTypeInsn(@Nonnull TypeInsnNode insn) { } } Operand opr = new Operand(insn, val, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); operandStack.push(opr); } private void convertVarLoadInsn(@Nonnull VarInsnNode insn) { int op = insn.getOpcode(); boolean dword = op == LLOAD || op == DLOAD; - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Operand opr = new Operand(insn, getOrCreateLocal(insn.var), this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); if (dword) { operandStack.pushDual(opr); } else { @@ -1226,9 +1226,9 @@ private void convertVarLoadInsn(@Nonnull VarInsnNode insn) { private void convertVarStoreInsn(@Nonnull VarInsnNode insn) { int op = insn.getOpcode(); boolean dword = op == LSTORE || op == DSTORE; - StackFrame frame = operandStack.getOrCreateStackframe(insn); + OperandMerging merging = operandStack.getOrCreateMerging(insn); Operand opr = dword ? operandStack.popDual() : operandStack.pop(); - frame.mergeInputs(opr); + merging.mergeInputs(opr); Local local = getOrCreateLocal(insn.var); AbstractDefinitionStmt as; if (opr.stackLocal == null) { @@ -1283,10 +1283,10 @@ private void convertLabel(@Nonnull LabelNode ln) { return; } - StackFrame frame = operandStack.getOrCreateStackframe(ln); + OperandMerging merging = operandStack.getOrCreateMerging(ln); JCaughtExceptionRef ref = JavaJimple.getInstance().newCaughtExceptionRef(); Operand opr = new Operand(ln, ref, this); - frame.mergeOutput(opr); + merging.mergeOutput(opr); if (opr.stackLocal == null) { opr.stackLocal = 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/OperandMerging.java similarity index 81% rename from sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/StackFrame.java rename to sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java index f673399921b..75de01c100c 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/OperandMerging.java @@ -28,42 +28,35 @@ import sootup.core.jimple.common.stmt.Stmt; import sootup.core.jimple.visitor.ReplaceUseStmtVisitor; -/** - * Frame of stack for an instruction. (see ... ) - * - * @author Aaloan Miftah - */ -final class StackFrame { +/** @author Aaloan Miftah */ +final class OperandMerging { @Nonnull private final AbstractInsnNode insn; /** - * 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. + * Interestingly, none of the operations that need to have any inputs/outputs tracked produce more + * than a single output, so this doesn't need to be a list. */ - @Nullable private Operand out; + @Nullable private Operand output; - @Nonnull final ArrayList in = new ArrayList<>(1); + @Nonnull final ArrayList inputOperands = new ArrayList<>(1); @Nonnull private final AsmMethodSource src; /** - * Constructs a new stack frame. + * Constructs a new operand merging information holder. * - * @param src source the frame belongs to. + * @param src source the merging belongs to. */ - StackFrame(@Nonnull AbstractInsnNode insn, @Nonnull AsmMethodSource src) { + OperandMerging(@Nonnull AbstractInsnNode insn, @Nonnull AsmMethodSource src) { this.insn = insn; this.src = src; } void mergeOutput(@Nonnull Operand outputOperand) { - if (out == null) { - out = outputOperand; + if (output == null) { + output = outputOperand; } else { - if (out.stackLocal != null) { + if (output.stackLocal != null) { assert outputOperand.stackLocal == null; - outputOperand.changeStackLocal(out.stackLocal); + outputOperand.changeStackLocal(output.stackLocal); } } } @@ -94,13 +87,13 @@ void mergeOutput(@Nonnull Operand outputOperand) { * old operands. */ void mergeInputs(@Nonnull Operand... oprs) { - if (in.isEmpty()) { - in.add(oprs); + if (inputOperands.isEmpty()) { + inputOperands.add(oprs); // There are no other operands to merge with return; } - if (in.get(0).length != oprs.length) { + if (inputOperands.get(0).length != oprs.length) { throw new IllegalArgumentException("Invalid in operands length!"); } @@ -114,8 +107,8 @@ void mergeInputs(@Nonnull Operand... oprs) { Local stack = null; // 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; + for (int j = 0; j != inputOperands.size(); j++) { + stack = inputOperands.get(j)[i].stackLocal; if (stack != null) { break; } @@ -126,7 +119,7 @@ void mergeInputs(@Nonnull Operand... oprs) { stack = newOp.stackLocal; } - if (stack == null && in.get(0)[i].value.equivTo(newOp.value)) { + if (stack == null && inputOperands.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 @@ -142,8 +135,8 @@ void mergeInputs(@Nonnull Operand... oprs) { } /* add assign statement for prevOp */ - for (int j = 0; j != in.size(); j++) { - Operand prevOp = in.get(j)[i]; + for (int j = 0; j != inputOperands.size(); j++) { + Operand prevOp = inputOperands.get(j)[i]; prevOp.changeStackLocal(stack); } newOp.changeStackLocal(stack); @@ -152,7 +145,7 @@ void mergeInputs(@Nonnull Operand... oprs) { // 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); + new ReplaceUseStmtVisitor(inputOperands.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") @@ -171,6 +164,6 @@ void mergeInputs(@Nonnull Operand... oprs) { } } - in.add(oprs); + inputOperands.add(oprs); } } diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java index a5dfc670f9a..62a113300aa 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java @@ -37,21 +37,21 @@ public class OperandStack { @Nonnull private final AsmMethodSource methodSource; private List stack; - @Nonnull public Map frames; + @Nonnull public Map mergings; public OperandStack(@Nonnull AsmMethodSource methodSource, int nrInsn) { this.methodSource = methodSource; - frames = new LinkedHashMap<>(nrInsn); + mergings = new LinkedHashMap<>(nrInsn); } @Nonnull - public StackFrame getOrCreateStackframe(@Nonnull AbstractInsnNode insn) { - StackFrame frame = frames.get(insn); - if (frame == null) { - frame = new StackFrame(insn, methodSource); - frames.put(insn, frame); + public OperandMerging getOrCreateMerging(@Nonnull AbstractInsnNode insn) { + OperandMerging merging = this.mergings.get(insn); + if (merging == null) { + merging = new OperandMerging(insn, methodSource); + this.mergings.put(insn, merging); } - return frame; + return merging; } public void push(@Nonnull Operand opr) { From e561ce18c3833973805366a9001b16a00a597abb Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Tue, 7 Nov 2023 16:51:25 +0100 Subject: [PATCH 10/14] add more comments --- .../bytecode/frontend/OperandMerging.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java index 75de01c100c..480d2ab2b53 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java @@ -28,12 +28,25 @@ import sootup.core.jimple.common.stmt.Stmt; import sootup.core.jimple.visitor.ReplaceUseStmtVisitor; -/** @author Aaloan Miftah */ +/** + * This class tracks the inputs and outputs for an instruction. + * + *

When an instruction is reached from different branches, the state of the operand stack might + * be different. In that case, the different operands from the different branches need to be merged. + * This class does that merging. + * + * @author Aaloan Miftah + */ final class OperandMerging { @Nonnull private final AbstractInsnNode insn; + /** - * Interestingly, none of the operations that need to have any inputs/outputs tracked produce more - * than a single output, so this doesn't need to be a list. + * Keep track of the result of the instruction. The output might get a stack local assigned when + * it is used as a local or immediate. When another branch produces the output and calls + * `mergeOutput`, it should be assigned to the same stack local. + * + *

Interestingly, none of the operations that need to have any inputs/outputs tracked produce + * more than a single output, so this doesn't need to be a list. */ @Nullable private Operand output; @@ -50,6 +63,14 @@ final class OperandMerging { this.src = src; } + /** + * Merges the output operand produced by the instruction. + * + *

The output from a previous branch might have been assigned to a (stack) local in which case + * any newly created outputs for different branches should be assigned to the same stack local. + * + * @param outputOperand the newly produced operand that will get pushed onto the operand stack + */ void mergeOutput(@Nonnull Operand outputOperand) { if (output == null) { output = outputOperand; @@ -65,6 +86,8 @@ void mergeOutput(@Nonnull Operand outputOperand) { * Merges the specified operands with the operands that were previously used with this * instruction. * + *

This should be called after the operands have been popped of the stack. + * *

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. @@ -79,9 +102,6 @@ void mergeOutput(@Nonnull Operand outputOperand) { * 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. From d2dc5e5ef6e639572f3074e90eadd59a465b4ee9 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Wed, 8 Nov 2023 15:14:21 +0100 Subject: [PATCH 11/14] remove remaining duplicated code paths --- .../bytecode/frontend/AsmMethodSource.java | 162 ++++++++---------- 1 file changed, 72 insertions(+), 90 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 1b266ff6ad5..6ef0c6a184b 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 @@ -407,10 +407,8 @@ private void convertFieldInsn(@Nonnull FieldInsnNode insn) { private void convertIincInsn(@Nonnull IincInsnNode insn) { Local local = getOrCreateLocal(insn.var); addReadOperandAssignments(local); - if (!insnToStmt.containsKey(insn)) { - JAddExpr add = Jimple.newAddExpr(local, IntConstant.getInstance(insn.incr)); - setStmt(insn, Jimple.newAssignStmt(local, add, getStmtPositionInfo())); - } + JAddExpr add = Jimple.newAddExpr(local, IntConstant.getInstance(insn.incr)); + setStmt(insn, Jimple.newAssignStmt(local, add, getStmtPositionInfo())); } private void convertConstInsn(@Nonnull InsnNode insn) { @@ -699,9 +697,7 @@ private void convertInsn(@Nonnull InsnNode insn) { /* * We can ignore NOP instructions, but for completeness, we handle them */ - if (!insnToStmt.containsKey(insn)) { - insnToStmt.put(insn, Jimple.newNopStmt(getStmtPositionInfo())); - } + setStmt(insn, Jimple.newNopStmt(getStmtPositionInfo())); } else if (op >= ACONST_NULL && op <= DCONST_1) { convertConstInsn(insn); } else if (op >= IALOAD && op <= SALOAD) { @@ -732,9 +728,7 @@ private void convertInsn(@Nonnull InsnNode insn) { } else if (op >= IRETURN && op <= ARETURN) { convertReturnInsn(insn); } else if (op == RETURN) { - if (!insnToStmt.containsKey(insn)) { - setStmt(insn, Jimple.newReturnVoidStmt(getStmtPositionInfo())); - } + setStmt(insn, Jimple.newReturnVoidStmt(getStmtPositionInfo())); } else if (op == ATHROW) { OperandMerging merging = operandStack.getOrCreateMerging(insn); Operand opr = operandStack.pop(); @@ -806,89 +800,79 @@ private void convertIntInsn(@Nonnull IntInsnNode insn) { private void convertJumpInsn(@Nonnull JumpInsnNode insn) { int op = insn.getOpcode(); if (op == GOTO) { - if (!insnToStmt.containsKey(insn)) { - BranchingStmt gotoStmt = Jimple.newGotoStmt(getStmtPositionInfo()); - stmtsThatBranchToLabel.put(gotoStmt, insn.label); - setStmt(insn, gotoStmt); - } + BranchingStmt gotoStmt = Jimple.newGotoStmt(getStmtPositionInfo()); + stmtsThatBranchToLabel.put(gotoStmt, insn.label); + setStmt(insn, gotoStmt); return; } /* must be ifX insn */ OperandMerging merging = operandStack.getOrCreateMerging(insn); - if (!insnToStmt.containsKey(insn)) { - Operand val = operandStack.pop(); - Immediate v = val.toImmediate(); - AbstractConditionExpr cond; - - if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { - Operand val1 = operandStack.pop(); - merging.mergeInputs(val, val1); - Immediate v1 = val1.toImmediate(); - switch (op) { - case IF_ICMPEQ: - case IF_ACMPEQ: - cond = Jimple.newEqExpr(v1, v); - break; - case IF_ICMPNE: - case IF_ACMPNE: - cond = Jimple.newNeExpr(v1, v); - break; - case IF_ICMPLT: - cond = Jimple.newLtExpr(v1, v); - break; - case IF_ICMPGE: - cond = Jimple.newGeExpr(v1, v); - break; - case IF_ICMPGT: - cond = Jimple.newGtExpr(v1, v); - break; - case IF_ICMPLE: - cond = Jimple.newLeExpr(v1, v); - break; - default: - throw new UnsupportedOperationException("Unknown if op: " + op); - } - } else { - merging.mergeInputs(val); - switch (op) { - case IFEQ: - cond = Jimple.newEqExpr(v, IntConstant.getInstance(0)); - break; - case IFNE: - cond = Jimple.newNeExpr(v, IntConstant.getInstance(0)); - break; - case IFLT: - cond = Jimple.newLtExpr(v, IntConstant.getInstance(0)); - break; - case IFGE: - cond = Jimple.newGeExpr(v, IntConstant.getInstance(0)); - break; - case IFGT: - cond = Jimple.newGtExpr(v, IntConstant.getInstance(0)); - break; - case IFLE: - cond = Jimple.newLeExpr(v, IntConstant.getInstance(0)); - break; - case IFNULL: - cond = Jimple.newEqExpr(v, NullConstant.getInstance()); - break; - case IFNONNULL: - cond = Jimple.newNeExpr(v, NullConstant.getInstance()); - break; - default: - throw new UnsupportedOperationException("Unknown if op: " + op); - } + Operand val = operandStack.pop(); + Immediate v = val.toImmediate(); + AbstractConditionExpr cond; + + if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { + Operand val1 = operandStack.pop(); + merging.mergeInputs(val, val1); + Immediate v1 = val1.toImmediate(); + switch (op) { + case IF_ICMPEQ: + case IF_ACMPEQ: + cond = Jimple.newEqExpr(v1, v); + break; + case IF_ICMPNE: + case IF_ACMPNE: + cond = Jimple.newNeExpr(v1, v); + break; + case IF_ICMPLT: + cond = Jimple.newLtExpr(v1, v); + break; + case IF_ICMPGE: + cond = Jimple.newGeExpr(v1, v); + break; + case IF_ICMPGT: + cond = Jimple.newGtExpr(v1, v); + break; + case IF_ICMPLE: + cond = Jimple.newLeExpr(v1, v); + break; + default: + throw new UnsupportedOperationException("Unknown if op: " + op); } - BranchingStmt ifStmt = Jimple.newIfStmt(cond, getStmtPositionInfo()); - stmtsThatBranchToLabel.put(ifStmt, insn.label); - setStmt(insn, ifStmt); } else { - if (op >= IF_ICMPEQ && op <= IF_ACMPNE) { - merging.mergeInputs(operandStack.pop(), operandStack.pop()); - } else { - merging.mergeInputs(operandStack.pop()); + merging.mergeInputs(val); + switch (op) { + case IFEQ: + cond = Jimple.newEqExpr(v, IntConstant.getInstance(0)); + break; + case IFNE: + cond = Jimple.newNeExpr(v, IntConstant.getInstance(0)); + break; + case IFLT: + cond = Jimple.newLtExpr(v, IntConstant.getInstance(0)); + break; + case IFGE: + cond = Jimple.newGeExpr(v, IntConstant.getInstance(0)); + break; + case IFGT: + cond = Jimple.newGtExpr(v, IntConstant.getInstance(0)); + break; + case IFLE: + cond = Jimple.newLeExpr(v, IntConstant.getInstance(0)); + break; + case IFNULL: + cond = Jimple.newEqExpr(v, NullConstant.getInstance()); + break; + case IFNONNULL: + cond = Jimple.newNeExpr(v, NullConstant.getInstance()); + break; + default: + throw new UnsupportedOperationException("Unknown if op: " + op); } } + BranchingStmt ifStmt = Jimple.newIfStmt(cond, getStmtPositionInfo()); + stmtsThatBranchToLabel.put(ifStmt, insn.label); + setStmt(insn, ifStmt); } private void convertLdcInsn(@Nonnull LdcInsnNode insn) { @@ -1065,7 +1049,7 @@ private void convertMethodInsn(@Nonnull MethodInsnNode insn) { operandStack.pushDual(opr); } else if (returnType != VoidType.getInstance()) { operandStack.push(opr); - } else if (!insnToStmt.containsKey(insn)) { + } else { JInvokeStmt stmt = Jimple.newInvokeStmt((AbstractInvokeExpr) opr.value, getStmtPositionInfo()); setStmt(insn, stmt); @@ -1125,7 +1109,7 @@ private void convertInvokeDynamicInsn(@Nonnull InvokeDynamicInsnNode insn) { operandStack.pushDual(opr); } else if (!(returnType instanceof VoidType)) { operandStack.push(opr); - } else if (!insnToStmt.containsKey(insn)) { + } else { JInvokeStmt stmt = Jimple.newInvokeStmt((AbstractInvokeExpr) opr.value, getStmtPositionInfo()); setStmt(insn, stmt); @@ -1258,9 +1242,7 @@ private void convertVarInsn(@Nonnull VarInsnNode insn) { convertVarStoreInsn(insn); } else if (op == RET) { /* we handle it, even though it should be removed */ - if (!insnToStmt.containsKey(insn)) { - setStmt(insn, Jimple.newRetStmt(getOrCreateLocal(insn.var), getStmtPositionInfo())); - } + setStmt(insn, Jimple.newRetStmt(getOrCreateLocal(insn.var), getStmtPositionInfo())); } else { throw new UnsupportedOperationException("Unknown var op: " + op); } From 949ed252630cb31fa8eedef8bd98c7af8ac2ca87 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Wed, 8 Nov 2023 15:53:22 +0100 Subject: [PATCH 12/14] don't replace the operand value when creating a new stack local when merging Instead, rely on the fact that all `convert` methods override their respective statements. That means just changing the incoming operands will automatically correctly update the statement. Also added a test case to make sure this works with the special case of using a normal local as a stack local. --- .../java6/binary/LocalMerging.class | Bin 885 -> 1078 bytes .../java6/source/LocalMerging.java | 6 +++ .../bytecode/frontend/AsmMethodSource.java | 5 +-- .../java/bytecode/frontend/Operand.java | 8 ---- .../bytecode/frontend/OperandMerging.java | 32 +------------- .../java/bytecode/frontend/OperandStack.java | 2 +- .../java6/LocalMergingTest.java | 40 ++++++++++++++++++ 7 files changed, 50 insertions(+), 43 deletions(-) diff --git a/shared-test-resources/miniTestSuite/java6/binary/LocalMerging.class b/shared-test-resources/miniTestSuite/java6/binary/LocalMerging.class index eb3b9b9edb71d248d9422dbcfa430f78e4fc8c0b..f908f52499a38628d63dc3358175332052cb7835 100644 GIT binary patch delta 497 zcmY*VO-lk%6g}_F=!i(634Pf3k*22BfV8TxS{Au-5!PyoFk>(hY1^WoF#UiQL2EY! zLH&TBP5-4`G@UmN4e@yA-FNPL=bn4NqEE5-`_Ic8fNfag_@Z!cMKT#fRzTP>o&S*$})BO~s zRsoVFfCvP4lvw1=+TDz0dwaFiryMoRU0Qtx(GF>fYcDi~h*J+S)Iq)jekb delta 319 zcmYk0O=z3G8YhyTesO{JK2a5T!i37`Uc*>je-jg;6ZW& zccQ-5!I6Sjf7DlRlRl<0hx2X^U<7}ic@;B}uabk7AU_Y6Vek_!rordm?`$zOEBP=b z)g0EKcmO0INFqgb*}2TZoa}8OYZih!)vj$Vc*8r@HB049?B)fUsu>Q7NTb273w1#b z+CfbmG(jm`c=Z@44sHvyBiz68rEYS%Jg|Vi1r(5o05OAhpJp+p6Jfr!7sRfLbXB3N T>djRa>Vzy=V diff --git a/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java b/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java index 3a5e64e0431..873d6053e73 100644 --- a/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java +++ b/shared-test-resources/miniTestSuite/java6/source/LocalMerging.java @@ -24,4 +24,10 @@ public void localMergingWithDuplicateValue(int n) { // the second argument isn't replaced as well. System.setProperty(n == 1 ? a : "two", "two"); } + + public void localMergingWithInlining(int n) { + String[] arr = new String[] {"a", "b"}; + int a = 1; + String b = arr[n == 1 ? 0 : a]; + } } 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 6ef0c6a184b..220f320e90b 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 @@ -1215,14 +1215,13 @@ private void convertVarStoreInsn(@Nonnull VarInsnNode insn) { merging.mergeInputs(opr); Local local = getOrCreateLocal(insn.var); AbstractDefinitionStmt as; - if (opr.stackLocal == null) { + if (opr.stackLocal == null || opr.stackLocal == local) { // 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) { + } else { as = Jimple.newAssignStmt(local, opr.toImmediate(), getStmtPositionInfo()); setStmt(insn, as); } 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 c8f9df5f8c2..1034916192c 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 @@ -46,9 +46,6 @@ class Operand { @Nonnull protected AbstractInsnNode insn; @Nonnull protected final Value value; - // TODO probably need to store the `insn` for the STORE instruction when a *real* - // local is used here and use that when merging, - // or more specifically when changing to a different stack local in `changeStackLocal` @Nullable protected Local stackLocal; @Nonnull private final AsmMethodSource methodSource; @@ -78,11 +75,6 @@ void emitStatement() { return; } - if (methodSource.getStmt(insn) != null) { - // the operand is already used, which means side effects already happen as well - return; - } - if (value instanceof AbstractInvokeExpr) { methodSource.setStmt( insn, diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java index 480d2ab2b53..1fe7d43446c 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandMerging.java @@ -23,10 +23,7 @@ import java.util.ArrayList; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.objectweb.asm.tree.AbstractInsnNode; import sootup.core.jimple.basic.Local; -import sootup.core.jimple.common.stmt.Stmt; -import sootup.core.jimple.visitor.ReplaceUseStmtVisitor; /** * This class tracks the inputs and outputs for an instruction. @@ -38,8 +35,6 @@ * @author Aaloan Miftah */ final class OperandMerging { - @Nonnull private final AbstractInsnNode insn; - /** * Keep track of the result of the instruction. The output might get a stack local assigned when * it is used as a local or immediate. When another branch produces the output and calls @@ -58,8 +53,7 @@ final class OperandMerging { * * @param src source the merging belongs to. */ - OperandMerging(@Nonnull AbstractInsnNode insn, @Nonnull AsmMethodSource src) { - this.insn = insn; + OperandMerging(@Nonnull AsmMethodSource src) { this.src = src; } @@ -148,8 +142,6 @@ void mergeInputs(@Nonnull Operand... oprs) { // 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(); } @@ -160,28 +152,6 @@ void mergeInputs(@Nonnull Operand... oprs) { 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(inputOperands.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()); - } } inputOperands.add(oprs); diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java index 62a113300aa..99cc68bd41d 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/OperandStack.java @@ -48,7 +48,7 @@ public OperandStack(@Nonnull AsmMethodSource methodSource, int nrInsn) { public OperandMerging getOrCreateMerging(@Nonnull AbstractInsnNode insn) { OperandMerging merging = this.mergings.get(insn); if (merging == null) { - merging = new OperandMerging(insn, methodSource); + merging = new OperandMerging(methodSource); this.mergings.put(insn, merging); } return merging; diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java index 346f88e6847..f1459ba58d1 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java6/LocalMergingTest.java @@ -40,6 +40,15 @@ public void test() { "void", Collections.singletonList("int"))); assertJimpleStmts(methodDuplicateValue, expectedBodyStmtsDuplicateValue()); + + SootMethod methodWithInlining = + loadMethod( + identifierFactory.getMethodSignature( + getDeclaredClassSignature(), + "localMergingWithInlining", + "void", + Collections.singletonList("int"))); + assertJimpleStmts(methodWithInlining, expectedBodyStmtsWithInlining()); } /** @@ -133,4 +142,35 @@ public List expectedBodyStmtsDuplicateValue() { "return") .collect(Collectors.toList()); } + + /** + * + * + *

+   * public void localMergingWithInlining(int n) {
+   *     String[] arr = new String[] {"a", "b"};
+   *     int a = 1;
+   *     String b = arr[n == 1 ? 0 : a];
+   * }
+   * 
+ */ + public List expectedBodyStmtsWithInlining() { + return Stream.of( + "$l0 := @this: LocalMerging", + "$l1 := @parameter0: int", + "$stack5 = newarray (java.lang.String)[2]", + "$stack5[0] = \"a\"", + "$stack5[1] = \"b\"", + "$l2 = $stack5", + "$l3 = 1", + "if $l1 != 1 goto label1", + "$stack6 = 0", + "goto label2", + "label1:", + "$stack6 = $l3", + "label2:", + "$l4 = $l2[$stack6]", + "return") + .collect(Collectors.toList()); + } } From 70dd6f2af701037bd6ba77c0819ce65574604e3e Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Wed, 8 Nov 2023 16:07:14 +0100 Subject: [PATCH 13/14] fix position info for operands --- .../java/sootup/java/bytecode/frontend/Operand.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) 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 1034916192c..5f185ce9224 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 @@ -28,6 +28,7 @@ import sootup.core.jimple.Jimple; import sootup.core.jimple.basic.Immediate; import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; import sootup.core.jimple.basic.Value; import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.stmt.JAssignStmt; @@ -48,6 +49,7 @@ class Operand { @Nonnull protected final Value value; @Nullable protected Local stackLocal; @Nonnull private final AsmMethodSource methodSource; + @Nonnull private final StmtPositionInfo positionInfo; /** * Constructs a new stack operand. @@ -60,6 +62,7 @@ class Operand { this.insn = insn; this.value = value; this.methodSource = methodSource; + this.positionInfo = methodSource == null ? null : methodSource.getStmtPositionInfo(); } Local getOrAssignValueToStackLocal() { @@ -76,9 +79,7 @@ void emitStatement() { } if (value instanceof AbstractInvokeExpr) { - methodSource.setStmt( - insn, - Jimple.newInvokeStmt((AbstractInvokeExpr) value, methodSource.getStmtPositionInfo())); + methodSource.setStmt(insn, Jimple.newInvokeStmt((AbstractInvokeExpr) value, positionInfo)); } else { // create an assignment that uses the value because it might have side effects getOrAssignValueToStackLocal(); @@ -95,10 +96,8 @@ void changeStackLocal(Local newStackLocal) { JAssignStmt assignStmt = methodSource.getStmt(insn); if (assignStmt == null) { - // TODO the position info is the position of the *usage* (which is only mostly correct?) // emit `$newStackLocal = value` - methodSource.setStmt( - insn, Jimple.newAssignStmt(newStackLocal, value, methodSource.getStmtPositionInfo())); + methodSource.setStmt(insn, Jimple.newAssignStmt(newStackLocal, value, positionInfo)); } else { assert assignStmt.getLeftOp() == oldStackLocal || assignStmt.getLeftOp() == newStackLocal; // replace `$oldStackLocal = value` with `$newStackLocal = value` From e7e565eb697e4cb69e2c65791fe374ad74b57de1 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Wed, 8 Nov 2023 16:23:29 +0100 Subject: [PATCH 14/14] fix position info for statements When converting the instructions for a branch, there might not be a line number node after the branch target. This would result in incorrect line numbers being used until the next line number node. The fix is to simply store the line number for branches and restore the current line number when handling that branch. --- .../java/bytecode/frontend/AsmMethodSource.java | 11 +++++++---- .../java/bytecode/frontend/BranchedInsnInfo.java | 9 ++++++++- 2 files changed, 15 insertions(+), 5 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 220f320e90b..72c45b3da82 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 @@ -1301,7 +1301,7 @@ private void addEdges( BranchedInsnInfo edge = edges.get(branchingInsn, tgt); if (edge == null) { // [ms] check why this edge could be already there - edge = new BranchedInsnInfo(tgt, operandStack.getStack()); + edge = new BranchedInsnInfo(tgt, operandStack.getStack(), currentLineNumber); edge.addToPrevStack(stackss); edges.put(branchingInsn, tgt, edge); conversionWorklist.add(edge); @@ -1349,20 +1349,23 @@ private void convert() { Operand opr = new Operand(handlerNode, ref, this); opr.stackLocal = local; - worklist.add(new BranchedInsnInfo(handlerNode, Collections.singletonList(opr))); + worklist.add( + new BranchedInsnInfo(handlerNode, Collections.singletonList(opr), currentLineNumber)); // Save the statements inlineExceptionHandlers.put(handlerNode, as); } else { - worklist.add(new BranchedInsnInfo(handlerNode, new ArrayList<>())); + worklist.add(new BranchedInsnInfo(handlerNode, new ArrayList<>(), currentLineNumber)); } } - worklist.add(new BranchedInsnInfo(instructions.getFirst(), Collections.emptyList())); + worklist.add( + new BranchedInsnInfo(instructions.getFirst(), Collections.emptyList(), currentLineNumber)); Table edges = HashBasedTable.create(1, 1); do { BranchedInsnInfo edge = worklist.pollLast(); AbstractInsnNode insn = edge.getInsn(); + currentLineNumber = edge.getLineNumber(); operandStack.setOperandStack( new ArrayList<>(edge.getOperandStacks().get(edge.getOperandStacks().size() - 1))); do { diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/BranchedInsnInfo.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/BranchedInsnInfo.java index aec943546d4..f12eef7ed0a 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/BranchedInsnInfo.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/frontend/BranchedInsnInfo.java @@ -33,11 +33,14 @@ class BranchedInsnInfo { @Nonnull private final LinkedList prevStacks; /* current stack at edge */ @Nullable private final List> operandStacks = new ArrayList<>(); + private final int lineNumber; - BranchedInsnInfo(@Nonnull AbstractInsnNode insn, @Nonnull List operands) { + BranchedInsnInfo( + @Nonnull AbstractInsnNode insn, @Nonnull List operands, int lineNumber) { this.insn = insn; this.prevStacks = new LinkedList<>(); this.operandStacks.add(operands); + this.lineNumber = lineNumber; } @Nonnull @@ -62,4 +65,8 @@ public LinkedList getPrevStacks() { public void addToPrevStack(@Nonnull Operand[] stacksOperands) { prevStacks.add(stacksOperands); } + + public int getLineNumber() { + return this.lineNumber; + } }