content: /*
   * Copyright (C) 2014 The Android Open Source Project
   *
   * 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 dexfuzz.rawdex;
  
  import dexfuzz.Log;
  import dexfuzz.program.MutatableCode;
  
  import java.io.IOException;
  import java.util.LinkedList;
  import java.util.List;
  
  public class CodeItem implements RawDexObject {
    public short registersSize;
    public short insSize;
    public short outsSize;
    public short triesSize;
    public int debugInfoOff; // NB: this is a special case
    public int insnsSize;
    public List<Instruction> insns;
    public TryItem[] tries;
    public EncodedCatchHandlerList handlers;
  
    private MutatableCode mutatableCode;
  
    public static class MethodMetaInfo {
      public String methodName;
      public boolean isStatic;
      public String shorty;
    }
  
    public MethodMetaInfo meta = new MethodMetaInfo();
  
    @Override
    public void read(DexRandomAccessFile file) throws IOException {
      file.alignForwards(4);
      file.getOffsetTracker().getNewOffsettable(file, this);
      registersSize = file.readUShort();
      insSize = file.readUShort();
      outsSize = file.readUShort();
      triesSize = file.readUShort();
      debugInfoOff = file.readUInt();
      insnsSize = file.readUInt();
      populateInstructionList(file);
      if (triesSize > 0) {
        if ((insnsSize % 2) != 0) {
          // Consume padding.
          file.readUShort();
        }
        tries = new TryItem[triesSize];
        for (int i = 0; i < triesSize; i++) {
          (tries[i] = new TryItem()).read(file);
        }
        (handlers = new EncodedCatchHandlerList()).read(file);
      }
    }
  
    private void populateInstructionList(DexRandomAccessFile file) throws IOException {
      insns = new LinkedList<Instruction>();
      long insnsOffset = file.getFilePointer();
      if (insnsOffset != 0) {
        long finger = insnsOffset;
        long insnsEnd = insnsOffset + (2 * insnsSize);
  
        while (finger < insnsEnd) {
          file.seek(finger);
          Instruction newInsn = new Instruction();
          newInsn.read(file);
          insns.add(newInsn);
          finger += (2 * newInsn.getSize());
        }
  
        file.seek(finger);
      }
    }
  
    @Override
    public void write(DexRandomAccessFile file) throws IOException {
      file.alignForwards(4);
      file.getOffsetTracker().updatePositionOfNextOffsettable(file);
      file.writeUShort(registersSize);
      file.writeUShort(insSize);
      file.writeUShort(outsSize);
      file.writeUShort(triesSize);
      // We do not support retaining debug info currently.
      file.writeUInt(0 /*debug_info_off*/);
      file.writeUInt(insnsSize);
      for (Instruction insn : insns) {
        insn.write(file);
      }
      if (triesSize > 0) {
        if ((insnsSize % 2) != 0) {
          // produce padding
          file.writeUShort((short) 0);
        }
        for (TryItem tryItem : tries) {
          tryItem.write(file);
        }
        handlers.write(file);
      }
    }
  
    /**
     * CodeTranslator should call this to notify a CodeItem about its
     * mutatable code, so it can always get the "latest" view of its
     * instructions.
     */
    public void registerMutatableCode(MutatableCode mutatableCode) {
      this.mutatableCode = mutatableCode;
    }
  
    @Override
    public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
      if (kind == IndexUpdateKind.TYPE_ID && triesSize > 0) {
        // EncodedCatchHandlerList (well, the EncodedTypeAddrPairs it owns)
        // are only interested in TYPE_IDs.
        handlers.incrementIndex(kind, insertedIdx);
      }
  
      if (kind == IndexUpdateKind.PROTO_ID) {
        // The only kind we can't encounter in an instruction.
        return;
      }
  
      List<Instruction> insnsToIncrement = insns;
  
      // If we have an associated MutatableCode, then it may have created some new insns
      // that we won't know about yet, during the mutation phase.
      //
      // Ask for the latest view of the insns.
      if (mutatableCode != null) {
        insnsToIncrement = mutatableCode.requestLatestInstructions();
      }
  
      for (Instruction insn : insnsToIncrement) {
        Opcode opcode = insn.info.opcode;
        switch (kind) {
          case STRING_ID:
            if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
              // STRING@BBBB
              if (insn.vregB >= insertedIdx) {
                insn.vregB++;
              }
            }
            break;
          case TYPE_ID:
            if (opcode == Opcode.CONST_CLASS
                || opcode == Opcode.CHECK_CAST
                || opcode == Opcode.NEW_INSTANCE
                || opcode == Opcode.FILLED_NEW_ARRAY
                || opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
              // TYPE@BBBB
              if (insn.vregB >= insertedIdx) {
                insn.vregB++;
              }
            } else if (opcode == Opcode.INSTANCE_OF || opcode == Opcode.NEW_ARRAY) {
              // TYPE@CCCC
              if (insn.vregC >= insertedIdx) {
                insn.vregC++;
              }
            }
            break;
          case FIELD_ID:
            if (Opcode.isBetween(opcode, Opcode.SGET, Opcode.SPUT_SHORT)) {
              // FIELD@BBBB
              if (insn.vregB >= insertedIdx) {
                insn.vregB++;
              }
            } else if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.IPUT_SHORT)) {
              // FIELD@CCCC
              if (insn.vregC >= insertedIdx) {
                insn.vregC++;
              }
            }
            break;
          case METHOD_ID:
            if (Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
                || Opcode.isBetween(opcode,
                    Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)) {
              // METHOD@BBBB
              if (insn.vregB >= insertedIdx) {
                insn.vregB++;
              }
            }
            break;
          default:
            Log.errorAndQuit("Unexpected IndexUpdateKind requested "
                + "in Instruction.incrementIndex()");
        }
      }
    }
  }
  