Skip to content

Commit a2791a0

Browse files
lrytzretronym
authored andcommitted
[asm-cherry-pick] Multiple methods for initializing analysis values
Introduces a number of methods that are called when initializing or updating abstract values in an analyzer frame. Before this commit, Analyzer.analyze and Frame.execute would always call Interpreter.newValue for initializing or updating frame values. Having multiple methods allows users to return more precise values for the individual cases. For example, in a nullness analysis, the initial value for the `this` parameter of an instance method can be set to not-null.
1 parent eb6050e commit a2791a0

File tree

3 files changed

+64
-9
lines changed

3 files changed

+64
-9
lines changed

src/main/java/scala/tools/asm/tree/analysis/Analyzer.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public Frame<V>[] analyze(final String owner, final MethodNode method) throws An
275275
if (newControlFlowExceptionEdge(insnIndex, tryCatchBlock)) {
276276
Frame<V> handler = newFrame(oldFrame);
277277
handler.clearStack();
278-
handler.push(interpreter.newValue(catchType));
278+
handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType));
279279
merge(insnList.indexOf(tryCatchBlock.handler), handler, subroutine);
280280
}
281281
}
@@ -382,21 +382,26 @@ private void findSubroutine(
382382
private Frame<V> computeInitialFrame(final String owner, final MethodNode method) {
383383
Frame<V> frame = newFrame(method.maxLocals, method.maxStack);
384384
int currentLocal = 0;
385-
if ((method.access & ACC_STATIC) == 0) {
385+
boolean isInstanceMethod = (method.access & ACC_STATIC) == 0;
386+
if (isInstanceMethod) {
386387
Type ownerType = Type.getObjectType(owner);
387-
frame.setLocal(currentLocal++, interpreter.newValue(ownerType));
388+
frame.setLocal(currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, ownerType));
389+
currentLocal++;
388390
}
389391
Type[] argumentTypes = Type.getArgumentTypes(method.desc);
390392
for (int i = 0; i < argumentTypes.length; ++i) {
391-
frame.setLocal(currentLocal++, interpreter.newValue(argumentTypes[i]));
393+
frame.setLocal(currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentTypes[i]));
394+
currentLocal++;
392395
if (argumentTypes[i].getSize() == 2) {
393-
frame.setLocal(currentLocal++, interpreter.newValue(null));
396+
frame.setLocal(currentLocal, interpreter.newEmptyValueAfterSize2Local(currentLocal));
397+
currentLocal++;
394398
}
395399
}
396400
while (currentLocal < method.maxLocals) {
397-
frame.setLocal(currentLocal++, interpreter.newValue(null));
401+
frame.setLocal(currentLocal, interpreter.newEmptyNonParameterLocalValue(currentLocal));
402+
currentLocal++;
398403
}
399-
frame.setReturn(interpreter.newValue(Type.getReturnType(method.desc)));
404+
frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc)));
400405
return frame;
401406
}
402407

src/main/java/scala/tools/asm/tree/analysis/Frame.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,12 @@ public void execute(final AbstractInsnNode insn, final Interpreter<V> interprete
279279
var = ((VarInsnNode) insn).var;
280280
setLocal(var, value1);
281281
if (value1.getSize() == 2) {
282-
setLocal(var + 1, interpreter.newValue(null));
282+
setLocal(var + 1, interpreter.newEmptyValueAfterSize2Local(var + 1));
283283
}
284284
if (var > 0) {
285285
Value local = getLocal(var - 1);
286286
if (local != null && local.getSize() == 2) {
287-
setLocal(var - 1, interpreter.newValue(null));
287+
setLocal(var - 1, interpreter.newEmptyValueForPreviousSize2Local(var - 1));
288288
}
289289
}
290290
break;

src/main/java/scala/tools/asm/tree/analysis/Interpreter.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import scala.tools.asm.Type;
3333
import scala.tools.asm.tree.AbstractInsnNode;
34+
import scala.tools.asm.tree.TryCatchBlockNode;
3435

3536
/**
3637
* A semantic bytecode interpreter. More precisely, this interpreter only manages the computation of
@@ -89,6 +90,55 @@ protected Interpreter(final int api) {
8990
*/
9091
public abstract V newOperation(AbstractInsnNode insn) throws AnalyzerException;
9192

93+
/**
94+
* Called by the analyzer for initializing the return type value of a frame.
95+
*/
96+
public V newReturnTypeValue(Type type) {
97+
return newValue(type);
98+
}
99+
100+
/**
101+
* Called by the analyzer when initializing the value of a parameter in a frame.
102+
*/
103+
public V newParameterValue(boolean isInstanceMethod, int local, Type type) {
104+
return newValue(type);
105+
}
106+
107+
/**
108+
* Called by the analyzer when initializing a non-parameter local in a frame.
109+
* This method has to return a size-1 value representing an empty slot.
110+
*/
111+
public V newEmptyNonParameterLocalValue(int local) {
112+
return newValue(null);
113+
}
114+
115+
/**
116+
* Called by the analyzer and the interpreter. When initializing or setting the value of a
117+
* size-2 local, the value of the subsequent slot is reset using this method.
118+
* This method has to return a size-1 value representing an empty slot.
119+
*/
120+
public V newEmptyValueAfterSize2Local(int local) {
121+
return newValue(null);
122+
}
123+
124+
/**
125+
* Called by the interpreter. When setting the value of a local variable, the interpreter checks
126+
* whether the current value stored at the preceding index is of size-2. In this case, the
127+
* preceding size-2 value is no longer valid and reset using this method.
128+
* This method has to return a size-1 value representing an empty slot.
129+
*/
130+
public V newEmptyValueForPreviousSize2Local(int local) {
131+
return newValue(null);
132+
}
133+
134+
/**
135+
* Called by the analyzer when initializing the exception value on the call stack at the entry
136+
* of an exception handler.
137+
*/
138+
public V newExceptionValue(TryCatchBlockNode tryCatchBlockNode, Frame handlerFrame, Type exceptionType) {
139+
return newValue(exceptionType);
140+
}
141+
92142
/**
93143
* Interprets a bytecode instruction that moves a value on the stack or to or from local
94144
* variables. This method is called for the following opcodes:

0 commit comments

Comments
 (0)