# -*- coding: utf-8 -*- require 'pathname' require 'rexml/document' require 'set' require 'bridgesupportparser.so' module Bridgesupportparser def self.add_blockattrs(hash) @blockattrs = @blockattrs0.nil? ? hash : @blockattrs0.merge(hash) end def self.blockattr?(k) @blockattrs ||= @blockattrs0 || {} @blockattrs.has_key?(k) end # This lambda (returned by Bridgesupportparser::custom_recursive_merge_64) # can be used to merge 64-bit attributes into 32-bit attributes, when # passed to Parser#set_custom_recursive_merge. @custom_recursive_merge_64 = lambda do |k, a0, a1| m = k.to_s.match(/^(.*)64$/) if m k0 = m[1].to_sym return if a0.include?(k0) && a0[k0].eql?(a1[k]) end a0[k] = a1[k] end def self.custom_recursive_merge_64 @custom_recursive_merge_64 end def self.rename_attrs64 { :_ctype => :_ctype64, :declared_type => :declared_type64, :sel_of_type => :sel_of_type64, :type => :type64, :value => :value64, } end class MergingArray < Array attr_reader :name def initialize(name = 'MergingArray', *args) super(*args) @name = name end def []=(n, v) if self[n].nil? super else if self[n].respond_to?(:recursive_merge!) self[n].recursive_merge!(v) else if !self[n].eql?(v) warn "MergingArray:#{@name}[#{n}]: #{self[n]} => #{v}" super end end end self[n] end def recursive_merge!(ary) (self.length >= ary.length ? self : ary).each_index do |i| self[i] = ary[i] unless ary[i].nil? end end end class MergingHash < Hash attr_reader :name def initialize(name = 'MergingHash', *args) super(*args) @name = name end def []=(k, v) if self.include?(k) if self[k].respond_to?(:recursive_merge!) #print "MergingHash: k=#{k} v=#{v.inspect} #{self[k].inspect}" #DEBUG self[k].recursive_merge!(v) #puts " => #{self[k].inspect}" #DEBUG else if !self[k].eql?(v) warn "MergingHash:#{@name}[#{k}]: #{self[k]} => #{v}" super end end else super end self[k] end def recursive_merge!(m) m.each { |k, v| self[k] = v } end end class MergingSequence < MergingArray attr_reader :name def initialize(name, *argv) super @index = 0 end def <<(a) a[:_index] = @index super @index += 1 end end class MergingSet < Set alias_method :orig_add, :add attr_reader :name def initialize(name = 'MergingSet', *args) super(*args) @name = name end def add(x) found = self.any? do |s| if s.eql?(x) s.recursive_merge!(x) if s.respond_to?(:recursive_merge!) true else false end end self.orig_add(x) if !found end def recursive_merge!(enum) enum.each { |e| add(e) } end end class Base # abstract attr_reader :attrs class << self attr_reader :attr_transform, :element_name def attrmap @attrmap ||= (@attrmap0 || {}) end def blockattr?(k) @blockattrs ||= @blockattrs0 || {} return true if Bridgesupportparser.blockattr?(k) @blockattrs.has_key?(k) end # By default, attributes are blocked if their names begin with # underscore, or if the blockattr? method returns true for that key. # The set_attr_transform method provides a more general approach # to determining what attributes will be displayed. It is passed # a Proc instance, and this Proc will be passed two argument. The # first argument is the attribute hash to process, while the second # argument is lambda that given a key, will call the class-specific # blockattr? method (which can be ignored if unneeded). The Proc # instance should return a new hash (or array of arrays of key/value # pairs) that will substitute for the original attribute hash. If # the return is nil or an empty hash/array, no xml will be generate. def set_attr_transform(t) @attr_transform = t end end @@valmap = { 'false' => false, 'true' => true, } def self.add_blockattrs(hash) @blockattrs = @blockattrs0.nil? ? hash : @blockattrs0.merge(hash) end def initialize(parser, name) @parser = parser @_name = "<#{self.class}>#{(name and !name.empty?) ? name : ''}" @attrs = MergingHash.new(@_name) @parser.add_attrs(@attrs) self[:name] = name if name and !name.empty? end def initialize_copy(orig) @attrs = orig.attrs.dup @parser.add_attrs(@attrs) end # default accessors (mutators not supported) def method_missing(sym) self[sym] end def [](k) @attrs[k] end def []=(k, v) if self.class.attrmap.include?(k) key, val = self.class.attrmap[k].call(k, v) else key, val = k, v end val = @@valmap.fetch(val, val); @attrs[key] = val unless val.nil? end def <=>(x) self[:name] <=> x[:name] end def delete(k) @attrs.delete(k) end def each(&block) if block @attrs.each &block end end def element attr_transform = self.class.attr_transform if attr_transform.nil? attrs = @attrs.select { |k, v| k.to_s[0] != ?_ && !self.class.blockattr?(k)} else klass = self.class attrs = attr_transform.call(@attrs, lambda { |a| klass.blockattr?(a) }) end #puts "Base: #{self.name} attrs=#{attrs.inspect}" #DEBUG return nil if attrs.nil? || attrs.empty? e = REXML::Element.new(self.class.element_name) attrs.each { |k, v| e.attributes[k.to_s] = v } e end def eql?(o) self[:name] == o[:name] end def hash self[:name].hash end def recursive_merge!(m) custom_recursive_merge = @parser.custom_recursive_merge #puts "Bridgesupportparser::Base.recursive_merge!: #{@attrs.inspect} #{m.attrs.inspect} custom_recursive_merge=#{custom_recursive_merge.inspect}" #DEBUG if custom_recursive_merge.nil? @attrs.recursive_merge!(m.attrs) else m.attrs.each_key { |k| custom_recursive_merge.call(k, @attrs, m.attrs) } end end def type # avoid deprecation warning for Object#type self[:type] end end class CFTypeInfo < Base @element_name = 'cftype' def initialize(parser, name, enc, attrs = nil, func = nil) super(parser, name) self[:type] = enc if enc and !enc.empty? attrs.each { |k, v| self[k] = v } if attrs self[:gettypeid_func] = func if func and !func.empty? end end class FunctionAliasInfo < Base @element_name = 'function_alias' def initialize(parser, name, orig) super(parser, name) self[:original] = orig end end class OpaqueInfo < Base @element_name = 'opaque' def initialize(parser, name, enc) super(parser, name) self[:type] = enc if enc and !enc.empty? end end class ValueInfo < Base # abstract @element_name = 'enum' def initialize(parser, name, value) super(parser, name) self[:value] = value unless value.nil? end end class EnumInfo < ValueInfo @element_name = 'enum' end class NumberInfo < ValueInfo @element_name = 'enum' end class NumberFuncCallInfo < ValueInfo @element_name = 'enum' def initialize(parser, name) super(parser, name, nil) end end class StringInfo < ValueInfo @element_name = 'string_constant' def initialize(parser, name, value, nsstring = false) # make sure the string is UTF-8. If not, assume it is MacRoman # and convert to UTF-8 if RUBY_VERSION < "1.9" require 'iconv' @@iconv ||= Iconv.new('UTF-8', 'MACROMAN') begin value.unpack('U*') # throws exception if not UTF-8 rescue value = @@iconv.iconv(value) end else begin value.encode!('UTF-8') rescue value.encode!('UTF-8', 'MACROMAN') end end super(parser, name, value) self[:nsstring] = true if nsstring end end class VarInfoBase < Base #abstract def initialize(parser, name, type, enc, attrs = nil, funcptr = nil) super(parser, name) if type self[:_ctype] = type t = type.gsub( /\[[^\]]*\]/, '*' ) re = /\b(__)?const\b/ if t.match(re) # don't set :const if the const is only part of the arguments # to a function pointer self[:const] = true if t.sub(/\(.*/, '').match(re) t.gsub!(re, '') end t.gsub!(/<[^>]*>/, '') t.gsub!(/\b(in|out|inout|oneway|const)\b/, '') t.gsub!(/\b(__private_extern__|restrict)\b/, '') t.strip! t.squeeze!(' ') t.sub!(/\s+(\*+)$/, '\1') raise "empty type (was '#{type}')" if t.empty? self[:declared_type] = t end #self[:type] = enc if enc if enc if enc =~ /^\^*\{\?=/ # An unnamed structure typedef. Use the typedef name # from declared_type. td = self[:declared_type].gsub(/[^\w]/, '') enc.sub!(/^(\^*)\{\?=/, "\\1{_#{td}=") self[:_type_override] = true elsif t case t when /^(BOOL|Boolean)$/ # the encoding should already be 'B' (custom header files) self[:_type_override] = true when /^(BOOL|Boolean)\s*\*$/ # the encoding should already be '^B' (custom header files) self[:_type_override] = true end end self[:type] = enc end attrs.each { |k, v| self[k] = v } if attrs if funcptr @funcptr = funcptr self[:function_pointer] = true end end def element e = super return nil if e.nil? if function_pointer? f = @funcptr.element #puts "VarInfo: #{self.name} funcptr=#{@funcptr} f=#{f.inspect}" #DEBUG f.each_element { |el| e.add_element(el) } if f end e end def function_pointer @funcptr end def function_pointer? self[:function_pointer] end def recursive_merge!(m) super if function_pointer? and !@funcptr.nil? @funcptr.recursive_merge!(m.function_pointer) if m.function_pointer? elsif m.function_pointer? @funcptr = m.function_pointer end end def pointer_type? self.type && self.type[0] == ?^ end end class VarInfo < VarInfoBase @attrmap0 = { :nonnull => lambda {|k, v| [:null_accepted, !v]}, } @element_name = 'constant' def initialize(parser, name, type, enc, attrs = nil, funcptr = nil) super parser.all_varinfos << self end end class ArgInfo < VarInfo @attrmap0 = { :nonnull => lambda {|k, v| [:null_accepted, !v]}, } @element_name = 'arg' end class FieldInfo < VarInfoBase attr_accessor :resolved @attrmap0 = { :nonnull => lambda {|k, v| [:null_accepted, !v]}, } @element_name = 'field' end class RetvalInfo < VarInfo @attrmap0 = { :nonnull => lambda {|k, v| [:null_accepted, !v]}, } @element_name = 'retval' end class RetvalPtrInfo < VarInfo @attrmap0 = { :nonnull => lambda {|k, v| [:null_accepted, !v]}, } @element_name = 'retval' end class ObjCArgInfo < ArgInfo @attrmap0 = { :in => lambda {|k, v| [:type_modifier, "n"]}, :_index => lambda {|k, v| [:index, v]}, :inout => lambda {|k, v| [:type_modifier, "N"]}, :nonnull => lambda {|k, v| [:null_accepted, !v]}, :out => lambda {|k, v| [:type_modifier, "o"]}, } @element_name = 'arg' def initialize(parser, name, type, enc, mod, attrs = nil, funcptr = nil) super(parser, name, type, enc, attrs, funcptr) if mod self[:type_modifier] = mod self[:_override] = true end end end class ObjCRetvalInfo < VarInfo @attrmap0 = { :nonnull => lambda {|k, v| [:null_accepted, !v]}, } @element_name = 'retval' end class CallableInfo < Base # abstract attr_reader :argc, :args, :ret def initialize(parser, name, ret, args, attrs, variadic) super(parser, name) @ret = ret if ret @args = args if args @argc = @args.size if args self[:variadic] = true if variadic return unless attrs attrs.each do |k, v| prefix, key = k.to_s.split(':', 2) if(!key.nil?) case prefix when 'return_value' @ret[key.intern] = v end next end case k when :format which, str, first = v.split(',') arg = @args[str.to_i - 1] arg[:printf_format] = true unless arg.nil? when :nonnull v.split(',').each do |n| arg = @args[n.to_i] arg[:null_accepted] = false unless arg.nil? end else self[k] = v end end end def each_argument(&block) if @args && block @args.each &block end end def recursive_merge!(m) super @ret.recursive_merge!(m.ret) if m.ret if @argc < m.argc warn "CallableInfo::recursive_merge!:#{@_name}:argc: #{@argc} => #{m.argc}" @argc = m.argc end @args.recursive_merge!(m.args) if m.args end def variadic? self[:variadic] == true end end class FuncInfo < CallableInfo @element_name = 'function' def initialize(parser, name, ret, args, attrs, variadic, inline=false) super(parser, name, ret, args, attrs, variadic) self[:name] = name if name and !name.empty? self[:inline] = true if inline end def <=>(x) self[:name] <=> x[:name] end # Split dylib_wrapper() into 32 and 64-bit handlers. The new # dylib_wrapper() now adds #ifdef __LP64__/#else/#endif if both # 32 and 64-bit are enabled. The 32-bit handler returns the empty # string if the return value or any argment doesn't have _ctype # (meaning the inline routine was only defined in 64-bit). The # 64-bit handler uses _ctype64 if available (meaning the 64-bit # prototype is different from the 32-bit one). def _dylib_wrapper32(prefix = '_dw_') return '' if @ret._ctype.nil? lines = [] args = [] targs = [] i = 0 @args.each do |a| return '' if a._ctype.nil? ai = "_a#{i}" args << ai # to be compatible with new clang change of float * to float [], in which case, # it cannot generate dylib #targs << "#{a._ctype} #{ai}" targs << "#{a._ctype.gsub(/\[.*\]/, '*')} #{ai}" i += 1 end name = "#{prefix}#{self[:name]}" proto = "#{@ret._ctype} #{name}(#{targs.join(', ')})" lines << "#{proto} __asm(\"_#{self[:name]}\");" lines << "#{proto} {" cline = " " cline << "return " unless @ret.type == 'v' cline << "#{self[:name]}(#{args.join(', ')});" lines << cline lines << "}\n" lines.join("\n") end def _dylib_wrapper64(prefix = '_dw_') lines = [] args = [] targs = [] i = 0 @args.each do |a| ai = "_a#{i}" args << ai # to be compatible with new clang change of float * to float [], in which case, # it cannot generate dylib #targs << "#{a._ctype64.nil? ? a._ctype : a._ctype64} #{ai}" targs << "#{(a._ctype64.nil? ? a._ctype : a._ctype64).gsub(/\[.*\]/, '*')} #{ai}" i += 1 end name = "#{prefix}#{self[:name]}" proto = "#{@ret._ctype64.nil? ? @ret._ctype : @ret._ctype64} #{name}(#{targs.join(', ')})" lines << "#{proto} __asm(\"_#{self[:name]}\");" lines << "#{proto} {" cline = " " cline << "return " unless @ret.type == 'v' cline << "#{self[:name]}(#{args.join(', ')});" lines << cline lines << "}\n" lines.join("\n") end def dylib_wrapper(enable_32, enable_64, prefix = '_dw_') pre = mid = post = '' if enable_32 && enable_64 pre = "#ifdef __LP64__\n" mid = "#else\n" post = "#endif\n" end wrapper = '' wrapper << pre wrapper << _dylib_wrapper64(prefix) if enable_64 wrapper << mid wrapper << _dylib_wrapper32(prefix) if enable_32 wrapper << post end def element # Function pointers may have no attributes at all, so could get # optimized away. Since the element is only use to hold the # arguments to be passed to another element, we just create a # dummy element, if needed e = super || REXML::Element.new('DUMMY') @args.each { |a| e.add_element(a.element) } rete = @ret.element e.add_element(rete) unless rete.nil? e end def eql?(o) self[:name] == o[:name] end def hash self[:name].hash end def inline? self[:inline] == true end end class MethodInfo < CallableInfo attr_reader :seltype @arg_select = nil @ret_select = nil class << self attr_reader :arg_select, :ret_select def set_arg_select(p) @arg_select = p end def set_ret_select(p) @ret_select = p end end @attrmap0 = { :name => lambda {|k, v| [:selector, v]}, } @element_name = 'method' def initialize(parser, selector, menc, ret, args, attrs, is_class, variadic) super(parser, selector, ret, args, attrs, variadic) self[:class_method] = true if is_class @seltype = menc end def <=>(o) cmp = self[:selector] <=> o[:selector] return cmp unless cmp == 0 return 0 if self[:class_method] == o[:class_method] return -1 if self[:class_method] return 1 end def class_method? self[:class_method] == true end def element arg_select = self.class.arg_select if arg_select.nil? args = @args else args = @args.select { |a| arg_select.call(a) } end ret_select = self.class.ret_select if ret_select.nil? ret = @ret else ret = @ret if ret_select.call(@ret) end eargs = args.collect { |a| a.element }.compact #puts "args.empty?=#{args.empty?} ret.nil?=#{ret.nil?}" #DEBUG return nil if eargs.empty? && ret.nil? && self[:type].nil? && !self[:_override] e = super eargs.each { |a| e.add_element(a) } if ret rete = ret.element e.add_element(rete) if rete end e end def eql?(o) self[:selector] == o[:selector] && self[:class_method] == o[:class_method] end def hash self[:selector].hash end end class ObjCContainerInfo # abstract attr_reader :methods, :name, :protocols class << self attr_reader :element_name end def initialize(parser, name, methods, protocols) @parser = parser @name = name @methods = methods @protocols = protocols end def concat(a) @methods.recursive_merge!(a.methods) end def dup_methods methods = Bridgesupportparser::MergingSet.new(@methods.name) @methods.each { |m| methods << m.dup } @methods = methods end def each_method(&block) if @methods && block @methods.each &block end end def each_protocol(&block) if @protocols && block @protocols.each &block end end def element methods = @methods.sort.collect { |m| m.element } .compact return nil if methods.empty? e = REXML::Element.new(self.class.element_name) e.attributes['name'] = @name methods.each { |m| e.add_element(m) } e end def recursive_merge!(m) @methods.recursive_merge!(m.methods) if @protocols @protocols.merge(m.protocols) if m.protocols elsif m.protocols @protocols = m.protocols end end end class ObjCCategoryInfo < ObjCContainerInfo attr_reader :klass @element_name = 'informal_protocol' def initialize(parser, name, klass, methods=Bridgesupportparser::MergingSet.new("category #{name} for #{klass}"), protocols=Set.new) super(parser, name, methods, protocols) @klass = klass end end class ObjCInterfaceInfo < ObjCContainerInfo @element_name = 'class' end class ObjCProtocolInfo < ObjCContainerInfo end class StructInfo < Base attr_accessor :resolved attr_reader :fields class << self attr_accessor :block_fields end @block_fields = false @element_name = 'struct' def initialize(parser, name, encoding, fields) super(parser, name) self[:name] = name if name and !name.empty? if encoding and !encoding.empty? if encoding =~ /^\{\?=/ enc = encoding.dup #DEBUG # An unnamed structure typedef. Use the StructInfo name # for compatibility with previous gen_bridge_metadata. encoding.sub!(/^\{\?=/, "{_#{name}=") #puts "StructInfo: #{name} \"#{enc}\" => \"#{encoding}\"" #DEBUG end self[:type] = encoding end @fields = fields end def each_field(&block) if @fields && block @fields.each &block end end def element e = super return nil if e.nil? if !self.class.block_fields @fields.each { |f| e.add_element(f.element) } end e end def recursive_merge!(m) super @fields.recursive_merge!(m.fields) if m.fields end def rename(n) self.delete(:name) # to avoid recursive_merge! warnings self[:name] = n end end FOUNDATIONSPECIAL = { # Special case: may be split off into # and '/usr/include/objc/NSObject.h' => [true, true], '/usr/include/objc/NSObjCRuntime.h' => [true, true], } SPECIALPATHDATA = { 'Foundation.framework' => FOUNDATIONSPECIAL, } class Parser attr_reader :all_attrs, :all_categories, :all_cftypes, :all_enums, :all_funcs, :all_func_aliases, :all_informal_protocols, :all_interfaces, :all_macronumbers, :all_macronumberfunccalls, :all_macrostrings, :all_opaques, :all_protocols, :all_structs, :all_varinfos, :all_vars, :custom_recursive_merge, :every_cftype, :special_method_encodings, :special_type_encodings # These values must match those in bridgesupport.cpp CONTENT = '__9_OxQk__4__c_0_n_T_3_n_t_' CONTENTEND = '__9_OxQk__4__3_0_F_' CONTENTMETHOD = CONTENT + '_m_3_T_h_0_d_' CONTENTTYPE = CONTENT + '_t_Y_p_3_' # There doesn't seem to be a foolproof way to determine if a typedef # whose name ends in "Ref" is a CFType or not. So assuming that each # system framework is consistent, we create a list (stored in a hash) # for framework names where such typedefs are not CFTypes. DISABLE_CFTYPES = { 'CarbonCore' => true, 'JavaScriptCore' => true, } # This regexp extracts the (bare) framework name FRAMEWORK_RE = Regexp.new('([^/]*)\.framework/') def initialize(incs, paths, special_methods, special_types, defines = nil, incdirs = nil, defaultincs = nil, sysroot = '/') @all_attrs = [] @parsepaths = incs puts "incs:\n#{incs.join("\n")}" if $DEBUG #DEBUG puts "paths:\n#{paths.join("\n")}" if $DEBUG #DEBUG if $DEBUG #DEBUG puts "incdirs:" incdirs.each { |i| puts "#{i[3..-1]} #{i[0,1]} #{i[1,1]} #{i[2,1]}" } end #DEBUG a = [] @special_methods = special_methods @special_types = special_types unless @special_methods.nil? @special_methods.each_index do |i| t = @special_methods[i].sub(/^\s*[-+]/, '').sub(/;\s*$/, '').strip a << "@interface #{CONTENTMETHOD}_#{i}\n" \ + "- #{t};\n" \ + "@end" end @special_method_encodings = [] end unless @special_types.nil? @special_types.each_index { |i| a << "#{@special_types[i]} #{CONTENTTYPE}_#{i};\n" } @special_type_encodings = [] end @parsecontent = a.join("\n") + "\n" #puts "parsecontent:\n#{@parsecontent}" if $DEBUG #@parsedefines = ['__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__=1070'] #@parsedefines.concat(defines) unless defines.nil? @parsedefines = defines; @parseincdirs = incdirs @parsedefaultincs = defaultincs @parsesysroot = sysroot # Allow header files in the directory hierarchies. For framework # directories, allow sub-framework headers. The basename of the # header must also match. @parse_select = Set.new @parse_select_basenames = {} select = Set.new walk = nil paths.each do |p| # If p is a relative path, walk through incdirs to find the # full path. pn = Pathname.new(p) if pn.relative? if walk.nil? # This algorithm is simplistic, and doesn't reorder # based on whether incdirs entries are Angled, Quoted # or System, or are marked isFramework. If that is # ever needed, this code will need to be extended. walk = incdirs.collect { |i| i[3..-1] } walk << '/usr/local/include' walk << '/usr/include' walk.concat(defaultincs) walk.map! { |i| i.start_with?('/usr/', '/System/') ? File.join(sysroot, i) : i } unless sysroot == '/' end pp = nil unless walk.any? do |i| pp = File.join(i, p) File.exists?(pp) end then raise "Can't find \"#{p}\"" end p = pp pn = Pathname.new(p) end select << pn.realpath.parent.to_s @parse_select_basenames[File.basename(p)] = true end # For frameworks, remove everything except the framework name select.each { |p| @parse_select << p.sub(%r{.*/([^/]*\.framework)/.*}, "\\1") } #puts "@parse_select:\n#{@parse_select.to_a.join("\n")}" #DEBUG #puts "@parse_select_basenames:\n#{@parse_select_basenames.keys.join("\n")}" #DEBUG @parsepathcache = {CONTENT => [true, true]} # Special cases: If a framework header splits off into header files # that are not co-located, we can add entries to SPECIALPATHDATA, # which get processed into @specialpathcache and are triggered in # inParseTree(). @specialpathcache = {} SPECIALPATHDATA.each do |k, v| if @parse_select.include?(k) v.each do |p, a| p = sysroot + p; @specialpathcache[Pathname.new(p).realpath.to_s] = a if File.exists?(p) end end end @all_categories = {} @all_cftypes = Bridgesupportparser::MergingHash.new('all_cftypes') @all_enums = Bridgesupportparser::MergingHash.new('all_enums') @all_funcs = Bridgesupportparser::MergingHash.new('all_funcs') @all_func_aliases = Bridgesupportparser::MergingHash.new('all_func_aliases') @all_informal_protocols = Bridgesupportparser::MergingHash.new('all_informal_protocols') @all_interfaces = Bridgesupportparser::MergingHash.new('all_interfaces') @all_macronumbers = Bridgesupportparser::MergingHash.new('all_macronumbers') @all_macrostrings = Bridgesupportparser::MergingHash.new('all_macrostrings') @all_macronumberfunccalls = Bridgesupportparser::MergingHash.new('all_macronumberfunccalls') @all_opaques = Bridgesupportparser::MergingHash.new('all_opaques') @all_protocols = Bridgesupportparser::MergingHash.new('all_protocols') @all_structs = Bridgesupportparser::MergingHash.new('all_structs') @all_varinfos = [] @all_vars = Bridgesupportparser::MergingHash.new('all_vars') @every_cftype = {} @only_classes = {} end ####### private ####### def __xattr__(x) "__attribute__((__annotate__(\"xattr:#{x}\")))" end # The difference between recursivefunc() and recursivefuncptr() is that # the first is called for functions (proper), and their return values # are instances of RetvalInfo. recursivefunc() calls recursivefuncptr() # if an argument or return value is a function pointer, and its return # value will be an instance of RetvalPtrInfo. recursivefuncptr() may # call itself recursively. def recursivefuncptr(f) return nil if f.nil? func, rettype, retenc, retattrs, retfunc, fattrs, variadic, inline = f.info #puts "#{func} #{rettype} #{retenc} retattrs=#{retattrs.inspect} fattrs=#{fattrs.inspect} retfunc=#{!retfunc.nil?} variadic=#{variadic} inline=#{inline}" #DEBUG ret = Bridgesupportparser::RetvalPtrInfo.new(self, nil, rettype, retenc, retattrs, recursivefuncptr(retfunc)) args = Bridgesupportparser::MergingSequence.new('recursivefuncptr') f.each_argument do |name, type, enc, attrs, funcptr| # While we can generate recursive function pointers, some # BridgeSupport users (like MacRuby) can't deal with them. # So we avoid the recursion for now #args << Bridgesupportparser::ArgInfo.new(self, name, type, enc, attrs, recursivefuncptr(funcptr)) args << Bridgesupportparser::ArgInfo.new(self, name, type, enc, attrs, nil) end Bridgesupportparser::FuncInfo.new(self, func, ret, args, fattrs, variadic, inline) end def recursivefunc(f) return nil if f.nil? func, rettype, retenc, retattrs, retfunc, fattrs, variadic, inline = f.info #puts "#{func} #{rettype} #{retenc} retattrs=#{retattrs.inspect} fattrs=#{fattrs.inspect} retfunc=#{!retfunc.nil?} variadic=#{variadic} inline=#{inline}" #DEBUG ret = Bridgesupportparser::RetvalInfo.new(self, nil, rettype, retenc, retattrs, recursivefuncptr(retfunc)) args = Bridgesupportparser::MergingSequence.new('recursivefunc') f.each_argument do |name, type, enc, attrs, funcptr| args << Bridgesupportparser::ArgInfo.new(self, name, type, enc, attrs, recursivefuncptr(funcptr)) end Bridgesupportparser::FuncInfo.new(self, func, ret, args, fattrs, variadic, inline) end def make_method(m) sel, menc, rettype, retenc, retattrs, retfunc, mattrs, classmeth, variadic = m.info #puts "Make method: sel:#{sel}, menc:#{menc}, rettype: #{rettype}, retenc: #{retenc}" ret = Bridgesupportparser::ObjCRetvalInfo.new(self, nil, rettype, retenc, retattrs, recursivefuncptr(retfunc)) args = Bridgesupportparser::MergingSequence.new("#{sel}.args") m.each_argument do |name, type, enc, mod, attrs, funcptr| args << Bridgesupportparser::ObjCArgInfo.new(self, name, type, enc, mod, attrs, recursivefuncptr(funcptr)) end Bridgesupportparser::MethodInfo.new(self, sel, menc, ret, args, mattrs, classmeth, variadic) end def make_opaque_or_struct(tname, name, decl) if decl sname, senc = decl.info # "sname" is the same as "name" @all_structs[tname] = make_struct(tname, senc, decl) else # promote forward reference to pointer to struct @all_opaques[tname] = Bridgesupportparser::OpaqueInfo.new(self, tname, "^{#{name}=}") end end def make_struct(sname, senc, struct) fields = Bridgesupportparser::MergingSequence.new(sname) struct.each_field do |name, type, enc, attrs, funcptr| fields << Bridgesupportparser::FieldInfo.new(self, name, type, enc, attrs, recursivefuncptr(funcptr)) end Bridgesupportparser::StructInfo.new(self, sname, senc, fields) end def recurse_append_protocol(a, b, seen) b.each_protocol do |p| #puts ">p>#{p} seen=#{seen[p] ? 'yes' : 'no'}" #DEBUG if !seen[p] seen[p] = 1 proto = @all_protocols[p] #proto.methods.sort.each {|m| puts ">p>#{m.selector}"} #DEBUG #puts '>p>----------' #DEBUG if proto.nil? warn "Unknown protocol: #{p} for interface #{a.name}" if $DEBUG else a.concat(proto) recurse_append_protocol(a, proto, seen) end end end end def inParseTree?(path) x = @parsepathcache[path] return x unless x.nil? # Allow header files in the directory hierarchies that match # the basenames. For frameworks, strip leading path components. pr = Pathname.new(path).realpath c = @specialpathcache[pr.to_s] return @parsepathcache[path] = c unless c.nil? p0 = pr.parent.to_s p = p0.sub(%r{.*/([^/]*\.framework/.*)}, "\\1") len = p.length i = @parse_select.any? do |d| dlen = d.length intree = case (dlen <=> len) when 1 false when 0 p == d else p[dlen] == ?/ && p[0...dlen] == d end intree && @parse_select_basenames[File.basename(path)] end # Only consider allowing CF types if the original path was in # /System. c = true if p0 =~ %r{/System/Library/Frameworks/} m = FRAMEWORK_RE.match(p) c = (!m || DISABLE_CFTYPES[m[1]]) ? true : false end @parsepathcache[path] = [i, c] end def splitName(name) idx = name.rindex('_') return name if idx.nil? [name[0,idx], name[(idx+1)..-1]] end def validName(name) !name.nil? and !name.empty? end def _parse(triple) #realp = Pathname.new(path).realpath #dir = File.dirname(realpath(path)) #puts triple #DEBUG Bridgesupportparser::BridgeSupportParser.parse(@parsepaths, @parsecontent, triple, @parsedefines, @parseincdirs, @parsedefaultincs, @parsesysroot, $DEBUG) do |top| next if @only_classes[top.class] intree, disableCFTypes = inParseTree?(top.path) #puts "_parse: name=#{top.info[0]} path=#{top.path} intree=#{intree} disableCFTypes=#{disableCFTypes}" #DEBUG case top when Bridgesupportparser::ATypedef tname, ttype, tattrs = top.info # puts "ATypedef: #{tname} #{ttype} intree=#{intree}" #DEBUG next unless validName(tname) i = 0 top.walk_types do |name, type, decl, attr| case i when 0 case type when 'Struct' break unless intree make_opaque_or_struct(tname, name, decl) break # only direct structs, no pointers to structs when 'Pointer' # do another loop when 'Typedef' if name == 'CFTypeRef' @every_cftype[tname] = 1 break end i = 10 # do case "else" else break end when 1 #puts "Typedef 1: name=#{tname} type=#{type} attrs=#{tattrs.inspect} diaCFT: #{disableCFTypes}" #DEBUG break if type == 'Pointer' if tname =~ /Ref$/ && !disableCFTypes # assume this is a CFType. t_enc = top.encoding # Since libclang doesn't return empty string for unknwon objects, we have to explicitly make it if t_enc == "?" t_enc = nil end @all_cftypes[tname] = Bridgesupportparser::CFTypeInfo.new(self, tname, t_enc, tattrs) if intree @every_cftype[tname] = 1 elsif type == 'Struct' next unless intree make_opaque_or_struct(tname, name, decl) end break else #puts "ATypedef:walk_types #{name} #{type}" #DEBUG case type when 'Struct' break unless intree make_opaque_or_struct(tname, name, decl) break # only direct structs, no pointers to structs when 'Typedef' if name == 'CFTypeRef' @every_cftype[tname] = 1 break end else break end end i += 1 end next end next unless intree case top when Bridgesupportparser::AnEnum # enum = top.info # don't need enum name top.each_value { |name, val| @all_enums[name] = Bridgesupportparser::EnumInfo.new(self, name, val) if validName(name) } when Bridgesupportparser::AFunction fname, rettype, retenc, retattrs, retfunc, fattrs, variadic, inline = top.info @all_funcs[fname] = recursivefunc(top) if validName(fname) when Bridgesupportparser::AMacroFunctionAlias faname, val = top.info @all_func_aliases[faname] = Bridgesupportparser::FunctionAliasInfo.new(self, faname, val) if validName(faname) when Bridgesupportparser::AMacroNumber nname, val = top.info next unless validName(nname) @all_macronumbers[nname] = Bridgesupportparser::NumberInfo.new(self, nname, val) if validName(nname) when Bridgesupportparser::AMacroNumberFuncCall nname = top.info next unless validName(nname) @all_macronumberfunccalls[nname] = Bridgesupportparser::NumberFuncCallInfo.new(self, nname) if validName(nname) when Bridgesupportparser::AMacroString sname, val, objcstr = top.info next unless validName(sname) val.sub!(/^"/, '') val.sub!(/"$/, '') @all_macrostrings[sname] = Bridgesupportparser::StringInfo.new(self, sname, val, objcstr) when Bridgesupportparser::AnObjCCategory klass, cname = top.info #puts "*** adding category #{cname} for #{klass}" #next unless validName(cname) # allow any name, including extensions methods = Bridgesupportparser::MergingSet.new("category #{cname} for #{klass}") #top.each_method { |m| methods << make_method(m) } top.each_method do |m| sel2 = m.info methods.delete_if { |m1| m1.selector == sel2[0] && m1.class_method == sel2[7] } methods << make_method(m) end protocols = Set.new top.each_protocol { |p| protocols.add(p) } if klass == 'NSObject' @all_informal_protocols[cname] = Bridgesupportparser::ObjCCategoryInfo.new(self, cname, klass, methods, protocols) else (@all_categories[klass] ||= Bridgesupportparser::MergingHash.new("category #{cname} for #{klass}"))[cname] = Bridgesupportparser::ObjCCategoryInfo.new(self, cname, klass, methods, protocols) end when Bridgesupportparser::AnObjCInterface iname = top.info content, idx = splitName(iname) #puts "AnObjCInterface #{iname} content=#{content} idx=#{idx}" #DEBUG if content == CONTENTMETHOD top.each_method do |m| sel, menc, rettype, retenc, retattrs, retfunc, mattrs, classmeth, variadic = m.info @special_method_encodings[idx.to_i] = menc break end next end next unless validName(iname) && iname != CONTENTEND #puts "*** adding interface #{iname}" #DEBUG methods = Bridgesupportparser::MergingSet.new("interface #{iname}") #top.each_method { |m| methods << make_method(m) } top.each_method do |m| sel2 = m.info methods.delete_if { |m1| m1.sel == sel2[0] && m1.class_method == sel2[7] } methods << make_method(m) end protocols = Set.new top.each_protocol { |p| protocols.add(p) } @all_interfaces[iname] = Bridgesupportparser::ObjCInterfaceInfo.new(self, iname, methods, protocols) when Bridgesupportparser::AnObjCProtocol pname = top.info next unless validName(pname) #puts "*** adding protocol #{pname}" #DEBUG methods = Bridgesupportparser::MergingSet.new("protocol #{pname}") top.each_method { |m| methods << make_method(m) } protocols = Set.new top.each_protocol { |p| protocols.add(p) } @all_protocols[pname] = Bridgesupportparser::ObjCProtocolInfo.new(self, pname, methods, protocols) when Bridgesupportparser::AStruct sname, senc = top.info @all_structs[sname] = make_struct(sname, senc, top) if validName(sname) when Bridgesupportparser::AVar vname, vtype, venc, attrs, funcptr = top.info content, idx = splitName(vname) if content == CONTENTTYPE @special_type_encodings[idx.to_i] = venc next end # We currently can't deal with globals that are function # pointers, so just ignore any #@all_vars[vname] = Bridgesupportparser::VarInfo.new(self, vname, vtype, venc, attrs, recursivefuncptr(funcptr)) if validName(vname) @all_vars[vname] = Bridgesupportparser::VarInfo.new(self, vname, vtype, venc, attrs) if validName(vname) && funcptr.nil? end end end def matchingbrace(str, off) while true off = str.index(/[{}]/, off + 1) if str[off] == ?{ off = matchingbrace(str, off) else break end end off end ###### public ###### def add_attrs(a) @all_attrs << a end def addXML(root) @all_structs.sort.each do |k, struct| e = struct.element root.add_element(e) unless e.nil? end @all_cftypes.sort.each do |k, cftype| e = cftype.element root.add_element(e) unless e.nil? end @all_opaques.sort.each do |k, opaque| e = opaque.element root.add_element(e) unless e.nil? end @all_vars.sort.each do |k, var| e = var.element root.add_element(e) unless e.nil? end @all_macrostrings.sort.each do |k, str| e = str.element root.add_element(e) unless e.nil? end enums = @all_enums.merge(@all_macronumbers) @all_macronumberfunccalls.each { |k, v| enums[k] = v unless v.value.nil? } enums.sort.each do |k, enum| e = enum.element root.add_element(e) unless e.nil? end @all_funcs.sort.each do |k, func| e = func.element root.add_element(e) unless e.nil? end @all_func_aliases.sort.each do |k, a| e = a.element root.add_element(e) unless e.nil? end @all_interfaces.sort.each do |k, interface| e = interface.element root.add_element(e) unless e.nil? #puts "addXML: not add_element #{k} #{interface.methods.length}" if e.nil? #DEBUG end $bsp_informal_protocols = true @all_informal_protocols.sort.each do |k, ip| e = ip.element root.add_element(e) unless e.nil? end $bsp_informal_protocols = nil end def each_attrs if block_given? @all_attrs.each { |a| yield a } end end def rename64! rename_attrs(Bridgesupportparser.rename_attrs64) # We need to fix up "string_constant" objects, which only have # "value" attributes (and *not* "value64"). @all_macrostrings.each { |k, v| v[:value] = v.delete(:value64) } end def mergeWith64!(p64) p64.rename_attrs(Bridgesupportparser.rename_attrs64) set_custom_recursive_merge(Bridgesupportparser::custom_recursive_merge_64) recursive_merge!(p64) end def parse(triple) menc, tenc = _parse(triple) # # Merge all category and protocol methods (recursively) # @all_interfaces.each do |name, interf| # #puts ">#{name}" #DEBUG # cseen = {} # pseen = {} # recurse_append_protocol(interf, interf, pseen); # categ = @all_categories[name] # if categ # categ.each_value do |c| # #puts ">c>#{c.name} seen=#{cseen[c.name] ? 'yes' : 'no'}" #DEBUG # if !cseen[c.name] # cseen[c.name] = 1 # #c.methods.sort.each {|m| puts ">c>#{m.selector}"} #DEBUG # #puts '>c>----------' #DEBUG # interf.concat(c) # recurse_append_protocol(interf, c, pseen); # end # end # end # end # Merge categories into interfaces, creating as needed @all_categories.each do |iname, h| interf = (@all_interfaces[iname] ||= Bridgesupportparser::ObjCInterfaceInfo.new(self, iname, Bridgesupportparser::MergingSet.new("interface #{iname}"), Set.new)) h.each_value { |c| interf.concat(c) } end # Create a NSObject interface, if it doesn't already exist nsobjcreated = false nsobj = @all_interfaces['NSObject'] if nsobj.nil? nsobj = Bridgesupportparser::ObjCInterfaceInfo.new(self, 'NSObject', Bridgesupportparser::MergingSet.new("interface NSObject"), Set.new) nsobjcreated = true end # Merge all informal protocol methods into the NSObject interface. @all_informal_protocols.each_value { |p| nsobj.concat(p) } # For formal protocols, to emulate the previous gen_bridge_metadata # behavior, protocol methods get merged into the NSObject interface. # We also create a separate informal protocol with the methods of # the formal protocol. @all_protocols.each_value do |p| nsobj.concat(p) (@all_informal_protocols[p.name] ||= Bridgesupportparser::ObjCCategoryInfo.new(self, p.name, 'NSObject')).concat(p) end @all_interfaces['NSObject'] = nsobj if nsobjcreated && nsobj.methods.length > 0 # Go through the cftypes and for those without the gettypeid_func # attribute, make up the standard name, and check against the # known functions @all_cftypes.each do |name, cftype| if !cftype.gettypeid_func func = name.sub(/Ref$/, 'GetTypeID') #puts "Trying #{func} for #{name}: #{@all_funcs[func]}" #DEBUG if @all_funcs.has_key?(func) cftype[:gettypeid_func] = func elsif func.sub!(/Mutable/, '') cftype[:gettypeid_func] = func if @all_funcs.has_key?(func) end end end # For functions returning a cftype, with "Create" or "Copy" in # the name, add the "already_retained" attribute to return value @all_funcs.each do |name, func| ret = func.ret next unless ret.type != 'v' next if @every_cftype[ret.declared_type].nil? ret[:already_retained] = true if /(Create|Copy)/.match(name) end # Walk through all the type attributes and replace # '^v' with '@' for cftypes each_attrs do |a| dt = a[:declared_type] next if dt.nil? || @every_cftype[dt].nil? if a[:type] == '^v' a.delete(:type) # delete to avoid recursive_merge! warning a[:type] = '@' end end # For each method in each informal protocol, set the method "type" # to the method encoding (we need to call dup_methods, so to # avoid having "type" show up in NSObject methods, which are shared) @all_informal_protocols.each do |name, ip| ip.dup_methods ip.each_method { |m| m[:type] = m.seltype } end # For each structure that has a nested anonymous-structure typedef, # with structure name showing as '?', we replace the '?' with the # type from the corresponding field. We have to make multiple # passes over the structures, until no more changes can be made. # Throw an exception if no changes occurs during a pass. changed = true tagged_structs = {} unresolved_structs = ['dummy'] while unresolved_structs.length > 0 raise "Infinite loop detected due to unresolved structure(s) that aren't resolving: #{unresolved_structs.inspect}" unless changed changed = false unresolved_structs.clear @all_structs.each do |structname, struct| next if struct.resolved #puts "struct #{structname} type=#{struct.type}" #DEBUG unless struct.type.match(/"\^*\{\?/) struct.resolved = true changed = true #puts "struct #{structname} already resolved" #DEBUG next end unresolved = false struct.each_field do |f| next if f.resolved sname = f.declared_type.sub(/\s*\*$/, '') s = @all_structs[sname] if s #puts "field=#{f.name} f.type=#{f.type} sname=#{sname} s.type=#{s.type}" #DEBUG if s.resolved #resolvedtype = s.type.gsub(/"[^"]*"/, '') #puts "struct #{structname} field #{f.name} type=\"#{f.type}\" resolvedtype=\"#{resolvedtype}\"" #DEBUG #f.type[f.type.index(/\{/)..-1] = resolvedtype[resolvedtype.index(/\{/)..-1] f.type[f.type.index(/\{/)..-1] = s.type[s.type.index(/\{/)..-1] f.resolved = s.type.sub(/^\^*/, '') changed = true #puts "struct #{structname} field #{f.name} => #{f.type} (#{f.resolved})" #DEBUG elsif m = f.type.match('^\^*\{([^=}]+)') # special case: the field is a (pointer to) # the structure itself (via a typedef) if m[1] == structname f.resolved = true changed = true #puts "struct #{structname} field #{f.name} refers to structure itself: resolved" #DEBUG # special case: if the field is a (pointer to) # typedef to a resolved structure, resolve it elsif fs = @all_structs[m[1]] and fs.resolved f.type[f.type.index(/\{/)..-1] = fs.type[s.type.index(/\{/)..-1] f.resolved = fs.type.sub(/^\^*/, '') changed = true #puts "struct #{structname} field #{f.name} refers to resolved structure #{fs.name}: resolved" #DEBUG else unresolved = true #puts "struct #{structname} field #{f.name}: struct #{s.name} still unresolved" #DEBUG end else unresolved = true #puts "struct #{structname} field #{f.name}: struct #{s.name} still unresolved" #DEBUG end else f.resolved = true changed = true end end if unresolved unresolved_structs << structname #puts "struct #{structname} remains unresolved" #DEBUG else origtype = struct.type.dup struct.each_field do |f| #puts "struct #{structname} field #{f.name} resolved=#{f.resolved}" #DEBUG next if f.resolved == true left = struct.type.index(/"#{f.name}"[^{]*\{/) left = struct.type.index(/\{/, left.nil? ? 0 : left) right = matchingbrace(struct.type, left) #puts ">struct #{structname} field #{f.name} => #{f.resolved}" #DEBUG #puts ">left=#{left} right=#{right} before=#{struct.type}" #DEBUG struct.type[left..right] = f.resolved #puts ">after=#{struct.type}" #DEBUG end if struct.type != origtype tagged_structs[structname] = struct.type.sub(/^\^*/, '').gsub(/"[^"]*"/, '') #puts ">>>Resolved: #{structname}" #DEBUG #puts "\"#{origtype}\" =>" #DEBUG #puts "\"#{struct.type}\"" #DEBUG end struct.resolved = true changed = true #puts "struct #{structname} is now resolved" #DEBUG end end end #puts '=== tagged_structs ==='; tagged_structs.each {|k,v| puts "#{k}: #{v}"} #DEBUG # Now walk through all the VarInfo instances, and replace with # the new tagged structs if tagged_structs.length > 0 @all_varinfos.each do |v| name = v.declared_type.sub(/\s*\*$/, '') t = tagged_structs[name] if t v.type[v.type.index(/\{/)..-1] = t v[:_type_override] = true end end end if $DEBUG #DEBUG puts "@parsepathcache:" #DEBUG @parsepathcache.sort.each { |k, v| puts "#{k} => #{v.inspect}" } #DEBUG end #DEBUG end def recursive_merge!(p) # purge members that occur in 32-bit (self) and not in 64-bit (p) # (@all_categories is a special case) @all_categories.delete_if { |k, v| !p.all_categories.has_key?(k) } @all_categories.each do |k, v| pp = p.all_categories[k] v.delete_if { |kk, vv| !pp.has_key?(kk) } v.recursive_merge!(pp) end @all_cftypes.delete_if { |k, v| !p.all_cftypes.has_key?(k) } @all_cftypes.recursive_merge!(p.all_cftypes) @all_enums.delete_if { |k, v| !p.all_enums.has_key?(k) } @all_enums.recursive_merge!(p.all_enums) @all_funcs.delete_if { |k, v| !p.all_funcs.has_key?(k) } @all_funcs.recursive_merge!(p.all_funcs) @all_func_aliases.delete_if { |k, v| !p.all_func_aliases.has_key?(k) } @all_func_aliases.recursive_merge!(p.all_func_aliases) @all_informal_protocols.delete_if { |k, v| !p.all_informal_protocols.has_key?(k) } @all_informal_protocols.recursive_merge!(p.all_informal_protocols) @all_interfaces.delete_if { |k, v| !p.all_interfaces.has_key?(k) } @all_interfaces.recursive_merge!(p.all_interfaces) @all_macronumbers.delete_if { |k, v| !p.all_macronumbers.has_key?(k) } @all_macronumbers.recursive_merge!(p.all_macronumbers) @all_macrostrings.delete_if { |k, v| !p.all_macrostrings.has_key?(k) } @all_macrostrings.recursive_merge!(p.all_macrostrings) @all_macronumberfunccalls.delete_if { |k, v| !p.all_macronumberfunccalls.has_key?(k) } @all_macronumberfunccalls.recursive_merge!(p.all_macronumberfunccalls) @all_opaques.delete_if { |k, v| !p.all_opaques.has_key?(k) } @all_opaques.recursive_merge!(p.all_opaques) @all_protocols.delete_if { |k, v| !p.all_protocols.has_key?(k) } @all_protocols.recursive_merge!(p.all_protocols) @all_structs.delete_if { |k, v| !p.all_structs.has_key?(k) } @all_structs.recursive_merge!(p.all_structs) @all_vars.delete_if { |k, v| !p.all_vars.has_key?(k) } @all_vars.recursive_merge!(p.all_vars) end def rename_attrs(h) @all_attrs.each do |attrs| h.each { |k, v| attrs[v] = attrs.delete(k) if attrs.include?(k) } end end # By default, we rely on MergingHash's recursive_merge! method # to merge the attributes. If set_custom_recursive_merge is passed # a lambda, that lambda will be responsible for merging each # attribute. The arguments passed to the lambda are: # key, self_attrs, other_attrs # where "key" is the current key symbol to merge, "self_attrs" is # the (MergingHash) attributes we are merging into, and # "other_attrs" is the (MergingHash) attributes we are merging. def set_custom_recursive_merge(m) #puts "set_custom_recursive_merge=#{m.inspect}" #DEBUG @custom_recursive_merge = m end ##################################################### #################### DEBUGGING ###################### ##################################################### def writeXML(file, version, doctype = false, indent = 4) xml = REXML::Document.new xml << REXML::XMLDecl.new xml << REXML::DocType.new(['signatures', 'SYSTEM', '"file://localhost/System/Library/DTDs/BridgeSupport.dtd"']) if doctype xml.add_element('signatures') root = xml.root root.attributes['version'] = version addXML(root) xml.write(file, indent) file.print "\n" end ####### private ####### def dumpargs(func, indent = 0) i = ' ' * indent func.each_argument do |a| puts "#{i}#{a.name}: attrs=#{a.attrs.inspect}" dumpfunc(a.function_pointer, indent + 1) if a.function_pointer? end end def dumpcateg(categ, indent = 0) i = ' ' * indent puts "---------\n" if indent == 0 protolist("#{i}#{categ.klass} (#{categ.name}): protocols=", categ) dumpmeths(categ, indent + 1) end def dumpfields(struct, indent = 0) i = ' ' * indent struct.each_field do |f| puts "#{i}#{f.name}: attrs=#{f.attrs.inspect}" dumpfunc(f.function_pointer, indent + 1) if f.function_pointer? end end def dumpfunc(func, indent = 0) i = ' ' * indent puts "---------\n" if indent == 0 puts "#{i}#{func.name}: retattrs=#{func.ret.attrs.inspect} attrs=#{func.attrs.inspect} variadic=#{func.variadic?} inline=#{func.inline?}" dumpfunc(func.ret.function_pointer, indent + 1) if func.ret.function_pointer? dumpargs(func, indent + 1) end def dumpmeths(container, indent = 0) i = ' ' * indent container.each_method do |meth| puts "#{i}#{meth.selector}: retattrs=#{meth.ret.attrs.inspect} attrs=#{meth.attrs.inspect} class_method=#{meth.class_method?} variadic=#{meth.variadic?}" dumpfunc(meth.ret.function_pointer, indent + 1) if meth.ret.function_pointer? dumpargs(meth, indent + 1) end end def protolist(str, container) print str container.each_protocol { |p| print " #{p}" } print "\n" end def dumpinterf(interf, indent = 0) i = ' ' * indent puts "---------\n" if indent == 0 protolist("#{i}#{interf.name}: protocols=", interf) dumpmeths(interf, indent + 1) end def dumpproto(proto, indent = 0) i = ' ' * indent puts "---------\n" if indent == 0 protolist("#{i}#{proto.name}: protocols=", proto) dumpmeths(proto, indent + 1) end def dumpstruct(struct, indent = 0) i = ' ' * indent puts "---------\n" if indent == 0 puts "#{i}#{struct.name}: attrs=#{struct.attrs.inspect}" dumpfields(struct, indent + 1) end ###### public ###### def dump puts "================ categories ===============" @all_categories.sort.each { |k, v| v.sort.each { |c, categ| dumpcateg(categ) }} puts "================ cftypes ===============" @all_cftypes.sort.each { |k, t| puts "#{t.name} attrs=#{t.attrs.inspect}" } puts "================ every cftype ===============" @every_cftype.sort.each { |t, v| puts t } puts "================ enum ===============" @all_enums.sort.each { |name, e| puts "#{name}: \"#{e.value}\" attrs=#{e.attrs.inspect}" } puts "================ funcs ===============" @all_funcs.sort.each { |k, func| dumpfunc(func) } puts "================ informal protocols ===============" @all_informal_protocols.sort.each { |k, categ| dumpcateg(categ) } puts "================ macrofunctionaliases ===============" @all_func_aliases.sort.each { |n, v| puts "#{n}: #{v.original}" } puts "================ macronumbers ===============" @all_macronumbers.sort.each { |n, v| puts "#{n}: #{v.value} attrs=#{v.attrs.inspect}" } puts "================ macronumberfunccalls ===============" @all_macronumberfunccalls.sort.each { |n, v| puts "#{n}: attrs=#{v.attrs.inspect}" } puts "================ macrostrings ===============" @all_macrostrings.sort.each { |n, v| puts "#{n}: \"#{v.value}\" nsstring=#{v.nsstring} attrs=#{v.attrs.inspect}" } puts "================ protocols ===============" @all_protocols.sort.each { |k, proto| dumpproto(proto) } puts "================ interfaces ===============" @all_interfaces.sort.each { |k, interf| dumpinterf(interf) } puts "================ structs ===============" @all_structs.sort.each { |k, struct| dumpstruct(struct) } @all_opaques.sort.each { |opaque, v| puts "opaque: #{opaque}" } puts "================ special method_encodings ===============" unless @special_method_encodings.nil? @special_methods.each_index { |i| puts "#{@special_methods[i]} => #{@special_method_encodings[i]}" } end puts "================ special_type_encodings ===============" unless @special_type_encodings.nil? @special_types.each_index { |i| puts "#{@special_types[i]} => #{@special_type_encodings[i]}" } end puts "================ vars ===============" @all_vars.sort.each do |k, v| puts "#{v.name}: attrs=#{v.attrs.inspect}" dumpfunc(v.function_pointer, 1) if v.function_pointer? end end def only_parse_class(c) @only_classes[c] = 1 end ##################################################### end # class Parser end # module Bridgesupportparser