void (@NotNull List<? extends Instruction> instructions, @NotNull InstructionClientVisitor<?> clientVisitor, int startOffset, int endOffset) { final WalkThroughStack walkThroughStack = new WalkThroughStack(instructions.size() / 2); walkThroughStack.push(startOffset); ControlFlowInstructionVisitor getNextOffsetVisitor = new ControlFlowInstructionVisitor() { @Override public void visitCallInstruction(CallInstruction instruction, int offset, int nextOffset) { int newOffset = instruction.offset; // 'procedure' pointed by call instruction should be processed regardless of whether it was already visited or not // clear procedure text and return instructions afterwards int i; for (i = instruction.procBegin; i < clientVisitor.processedInstructions.length && (i < instruction.procEnd || i < instructions.size() && instructions.get(i) instanceof ReturnInstruction); i++) { clientVisitor.processedInstructions[i] = false; } clientVisitor.procedureEntered(instruction.procBegin, i); walkThroughStack.currentStack = new CallStackItem(walkThroughStack.currentStack, offset + 1); walkThroughStack.push(offset, newOffset); walkThroughStack.push(newOffset); } @Override public void visitReturnInstruction(ReturnInstruction instruction, int offset, int nextOffset) { int newOffset = -1; if (walkThroughStack.currentStack != null) { newOffset = walkThroughStack.currentStack.target; walkThroughStack.currentStack = walkThroughStack.currentStack.next; } if (instruction.offset != 0) { newOffset = instruction.offset; } if (newOffset != -1) { walkThroughStack.push(offset, newOffset); walkThroughStack.push(newOffset); } } @Override public void visitBranchingInstruction(BranchingInstruction instruction, int offset, int nextOffset) { int newOffset = instruction.offset; walkThroughStack.push(offset, newOffset); walkThroughStack.push(newOffset); } @Override public void visitConditionalBranchingInstruction(ConditionalBranchingInstruction instruction, int offset, int nextOffset) { int newOffset = instruction.offset; walkThroughStack.push(offset, newOffset); walkThroughStack.push(offset, offset + 1); walkThroughStack.push(newOffset); walkThroughStack.push(offset + 1); } @Override public void visitInstruction(Instruction instruction, int offset, int nextOffset) { int newOffset = offset + 1; walkThroughStack.push(offset, newOffset); walkThroughStack.push(newOffset); } }; while (!walkThroughStack.isEmpty()) { final int offset = walkThroughStack.peekOldOffset(); final int newOffset = walkThroughStack.popNewOffset(); if (offset >= endOffset) { continue; } Instruction instruction = instructions.get(offset); if (clientVisitor.processedInstructions[offset]) { if (newOffset != -1) { instruction.accept(clientVisitor, offset, newOffset); } continue; } clientVisitor.processedInstructions[offset] = true; instruction.accept(getNextOffsetVisitor, offset, newOffset); } }