require 'gir_ffi/builders/argument_builder' require 'gir_ffi/variable_name_generator' require 'gir_ffi/field_argument_info' module GirFFI module Builders # Creates field getter and setter code for a given IFieldInfo. class FieldBuilder # Convertor for fields for field getters. Used when building getter # methods. class GetterArgumentBuilder < BaseArgumentBuilder def initialize(var_gen, field_argument_info, field_info, options = {}) super(var_gen, field_argument_info) @field_info = field_info @length_arg = options.fetch(:length_argument) { NullArgumentBuilder.new } end def pre_conversion [ "#{field_ptr} = @struct.to_ptr + #{field_offset}", "#{typed_ptr} = GirFFI::InOutPointer.new(#{field_type_tag}, #{field_ptr})", "#{bare_value} = #{typed_ptr}.to_value" ] end def capture_variable_name nil end def post_converted_name @post_converted_name ||= if has_post_conversion? new_variable else bare_value end end def return_value_name post_converted_name end def post_conversion if has_post_conversion? ["#{post_converted_name} = #{post_convertor.conversion}"] else [] end end private def field_offset @field_info.offset end def field_ptr @field_ptr ||= @var_gen.new_var end def typed_ptr @typed_ptr ||= @var_gen.new_var end def bare_value @bare_value ||= @var_gen.new_var end def field_type_tag @field_type_tag ||= @field_info.field_type.tag_or_class.inspect end def field_type @field_type ||= @field_info.field_type end def has_post_conversion? type_info.needs_c_to_ruby_conversion_for_functions? end def post_convertor @post_convertor ||= CToRubyConvertor.new(type_info, bare_value, length_arg.post_converted_name) end end # Class to represent argument info for the argument of a getter method. # Implements the necessary parts of IArgumentInfo's interface. class GetterArgumentInfo attr_reader :name, :argument_type def initialize(name, type) @name = name @argument_type = type end def closure -1 end def direction :out end def ownership_transfer :everything end def caller_allocates? false end def skip? false end end # Builder for field getters class GetterBuilder def initialize(info) @info = info end def method_definition template.method_definition end def singleton_method? false end def method_name @info.name end def method_arguments [] end def preparation [] end def invocation nil end def result [getter_argument_builder.return_value_name] end private def var_gen @var_gen ||= VariableNameGenerator.new end def template @template ||= MethodTemplate.new(self, argument_builders) end def argument_builders @argument_builders ||= ArgumentBuilderCollection.new( NullReturnValueBuilder.new, [getter_argument_builder, length_argument_builder]) end def getter_argument_builder @getter_argument_builder ||= GetterArgumentBuilder.new(var_gen, field_argument_info, @info, length_argument: length_argument_builder) end def length_argument_builder @length_argument_builder ||= if array_length_field GetterArgumentBuilder.new(var_gen, length_argument_info, array_length_field) else NullArgumentBuilder.new end end def array_length_field @info.related_array_length_field end def length_argument_info @length_argument_info ||= GetterArgumentInfo.new 'length', array_length_field.field_type end def field_offset @info.offset end def field_type_tag @field_type_tag ||= @info.field_type.tag_or_class.inspect end def field_type @field_type ||= @info.field_type end def field_argument_info @field_argument_info ||= GetterArgumentInfo.new 'value', field_type end end attr_reader :info def initialize(field_info) @info = field_info end def build setup_getter setup_setter end def setup_getter container_class.class_eval getter_def unless container_defines_getter_method? end def container_defines_getter_method? container_info.find_instance_method info.name end def setup_setter container_class.class_eval setter_def if info.writable? end def getter_def getter_builder = GetterBuilder.new(info) getter_builder.method_definition end # TODO: Use MethodTemplate def setter_def builder = setter_builder field_ptr = builder.new_variable typed_ptr = builder.new_variable <<-CODE.reset_indentation def #{info.name}= #{builder.method_argument_name} #{field_ptr} = @struct.to_ptr + #{info.offset} #{typed_ptr} = GirFFI::InOutPointer.new(#{field_type_tag}, #{field_ptr}) #{builder.pre_conversion.join("\n ")} #{typed_ptr}.set_value #{builder.call_argument_name} end CODE end private def field_type_tag @field_type_tag ||= info.field_type.tag_or_class.inspect end def container_class @container_class ||= container_module.const_get(container_info.safe_name) end def container_module @container_module ||= Object.const_get(container_info.safe_namespace) end def container_info @container_info ||= info.container end def field_type @field_type ||= @info.field_type end def field_argument_info @field_argument_info ||= FieldArgumentInfo.new 'value', field_type end def setter_builder @setter_builder ||= ArgumentBuilder.new(VariableNameGenerator.new, field_argument_info) end end end end