proguard.analysis.cpa.jvm.transfer.JvmTransferRelation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-core Show documentation
Show all versions of proguard-core Show documentation
ProGuardCORE is a free library to read, analyze, modify, and write Java class files.
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2022 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package proguard.analysis.cpa.jvm.transfer;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import proguard.analysis.datastructure.callgraph.Call;
import proguard.classfile.Clazz;
import proguard.classfile.Method;
import proguard.classfile.MethodSignature;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.instruction.BranchInstruction;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.SimpleInstruction;
import proguard.classfile.instruction.SwitchInstruction;
import proguard.classfile.instruction.VariableInstruction;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.ClassUtil;
import proguard.analysis.cpa.defaults.LatticeAbstractState;
import proguard.analysis.cpa.interfaces.AbstractState;
import proguard.analysis.cpa.interfaces.Precision;
import proguard.analysis.cpa.interfaces.ProgramLocationDependentTransferRelation;
import proguard.analysis.cpa.jvm.cfa.edges.JvmCallCfaEdge;
import proguard.analysis.cpa.jvm.cfa.edges.JvmCfaEdge;
import proguard.analysis.cpa.jvm.cfa.edges.JvmInstructionCfaEdge;
import proguard.analysis.cpa.jvm.cfa.nodes.JvmCfaNode;
import proguard.analysis.cpa.jvm.state.JvmAbstractState;
import proguard.analysis.cpa.jvm.util.ConstantLookupVisitor;
import proguard.analysis.cpa.jvm.util.InstructionClassifier;
import proguard.evaluation.ClassConstantValueFactory;
import proguard.evaluation.value.DoubleValue;
import proguard.evaluation.value.FloatValue;
import proguard.evaluation.value.IntegerValue;
import proguard.evaluation.value.LongValue;
import proguard.evaluation.value.ParticularValueFactory;
import proguard.evaluation.value.ReferenceValue;
import proguard.evaluation.value.Value;
/**
* The {@link JvmTransferRelation} computes the successors of an {@link JvmAbstractState} for a given instruction. It stores category 2 computational types as tuples of the abstract state containing
* the information about the value in the most significant bits and a default abstract state in the least significant bits of the big-endian notation.
*
* @author Dmitry Ivanov
*/
public abstract class JvmTransferRelation>
implements ProgramLocationDependentTransferRelation
{
// implementations for ProgramLocationDependentTransferRelation
@Override
public AbstractState getEdgeAbstractSuccessor(AbstractState abstractState, JvmCfaEdge edge, Precision precision)
{
if (!(abstractState instanceof JvmAbstractState))
{
throw new IllegalArgumentException(getClass().getName() + " does not support " + abstractState.getClass().getName());
}
JvmAbstractState state = (JvmAbstractState) abstractState;
JvmAbstractState successor = state.copy();
if (edge instanceof JvmCallCfaEdge)
{
successor.setProgramLocation(edge.getSource().getLeavingEdges().stream().filter(JvmInstructionCfaEdge.class::isInstance).findFirst().get().getTarget());
processCall(successor, ((JvmCallCfaEdge) edge).getCall());
}
else
{
successor.setProgramLocation(edge.getTarget());
if (edge instanceof JvmInstructionCfaEdge)
{
Instruction instruction = ((JvmInstructionCfaEdge) edge).getInstruction();
if (InstructionClassifier.isInvoke(instruction.opcode))
{
return null;
}
successor = getAbstractSuccessorForInstruction(successor, ((JvmInstructionCfaEdge) edge).getInstruction(), state.getProgramLocation().getClazz(), precision);
}
}
return successor;
}
/**
* Returns the result of applying {@code instruction} to the {@code abstractState}.
*/
protected JvmAbstractState getAbstractSuccessorForInstruction(JvmAbstractState abstractState, Instruction instruction, Clazz clazz, Precision precision)
{
instruction.accept(clazz, null, null, 0, new InstructionAbstractInterpreter(abstractState));
return abstractState;
}
/**
* Returns the result of the instruction application. The default implementation computes join over its arguments.
*/
protected List applyInstruction(Instruction instruction, List operands, int resultCount)
{
List answer = new ArrayList<>(resultCount);
StateT answerContent = operands.stream().reduce(getAbstractDefault(), StateT::join);
for (int i = 0; i < resultCount; i++)
{
answer.add(answerContent);
}
return answer;
}
/**
* Returns an abstract representation of a byte constant {@code b}.
*/
public StateT getAbstractByteConstant(byte b)
{
return getAbstractDefault();
}
/**
* Returns a default abstract state. In case of lattice abstract domains, it should be the bottom element.
*/
public abstract StateT getAbstractDefault();
/**
* Returns an abstract representation of a double constant {@code d}.
*/
public List getAbstractDoubleConstant(double d)
{
return Arrays.asList(getAbstractDefault(), getAbstractDefault());
}
/**
* Returns an abstract representation of a float constant {@code f}.
*/
public StateT getAbstractFloatConstant(float f)
{
return getAbstractDefault();
}
/**
* Returns an abstract representation of an integer constant {@code i}.
*/
public StateT getAbstractIntegerConstant(int i)
{
return getAbstractDefault();
}
/**
* Returns an abstract representation of a long constant {@code l}.
*/
public List getAbstractLongConstant(long l)
{
return Arrays.asList(getAbstractDefault(), getAbstractDefault());
}
/**
* Returns an abstract representation of a null reference.
*/
public StateT getAbstractNull()
{
return getAbstractDefault();
}
/**
* Returns an abstract representation of a short constant {@code s}.
*/
public StateT getAbstractShortConstant(short s)
{
return getAbstractDefault();
}
/**
* Pops the arguments from the operand stack and passes them to {@code invokeMethod}.
*/
protected void processCall(JvmAbstractState state, Call call)
{
boolean isStatic = call.invocationOpcode == Instruction.OP_INVOKESTATIC || call.invocationOpcode == Instruction.OP_INVOKEDYNAMIC;
List operands = new ArrayList<>();
if (call.getTarget().descriptor.argumentTypes != null)
{
List argumentTypes = call.getTarget().descriptor.argumentTypes;
for (int i = argumentTypes.size() - 1; i >= 0; i--)
{
boolean isCategory2 = ClassUtil.isInternalCategory2Type(argumentTypes.get(i));
if (isCategory2)
{
StateT higherByte = state.pop();
operands.add(state.pop());
operands.add(higherByte);
}
else
{
operands.add(state.pop());
}
}
}
if (!isStatic)
{
operands.add(state.pop());
}
Collections.reverse(operands);
invokeMethod(state, call, operands);
}
/**
* The default implementation computes join over its arguments.
*/
public void invokeMethod(JvmAbstractState state, Call call, List operands)
{
int pushCount = ClassUtil.internalTypeSize(call.getTarget().descriptor.returnType == null ? "?" : call.getTarget().descriptor.returnType);
StateT answerContent = operands.stream().reduce(getAbstractDefault(), StateT::join);
for (int i = 0; i < pushCount; i++)
{
state.push(answerContent);
}
}
/**
* Returns an abstract state representing the result of the {@code instanceof} operation.
*/
protected StateT isInstanceOf(StateT state, String type)
{
return getAbstractDefault();
}
/**
* This {@link InstructionVisitor} performs generic operations (e.g., loads, stores) parametrized by the specific behavior of {@link JvmTransferRelation} for instruction applications, method
* invocations, and constructing literals.
*/
private class InstructionAbstractInterpreter
implements InstructionVisitor
{
private final JvmAbstractState abstractState;
private final ClassConstantValueFactory classConstantValueFactory = new ClassConstantValueFactory(new ParticularValueFactory());
private final ConstantLookupVisitor constantLookupVisitor = new ConstantLookupVisitor();
public InstructionAbstractInterpreter(JvmAbstractState abstractState)
{
this.abstractState = abstractState;
}
// implementations for InstructionVisitor
@Override
public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
{
switch (simpleInstruction.opcode)
{
case Instruction.OP_ACONST_NULL:
abstractState.push(getAbstractNull());
break;
case Instruction.OP_ICONST_M1:
case Instruction.OP_ICONST_0:
case Instruction.OP_ICONST_1:
case Instruction.OP_ICONST_2:
case Instruction.OP_ICONST_3:
case Instruction.OP_ICONST_4:
case Instruction.OP_ICONST_5:
abstractState.push(getAbstractIntegerConstant(simpleInstruction.constant));
break;
case Instruction.OP_LCONST_0:
case Instruction.OP_LCONST_1:
abstractState.pushAll(getAbstractLongConstant(simpleInstruction.constant));
break;
case Instruction.OP_FCONST_0:
case Instruction.OP_FCONST_1:
case Instruction.OP_FCONST_2:
abstractState.push(getAbstractFloatConstant(simpleInstruction.constant));
break;
case Instruction.OP_DCONST_0:
case Instruction.OP_DCONST_1:
abstractState.pushAll(getAbstractDoubleConstant(simpleInstruction.constant));
break;
case Instruction.OP_IALOAD:
case Instruction.OP_FALOAD:
case Instruction.OP_AALOAD:
case Instruction.OP_BALOAD:
case Instruction.OP_CALOAD:
case Instruction.OP_SALOAD:
{
StateT index = abstractState.pop();
abstractState.push(abstractState.getArrayElementOrDefault(abstractState.pop(), index, getAbstractDefault()));
break;
}
case Instruction.OP_LALOAD:
case Instruction.OP_DALOAD:
{
StateT index = abstractState.pop();
StateT array = abstractState.pop();
abstractState.push(getAbstractDefault());
abstractState.push(abstractState.getArrayElementOrDefault(array, index, getAbstractDefault()));
break;
}
case Instruction.OP_IASTORE:
case Instruction.OP_FASTORE:
case Instruction.OP_AASTORE:
case Instruction.OP_BASTORE:
case Instruction.OP_CASTORE:
case Instruction.OP_SASTORE:
{
StateT value = abstractState.pop();
StateT index = abstractState.pop();
abstractState.setArrayElement(abstractState.pop(), index, value);
break;
}
case Instruction.OP_LASTORE:
case Instruction.OP_DASTORE:
{
StateT value = abstractState.pop();
abstractState.pop();
StateT index = abstractState.pop();
abstractState.setArrayElement(abstractState.pop(), index, value);
break;
}
case Instruction.OP_BIPUSH:
abstractState.push(getAbstractByteConstant((byte) simpleInstruction.constant));
break;
case Instruction.OP_SIPUSH:
abstractState.push(getAbstractShortConstant((short) simpleInstruction.constant));
break;
case Instruction.OP_POP:
case Instruction.OP_MONITORENTER: // TODO synchronization is not yet modeled
case Instruction.OP_MONITOREXIT:
abstractState.pop();
break;
case Instruction.OP_POP2:
abstractState.pop();
abstractState.pop();
break;
case Instruction.OP_DUP:
abstractState.push(abstractState.peek());
break;
case Instruction.OP_DUP_X1:
{
StateT state1 = abstractState.pop();
StateT state2 = abstractState.pop();
abstractState.push(state1);
abstractState.push(state2);
abstractState.push(state1);
break;
}
case Instruction.OP_DUP_X2:
{
StateT state1 = abstractState.pop();
StateT state2 = abstractState.pop();
StateT state3 = abstractState.pop();
abstractState.push(state1);
abstractState.push(state3);
abstractState.push(state2);
abstractState.push(state1);
break;
}
case Instruction.OP_DUP2:
{
StateT state1 = abstractState.peek();
StateT state2 = abstractState.peek(1);
abstractState.push(state2);
abstractState.push(state1);
break;
}
case Instruction.OP_DUP2_X1:
{
StateT state1 = abstractState.pop();
StateT state2 = abstractState.pop();
StateT state3 = abstractState.pop();
abstractState.push(state2);
abstractState.push(state1);
abstractState.push(state3);
abstractState.push(state2);
abstractState.push(state1);
break;
}
case Instruction.OP_DUP2_X2:
{
StateT state1 = abstractState.pop();
StateT state2 = abstractState.pop();
StateT state3 = abstractState.pop();
StateT state4 = abstractState.pop();
abstractState.push(state2);
abstractState.push(state1);
abstractState.push(state4);
abstractState.push(state3);
abstractState.push(state2);
abstractState.push(state1);
break;
}
case Instruction.OP_SWAP:
{
StateT state1 = abstractState.pop();
StateT state2 = abstractState.pop();
abstractState.push(state1);
abstractState.push(state2);
break;
}
// in case of return we don't touch the value on the stack to be able to provide it to the caller
case Instruction.OP_RETURN:
case Instruction.OP_ARETURN:
case Instruction.OP_DRETURN:
case Instruction.OP_FRETURN:
case Instruction.OP_LRETURN:
case Instruction.OP_IRETURN:
break;
case Instruction.OP_ATHROW:
StateT exceptionState = abstractState.pop();
abstractState.clearOperandStack();
abstractState.push(exceptionState);
break;
default: // arithmetic instructions
{
List operands = new ArrayList<>(simpleInstruction.stackPopCount(clazz));
// long shift instruction have to be considered separately because they have a category2 and a category1 parameter
if (InstructionClassifier.isLongShift(simpleInstruction.opcode))
{
operands.add(abstractState.pop());
StateT higherByte = abstractState.pop();
operands.add(abstractState.pop());
operands.add(higherByte);
}
else
{
for (int i = 0; i < simpleInstruction.stackPopCount(clazz) / (simpleInstruction.isCategory2() ? 2 : 1); i++)
{
if (simpleInstruction.isCategory2())
{
StateT higherByte = abstractState.pop();
operands.add(abstractState.pop());
operands.add(higherByte);
}
else
{
operands.add(abstractState.pop());
}
}
}
Collections.reverse(operands);
abstractState.pushAll(applyInstruction(simpleInstruction, operands, simpleInstruction.stackPushCount(clazz)));
}
}
}
@Override
public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
{
if (variableInstruction.opcode == Instruction.OP_IINC)
{
List operands = new ArrayList<>(2);
operands.add(abstractState.getVariableOrDefault(variableInstruction.variableIndex, getAbstractDefault()));
operands.add(getAbstractIntegerConstant(variableInstruction.constant));
List res = applyInstruction(variableInstruction, operands, 1);
if (res.size() != 1)
{
throw new IllegalStateException("applyInstruction should return a list of size 1");
}
abstractState.setVariable(variableInstruction.variableIndex, res.get(0), getAbstractDefault());
}
else if (variableInstruction.isLoad())
{
if (variableInstruction.isCategory2())
{
abstractState.push(abstractState.getVariableOrDefault(variableInstruction.variableIndex + 1, getAbstractDefault()));
}
abstractState.push(abstractState.getVariableOrDefault(variableInstruction.variableIndex, getAbstractDefault()));
}
else if (variableInstruction.isStore())
{
abstractState.setVariable(variableInstruction.variableIndex, abstractState.pop(), getAbstractDefault());
if (variableInstruction.isCategory2())
{
abstractState.setVariable(variableInstruction.variableIndex + 1, abstractState.pop(), getAbstractDefault());
}
}
}
@Override
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
constantLookupVisitor.resetResult();
switch (constantInstruction.opcode)
{
case Instruction.OP_LDC:
case Instruction.OP_LDC_W:
case Instruction.OP_LDC2_W:
abstractState.pushAll(getAbstactValue(classConstantValueFactory.constantValue(clazz, constantInstruction.constantIndex)));
break;
case Instruction.OP_GETSTATIC:
constantLookupVisitor.isStatic = true;
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
if (constantLookupVisitor.resultSize > 1)
{
abstractState.push(getAbstractDefault());
}
abstractState.push(abstractState.getStaticOrDefault(constantLookupVisitor.result, getAbstractDefault()));
break;
case Instruction.OP_PUTSTATIC:
{
constantLookupVisitor.isStatic = true;
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
StateT value = abstractState.pop();
if (constantLookupVisitor.resultSize > 1)
{
abstractState.pop();
}
abstractState.setStatic(constantLookupVisitor.result, value);
break;
}
case Instruction.OP_GETFIELD:
{
constantLookupVisitor.isStatic = false;
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
StateT result = abstractState.getFieldOrDefault(abstractState.pop(), constantLookupVisitor.result, getAbstractDefault());
if (constantLookupVisitor.resultSize > 1)
{
abstractState.push(getAbstractDefault());
}
abstractState.push(result);
break;
}
case Instruction.OP_PUTFIELD:
{
constantLookupVisitor.isStatic = false;
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
StateT value = abstractState.pop();
if (constantLookupVisitor.resultSize > 1)
{
abstractState.pop();
}
abstractState.setField(abstractState.pop(), constantLookupVisitor.result, value);
break;
}
case Instruction.OP_INVOKESTATIC:
case Instruction.OP_INVOKEDYNAMIC:
case Instruction.OP_INVOKEVIRTUAL:
case Instruction.OP_INVOKESPECIAL:
case Instruction.OP_INVOKEINTERFACE:
throw new IllegalStateException(getClass().getName() + " encountered an unexpected call instruction");
case Instruction.OP_INSTANCEOF:
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
abstractState.push(isInstanceOf(abstractState.pop(), constantLookupVisitor.result));
break;
case Instruction.OP_NEW:
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
abstractState.push(abstractState.newObject(constantLookupVisitor.result));
break;
case Instruction.OP_NEWARRAY:
abstractState.push(abstractState.newArray(String.valueOf(proguard.classfile.instruction.InstructionUtil.internalTypeFromArrayType((byte) constantInstruction.constant)),
Collections.singletonList(abstractState.pop())));
break;
case Instruction.OP_ANEWARRAY:
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
abstractState.push(abstractState.newArray(constantLookupVisitor.result,
Collections.singletonList(abstractState.pop())));
break;
case Instruction.OP_MULTIANEWARRAY:
{
List dimensions = new ArrayList<>(constantInstruction.constant);
for (int i = 0; i < constantInstruction.stackPopCount(clazz); i++)
{
dimensions.add(abstractState.pop());
}
Collections.reverse(dimensions);
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, constantLookupVisitor);
abstractState.push(abstractState.newArray(constantLookupVisitor.result,
dimensions));
break;
}
case Instruction.OP_CHECKCAST:
break;
default: // should never happen
throw new InvalidParameterException("The opcode " + constantInstruction.opcode + " is not supported by the constant instruction visitor");
}
}
@Override
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
{
for (int i = 0; i < branchInstruction.stackPopCount(clazz); i++)
{
abstractState.pop();
}
for (int i = 0; i < branchInstruction.stackPushCount(clazz); i++)
{
abstractState.push(getAbstractDefault());
}
}
@Override
public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
{
for (int i = 0; i < switchInstruction.stackPopCount(clazz); i++)
{
abstractState.pop();
}
}
private List getAbstactValue(Value value)
{
if (value instanceof IntegerValue)
{
return Collections.singletonList(getAbstractIntegerConstant(((IntegerValue) value).value()));
}
else if (value instanceof LongValue)
{
return getAbstractLongConstant(((LongValue) value).value());
}
else if (value instanceof FloatValue)
{
return Collections.singletonList(getAbstractFloatConstant(((FloatValue) value).value()));
}
else if (value instanceof DoubleValue)
{
return getAbstractDoubleConstant(((DoubleValue) value).value());
}
else if (value instanceof ReferenceValue)
{
return Collections.singletonList(getAbstractDefault());
}
return Collections.singletonList(getAbstractDefault());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy