<commit_before>module Furnace
  class SSA::Function
    attr_accessor :instrumentation

    attr_reader   :original_name
    attr_reader   :name
    attr_reader   :arguments
    attr_reader   :return_type

    attr_accessor :entry

    def initialize(name=nil, arguments=[], return_type=Type::Bottom.new, instrument=nil)
      @original_name   = name
      @instrumentation = instrument
      self.name        = name
      self.arguments   = arguments
      self.return_type = return_type

      @basic_blocks    = Set.new

      @name_prefixes   = [""].to_set
      @next_name       = 0
    end

    def initialize_copy(original)
      @name_prefixes = [""].to_set
      @name          = @original_name

      if @instrumentation
        @instrumentation = SSA::EventStream.new
      end

      value_map = Hash.new do |value_map, value|
        new_value = value.dup
        value_map[value] = new_value

        if new_value.is_a? SSA::User
          new_value.function = self
          new_value.operands = value.translate_operands(value_map)
        end

        new_value
      end

      self.arguments = @arguments.map do |arg|
        new_arg = arg.dup
        new_arg.function = self
        value_map[arg] = new_arg

        new_arg
      end

      old_basic_blocks = @basic_blocks
      @basic_blocks    = Set[]

      old_basic_blocks.each do |bb|
        new_bb = bb.dup
        new_bb.function = self

        value_map[bb] = new_bb

        add new_bb
      end

      @entry = value_map[@entry]

      original.each do |bb|
        new_bb = value_map[bb]

        bb.each do |insn|
          new_insn = value_map[insn]
          new_insn.basic_block = new_bb
          new_bb.append new_insn
        end
      end
    end

    def name=(name)
      @name = name
    end

    def arguments=(arguments)
      @arguments = sanitize_arguments(arguments)
      instrument { |i| i.set_arguments @arguments }
    end

    def return_type=(return_type)
      @return_type = return_type.to_type if return_type
      instrument { |i| i.set_return_type @return_type }
    end

    def make_name(prefix=nil)
      if prefix.nil?
        (@next_name += 1).to_s
      else
        prefix = prefix.to_s

        if @name_prefixes.include? prefix
          "#{prefix}.#{@next_name += 1}"
        else
          @name_prefixes.add prefix
          prefix
        end
      end
    end

    def each(&proc)
      @basic_blocks.each(&proc)
    end

    alias each_basic_block each

    def include?(name)
      @basic_blocks.any? { |n| n.name == name }
    end

    def find(name)
      if block = @basic_blocks.find { |n| n.name == name }
        block
      else
        raise ArgumentError, "Cannot find basic block #{name}"
      end
    end

    def add(block)
      @basic_blocks.add block

      instrument { |i| i.add block }
    end

    alias << add<commit_msg>Implement Function#size.<commit_after>module Furnace
  class SSA::Function
    attr_accessor :instrumentation

    attr_reader   :original_name
    attr_reader   :name
    attr_reader   :arguments
    attr_reader   :return_type

    attr_accessor :entry

    def initialize(name=nil, arguments=[], return_type=Type::Bottom.new, instrument=nil)
      @original_name   = name
      @instrumentation = instrument
      self.name        = name
      self.arguments   = arguments
      self.return_type = return_type

      @basic_blocks    = Set.new

      @name_prefixes   = [""].to_set
      @next_name       = 0
    end

    def initialize_copy(original)
      @name_prefixes = [""].to_set
      @name          = @original_name

      if @instrumentation
        @instrumentation = SSA::EventStream.new
      end

      value_map = Hash.new do |value_map, value|
        new_value = value.dup
        value_map[value] = new_value

        if new_value.is_a? SSA::User
          new_value.function = self
          new_value.operands = value.translate_operands(value_map)
        end

        new_value
      end

      self.arguments = @arguments.map do |arg|
        new_arg = arg.dup
        new_arg.function = self
        value_map[arg] = new_arg

        new_arg
      end

      old_basic_blocks = @basic_blocks
      @basic_blocks    = Set[]

      old_basic_blocks.each do |bb|
        new_bb = bb.dup
        new_bb.function = self

        value_map[bb] = new_bb

        add new_bb
      end

      @entry = value_map[@entry]

      original.each do |bb|
        new_bb = value_map[bb]

        bb.each do |insn|
          new_insn = value_map[insn]
          new_insn.basic_block = new_bb
          new_bb.append new_insn
        end
      end
    end

    def name=(name)
      @name = name
    end

    def arguments=(arguments)
      @arguments = sanitize_arguments(arguments)
      instrument { |i| i.set_arguments @arguments }
    end

    def return_type=(return_type)
      @return_type = return_type.to_type if return_type
      instrument { |i| i.set_return_type @return_type }
    end

    def make_name(prefix=nil)
      if prefix.nil?
        (@next_name += 1).to_s
      else
        prefix = prefix.to_s

        if @name_prefixes.include? prefix
          "#{prefix}.#{@next_name += 1}"
        else
          @name_prefixes.add prefix
          prefix
        end
      end
    end

    def each(&proc)
      @basic_blocks.each(&proc)
    end

    alias each_basic_block each

    def size
      @basic_blocks.count
    end

    def include?(name)
      @basic_blocks.any? { |n| n.name == name }
    end

    def find(name)
      if block = @basic_blocks.find { |n| n.name == name }
        block
      else
        raise ArgumentError, "Cannot find basic block #{name}"
      end
    end

    def add(block)
      @basic_blocks.add block

      instrument { |i| i.add block }
    end

    alias << add
{'fraction_non_alphanumeric': 0.08518710122434903, 'fraction_numerical': 0.0010346611484738748, 'mean_word_length': 1.9219143576826196, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2732039', 'n_tokens_mistral': 1868, 'n_tokens_neox': 1815, 'n_words': 416}