From f50e2a1d56bfaaf9a77f92c9b7bfc8be88243cfc Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Mon, 11 Jun 2018 11:19:03 +1000 Subject: [PATCH 1/2] 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 | 22 ++-- .../objectweb/asm/tree/analysis/Frame.java | 4 +- .../asm/tree/analysis/Interpreter.java | 107 ++++++++++++++++++ 3 files changed, 124 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 4237b2dc..b8589ef7 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 @@ -274,7 +274,7 @@ public class Analyzer implements Opcodes { 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); } } @@ -381,21 +381,29 @@ public class Analyzer implements Opcodes { 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 fd812c41..46c6dab9 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 @@ -264,12 +264,12 @@ public class Frame { 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 2f38f9ef..0ac5c3bc 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 @@ -30,6 +30,7 @@ package org.objectweb.asm.tree.analysis; import java.util.List; 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 @@ -67,6 +68,14 @@ public abstract class Interpreter { *

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 @@ -88,6 +97,104 @@ public abstract class Interpreter { */ 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: -- GitLab From cd93d4c9e5b68aa1308e112211ceaae296ceb715 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 14 Jun 2018 07:55:20 +1000 Subject: [PATCH 2/2] Rationalize API for new unitialized values --- .../objectweb/asm/tree/analysis/Analyzer.java | 4 +- .../objectweb/asm/tree/analysis/Frame.java | 4 +- .../asm/tree/analysis/Interpreter.java | 51 ++++--------------- 3 files changed, 13 insertions(+), 46 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 b8589ef7..d4f0564a 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 @@ -395,12 +395,12 @@ public class Analyzer implements Opcodes { interpreter.newParameterValue(isInstanceMethod, currentLocal, argumentTypes[i])); currentLocal++; if (argumentTypes[i].getSize() == 2) { - frame.setLocal(currentLocal, interpreter.newEmptyValueAfterSize2Local(currentLocal)); + frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); currentLocal++; } } while (currentLocal < method.maxLocals) { - frame.setLocal(currentLocal, interpreter.newEmptyNonParameterLocalValue(currentLocal)); + frame.setLocal(currentLocal, interpreter.newEmptyValue(currentLocal)); currentLocal++; } frame.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))); 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 46c6dab9..0da516bd 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 @@ -264,12 +264,12 @@ public class Frame { var = ((VarInsnNode) insn).var; setLocal(var, value1); if (value1.getSize() == 2) { - setLocal(var + 1, interpreter.newEmptyValueAfterSize2Local(var + 1)); + setLocal(var + 1, interpreter.newEmptyValue(var + 1)); } if (var > 0) { Value local = getLocal(var - 1); if (local != null && local.getSize() == 2) { - setLocal(var - 1, interpreter.newEmptyValueForPreviousSize2Local(var - 1)); + setLocal(var - 1, interpreter.newEmptyValue(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 0ac5c3bc..c06d748c 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 @@ -70,9 +70,7 @@ public abstract class Interpreter { * *

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 + * Type)}, {@link Interpreter#newEmptyValue(int)}, {@link * Interpreter#newExceptionValue(TryCatchBlockNode, Frame, Type)} to distinguish different types * of new value. * @@ -130,50 +128,19 @@ public abstract class Interpreter { } /** - * 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. + * Called by the analyzer when: a) initializing a non-parameter local in a frame, or b) resetting + * the subsequent slot of a new size-2 value, or c) resetting the a size-2 slot when its second + * half (the subsequent local) is assigned size-1 value. * - *

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. + *

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. + * @return a value representing an uninitialized value of size-1 * @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) { + public V newEmptyValue(int local) { return newValue(null); } @@ -186,8 +153,8 @@ public abstract class Interpreter { * @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. + * @return a value that represents the given exceptionType. The size of the returned + * value must be equal to the size of the given type. * @since ASM 1.7 */ public V newExceptionValue( -- GitLab