protected boolean isRegisterCompileTime(
     int regId, int ip, HashMap<Integer, List<Integer>> refs, AVM2Code code) {
   Set<Integer> previous = new HashSet<>();
   AVM2Code.getPreviousReachableIps(ip, refs, previous, new HashSet<>());
   for (int p : previous) {
     if (p < 0) {
       continue;
     }
     if (p >= code.code.size()) {
       continue;
     }
     AVM2Instruction sins = code.code.get(p);
     if (code.code.get(p).definition instanceof SetLocalTypeIns) {
       SetLocalTypeIns sl = (SetLocalTypeIns) sins.definition;
       if (sl.getRegisterId(sins) == regId) {
         if (!AVM2Code.isDirectAncestor(ip, p, refs)) {
           return false;
         }
       }
     }
     if ((code.code.get(p).definition instanceof IncLocalIns)
         || (code.code.get(p).definition instanceof IncLocalIIns)
         || (code.code.get(p).definition instanceof DecLocalIns)
         || (code.code.get(p).definition instanceof DecLocalIIns)) {
       if (sins.operands[0] == regId) {
         if (!AVM2Code.isDirectAncestor(ip, p, refs)) {
           return false;
         }
       }
     }
   }
   return true;
 }
 @Override
 public void avm2CodeRemoveTraps(
     String path,
     int classIndex,
     boolean isStatic,
     int scriptIndex,
     ABC abc,
     Trait trait,
     int methodInfo,
     MethodBody body)
     throws InterruptedException {
   AVM2Code code = body.getCode();
   code.removeDeadCode(body);
   removeObfuscationIfs(classIndex, isStatic, scriptIndex, abc, body, null);
   removeZeroJumps(code, body);
 }
  protected boolean removeObfuscationIfs(
      int classIndex,
      boolean isStatic,
      int scriptIndex,
      ABC abc,
      MethodBody body,
      AVM2Instruction inlineIns)
      throws InterruptedException {
    AVM2Code code = body.getCode();
    if (code.code.isEmpty()) {
      return false;
    }

    Map<Integer, Object> staticRegs = new HashMap<>();
    if (inlineIns != null && inlineIns.definition instanceof GetLocalTypeIns) {
      staticRegs.put(
          ((GetLocalTypeIns) inlineIns.definition).getRegisterId(inlineIns), Undefined.INSTANCE);
    }

    if (code.code.isEmpty()) {
      return false;
    }

    FixItemCounterStack stack = new FixItemCounterStack();
    LocalDataArea localData = new LocalDataArea();
    localData.operandStack = stack;

    int localReservedCount = body.getLocalReservedCount();
    for (int i = 0; i < code.code.size(); i++) {
      if (Thread.currentThread().isInterrupted()) {
        throw new InterruptedException();
      }

      localData.clear();
      initLocalRegs(localData, localReservedCount, body.max_regs, i == 0);

      if (executeInstructions(
          staticRegs, body, abc, code, localData, i, code.code.size() - 1, null, inlineIns)) {
        code.removeDeadCode(body);
        i--;
      }
    }

    return false;
  }
  protected boolean removeZeroJumps(AVM2Code code, MethodBody body) throws InterruptedException {
    boolean result = false;
    for (int i = 0; i < code.code.size(); i++) {
      AVM2Instruction ins = code.code.get(i);
      if (ins.definition instanceof JumpIns) {
        if (ins.operands[0] == 0) {
          if (Thread.currentThread().isInterrupted()) {
            throw new InterruptedException();
          }

          code.removeInstruction(i, body);
          i--;
          result = true;
        }
      }
    }
    return result;
  }
  private int visitCode(
      Reference<AVM2Instruction> assignment,
      Set<Integer> visited,
      TranslateStack stack,
      int classIndex,
      boolean isStatic,
      MethodBody body,
      int scriptIndex,
      ABC abc,
      AVM2Code code,
      int idx,
      int endIdx,
      Set<Integer> ignored,
      Set<Integer> ignoredGets)
      throws InterruptedException {
    List<GraphTargetItem> output = new ArrayList<>();
    AVM2LocalData localData =
        newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex);
    initLocalRegs(localData, body.getLocalReservedCount(), body.max_regs);
    localData.localRegs.put(0, new NullAVM2Item(null, null)); // this

    List<Integer> toVisit = new ArrayList<>();
    toVisit.add(idx);
    List<TranslateStack> toVisitStacks = new ArrayList<>();
    toVisitStacks.add(stack);
    outer:
    while (!toVisit.isEmpty()) {
      if (Thread.currentThread().isInterrupted()) {
        throw new InterruptedException();
      }

      idx = toVisit.remove(0);
      stack = toVisitStacks.remove(0);

      while (true) {
        if (idx > endIdx) {
          break;
        }
        if (visited.contains(idx)) {
          break;
        }
        visited.add(idx);

        AVM2Instruction ins = code.code.get(idx);
        InstructionDefinition def = ins.definition;
        // System.err.println("" + idx + ": " + ins + " stack:" + stack.size());

        // do not throw EmptyStackException, much faster
        int requiredStackSize = ins.getStackPopCount(localData);
        if (stack.size() < requiredStackSize) {
          continue outer;
        }

        ins.translate(localData, stack, output, Graph.SOP_USE_STATIC, "");
        if (def instanceof SetLocalTypeIns) {
          int regId = ((SetLocalTypeIns) def).getRegisterId(ins);
          if (!ignored.contains(regId)) {
            assignment.setVal(ins);
            return regId;
          }
        } else if (def instanceof GetLocalTypeIns) {
          int regId = ((GetLocalTypeIns) def).getRegisterId(ins);
          if (!ignored.contains(regId) && !ignoredGets.contains(regId)) {
            assignment.setVal(ins);
            return regId;
          }
        } else if (!(def instanceof KillIns) && !(def instanceof DebugIns)) {
          // can be inclocal, declocal, hasnext...
          for (int p = 0; p < ins.definition.operands.length; p++) {
            int op = ins.definition.operands[p];
            if (op == AVM2Code.DAT_LOCAL_REG_INDEX) {
              int regId = ins.operands[p];
              if (!ignored.contains(regId)) {
                assignment.setVal(ins);
                return regId;
              }
            }
          }
        }

        idx++;

        if (ins.definition instanceof JumpIns) {

          long address = ins.offset + ins.getBytesLength() + ins.operands[0];
          idx = code.adr2pos(address); // code.indexOf(code.getByAddress(address));
          if (idx == -1) {
            throw new TranslateException("Jump target not found: " + address);
          }
        }

        if (ins.isBranch()) {
          List<Integer> branches =
              ins.getBranches(
                  new GraphSource() {

                    @Override
                    public int size() {
                      throw new UnsupportedOperationException(
                          "Not supported yet."); // To change body of generated methods, choose
                      // Tools | Templates.
                    }

                    @Override
                    public GraphSourceItem get(int pos) {
                      throw new UnsupportedOperationException(
                          "Not supported yet."); // To change body of generated methods, choose
                      // Tools | Templates.
                    }

                    @Override
                    public boolean isEmpty() {
                      throw new UnsupportedOperationException(
                          "Not supported yet."); // To change body of generated methods, choose
                      // Tools | Templates.
                    }

                    @Override
                    public List<GraphTargetItem> translatePart(
                        GraphPart part,
                        BaseLocalData localData,
                        TranslateStack stack,
                        int start,
                        int end,
                        int staticOperation,
                        String path)
                        throws InterruptedException {
                      throw new UnsupportedOperationException(
                          "Not supported yet."); // To change body of generated methods, choose
                      // Tools | Templates.
                    }

                    @Override
                    public int adr2pos(long adr) {
                      return code.adr2pos(adr);
                    }

                    @Override
                    public long pos2adr(int pos) {
                      return code.pos2adr(pos);
                    }
                  });
          idx = branches.get(0);
          for (int n = 1; n < branches.size(); n++) {
            // visitCode(visited, (TranslateStack) stack.clone(), classIndex, isStatic, body,
            // scriptIndex, abc, code, branches.get(n), endIdx, result);
            int nidx = branches.get(n);
            if (visited.contains(nidx)) {
              continue;
            }
            toVisit.add(nidx);
            toVisitStacks.add((TranslateStack) stack.clone());
          }
        }
        /*if (ins.definition instanceof IfTypeIns) {
        long address = ins.offset + ins.getBytes().length + ins.operands[0];
        int newIdx = code.adr2pos(address);
        if (newIdx == -1) {
        throw new TranslateException("If target not found: " + address);
        }
        visitCode(visited, (TranslateStack) stack.clone(), classIndex, isStatic, body, scriptIndex, abc, code, newIdx, endIdx, result);
        }*/

        if (ins.definition instanceof ReturnValueIns) {
          break;
        }

        if (ins.definition instanceof ThrowIns) {
          break;
        }

        if (ins.definition instanceof ReturnVoidIns) {
          break;
        }
      }
    }
    return -1;
  }
  private boolean executeInstructions(
      Map<Integer, Object> staticRegs,
      MethodBody body,
      ABC abc,
      AVM2Code code,
      LocalDataArea localData,
      int idx,
      int endIdx,
      ExecutionResult result,
      AVM2Instruction inlineIns)
      throws InterruptedException {
    int instructionsProcessed = 0;

    FixItemCounterStack stack = (FixItemCounterStack) localData.operandStack;
    Set<Long> refs = code.getImportantOffsets(body);
    boolean modified = false;
    while (true) {
      if (idx > endIdx) {
        break;
      }

      if (instructionsProcessed > executionLimit) {
        break;
      }

      AVM2Instruction ins = code.code.get(idx);
      if (instructionsProcessed > 0 && refs.contains(ins.getOffset())) {
        break;
      }

      modified = modified | code.inlineJumpExit();
      InstructionDefinition def = ins.definition;
      if (inlineIns == ins) {
        if (def instanceof SetLocalTypeIns) {
          int regId = ((SetLocalTypeIns) def).getRegisterId(ins);
          staticRegs.put(regId, localData.localRegisters.get(regId));
          code.replaceInstruction(
              idx, new AVM2Instruction(0, DeobfuscatePopIns.getInstance(), null), body);
          modified = true;
        }
      }
      if (def instanceof GetLocalTypeIns) {
        int regId = ((GetLocalTypeIns) def).getRegisterId(ins);
        if (staticRegs.containsKey(regId)) {
          AVM2Instruction pushins = abc.constants.makePush(staticRegs.get(regId));
          if (pushins == null) {
            break;
          }

          code.replaceInstruction(idx, pushins, body);
          modified = true;
          ins = pushins;
          def = ins.definition;
        }
      }

      if (def instanceof NewFunctionIns
          && idx + 1 < code.code.size()
          && code.code.get(idx + 1).definition instanceof PopIns) {
        code.removeInstruction(idx + 1, body);
        code.removeInstruction(idx, body);
        modified = true;
        continue;
      }

      boolean ok = false;
      // todo: honfika: order by statistics
      if (def.isNotCompileTimeSupported()
          || def instanceof PushByteIns
          || def instanceof PushShortIns
          || def instanceof PushIntIns
          || def instanceof PushDoubleIns
          || def instanceof PushStringIns
          || def instanceof PushNullIns
          || def instanceof PushUndefinedIns
          || def instanceof PushFalseIns
          || def instanceof PushTrueIns
          || def instanceof DupIns
          || def instanceof SwapIns
          || def instanceof AddIns
          || def instanceof AddIIns
          || def instanceof SubtractIns
          || def instanceof SubtractIIns
          || def instanceof ModuloIns
          || def instanceof MultiplyIns
          || def instanceof MultiplyIIns //
          || def instanceof DivideIns //
          || def instanceof BitAndIns
          || def instanceof BitXorIns
          || def instanceof BitOrIns
          || def instanceof LShiftIns
          || def instanceof RShiftIns
          || def instanceof URShiftIns
          || def instanceof EqualsIns
          || def instanceof NotIns
          || def instanceof NegateIns //
          || def instanceof NegateIIns //
          || def instanceof IncrementIns //
          || def instanceof IncrementIIns //
          || def instanceof DecrementIns //
          || def instanceof DecrementIIns //
          || def instanceof IfTypeIns
          || def instanceof JumpIns
          || def instanceof EqualsIns
          || def instanceof LessEqualsIns
          || def instanceof GreaterEqualsIns
          || def instanceof GreaterThanIns
          || def instanceof LessThanIns
          || def instanceof StrictEqualsIns
          || def instanceof PopIns
          || def instanceof GetLocalTypeIns
          || def instanceof SetLocalTypeIns
          || def instanceof NewFunctionIns
          || def instanceof CoerceOrConvertTypeIns) {
        ok = true;
      }

      if (!ok) {
        break;
      }

      if (!(def instanceof NewFunctionIns)) {
        // do not throw EmptyStackException, much faster
        int requiredStackSize = def.getStackPopCount(ins, abc);
        if (stack.size() < requiredStackSize) {
          break;
        }

        if (requiredStackSize > 0 && !def.isNotCompileTimeSupported()) {
          boolean notCompileTime = false;
          for (int i = 0; i < requiredStackSize; i++) {
            if (stack.peek(i + 1) == NotCompileTime.INSTANCE) {
              notCompileTime = true;
              break;
            }
          }

          if (notCompileTime) {
            break;
          }
        }

        if (localData.scopeStack.size() < -def.getScopeStackDelta(ins, abc)) {
          break;
        }

        boolean supported;
        try {
          localData.jump = null;
          supported = def.execute(localData, abc.constants, ins);
        } catch (AVM2ExecutionException ex) {
          supported = false;
        }

        if (!supported) {
          break;
        }
      }

      boolean ifed = false;
      if (def instanceof IfTypeIns && !(def instanceof JumpIns)) {
        long address = ins.getTargetAddress();
        int nidx = code.adr2pos(address);
        AVM2Instruction tarIns = code.code.get(nidx);

        // Some IfType instructions need more than 1 operand, we must pop out all of them
        int stackCount = -def.getStackDelta(ins, abc);

        if (localData.jump != null) {
          // System.err.println("replacing " + ins + " on " + idx + " with jump");
          AVM2Instruction jumpIns = new AVM2Instruction(0, AVM2Instructions.Jump, new int[] {0});
          // jumpIns.operands[0] = ins.operands[0] /*- ins.getBytes().length*/ +
          // jumpIns.getBytes().length;
          code.replaceInstruction(idx, jumpIns, body);
          jumpIns.operands[0] =
              (int) (tarIns.getOffset() - jumpIns.getOffset() - jumpIns.getBytesLength());
          for (int s = 0; s < stackCount; s++) {
            code.insertInstruction(
                idx,
                new AVM2Instruction(ins.getOffset(), DeobfuscatePopIns.getInstance(), null),
                true,
                body);
          }

          idx = code.adr2pos(jumpIns.getTargetAddress());
        } else {
          // System.err.println("replacing " + ins + " on " + idx + " with pop");
          code.replaceInstruction(
              idx,
              new AVM2Instruction(ins.getOffset(), DeobfuscatePopIns.getInstance(), null),
              body);
          for (int s = 1 /*first is replaced*/; s < stackCount; s++) {
            code.insertInstruction(
                idx,
                new AVM2Instruction(ins.getOffset(), DeobfuscatePopIns.getInstance(), null),
                true,
                body);
          }
          // ins.definition = DeobfuscatePopIns.getInstance();
          idx++;
        }

        modified = true;
        ifed = true;
      } else {
        idx++;
      }

      instructionsProcessed++;

      if (result != null && stack.allItemsFixed()) {
        result.idx = idx == code.code.size() ? idx - 1 : idx;
        result.instructionsProcessed = instructionsProcessed;
        result.stack.clear();
        result.stack.addAll(stack);
      }

      if (ifed) {
        break;
      }

      if (localData.jump != null) {
        idx = code.adr2pos(localData.jump);

        if (idx == -1) {
          throw new TranslateException("Jump target not found: " + localData.jump);
        }
      }
    }

    return modified;
  }