FileDocCategorySizeDatePackage
Simulator.javaAPI DocAndroid 5.1 API28980Thu Mar 12 22:18:30 GMT 2015com.android.dx.cf.code

Simulator

public class Simulator extends Object
Class which knows how to simulate the effects of executing bytecode.

Note: This class is not thread-safe. If multiple threads need to use a single instance, they must synchronize access explicitly between themselves.

Fields Summary
private static final String
LOCAL_MISMATCH_ERROR
{@code non-null;} canned error message for local variable table mismatches
private final Machine
machine
{@code non-null;} machine to use when simulating
private final BytecodeArray
code
{@code non-null;} array of bytecode
private final LocalVariableList
localVariables
{@code non-null;} local variable information
private final SimVisitor
visitor
{@code non-null;} visitor instance to use
Constructors Summary
public Simulator(Machine machine, ConcreteMethod method)
Constructs an instance.

param
machine {@code non-null;} machine to use when simulating
param
method {@code non-null;} method data to use


                             
         
        if (machine == null) {
            throw new NullPointerException("machine == null");
        }

        if (method == null) {
            throw new NullPointerException("method == null");
        }

        this.machine = machine;
        this.code = method.getCode();
        this.localVariables = method.getLocalVariables();
        this.visitor = new SimVisitor();
    
Methods Summary
private static SimExceptionillegalTos()
Constructs an "illegal top-of-stack" exception, for the stack manipulation opcodes.

        return new SimException("stack mismatch: illegal " +
                "top-of-stack for opcode");
    
private static com.android.dx.rop.type.TyperequiredArrayTypeFor(com.android.dx.rop.type.Type impliedType, com.android.dx.rop.type.Type foundArrayType)
Returns the required array type for an array load or store instruction, based on a given implied type and an observed actual array type.

The interesting cases here have to do with object arrays, byte[]s, boolean[]s, and known-nulls.

In the case of arrays of objects, we want to narrow the type to the actual array present on the stack, as long as what is present is an object type. Similarly, due to a quirk of the original bytecode representation, the instructions for dealing with byte[] and boolean[] are undifferentiated, and we aim here to return whichever one was actually present on the stack.

In the case where there is a known-null on the stack where an array is expected, we just fall back to the implied type of the instruction. Due to the quirk described above, this means that source code that uses boolean[] might get translated surprisingly -- but correctly -- into an instruction that specifies a byte[]. It will be correct, because should the code actually execute, it will necessarily throw a NullPointerException, and it won't matter what opcode variant is used to achieve that result.

param
impliedType {@code non-null;} type implied by the instruction; is not an array type
param
foundArrayType {@code non-null;} type found on the stack; is either an array type or a known-null
return
{@code non-null;} the array type that should be required in this context

        if (foundArrayType == Type.KNOWN_NULL) {
            return impliedType.getArrayType();
        }

        if ((impliedType == Type.OBJECT)
                && foundArrayType.isArray()
                && foundArrayType.getComponentType().isReference()) {
            return foundArrayType;
        }

        if ((impliedType == Type.BYTE)
                && (foundArrayType == Type.BOOLEAN_ARRAY)) {
            /*
             * Per above, an instruction with implied byte[] is also
             * allowed to be used on boolean[].
             */
            return Type.BOOLEAN_ARRAY;
        }

        return impliedType.getArrayType();
    
public voidsimulate(ByteBlock bb, Frame frame)
Simulates the effect of executing the given basic block. This modifies the passed-in frame to represent the end result.

param
bb {@code non-null;} the basic block
param
frame {@code non-null;} frame to operate on

        int end = bb.getEnd();

        visitor.setFrame(frame);

        try {
            for (int off = bb.getStart(); off < end; /*off*/) {
                int length = code.parseInstruction(off, visitor);
                visitor.setPreviousOffset(off);
                off += length;
            }
        } catch (SimException ex) {
            frame.annotate(ex);
            throw ex;
        }
    
public intsimulate(int offset, Frame frame)
Simulates the effect of the instruction at the given offset, by making appropriate calls on the given frame.

param
offset {@code >= 0;} offset of the instruction to simulate
param
frame {@code non-null;} frame to operate on
return
the length of the instruction, in bytes

        visitor.setFrame(frame);
        return code.parseInstruction(offset, visitor);