From 644af4f0db5f139c8fff39152cd42e3dfedc3bd3 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 11 Jun 2018 11:19:03 +1000 Subject: [PATCH] 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. --- .../objectweb/asm/tree/analysis/Analyzer.java | 20 ++-- .../objectweb/asm/tree/analysis/Frame.java | 4 +- .../asm/tree/analysis/Interpreter.java | 111 ++++++++++++++++++ 3 files changed, 126 insertions(+), 9 deletions(-) diff --git a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java index 73113e9c..55e4c6f6 100644 --- a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java +++ b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Analyzer.java @@ -275,7 +275,7 @@ public Frame[] analyze(final String owner, final MethodNode method) throws An if (newControlFlowExceptionEdge(insnIndex, tryCatchBlock)) { Frame handler = newFrame(oldFrame); handler.clearStack(); - handler.push(interpreter.newValue(catchType)); + handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType)); merge(insnList.indexOf(tryCatchBlock.handler), handler, subroutine); } } @@ -379,24 +379,30 @@ private void findSubroutine( * @param method the method to be analyzed. * @return the initial execution stack frame of the 'method'. */ + private Frame computeInitialFrame(final String owner, final MethodNode method) { Frame frame = newFrame(method.maxLocals, method.maxStack); int currentLocal = 0; - if ((method.access & ACC_STATIC) == 0) { + boolean isInstanceMethod = (method.access & ACC_STATIC) == 0; + if (isInstanceMethod) { Type ownerType = Type.getObjectType(owner); - frame.setLocal(currentLocal++, interpreter.newValue(ownerType)); + frame.setLocal(currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, ownerType)); + currentLocal++; } Type[] argumentTypes = Type.getArgumentTypes(method.desc); for (int i = 0; i < argumentTypes.length; ++i) { - frame.setLocal(currentLocal++, interpreter.newValue(argumentTypes[i])); + frame.setLocal(currentLocal, interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentTypes[i])); + currentLocal++; if (argumentTypes[i].getSize() == 2) { - frame.setLocal(currentLocal++, interpreter.newValue(null)); + frame.setLocal(currentLocal, interpreter.newEmptyValueAfterSize2Local(currentLocal)); + currentLocal++; } } while (currentLocal < method.maxLocals) { - frame.setLocal(currentLocal++, interpreter.newValue(null)); + frame.setLocal(currentLocal, interpreter.newEmptyNonParameterLocalValue(currentLocal)); + currentLocal++; } - frame.setReturn(interpreter.newValue(Type.getReturnType(method.desc))); + frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))); return frame; } diff --git a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java index a3dd6c86..37e77609 100644 --- a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java +++ b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Frame.java @@ -265,12 +265,12 @@ public void execute(final AbstractInsnNode insn, final Interpreter interprete var = ((VarInsnNode) insn).var; setLocal(var, value1); if (value1.getSize() == 2) { - setLocal(var + 1, interpreter.newValue(null)); + setLocal(var + 1, interpreter.newEmptyValueAfterSize2Local(var + 1)); } if (var > 0) { Value local = getLocal(var - 1); if (local != null && local.getSize() == 2) { - setLocal(var - 1, interpreter.newValue(null)); + setLocal(var - 1, interpreter.newEmptyValueForPreviousSize2Local(var - 1)); } } break; diff --git a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Interpreter.java b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Interpreter.java index c0a7cbdd..bfd87beb 100644 --- a/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Interpreter.java +++ b/asm-analysis/src/main/java/org/objectweb/asm/tree/analysis/Interpreter.java @@ -31,6 +31,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.TryCatchBlockNode; /** * A semantic bytecode interpreter. More precisely, this interpreter only manages the computation of @@ -68,6 +69,14 @@ protected Interpreter(final int api) { *

Called for method parameters (including this), exception handler variable and * with null type for variables reserved by long and double types. * + * An interpreter may choose to implement one or more of {@link Interpreter#newReturnTypeValue(Type)}, + * {@link Interpreter#newParameterValue(boolean, int, Type)}, + * {@link Interpreter#newEmptyNonParameterLocalValue(int)}, + * {@link Interpreter#newEmptyValueAfterSize2Local(int)}, + * {@link Interpreter#newEmptyValueForPreviousSize2Local(int)}, + * {@link Interpreter#newExceptionValue(TryCatchBlockNode, Frame, Type)} to distinguish + * different types of new value. + * * @param type a primitive or reference type, or null to represent an uninitialized * value. * @return a value that represents the given type. The size of the returned value must be equal to @@ -89,6 +98,108 @@ protected Interpreter(final int api) { */ public abstract V newOperation(AbstractInsnNode insn) throws AnalyzerException; + + /** + * Called by the analyzer for initializing the return type value of a frame. + * + * By default, calls newValue(type). + * + * @param type a primitive or reference type, or null to represent an uninitialized + * value. + * @return a value that represents the given type. The size of the returned value must be equal to + * the size of the given type. + * + * @since ASM 1.7 + */ + public V newReturnTypeValue(Type type) { + return newValue(type); + } + + /** + * Called by the analyzer when initializing the value of a parameter in a frame. + * + * By default, calls newValue(type). + * + * @param isInstanceMethod true if the owner of the parameter is is non-static method, a + * primitive or reference type, or false otherwise. value. + * @param type a primitive or reference type, or null to represent an uninitialized value. + * @return a value that represents the given type. The size of the returned value must be equal + * to the size of the given type. + * @since ASM 1.7 + */ + public V newParameterValue(boolean isInstanceMethod, int local, Type type) { + return newValue(type); + } + + /** + * Called by the analyzer when initializing a non-parameter local in a frame. + * This method has to return a size-1 value representing an empty slot. + * + * By default, calls newValue(null). + * + * @param local The index of the local in the frame. + * @return a value that represents the given type. The size of the returned value must be equal + * to the size of the given type. + * + * @since ASM 1.7 + */ + public V newEmptyNonParameterLocalValue(int local) { + return newValue(null); + } + + /** + * Called by the analyzer and the interpreter. When initializing or setting the value of a + * size-2 local, the value of the subsequent slot is reset using this method. + * This method has to return a size-1 value representing an empty slot. + * + * By default, calls newValue(null). + * + * @param local The index of the local in the frame. + * @return a value that represents the given type. The size of the returned value must be equal + * to the size of the given type. + * + * @since ASM 1.7 + */ + public V newEmptyValueAfterSize2Local(int local) { + return newValue(null); + } + + /** + * Called by the interpreter. When setting the value of a local variable, the interpreter checks + * whether the current value stored at the preceding index is of size-2. In this case, the + * preceding size-2 value is no longer valid and reset using this method. + * This method has to return a size-1 value representing an empty slot. + * + * By default, calls newValue(null). + * + * @param local The index of the local in the frame. + * @return a value that represents the given type. The size of the returned value must be equal + * to the size of the given type. + * + * @since ASM 1.7 + */ + public V newEmptyValueForPreviousSize2Local(int local) { + return newValue(null); + } + + /** + * Called by the analyzer when initializing the exception value on the call stack at the entry + * of an exception handler. + * + * By default, calls newValue(exceptionType). + * + * @param tryCatchBlockNode The exception handler + * @param handlerFrame The frame of the handler catching an exception from the current instruction + * @param exceptionType The excption type handled by this handler. + * @return a value that represents the given type. The size of the returned value must be equal + * to the size of the given type. + * + * @since ASM 1.7 + */ + public V newExceptionValue(TryCatchBlockNode tryCatchBlockNode, Frame handlerFrame, Type exceptionType) { + return newValue(exceptionType); + } + /** * Interprets a bytecode instruction that moves a value on the stack or to or from local * variables. This method is called for the following opcodes: