<commit_before>require 'gir_ffi/builder_helper'
module GirFFI
  # Builds a class based on information found in the introspection
  # repository.
  class ClassBuilder
    include BuilderHelper
    def initialize namespace, classname
      @namespace = namespace
      @classname = classname
    end

    def generate
      build_class
    end

    def setup_method method
      definition = prepare_method method.to_s

      return false if definition.nil?

      klass = build_class
      meta = (class << klass; self; end)
      meta.class_eval definition

      true
    end

    def setup_instance_method method
      definition = prepare_method method.to_s

      if definition.nil?
	if info.parent
	  return superclass.gir_ffi_builder.setup_instance_method method
	else
	  return false
	end
      end

      klass = build_class
      klass.class_eval "undef #{method}"
      klass.class_eval definition

      true
    end

    private

    def build_class
      unless defined? @klass
	case info.type
	  when :object, :struct
	    instantiate_class
	    setup_class unless already_set_up
	  when :enum, :flags
	    @klass = optionally_define_constant namespace_module, @classname do
	      vals = info.values.map {|v| [v.name.to_sym, v.value]}.flatten
	      lib.enum(@classname.to_sym, vals)
	    end
	end
      end
      @klass
    end

    def info
      unless defined? @info
	@info = gir.find_by_name @namespace, @classname
	raise "Class #{@classname} not found in namespace #{@namespace}" if @info.nil?
      end
      @info
    end

    def parent
      # FIXME: This does not work without this cacheing .. very odd!
      unless defined? @parent
	@parent = info.type == :object ? info.parent : nil
      end
      @parent
    end

    def superclass
      unless defined? @superclass
	if parent
	  @superclass = Builder.build_class parent.namespace, parent.name
	else
	  @superclass = GirFFI::ClassBase
	end
      end
      @superclass
    end

    def namespace_module
      @namespace_module ||= Builder.build_module @namespace
    end

    def lib
      @lib ||= namespace_module.const_get :Lib
    end

    def instantiate_class
      @klass = get_or_define_class namespace_module, @classname, superclass
      @structklass = get_or_define_class @klass, :Struct, FFI::Struct
    end

    def setup_class
      setup_layout
      setup_constants
      alias_instance_methods

      if info.type == :struct
	(class << @klass; self; end).class_eval {
	  alias_method :new, :_real_new
	}
      end
    end

    def setup_layout
      spec = layout_specification
      @structklass.class_eval { layout(*spec) }
    end

    def layout_specification
      fields = info.fields

      if fields.empty?
	if parent
	  return [:parent, superclass.const_get(:Struct), 0]
	end
      end

      fields.map do |f|
	[ f.name.to_sym,
	  itypeinfo_to_ffitype_for_struct(f.type),
	  f.offset ]
      end.flatten
    end

    def itypeinfo_to_ffitype_for_struct typeinfo
      ffitype = Builder.itypeinfo_to_ffitype typeinfo
      if ffitype.kind_of?(Class) and const_defined_for ffitype, :Struct
	ffitype = ffitype.const_get :Struct
      end
      ffitype
    end

    def alias_instance_methods
      info.methods.each do |m|
	@klass.class_eval "
	  def #{m.name} *args, &block
	    method_missing :#{m.name}, *args, &block
	  end
	"
      end
    end

    def setup_constants
      @klass.const_set :GIR_INFO, info
      @klass.const_set :GIR_FFI_BUILDER, self
    end

    def already_set_up
      const_defined_for @klass, :GIR_FFI_BUILDER
    end

    def method_introspection_data method
      return info.find_method method
    end

    def function_definition go
      fdbuilder = if go.constructor?
		    ConstructorDefinitionBuilder
		  else
		    FunctionDefinitionBuilder
		  end
      fdbuilder.new(go, lib).generate
    end

    def prepare_method method
      go = method_introspection_data method

      return nil if go.nil?
      return nil if go.type != :function

      Builder.attach_ffi_function lib, go
      function_definition go
    end

    def gir
      unless defined? @gir
	@gir = IRepository.default
	@gir.require @namespace, nil
      end
      @gir
    end

    def get_or_define_class namespace, name, parent
      optionally_define_constant(namespace, name) {
	Class.new parent
      }
    end
  end
end<commit_msg>Remove useless check.<commit_after>require 'gir_ffi/builder_helper'
module GirFFI
  # Builds a class based on information found in the introspection
  # repository.
  class ClassBuilder
    include BuilderHelper
    def initialize namespace, classname
      @namespace = namespace
      @classname = classname
    end

    def generate
      build_class
    end

    def setup_method method
      definition = prepare_method method.to_s

      return false if definition.nil?

      klass = build_class
      meta = (class << klass; self; end)
      meta.class_eval definition

      true
    end

    def setup_instance_method method
      definition = prepare_method method.to_s

      if definition.nil?
	if info.parent
	  return superclass.gir_ffi_builder.setup_instance_method method
	else
	  return false
	end
      end

      klass = build_class
      klass.class_eval "undef #{method}"
      klass.class_eval definition

      true
    end

    private

    def build_class
      unless defined? @klass
	case info.type
	  when :object, :struct
	    instantiate_class
	    setup_class unless already_set_up
	  when :enum, :flags
	    @klass = optionally_define_constant namespace_module, @classname do
	      vals = info.values.map {|v| [v.name.to_sym, v.value]}.flatten
	      lib.enum(@classname.to_sym, vals)
	    end
	end
      end
      @klass
    end

    def info
      unless defined? @info
	@info = gir.find_by_name @namespace, @classname
	raise "Class #{@classname} not found in namespace #{@namespace}" if @info.nil?
      end
      @info
    end

    def parent
      # FIXME: This does not work without this cacheing .. very odd!
      unless defined? @parent
	@parent = info.type == :object ? info.parent : nil
      end
      @parent
    end

    def superclass
      unless defined? @superclass
	if parent
	  @superclass = Builder.build_class parent.namespace, parent.name
	else
	  @superclass = GirFFI::ClassBase
	end
      end
      @superclass
    end

    def namespace_module
      @namespace_module ||= Builder.build_module @namespace
    end

    def lib
      @lib ||= namespace_module.const_get :Lib
    end

    def instantiate_class
      @klass = get_or_define_class namespace_module, @classname, superclass
      @structklass = get_or_define_class @klass, :Struct, FFI::Struct
    end

    def setup_class
      setup_layout
      setup_constants
      alias_instance_methods

      if info.type == :struct
	(class << @klass; self; end).class_eval {
	  alias_method :new, :_real_new
	}
      end
    end

    def setup_layout
      spec = layout_specification
      @structklass.class_eval { layout(*spec) }
    end

    def layout_specification
      fields = info.fields

      if fields.empty?
	if parent
	  return [:parent, superclass.const_get(:Struct), 0]
	end
      end

      fields.map do |f|
	[ f.name.to_sym,
	  itypeinfo_to_ffitype_for_struct(f.type),
	  f.offset ]
      end.flatten
    end

    def itypeinfo_to_ffitype_for_struct typeinfo
      ffitype = Builder.itypeinfo_to_ffitype typeinfo
      if ffitype.kind_of?(Class) and const_defined_for ffitype, :Struct
	ffitype = ffitype.const_get :Struct
      end
      ffitype
    end

    def alias_instance_methods
      info.methods.each do |m|
	@klass.class_eval "
	  def #{m.name} *args, &block
	    method_missing :#{m.name}, *args, &block
	  end
	"
      end
    end

    def setup_constants
      @klass.const_set :GIR_INFO, info
      @klass.const_set :GIR_FFI_BUILDER, self
    end

    def already_set_up
      const_defined_for @klass, :GIR_FFI_BUILDER
    end

    def method_introspection_data method
      info.find_method method
    end

    def function_definition go
      fdbuilder = if go.constructor?
		    ConstructorDefinitionBuilder
		  else
		    FunctionDefinitionBuilder
		  end
      fdbuilder.new(go, lib).generate
    end

    def prepare_method method
      go = method_introspection_data method

      return nil if go.nil?

      Builder.attach_ffi_function lib, go
      function_definition go
    end

    def gir
      unless defined? @gir
	@gir = IRepository.default
	@gir.require @namespace, nil
      end
      @gir
    end

    def get_or_define_class namespace, name, parent
      optionally_define_constant(namespace, name) {
	Class.new parent
      }
    end
  end
end
{'fraction_non_alphanumeric': 0.06592011082890788, 'fraction_numerical': 0.00023089355806972986, 'mean_word_length': 2.5475020475020473, 'pattern_counts': {'":': 0, '<': 11, '<?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': '2201536', 'n_tokens_mistral': 2799, 'n_tokens_neox': 2706, 'n_words': 725}