コード例 #1
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 private void addBreakLabel(Edge exitEdge, BlockNode exit, InsnNode breakInsn) {
   BlockNode outBlock = BlockUtils.getNextBlock(exitEdge.getTarget());
   if (outBlock == null) {
     return;
   }
   List<LoopInfo> exitLoop = mth.getAllLoopsForBlock(outBlock);
   if (!exitLoop.isEmpty()) {
     return;
   }
   List<LoopInfo> inLoops = mth.getAllLoopsForBlock(exitEdge.getSource());
   if (inLoops.size() < 2) {
     return;
   }
   // search for parent loop
   LoopInfo parentLoop = null;
   for (LoopInfo loop : inLoops) {
     if (loop.getParentLoop() == null) {
       parentLoop = loop;
       break;
     }
   }
   if (parentLoop == null) {
     return;
   }
   if (parentLoop.getEnd() != exit && !parentLoop.getExitNodes().contains(exit)) {
     LoopLabelAttr labelAttr = new LoopLabelAttr(parentLoop);
     breakInsn.addAttr(labelAttr);
     parentLoop.getStart().addAttr(labelAttr);
   }
 }
コード例 #2
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
  private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) {
    BlockNode start = handler.getHandlerBlock();
    if (start == null) {
      return;
    }
    RegionStack stack = new RegionStack(mth);
    BlockNode dom;
    if (handler.isFinally()) {
      SplitterBlockAttr splitterAttr = start.get(AType.SPLITTER_BLOCK);
      if (splitterAttr == null) {
        return;
      }
      dom = splitterAttr.getBlock();
    } else {
      dom = start;
      stack.addExits(exits);
    }
    BitSet domFrontier = dom.getDomFrontier();
    List<BlockNode> handlerExits = BlockUtils.bitSetToBlocks(mth, domFrontier);
    boolean inLoop = mth.getLoopForBlock(start) != null;
    for (BlockNode exit : handlerExits) {
      if ((!inLoop || BlockUtils.isPathExists(start, exit))
          && RegionUtils.isRegionContainsBlock(mth.getRegion(), exit)) {
        stack.addExit(exit);
      }
    }
    handler.setHandlerRegion(makeRegion(start, stack));

    ExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER);
    if (excHandlerAttr == null) {
      LOG.warn("Missing exception handler attribute for start block");
    } else {
      handler.getHandlerRegion().addAttr(excHandlerAttr);
    }
  }
コード例 #3
0
ファイル: IntegrationTest.java プロジェクト: arteme/jadx
 protected MethodNode getMethod(ClassNode cls, String method) {
   for (MethodNode mth : cls.getMethods()) {
     if (mth.getName().equals(method)) {
       return mth;
     }
   }
   fail("Method not found " + method + " in class " + cls);
   return null;
 }
コード例 #4
0
  public DebugInfoParser(MethodNode mth, int debugOffset, InsnNode[] insnByOffset) {
    this.mth = mth;
    this.dex = mth.dex();
    this.section = dex.openSection(debugOffset);

    int regsCount = mth.getRegsCount();
    this.locals = new LocalVar[regsCount];
    this.activeRegisters = new InsnArg[regsCount];
    this.insnByOffset = insnByOffset;
  }
コード例 #5
0
ファイル: IntegrationTest.java プロジェクト: arteme/jadx
 private static void checkCode(ClassNode cls) {
   assertTrue(
       "Inconsistent cls: " + cls,
       !cls.contains(AFlag.INCONSISTENT_CODE) && !cls.contains(AType.JADX_ERROR));
   for (MethodNode mthNode : cls.getMethods()) {
     assertTrue(
         "Inconsistent method: " + mthNode,
         !mthNode.contains(AFlag.INCONSISTENT_CODE) && !mthNode.contains(AType.JADX_ERROR));
   }
   assertThat(cls.getCode().toString(), not(containsString("inconsistent")));
 }
コード例 #6
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 private boolean inExceptionHandlerBlocks(BlockNode loopEnd) {
   if (mth.getExceptionHandlersCount() == 0) {
     return false;
   }
   for (ExceptionHandler eh : mth.getExceptionHandlers()) {
     if (eh.getBlocks().contains(loopEnd)) {
       return true;
     }
   }
   return false;
 }
コード例 #7
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 private static void renameVariables(MethodNode mth) {
   int regsCount = mth.getRegsCount();
   SSAVar[] vars = new SSAVar[regsCount];
   int[] versions = new int[regsCount];
   // init method arguments
   for (RegisterArg arg : mth.getArguments(true)) {
     int regNum = arg.getRegNum();
     vars[regNum] = mth.makeNewSVar(regNum, versions, arg);
   }
   renameVar(mth, vars, versions, mth.getEnterBlock());
 }
コード例 #8
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 /** Select loop exit and construct LoopRegion */
 private LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) {
   for (BlockNode block : exitBlocks) {
     if (block.contains(AType.EXC_HANDLER)
         || block.getInstructions().size() != 1
         || block.getInstructions().get(0).getType() != InsnType.IF) {
       continue;
     }
     List<LoopInfo> loops = block.getAll(AType.LOOP);
     if (!loops.isEmpty() && loops.get(0) != loop) {
       // skip nested loop condition
       continue;
     }
     LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, block == loop.getEnd());
     boolean found;
     if (block == loop.getStart()
         || block == loop.getEnd()
         || BlockUtils.isEmptySimplePath(loop.getStart(), block)) {
       found = true;
     } else if (block.getPredecessors().contains(loop.getStart())) {
       loopRegion.setPreCondition(loop.getStart());
       // if we can't merge pre-condition this is not correct header
       found = loopRegion.checkPreCondition();
     } else {
       found = false;
     }
     if (found) {
       List<LoopInfo> list = mth.getAllLoopsForBlock(block);
       if (list.size() >= 2) {
         // bad condition if successors going out of all loops
         boolean allOuter = true;
         for (BlockNode outerBlock : block.getCleanSuccessors()) {
           List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock);
           outLoopList.remove(loop);
           if (!outLoopList.isEmpty()) {
             // goes to outer loop
             allOuter = false;
             break;
           }
         }
         if (allOuter) {
           found = false;
         }
       }
     }
     if (found) {
       return loopRegion;
     }
   }
   // no exit found => endless loop
   return null;
 }
コード例 #9
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
  private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
    List<BlockNode> blocks = mth.getBasicBlocks();
    int blocksCount = blocks.size();
    BitSet hasPhi = new BitSet(blocksCount);
    BitSet processed = new BitSet(blocksCount);
    Deque<BlockNode> workList = new LinkedList<BlockNode>();

    BitSet assignBlocks = la.getAssignBlocks(regNum);
    for (int id = assignBlocks.nextSetBit(0); id >= 0; id = assignBlocks.nextSetBit(id + 1)) {
      processed.set(id);
      workList.add(blocks.get(id));
    }
    while (!workList.isEmpty()) {
      BlockNode block = workList.pop();
      BitSet domFrontier = block.getDomFrontier();
      for (int id = domFrontier.nextSetBit(0); id >= 0; id = domFrontier.nextSetBit(id + 1)) {
        if (!hasPhi.get(id) && la.isLive(id, regNum)) {
          BlockNode df = blocks.get(id);
          addPhi(df, regNum);
          hasPhi.set(id);
          if (!processed.get(id)) {
            processed.set(id);
            workList.add(df);
          }
        }
      }
    }
  }
コード例 #10
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 @Override
 public void visit(MethodNode mth) throws JadxException {
   if (mth.isNoCode()) {
     return;
   }
   process(mth);
 }
コード例 #11
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 private static boolean removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) {
   if (insnToRemove.isEmpty()) {
     return false;
   }
   for (BlockNode block : mth.getBasicBlocks()) {
     PhiListAttr phiList = block.get(AType.PHI_LIST);
     if (phiList == null) {
       continue;
     }
     List<PhiInsn> list = phiList.getList();
     for (PhiInsn phiInsn : insnToRemove) {
       if (list.remove(phiInsn)) {
         for (InsnArg arg : phiInsn.getArguments()) {
           SSAVar sVar = ((RegisterArg) arg).getSVar();
           if (sVar != null) {
             sVar.setUsedInPhi(null);
           }
         }
         InstructionRemover.remove(mth, block, phiInsn);
       }
     }
     if (list.isEmpty()) {
       block.remove(AType.PHI_LIST);
     }
   }
   insnToRemove.clear();
   return true;
 }
コード例 #12
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 private static void renameVar(MethodNode mth, SSAVar[] vars, int[] vers, BlockNode block) {
   SSAVar[] inputVars = Arrays.copyOf(vars, vars.length);
   for (InsnNode insn : block.getInstructions()) {
     if (insn.getType() != InsnType.PHI) {
       for (InsnArg arg : insn.getArguments()) {
         if (!arg.isRegister()) {
           continue;
         }
         RegisterArg reg = (RegisterArg) arg;
         int regNum = reg.getRegNum();
         SSAVar var = vars[regNum];
         if (var == null) {
           throw new JadxRuntimeException(
               "Not initialized variable reg: "
                   + regNum
                   + ", insn: "
                   + insn
                   + ", block:"
                   + block
                   + ", method: "
                   + mth);
         }
         var.use(reg);
       }
     }
     RegisterArg result = insn.getResult();
     if (result != null) {
       int regNum = result.getRegNum();
       vars[regNum] = mth.makeNewSVar(regNum, vers, result);
     }
   }
   for (BlockNode s : block.getSuccessors()) {
     PhiListAttr phiList = s.get(AType.PHI_LIST);
     if (phiList == null) {
       continue;
     }
     int j = s.getPredecessors().indexOf(block);
     if (j == -1) {
       throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
     }
     for (PhiInsn phiInsn : phiList.getList()) {
       if (j >= phiInsn.getArgsCount()) {
         continue;
       }
       int regNum = phiInsn.getResult().getRegNum();
       SSAVar var = vars[regNum];
       if (var == null) {
         continue;
       }
       var.use(phiInsn.getArg(j));
       var.setUsedInPhi(phiInsn);
     }
   }
   for (BlockNode domOn : block.getDominatesOn()) {
     renameVar(mth, vars, vers, domOn);
   }
   System.arraycopy(inputVars, 0, vars, 0, vars.length);
 }
コード例 #13
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 private static void process(MethodNode mth) {
   LiveVarAnalysis la = new LiveVarAnalysis(mth);
   la.runAnalysis();
   int regsCount = mth.getRegsCount();
   for (int i = 0; i < regsCount; i++) {
     placePhi(mth, i, la);
   }
   renameVariables(mth);
   fixLastTryCatchAssign(mth);
   if (removeUselessPhi(mth)) {
     renameVariables(mth);
   }
 }
コード例 #14
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 private static boolean removeUselessPhi(MethodNode mth) {
   List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>();
   for (SSAVar var : mth.getSVars()) {
     // phi result not used
     if (var.getUseCount() == 0) {
       InsnNode assignInsn = var.getAssign().getParentInsn();
       if (assignInsn != null && assignInsn.getType() == InsnType.PHI) {
         insnToRemove.add((PhiInsn) assignInsn);
       }
     }
   }
   for (BlockNode block : mth.getBasicBlocks()) {
     PhiListAttr phiList = block.get(AType.PHI_LIST);
     if (phiList == null) {
       continue;
     }
     for (PhiInsn phi : phiList.getList()) {
       removePhiWithSameArgs(phi, insnToRemove);
     }
   }
   return removePhiList(mth, insnToRemove);
 }
コード例 #15
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 private static void fixLastTryCatchAssign(MethodNode mth) {
   for (BlockNode block : mth.getBasicBlocks()) {
     PhiListAttr phiList = block.get(AType.PHI_LIST);
     if (phiList == null || !block.contains(AType.EXC_HANDLER)) {
       continue;
     }
     for (PhiInsn phi : phiList.getList()) {
       for (int i = 0; i < phi.getArgsCount(); i++) {
         RegisterArg arg = phi.getArg(i);
         InsnNode parentInsn = arg.getAssignInsn();
         if (parentInsn != null
             && parentInsn.getResult() != null
             && parentInsn.contains(AFlag.TRY_LEAVE)) {
           phi.removeArg(arg);
         }
       }
     }
   }
 }
コード例 #16
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 private boolean canInsertBreak(BlockNode exit) {
   if (exit.contains(AFlag.RETURN) || BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
     return false;
   }
   List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);
   if (!simplePath.isEmpty()) {
     BlockNode lastBlock = simplePath.get(simplePath.size() - 1);
     if (lastBlock.contains(AFlag.RETURN) || lastBlock.getSuccessors().isEmpty()) {
       return false;
     }
   }
   // check if there no outer switch (TODO: very expensive check)
   Set<BlockNode> paths = BlockUtils.getAllPathsBlocks(mth.getEnterBlock(), exit);
   for (BlockNode block : paths) {
     if (BlockUtils.checkLastInsnType(block, InsnType.SWITCH)) {
       return false;
     }
   }
   return true;
 }
コード例 #17
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 public void processTryCatchBlocks(MethodNode mth) {
   Set<TryCatchBlock> tcs = new HashSet<TryCatchBlock>();
   for (ExceptionHandler handler : mth.getExceptionHandlers()) {
     tcs.add(handler.getTryBlock());
   }
   for (TryCatchBlock tc : tcs) {
     List<BlockNode> blocks = new ArrayList<BlockNode>(tc.getHandlersCount());
     Set<BlockNode> splitters = new HashSet<BlockNode>();
     for (ExceptionHandler handler : tc.getHandlers()) {
       BlockNode handlerBlock = handler.getHandlerBlock();
       if (handlerBlock != null) {
         blocks.add(handlerBlock);
         splitters.addAll(handlerBlock.getPredecessors());
       } else {
         LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler));
       }
     }
     Set<BlockNode> exits = new HashSet<BlockNode>();
     for (BlockNode splitter : splitters) {
       for (BlockNode handler : blocks) {
         List<BlockNode> s = splitter.getSuccessors();
         if (s.isEmpty()) {
           LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No successors for splitter: " + splitter));
           continue;
         }
         BlockNode ss = s.get(0);
         BlockNode cross = BlockUtils.getPathCross(mth, ss, handler);
         if (cross != null && cross != ss && cross != handler) {
           exits.add(cross);
         }
       }
     }
     for (ExceptionHandler handler : tc.getHandlers()) {
       processExcHandler(handler, exits);
     }
   }
 }
コード例 #18
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 public RegionMaker(MethodNode mth) {
   this.mth = mth;
   if (Consts.DEBUG) {
     this.processedBlocks = new BitSet(mth.getBasicBlocks().size());
   }
 }
コード例 #19
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
  private BlockNode processSwitch(
      IRegion currentRegion, BlockNode block, SwitchNode insn, RegionStack stack) {
    SwitchRegion sw = new SwitchRegion(currentRegion, block);
    currentRegion.getSubBlocks().add(sw);

    int len = insn.getTargets().length;
    // sort by target
    Map<Integer, List<Object>> casesMap = new LinkedHashMap<Integer, List<Object>>(len);
    for (int i = 0; i < len; i++) {
      Object key = insn.getKeys()[i];
      int targ = insn.getTargets()[i];
      List<Object> keys = casesMap.get(targ);
      if (keys == null) {
        keys = new ArrayList<Object>(2);
        casesMap.put(targ, keys);
      }
      keys.add(key);
    }

    Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<BlockNode, List<Object>>(len);
    for (Map.Entry<Integer, List<Object>> entry : casesMap.entrySet()) {
      BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors());
      assert c != null;
      blocksMap.put(c, entry.getValue());
    }
    BlockNode defCase = getBlockByOffset(insn.getDefaultCaseOffset(), block.getSuccessors());
    if (defCase != null) {
      blocksMap.remove(defCase);
    }
    LoopInfo loop = mth.getLoopForBlock(block);

    Map<BlockNode, BlockNode> fallThroughCases = new LinkedHashMap<BlockNode, BlockNode>();

    List<BlockNode> basicBlocks = mth.getBasicBlocks();
    BitSet outs = new BitSet(basicBlocks.size());
    outs.or(block.getDomFrontier());
    for (BlockNode s : block.getCleanSuccessors()) {
      BitSet df = s.getDomFrontier();
      // fall through case block
      if (df.cardinality() > 1) {
        if (df.cardinality() > 2) {
          LOG.debug("Unexpected case pattern, block: {}, mth: {}", s, mth);
        } else {
          BlockNode first = basicBlocks.get(df.nextSetBit(0));
          BlockNode second = basicBlocks.get(df.nextSetBit(first.getId() + 1));
          if (second.getDomFrontier().get(first.getId())) {
            fallThroughCases.put(s, second);
            df = new BitSet(df.size());
            df.set(first.getId());
          } else if (first.getDomFrontier().get(second.getId())) {
            fallThroughCases.put(s, first);
            df = new BitSet(df.size());
            df.set(second.getId());
          }
        }
      }
      outs.or(df);
    }
    outs.clear(block.getId());
    if (loop != null) {
      outs.clear(loop.getStart().getId());
    }

    stack.push(sw);
    stack.addExits(BlockUtils.bitSetToBlocks(mth, outs));

    // check cases order if fall through case exists
    if (!fallThroughCases.isEmpty()) {
      if (isBadCasesOrder(blocksMap, fallThroughCases)) {
        LOG.debug("Fixing incorrect switch cases order, method: {}", mth);
        blocksMap = reOrderSwitchCases(blocksMap, fallThroughCases);
        if (isBadCasesOrder(blocksMap, fallThroughCases)) {
          LOG.error("Can't fix incorrect switch cases order, method: {}", mth);
          mth.add(AFlag.INCONSISTENT_CODE);
        }
      }
    }

    // filter 'out' block
    if (outs.cardinality() > 1) {
      // remove exception handlers
      BlockUtils.cleanBitSet(mth, outs);
    }
    if (outs.cardinality() > 1) {
      // filter loop start and successors of other blocks
      for (int i = outs.nextSetBit(0); i >= 0; i = outs.nextSetBit(i + 1)) {
        BlockNode b = basicBlocks.get(i);
        outs.andNot(b.getDomFrontier());
        if (b.contains(AFlag.LOOP_START)) {
          outs.clear(b.getId());
        } else {
          for (BlockNode s : b.getCleanSuccessors()) {
            outs.clear(s.getId());
          }
        }
      }
    }

    if (loop != null && outs.cardinality() > 1) {
      outs.clear(loop.getEnd().getId());
    }
    if (outs.cardinality() == 0) {
      // one or several case blocks are empty,
      // run expensive algorithm for find 'out' block
      for (BlockNode maybeOut : block.getSuccessors()) {
        boolean allReached = true;
        for (BlockNode s : block.getSuccessors()) {
          if (!isPathExists(s, maybeOut)) {
            allReached = false;
            break;
          }
        }
        if (allReached) {
          outs.set(maybeOut.getId());
          break;
        }
      }
    }
    BlockNode out = null;
    if (outs.cardinality() == 1) {
      out = basicBlocks.get(outs.nextSetBit(0));
      stack.addExit(out);
    } else if (loop == null && outs.cardinality() > 1) {
      LOG.warn("Can't detect out node for switch block: {} in {}", block, mth);
    }
    if (loop != null) {
      // check if 'continue' must be inserted
      BlockNode end = loop.getEnd();
      if (out != end && out != null) {
        insertContinueInSwitch(block, out, end);
      }
    }

    if (!stack.containsExit(defCase)) {
      sw.setDefaultCase(makeRegion(defCase, stack));
    }
    for (Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) {
      BlockNode caseBlock = entry.getKey();
      if (stack.containsExit(caseBlock)) {
        // empty case block
        sw.addCase(entry.getValue(), new Region(stack.peekRegion()));
      } else {
        BlockNode next = fallThroughCases.get(caseBlock);
        stack.addExit(next);
        Region caseRegion = makeRegion(caseBlock, stack);
        stack.removeExit(next);
        if (next != null) {
          next.add(AFlag.FALL_THROUGH);
          caseRegion.add(AFlag.FALL_THROUGH);
        }
        sw.addCase(entry.getValue(), caseRegion);
        // 'break' instruction will be inserted in RegionMakerVisitor.PostRegionVisitor
      }
    }

    stack.pop();
    return out;
  }
コード例 #20
0
  public void process() throws DecodeException {
    int addr = 0;
    int line = section.readUleb128();

    int paramsCount = section.readUleb128();
    List<RegisterArg> mthArgs = mth.getArguments(false);
    assert paramsCount == mthArgs.size();

    for (int i = 0; i < paramsCount; i++) {
      int id = section.readUleb128() - 1;
      if (id != DexNode.NO_INDEX) {
        String name = dex.getString(id);
        mthArgs.get(i).setName(name);
      }
    }

    for (RegisterArg arg : mthArgs) {
      int rn = arg.getRegNum();
      locals[rn] = new LocalVar(arg);
      activeRegisters[rn] = arg;
    }

    // process '0' instruction
    addrChange(-1, 1, line);
    setLine(addr, line);

    int c = section.readByte() & 0xFF;
    while (c != DBG_END_SEQUENCE) {
      switch (c) {
        case DBG_ADVANCE_PC:
          {
            int addrInc = section.readUleb128();
            addr = addrChange(addr, addrInc, line);
            setLine(addr, line);
            break;
          }
        case DBG_ADVANCE_LINE:
          {
            line += section.readSleb128();
            break;
          }

        case DBG_START_LOCAL:
          {
            int regNum = section.readUleb128();
            int nameId = section.readUleb128() - 1;
            int type = section.readUleb128() - 1;
            LocalVar var = new LocalVar(dex, regNum, nameId, type, DexNode.NO_INDEX);
            startVar(var, addr, line);
            break;
          }
        case DBG_START_LOCAL_EXTENDED:
          {
            int regNum = section.readUleb128();
            int nameId = section.readUleb128() - 1;
            int type = section.readUleb128() - 1;
            int sign = section.readUleb128() - 1;
            LocalVar var = new LocalVar(dex, regNum, nameId, type, sign);
            startVar(var, addr, line);
            break;
          }
        case DBG_RESTART_LOCAL:
          {
            int regNum = section.readUleb128();
            LocalVar var = locals[regNum];
            if (var != null) {
              var.end(addr, line);
              setVar(var);
              var.start(addr, line);
            }
            break;
          }
        case DBG_END_LOCAL:
          {
            int regNum = section.readUleb128();
            LocalVar var = locals[regNum];
            if (var != null) {
              var.end(addr, line);
              setVar(var);
            }
            break;
          }

        case DBG_SET_PROLOGUE_END:
        case DBG_SET_EPILOGUE_BEGIN:
          // do nothing
          break;

        case DBG_SET_FILE:
          {
            int idx = section.readUleb128() - 1;
            if (idx != DexNode.NO_INDEX) {
              String sourceFile = dex.getString(idx);
              mth.addAttr(new SourceFileAttr(sourceFile));
            }
            break;
          }

        default:
          {
            if (c >= DBG_FIRST_SPECIAL) {
              int adjustedOpcode = c - DBG_FIRST_SPECIAL;
              int addrInc = adjustedOpcode / DBG_LINE_RANGE;
              addr = addrChange(addr, addrInc, line);
              line += DBG_LINE_BASE + (adjustedOpcode % DBG_LINE_RANGE);
              setLine(addr, line);
            } else {
              throw new DecodeException("Unknown debug insn code: " + c);
            }
            break;
          }
      }
      c = section.readByte() & 0xFF;
    }

    for (LocalVar var : locals) {
      if (var != null && !var.isEnd()) {
        var.end(addr, line);
        setVar(var);
      }
    }
    setSourceLines(addr, insnByOffset.length, line);
  }