From d79eaffd3af5556949937c666d90a6ccaf42e4a5 Mon Sep 17 00:00:00 2001 From: Cornelius Riemenschneider Date: Tue, 10 Sep 2019 14:31:10 +0200 Subject: [PATCH] Prune unreachable paths in the Java dataflow library based on call context. We now detect patterns like f(bool cond){ if(cond) then A else B and prune branches for calls like f(true) or f(false). This pruning is done both in the local (bigstep) flow graph as well as in the inter-procedural dataflow graph. --- .../java/dataflow/internal/DataFlowImpl.qll | 54 ++++++++------ .../dataflow/internal/DataFlowImplCommon.qll | 72 +++++++++++++++++-- .../dataflow/internal/DataFlowPrivate.qll | 44 ++++++++++++ .../dataflow/call-sensitivity/flow.expected | 33 --------- 4 files changed, 145 insertions(+), 58 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index ab2f43dea64..25db3e1e72c 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) { */ pragma[nomagic] private predicate localFlowStepPlus( - Node node1, Node node2, boolean preservesValue, Configuration config + Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc ) { + not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and + ( localFlowEntry(node1, config) and ( localFlowStep(node1, node2, config) and preservesValue = true @@ -914,22 +916,24 @@ private predicate localFlowStepPlus( additionalLocalFlowStep(node1, node2, config) and preservesValue = false ) and node1 != node2 and + cc.validFor(node1) and nodeCand(node2, unbind(config)) or exists(Node mid | - localFlowStepPlus(node1, mid, preservesValue, config) and + localFlowStepPlus(node1, mid, preservesValue, config, cc) and localFlowStep(mid, node2, config) and not mid instanceof CastNode and nodeCand(node2, unbind(config)) ) or exists(Node mid | - localFlowStepPlus(node1, mid, _, config) and + localFlowStepPlus(node1, mid, _, config, cc) and additionalLocalFlowStep(mid, node2, config) and not mid instanceof CastNode and preservesValue = false and nodeCand(node2, unbind(config)) ) + ) } /** @@ -938,9 +942,9 @@ private predicate localFlowStepPlus( */ pragma[noinline] private predicate localFlowBigStep( - Node node1, Node node2, boolean preservesValue, Configuration config + Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext ) { - localFlowStepPlus(node1, node2, preservesValue, config) and + localFlowStepPlus(node1, node2, preservesValue, config, callContext) and localFlowExit(node2, config) } @@ -1000,7 +1004,7 @@ private class AccessPathFrontNilNode extends Node { ( any(Configuration c).isSource(this) or - localFlowBigStep(_, this, false, _) + localFlowBigStep(_, this, false, _, _) or additionalJumpStep(_, this, _) ) @@ -1023,12 +1027,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf, ( exists(Node mid | flowCandFwd(mid, fromArg, apf, config) and - localFlowBigStep(mid, node, true, config) + localFlowBigStep(mid, node, true, config, _) ) or exists(Node mid, AccessPathFrontNil nil | flowCandFwd(mid, fromArg, nil, config) and - localFlowBigStep(mid, node, false, config) and + localFlowBigStep(mid, node, false, config, _) and apf = node.(AccessPathFrontNilNode).getApf() ) or @@ -1122,13 +1126,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co apf instanceof AccessPathFrontNil or exists(Node mid | - localFlowBigStep(node, mid, true, config) and + localFlowBigStep(node, mid, true, config, _) and flowCand(mid, toReturn, apf, config) ) or exists(Node mid, AccessPathFrontNil nil | flowCandFwd(node, _, apf, config) and - localFlowBigStep(node, mid, false, config) and + localFlowBigStep(node, mid, false, config, _) and flowCand(mid, toReturn, nil, config) and apf instanceof AccessPathFrontNil ) @@ -1363,12 +1367,12 @@ private predicate flowFwd0( ( exists(Node mid | flowFwd(mid, fromArg, apf, ap, config) and - localFlowBigStep(mid, node, true, config) + localFlowBigStep(mid, node, true, config, _) ) or exists(Node mid, AccessPathNil nil | flowFwd(mid, fromArg, _, nil, config) and - localFlowBigStep(mid, node, false, config) and + localFlowBigStep(mid, node, false, config, _) and ap = node.(AccessPathNilNode).getAp() and apf = ap.(AccessPathNil).getFront() ) @@ -1472,13 +1476,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio ap instanceof AccessPathNil or exists(Node mid | - localFlowBigStep(node, mid, true, config) and + localFlowBigStep(node, mid, true, config, _) and flow(mid, toReturn, ap, config) ) or exists(Node mid, AccessPathNil nil | flowFwd(node, _, _, ap, config) and - localFlowBigStep(node, mid, false, config) and + localFlowBigStep(node, mid, false, config, _) and flow(mid, toReturn, nil, config) and ap instanceof AccessPathNil ) @@ -1664,8 +1668,11 @@ module PathGraph { */ private class PathNodeMid extends PathNode, TPathNodeMid { Node node; + CallContext cc; + AccessPath ap; + Configuration config; PathNodeMid() { this = TPathNodeMid(node, cc, ap, config) } @@ -1711,6 +1718,7 @@ private class PathNodeMid extends PathNode, TPathNodeMid { */ private class PathNodeSink extends PathNode, TPathNodeSink { Node node; + Configuration config; PathNodeSink() { this = TPathNodeSink(node, config) } @@ -1729,15 +1737,18 @@ private class PathNodeSink extends PathNode, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) { - localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and + exists(LocalCallContext localCC | localCC.matchesCallContext(cc) | + localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration(), localCC) and cc = mid.getCallContext() and ap = mid.getAp() or - localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and + localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration(), localCC) and cc = mid.getCallContext() and mid.getAp() instanceof AccessPathNil and ap = node.(AccessPathNilNode).getAp() - or + ) or + not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and + ( jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and ap = mid.getAp() @@ -1760,6 +1771,7 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat pathThroughCallable(mid, node, cc, ap) or valuePathThroughCallable(mid, node, cc) and ap = mid.getAp() + ) } pragma[noinline] @@ -1880,7 +1892,7 @@ private predicate pathIntoCallable( pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and p.isParameterOf(callable, i) | - if reducedViableImplInCallContext(_, callable, call) + if recordDataFlowCallSite(call, callable) then innercc = TSpecificCall(call, i, emptyAp) else innercc = TSomeCall(p, emptyAp) ) @@ -2180,8 +2192,11 @@ private module FlowExploration { private class PartialPathNodePriv extends PartialPathNode { Node node; + CallContext cc; + PartialAccessPath ap; + Configuration config; PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, ap, config) } @@ -2378,7 +2393,7 @@ private module FlowExploration { partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and p.isParameterOf(callable, i) | - if reducedViableImplInCallContext(_, callable, call) + if recordDataFlowCallSite(call, callable) then innercc = TSpecificCall(call, i, emptyAp) else innercc = TSomeCall(p, emptyAp) ) @@ -2446,7 +2461,6 @@ private module FlowExploration { ) } } - import FlowExploration private predicate partialFlow( diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index 8b4b01de79d..106baa0702e 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -125,7 +125,7 @@ private module ImplCommon { outercc = TSomeCall(getAParameter(c), _) or exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) | - reducedViableImplInCallContext(_, c, other) + recordDataFlowCallSite(other, c) ) ) } @@ -152,7 +152,7 @@ private module ImplCommon { exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) | - if reducedViableImplInCallContext(_, callable, call) + if recordDataFlowCallSite(call, callable) then innercc = TSpecificCall(call, i, true) else innercc = TSomeCall(p, true) ) @@ -164,7 +164,7 @@ private module ImplCommon { exists(DataFlowCall call, int i, DataFlowCallable callable | result = TSpecificCall(call, i, _) and p.isParameterOf(callable, i) and - reducedViableImplInCallContext(_, callable, call) + recordDataFlowCallSite(call, callable) ) } @@ -575,11 +575,21 @@ private module ImplCommon { exists(ArgumentNode arg | arg.argumentOf(call, -1)) } + /** + * Record a call site in the dataflow graph if it either improves + * virtual dispatch or if we can remove unreachable edges by recoring this call site + */ + cached + predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { + reducedViableImplInCallContext(_, callable, call) or + exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call)) + } + cached newtype TCallContext = TAnyCallContext() or TSpecificCall(DataFlowCall call, int i, boolean emptyAp) { - reducedViableImplInCallContext(_, _, call) and + recordDataFlowCallSite(call, _) and (emptyAp = true or emptyAp = false) and ( exists(call.getArgument(i)) @@ -593,6 +603,11 @@ private module ImplCommon { cached newtype TReturnPosition = TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) } + + cached + newtype TLocalFlowCallContext = + TAnyLocalCall() or + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) } } pragma[noinline] @@ -609,7 +624,8 @@ private module ImplCommon { * - `TAnyCallContext()` : No restrictions on method flow. * - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th * parameter at the given `call`. This call improves the set of viable - * dispatch targets for at least one method call in the current callable. + * dispatch targets for at least one method call in the current callable + * or helps pruning unreachable nodes from the data flow graph. * - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The * originating call does not improve the set of dispatch targets for any * method call in the current callable and was therefore not recorded. @@ -633,6 +649,8 @@ private module ImplCommon { result = "CcCall(" + call + ", " + i + ")" ) } + + DataFlowCall getCall() { this = TSpecificCall(result, _, _) } } class CallContextSomeCall extends CallContextCall, TSomeCall { @@ -645,9 +663,53 @@ private module ImplCommon { } } + /** + * A call context which is used to restrict local data flow nodes + * to nodes which are actually reachable in a call context. + */ + abstract class LocalCallContext extends TLocalFlowCallContext { + abstract string toString(); + + abstract predicate matchesCallContext(CallContext ctx); + + abstract predicate validFor(Node n); + } + + class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { + override string toString() { result = "LocalCcAny" } + + override predicate matchesCallContext(CallContext ctx) { + not ctx instanceof CallContextSpecificCall or + not exists(TSpecificLocalCall(ctx.(CallContextSpecificCall).getCall())) + } + + override predicate validFor(Node n) { any() } + } + + class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { + LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } + + DataFlowCall call; + + DataFlowCall getCall() { result = call } + + override string toString() { result = "LocalCcCall(" + call + ")" } + + override predicate matchesCallContext(CallContext ctx) { + ctx.(CallContextSpecificCall).getCall() = call + } + + override predicate validFor(Node n) { + exists(Node n2 | + isUnreachableInCall(n2, call) and n2.getEnclosingCallable() = n.getEnclosingCallable() + ) + } + } + /** A callable tagged with a relevant return kind. */ class ReturnPosition extends TReturnPosition0 { private DataFlowCallable c; + private ReturnKind kind; ReturnPosition() { this = TReturnPosition0(c, kind) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index b2b6295028b..771f214c3f9 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -1,7 +1,10 @@ private import java private import DataFlowUtil +private import DataFlowImplCommon private import DataFlowDispatch +private import semmle.code.java.controlflow.Guards private import semmle.code.java.dataflow.SSA +private import semmle.code.java.dataflow.DefUse private import semmle.code.java.dataflow.TypeFlow private newtype TReturnKind = TNormalReturnKind() @@ -283,3 +286,44 @@ class DataFlowCall extends Call { /** Gets the data flow node corresponding to this call. */ ExprNode getNode() { result.getExpr() = this } } + +/** An expression that always has the same boolean value. */ +private predicate constantBooleanExpr(Expr e, boolean val) { + e.(CompileTimeConstantExpr).getBooleanValue() = val + or + exists(SsaExplicitUpdate v, Expr src | + e = v.getAUse() and + src = v.getDefiningExpr().(VariableAssign).getSource() and + constantBooleanExpr(src, val) + ) +} + +/** An expression that always has the same boolean value. */ +class ConstantBooleanExprNode extends ArgumentNode, ExprNode { + ConstantBooleanExprNode() { constantBooleanExpr(this.getExpr(), _) } + + /** Gets the boolean value of this expression. */ + boolean getBooleanValue() { constantBooleanExpr(this.getExpr(), result) } +} + +/** + * holds if the node `n` is unreachable when called from `call` + */ +cached +predicate isUnreachableInCall(Node n, DataFlowCall call) { + exists( + ExplicitParameterNode paramNode, ConstantBooleanExprNode arg, BasicBlock bb, + SsaImplicitInit varInit, Guard guard + | + // get argument and parameter for this call + viableParamArg(call, paramNode, arg) and + // get the ssa variable definition for this parameter + varInit.isParameterDefinition(paramNode.getParameter()) and + // which is used in a guard + varInit.getAUse() = guard and + // which controls that bb is not active + guard.controls(bb, arg.getBooleanValue().booleanNot()) and + // and the node we pass in is in this bb + bb.getANode() = n.asExpr() + ) +} diff --git a/java/ql/test/library-tests/dataflow/call-sensitivity/flow.expected b/java/ql/test/library-tests/dataflow/call-sensitivity/flow.expected index 31d54b61926..e8a29134867 100644 --- a/java/ql/test/library-tests/dataflow/call-sensitivity/flow.expected +++ b/java/ql/test/library-tests/dataflow/call-sensitivity/flow.expected @@ -3,25 +3,14 @@ edges | A.java:12:30:12:37 | o [ : Number] | A.java:14:9:14:9 | o | | A.java:18:31:18:38 | o [ : Number] | A.java:20:9:20:9 | o | | A.java:24:35:24:42 | o [ : Number] | A.java:32:8:32:9 | o3 | -| A.java:37:18:37:31 | new Integer(...) [ : Number] | A.java:6:29:6:36 | o [ : Number] | -| A.java:38:19:38:32 | new Integer(...) [ : Number] | A.java:12:30:12:37 | o [ : Number] | -| A.java:39:20:39:33 | new Integer(...) [ : Number] | A.java:18:31:18:38 | o [ : Number] | -| A.java:40:24:40:37 | new Integer(...) [ : Number] | A.java:24:35:24:42 | o [ : Number] | | A.java:42:18:42:31 | new Integer(...) [ : Number] | A.java:6:29:6:36 | o [ : Number] | | A.java:43:19:43:32 | new Integer(...) [ : Number] | A.java:12:30:12:37 | o [ : Number] | | A.java:44:20:44:33 | new Integer(...) [ : Number] | A.java:18:31:18:38 | o [ : Number] | | A.java:45:24:45:37 | new Integer(...) [ : Number] | A.java:24:35:24:42 | o [ : Number] | -| A.java:52:18:52:31 | new Integer(...) [ : Number] | A.java:6:29:6:36 | o [ : Number] | -| A.java:53:19:53:32 | new Integer(...) [ : Number] | A.java:12:30:12:37 | o [ : Number] | -| A.java:54:20:54:33 | new Integer(...) [ : Number] | A.java:18:31:18:38 | o [ : Number] | -| A.java:55:24:55:37 | new Integer(...) [ : Number] | A.java:24:35:24:42 | o [ : Number] | | A.java:57:18:57:31 | new Integer(...) [ : Number] | A.java:6:29:6:36 | o [ : Number] | | A.java:58:19:58:32 | new Integer(...) [ : Number] | A.java:12:30:12:37 | o [ : Number] | | A.java:59:20:59:33 | new Integer(...) [ : Number] | A.java:18:31:18:38 | o [ : Number] | | A.java:60:24:60:37 | new Integer(...) [ : Number] | A.java:24:35:24:42 | o [ : Number] | -| A.java:67:20:67:33 | new Integer(...) [ : Number] | A.java:78:30:78:37 | o [ : Number] | -| A.java:68:21:68:34 | new Integer(...) [ : Number] | A.java:85:31:85:38 | o [ : Number] | -| A.java:69:26:69:39 | new Integer(...) [ : Number] | A.java:92:36:92:43 | o [ : Number] | | A.java:71:20:71:33 | new Integer(...) [ : Number] | A.java:78:30:78:37 | o [ : Number] | | A.java:72:21:72:34 | new Integer(...) [ : Number] | A.java:85:31:85:38 | o [ : Number] | | A.java:73:26:73:39 | new Integer(...) [ : Number] | A.java:92:36:92:43 | o [ : Number] | @@ -37,25 +26,14 @@ nodes | A.java:20:9:20:9 | o | semmle.label | o | | A.java:24:35:24:42 | o [ : Number] | semmle.label | o [ : Number] | | A.java:32:8:32:9 | o3 | semmle.label | o3 | -| A.java:37:18:37:31 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:38:19:38:32 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:39:20:39:33 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:40:24:40:37 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:42:18:42:31 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:43:19:43:32 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:44:20:44:33 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:45:24:45:37 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:52:18:52:31 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:53:19:53:32 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:54:20:54:33 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:55:24:55:37 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:57:18:57:31 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:58:19:58:32 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:59:20:59:33 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:60:24:60:37 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:67:20:67:33 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:68:21:68:34 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | -| A.java:69:26:69:39 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:71:20:71:33 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:72:21:72:34 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | | A.java:73:26:73:39 | new Integer(...) [ : Number] | semmle.label | new Integer(...) [ : Number] | @@ -66,25 +44,14 @@ nodes | A.java:92:36:92:43 | o [ : Number] | semmle.label | o [ : Number] | | A.java:100:9:100:10 | o3 | semmle.label | o3 | #select -| A.java:37:18:37:31 | new Integer(...) [ : Number] | A.java:37:18:37:31 | new Integer(...) [ : Number] | A.java:8:9:8:9 | o | $@ | A.java:8:9:8:9 | o | o | -| A.java:38:19:38:32 | new Integer(...) [ : Number] | A.java:38:19:38:32 | new Integer(...) [ : Number] | A.java:14:9:14:9 | o | $@ | A.java:14:9:14:9 | o | o | -| A.java:39:20:39:33 | new Integer(...) [ : Number] | A.java:39:20:39:33 | new Integer(...) [ : Number] | A.java:20:9:20:9 | o | $@ | A.java:20:9:20:9 | o | o | -| A.java:40:24:40:37 | new Integer(...) [ : Number] | A.java:40:24:40:37 | new Integer(...) [ : Number] | A.java:32:8:32:9 | o3 | $@ | A.java:32:8:32:9 | o3 | o3 | | A.java:42:18:42:31 | new Integer(...) [ : Number] | A.java:42:18:42:31 | new Integer(...) [ : Number] | A.java:8:9:8:9 | o | $@ | A.java:8:9:8:9 | o | o | | A.java:43:19:43:32 | new Integer(...) [ : Number] | A.java:43:19:43:32 | new Integer(...) [ : Number] | A.java:14:9:14:9 | o | $@ | A.java:14:9:14:9 | o | o | | A.java:44:20:44:33 | new Integer(...) [ : Number] | A.java:44:20:44:33 | new Integer(...) [ : Number] | A.java:20:9:20:9 | o | $@ | A.java:20:9:20:9 | o | o | | A.java:45:24:45:37 | new Integer(...) [ : Number] | A.java:45:24:45:37 | new Integer(...) [ : Number] | A.java:32:8:32:9 | o3 | $@ | A.java:32:8:32:9 | o3 | o3 | -| A.java:52:18:52:31 | new Integer(...) [ : Number] | A.java:52:18:52:31 | new Integer(...) [ : Number] | A.java:8:9:8:9 | o | $@ | A.java:8:9:8:9 | o | o | -| A.java:53:19:53:32 | new Integer(...) [ : Number] | A.java:53:19:53:32 | new Integer(...) [ : Number] | A.java:14:9:14:9 | o | $@ | A.java:14:9:14:9 | o | o | -| A.java:54:20:54:33 | new Integer(...) [ : Number] | A.java:54:20:54:33 | new Integer(...) [ : Number] | A.java:20:9:20:9 | o | $@ | A.java:20:9:20:9 | o | o | -| A.java:55:24:55:37 | new Integer(...) [ : Number] | A.java:55:24:55:37 | new Integer(...) [ : Number] | A.java:32:8:32:9 | o3 | $@ | A.java:32:8:32:9 | o3 | o3 | | A.java:57:18:57:31 | new Integer(...) [ : Number] | A.java:57:18:57:31 | new Integer(...) [ : Number] | A.java:8:9:8:9 | o | $@ | A.java:8:9:8:9 | o | o | | A.java:58:19:58:32 | new Integer(...) [ : Number] | A.java:58:19:58:32 | new Integer(...) [ : Number] | A.java:14:9:14:9 | o | $@ | A.java:14:9:14:9 | o | o | | A.java:59:20:59:33 | new Integer(...) [ : Number] | A.java:59:20:59:33 | new Integer(...) [ : Number] | A.java:20:9:20:9 | o | $@ | A.java:20:9:20:9 | o | o | | A.java:60:24:60:37 | new Integer(...) [ : Number] | A.java:60:24:60:37 | new Integer(...) [ : Number] | A.java:32:8:32:9 | o3 | $@ | A.java:32:8:32:9 | o3 | o3 | -| A.java:67:20:67:33 | new Integer(...) [ : Number] | A.java:67:20:67:33 | new Integer(...) [ : Number] | A.java:80:10:80:10 | o | $@ | A.java:80:10:80:10 | o | o | -| A.java:68:21:68:34 | new Integer(...) [ : Number] | A.java:68:21:68:34 | new Integer(...) [ : Number] | A.java:87:10:87:10 | o | $@ | A.java:87:10:87:10 | o | o | -| A.java:69:26:69:39 | new Integer(...) [ : Number] | A.java:69:26:69:39 | new Integer(...) [ : Number] | A.java:100:9:100:10 | o3 | $@ | A.java:100:9:100:10 | o3 | o3 | | A.java:71:20:71:33 | new Integer(...) [ : Number] | A.java:71:20:71:33 | new Integer(...) [ : Number] | A.java:80:10:80:10 | o | $@ | A.java:80:10:80:10 | o | o | | A.java:72:21:72:34 | new Integer(...) [ : Number] | A.java:72:21:72:34 | new Integer(...) [ : Number] | A.java:87:10:87:10 | o | $@ | A.java:87:10:87:10 | o | o | | A.java:73:26:73:39 | new Integer(...) [ : Number] | A.java:73:26:73:39 | new Integer(...) [ : Number] | A.java:100:9:100:10 | o3 | $@ | A.java:100:9:100:10 | o3 | o3 |