class GObjectIntrospection::Loader - gobject-introspection-3.1.1 Documentation

# class GObjectIntrospection::Loader

### Attributes

version[RW]

### Public Class Methods

load(namespace, base_module, options={}) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 22
    def load(namespace, base_module, options={})
      loader = new(base_module)
      loader.version = options[:version]
      loader.load(namespace)
    end

new(base_module) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 30
    def initialize(base_module)
      @base_module = base_module
      @version = nil
    end

### Public Instance Methods

load(namespace) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 35
    def load(namespace)
      repository = Repository.default
      repository.require(namespace, @version)
      pre_load(repository, namespace)
      repository.each(namespace) do |info|
        load_info(info) if info.is_a?(InterfaceInfo)
      end
      repository.each(namespace) do |info|
        load_info(info) unless info.is_a?(InterfaceInfo)
      end
      post_load(repository, namespace)
    end

### Private Instance Methods

define_boxed(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 147
    def define_boxed(info)
      klass = self.class.define_class(info.gtype, info.name, @base_module)
      _ = klass # TODO: Remove me. It is just for suppressing a warning.
      # TODO
      # load_fields(info, klass)
      # load_methods(info, klass)
    end

define_constant(name, info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 570
    def define_constant(name, info)
      @base_module.const_set(name, info.value)
    end

define_enum(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 163
    def define_enum(info)
      self.class.define_class(info.gtype,
                              rubyish_class_name(info),
                              @base_module)
    end

define_equal_style_setter(info, klass, method_name) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 541
    def define_equal_style_setter(info, klass, method_name)
      if /\Aset_/ =~ method_name and info.n_args == 1
        setter_method_name = "#{$POSTMATCH}="
        remove_existing_method(klass, setter_method_name)
        klass.__send__(:alias_method, setter_method_name, method_name)
      end
    end

define_error(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 169
    def define_error(info)
      self.class.define_error(info.error_domain,
                              rubyish_class_name(info),
                              @base_module,
                              :parent => error_parent_class(info),
                              :gtype => info.gtype)
    end

define_method(info, klass, method_name) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 512
    def define_method(info, klass, method_name)
      info.unlock_gvl = should_unlock_gvl?(info, klass)
      remove_existing_method(klass, method_name)
      function_info_p = (info.class == FunctionInfo)
      have_return_value_p = info.have_return_value?
      arguments_builder = ArgumentsBuilder.new(info, "#{klass}\##{method_name}")
      require_callback_p = info.require_callback?
      klass.__send__(:define_method, method_name) do |*arguments, &block|
        arguments.unshift(self) if function_info_p
        arguments_builder.build(arguments, block)
        arguments = arguments_builder.arguments
        block = arguments_builder.block
        if block.nil? and require_callback_p
          to_enum(method_name, *arguments)
        else
          if function_info_p
            return_value = info.invoke(arguments, &block)
          else
            return_value = info.invoke(self, arguments, &block)
          end
          if have_return_value_p
            return_value
          else
            self
          end
        end
      end
    end

define_module_function(target_module, name, function_info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 83
    def define_module_function(target_module, name, function_info)
      function_info.unlock_gvl = should_unlock_gvl?(function_info, target_module)
      method_name = "#{target_module}\#.#{name}"
      arguments_builder = ArgumentsBuilder.new(function_info, method_name)
      target_module.module_eval do
        define_method(name) do |*arguments, &block|
          arguments_builder.build(arguments, block)
          arguments = arguments_builder.arguments
          block = arguments_builder.block
          function_info.invoke(arguments, &block)
        end
        module_function(name)
      end
    end

define_singleton_method(klass, name, info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 98
    def define_singleton_method(klass, name, info)
      info.unlock_gvl = should_unlock_gvl?(info, klass)
      arguments_builder = ArgumentsBuilder.new(info, "#{klass}.#{name}")
      require_callback_p = info.require_callback?
      singleton_class = (class << klass; self; end)
      singleton_class.__send__(:define_method, name) do |*arguments, &block|
        arguments_builder.build(arguments, block)
        arguments = arguments_builder.arguments
        block = arguments_builder.block
        if block.nil? and require_callback_p
          to_enum(name, *arguments)
        else
          info.invoke(arguments, &block)
        end
      end
    end

define_struct(info, options={}) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 115
    def define_struct(info, options={})
      name = rubyish_class_name(info)
      if info.gtype == GLib::Type::NONE
        klass = self.class.define_struct(info.size, name, @base_module,
                                         :parent => options[:parent])
      else
        klass = self.class.define_class(info.gtype, name, @base_module,
                                        :parent => options[:parent],
                                        :size   => info.size)
      end
      load_fields(info, klass)
      load_methods(info, klass)
    end

error_parent_class(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 177
    def error_parent_class(info)
      nil
    end

field_name(field_info, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 232
    def field_name(field_info, klass)
      field_info.name
    end

find_suitable_callable_info(infos, arguments) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 314
    def find_suitable_callable_info(infos, arguments)
      min_n_args = nil
      max_n_args = nil
      candidate_infos = []
      infos.each do |info|
        n_in_args = info.n_in_args
        n_required_in_args = info.n_required_in_args
        if (n_required_in_args..n_in_args).cover?(arguments.size)
          candidate_infos << info
        end
        min_n_args = [min_n_args || n_required_in_args, n_required_in_args].min
        max_n_args = [max_n_args || n_in_args, n_in_args].max
      end

      if candidate_infos.size == 1
        return candidate_infos.first
      elsif candidate_infos.size > 1
        candidate_info = candidate_infos.find do |info|
          in_arg_infos = info.in_args
          arguments.each.with_index.all? do |argument, i|
            match_argument?(in_arg_infos[i], argument)
          end
        end
        return candidate_info || candidate_infos.first
      end

      detail = "#{arguments.size} for #{min_n_args}"
      if min_n_args < max_n_args
        detail << "..#{max_n_args}"
      end
      raise ArgumentError, "wrong number of arguments (#{detail})"
    end

flags_class_name(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 213
    def flags_class_name(info)
      info.name
    end

initialize_post(object) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 311
    def initialize_post(object)
    end

load_boxed_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 155
    def load_boxed_info(info)
      define_boxed(info)
    end

load_constant_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 566
    def load_constant_info(info)
      define_constant(info.name, info)
    end

load_constructor_infos(infos, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 282
    def load_constructor_infos(infos, klass)
      return if infos.empty?

      call_initialize_post = lambda do |object|
        initialize_post(object)
      end
      infos.each do |info|
        name = "initialize_#{info.name}"
        info.unlock_gvl = should_unlock_gvl?(info, klass)
        arguments_builder = ArgumentsBuilder.new(info, "#{klass}\##{name}")
        klass.__send__(:define_method, name) do |*arguments, &block|
          arguments_builder.build(arguments, block)
          arguments = arguments_builder.arguments
          block = arguments_builder.block
          info.invoke(self, arguments, &block)
          call_initialize_post.call(self)
        end
        klass.__send__(:private, name)
      end

      find_info = lambda do |arguments|
        find_suitable_callable_info(infos, arguments)
      end
      klass.__send__(:define_method, "initialize") do |*arguments, &block|
        info = find_info.call(arguments, &block)
        __send__("initialize_#{info.name}", *arguments, &block)
      end
    end

load_enum_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 181
    def load_enum_info(info)
      if info.gtype == GLib::Type::NONE
        enum_module = Module.new
        info.values.each do |value_info|
          load_enum_value(value_info, enum_module)
        end
        @base_module.const_set(rubyish_class_name(info), enum_module)
      else
        if info.error_domain
          define_error(info)
        else
          define_enum(info)
        end
      end
    end

load_enum_value(value_info, enum_module) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 159
    def load_enum_value(value_info, enum_module)
      enum_module.const_set(value_info.name.upcase, value_info.value)
    end

load_field(info, i, field_info, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 236
    def load_field(info, i, field_info, klass)
      name = field_name(field_info, klass)
      flags = field_info.flags

      if flags.readable?
        if field_info.type.tag == TypeTag::BOOLEAN
          reader_method_name = "#{name}?"
        else
          reader_method_name = name
        end
        remove_existing_method(klass, reader_method_name)
        klass.__send__(:define_method, reader_method_name) do ||
          info.get_field_value(self, i)
        end
      end

      if flags.writable?
        klass.__send__(:define_method, "#{name}=") do |value|
          info.set_field_value(self, i, value)
        end
      end
    end

load_fields(info, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 225
    def load_fields(info, klass)
      info.n_fields.times do |i|
        field_info = info.get_field(i)
        load_field(info, i, field_info, klass)
      end
    end

load_flag_value(value_info, flags_module) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 197
    def load_flag_value(value_info, flags_module)
      flags_module.const_set(value_info.name.upcase, value_info.value)
    end

load_flags_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 201
    def load_flags_info(info)
      if info.gtype == GLib::Type::NONE
        flags_module = Module.new
        info.values.each do |value_info|
          load_flag_value(value_info, flags_module)
        end
        @base_module.const_set(rubyish_class_name(info), flags_module)
      else
        self.class.define_class(info.gtype, flags_class_name(info), @base_module)
      end
    end

load_function_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 78
    def load_function_info(info)
      name = rubyish_method_name(info)
      define_singleton_method(@base_module, name, info)
    end

load_function_infos(infos, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 549
    def load_function_infos(infos, klass)
      infos.each do |info|
        name = rubyish_method_name(info)
        next if name == "new"
        next if name == "alloc"
        define_singleton_method(klass, name, info)
      end
    end

load_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 55
    def load_info(info)
      case info
      when FunctionInfo
        load_function_info(info)
      when StructInfo
        load_struct_info(info)
      when BoxedInfo
        load_boxed_info(info)
      when FlagsInfo
        load_flags_info(info)
      when EnumInfo
        load_enum_info(info)
      when ObjectInfo
        load_object_info(info)
      when InterfaceInfo
        load_interface_info(info)
      when ConstantInfo
        load_constant_info(info)
      when UnionInfo
        load_union_info(info)
      end
    end

load_interface_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 558
    def load_interface_info(info)
      interface_module =
        self.class.define_interface(info.gtype,
                                    rubyish_class_name(info),
                                    @base_module)
      load_methods(info, interface_module)
    end

load_method_info(info, klass, method_name) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 501
    def load_method_info(info, klass, method_name)
      define_method(info, klass, method_name)
      define_equal_style_setter(info, klass, method_name)
    end

load_method_infos(infos, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 494
    def load_method_infos(infos, klass)
      infos.each do |info|
        method_name = rubyish_method_name(info)
        load_method_info(info, klass, method_name)
      end
    end

load_methods(info, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 259
    def load_methods(info, klass)
      grouped_methods = info.methods.group_by do |method_info|
        method_info.class
      end
      grouped_methods.each do |method_info_class, method_infos|
        next if method_infos.empty?
        case method_infos.first
        when ConstructorInfo
          load_constructor_infos(method_infos, klass)
        when MethodInfo
          load_method_infos(method_infos, klass)
        when FunctionInfo
          load_function_infos(method_infos, klass)
        else
          raise "TODO: #{method_info_class}"
        end
      end
      if klass.method_defined?(:each) and
          klass.instance_method(:each).owner == klass
        klass.include(Enumerable)
      end
    end

load_object_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 217
    def load_object_info(info)
      klass = self.class.define_class(info.gtype,
                                      rubyish_class_name(info),
                                      @base_module)
      load_fields(info, klass)
      load_methods(info, klass)
    end

load_struct_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 129
    def load_struct_info(info)
      case info.name
      when /Class\z/
        base_class_name = rubyish_class_name(info)
        method_infos = info.methods.find_all do |method_info|
          method_info.is_a?(MethodInfo)
        end
        unless methods.empty?
          base_class = @base_module.const_get(base_class_name)
          load_method_infos(method_infos, base_class.singleton_class)
        end
      else
        return if info.gtype_struct?

        define_struct(info)
      end
    end

load_union_info(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 574
    def load_union_info(info)
      klass = self.class.define_class(info.gtype, info.name, @base_module)
      load_fields(info, klass)
      load_methods(info, klass)
    end

match_argument?(arg_info, argument) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 347
    def match_argument?(arg_info, argument)
      type = arg_info.type
      case type.tag
      when TypeTag::BOOLEAN
        argument == true or argument == false
      when TypeTag::INT8,
           TypeTag::UINT8,
           TypeTag::INT16,
           TypeTag::UINT16,
           TypeTag::INT32,
           TypeTag::UINT32,
           TypeTag::INT64,
           TypeTag::UINT64,
           TypeTag::FLOAT,
           TypeTag::DOUBLE
        argument.is_a?(Numeric)
      when TypeTag::GTYPE
        argument.is_a?(GLib::Type)
      when TypeTag::UTF8
        argument.is_a?(String)
      when TypeTag::FILENAME
        argument.is_a?(String)
      when TypeTag::ARRAY
        argument.is_a?(Array)
      when TypeTag::INTERFACE
        interface = type.interface
        case interface.type
        when InfoType::STRUCT
          match_argument_interface_struct?(arg_info, interface, argument)
        when InfoType::OBJECT,
             InfoType::INTERFACE,
             InfoType::FLAGS,
             InfoType::ENUM
          argument.is_a?(interface.gtype.to_class)
        else
          # TODO
          false
        end
      when TypeTag::GLIST,
           TypeTag::GSLIST
        argument.is_a?(Array)
      else
        # TODO
        false
      end
    end

match_argument_interface_struct?(arg_info, interface, argument) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 394
    def match_argument_interface_struct?(arg_info, interface, argument)
      gtype = interface.gtype
      case gtype.name
      when "void"
        # TODO
        false
      when "CairoSurface"
        if Object.const_defined?(:Cairo)
          argument.is_a?(Cairo::Surface)
        else
          false
        end
      else
        argument.is_a?(gtype.to_class)
      end
    end

post_load(repository, namespace) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 52
    def post_load(repository, namespace)
    end

pre_load(repository, namespace) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 49
    def pre_load(repository, namespace)
    end

remove_existing_method(klass, method_name) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 506
    def remove_existing_method(klass, method_name)
      return unless klass.method_defined?(method_name)
      return unless klass.instance_method(method_name).owner == klass
      klass.__send__(:remove_method, method_name)
    end

rubyish_class_name(info) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 475
    def rubyish_class_name(info)
      name = info.name
      case info
      when StructInfo
        case name
        when /Class\z/
          $PREMATCH
        else
          name
        end
      else
        name
      end
    end

rubyish_method_name(function_info, options={}) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 411
    def rubyish_method_name(function_info, options={})
      name = function_info.name
      if options[:prefix]
        name = name.gsub(/\A#{Regexp.escape(options[:prefix])}/, "")
      end
      return_type = function_info.return_type
      return_type_tag = return_type.tag
      case return_type_tag
      when TypeTag::BOOLEAN
        case name
        when "equal"
          if function_info.n_in_args == 1
            "=="
          else
            name
          end
        when /\A(?:is|get_is|can_be)_/
          "#{$POSTMATCH}?"
        when /\Aget_/
          if function_info.n_in_args.zero?
            if function_info.n_out_args.zero?
              "#{$POSTMATCH}?"
            else
              $POSTMATCH
            end
          else
            name
          end
        when /\A(?:has|use|can|in|on|.*_is)_/
          "#{name}?"
        when "exists"
          "exist?"
        else
          name
        end
      when TypeTag::GLIST, TypeTag::GSLIST
        case name
        when /\A(?:list|get)_/
          if function_info.n_in_args.zero?
            $POSTMATCH
          else
            name
          end
        else
          name
        end
      else
        case name
        when /\Aget_/
          if function_info.n_in_args.zero?
            $POSTMATCH
          else
            name
          end
        when "to_string"
          "to_s"
        when "foreach"
          "each"
        else
          name
        end
      end
    end

should_unlock_gvl?(function_info, klass) click to toggle source

    # File lib/gobject-introspection/loader.rb, line 490
    def should_unlock_gvl?(function_info, klass)
      false
    end