Class: TypeProf::RubySignatureExporter
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Inherits: | Object |
Defined in: | lib/typeprof/export.rb |
Class Method Summary
Instance Method Summary
Constructor Details
.new(scratch, class_defs, iseq_method_to_ctxs) ⇒ RubySignatureExporter
# File 'lib/typeprof/export.rb', line 89
def initialize( scratch, class_defs, iseq_method_to_ctxs ) @scratch = scratch @class_defs = class_defs @iseq_method_to_ctxs = iseq_method_to_ctxs end
Instance Method Details
#build_class_hierarchy(namespace, hierarchy)
[ GitHub ]# File 'lib/typeprof/export.rb', line 488
def build_class_hierarchy(namespace, hierarchy) hierarchy.map do |name, h| class_def = h.delete(:class_def) class_data = conv_class(namespace, class_def, build_class_hierarchy(namespace + [name], h)) class_data end.compact end
#conv_class(namespace, class_def, inner_classes)
[ GitHub ]# File 'lib/typeprof/export.rb', line 98
def conv_class(namespace, class_def, inner_classes) @scratch.namespace = namespace if class_def.klass_obj.superclass != :__root__ && class_def.klass_obj.superclass omit = class_def.klass_obj.superclass == Type::Builtin[:obj] || class_def.klass_obj == Type::Builtin[:obj] superclass = omit ? nil : @scratch.get_class_name(class_def.klass_obj.superclass) type_args = class_def.klass_obj.superclass_type_args if type_args && !type_args.empty? superclass += "[#{ type_args.map {|ty| ty.screen_name(@scratch) }.join(", ") }]" end end @scratch.namespace = class_def.name consts = {} class_def.consts.each do |name, (ty, loc)| next unless loc next if ty.is_a?(Type::Class) next if Config.current.check_dir_filter(loc[0]) == :exclude consts[name] = ty.screen_name(@scratch) end modules = class_def.modules.to_h do |kind, mods| mods = mods.to_h do |singleton, mods| mods = mods.filter_map do |mod_def, _type_args, absolute_paths| next if absolute_paths.all? {|path| !path || Config.current.check_dir_filter(path) == :exclude } Type::Instance.new(mod_def.klass_obj).screen_name(@scratch) end [singleton, mods] end [kind, mods] end visibilities = {} source_locations = {} methods = {} ivars = class_def.ivars.dump cvars = class_def.cvars.dump class_def.methods.each do |(singleton, mid), mdefs| mdefs.each do |mdef| case mdef when ISeqMethodDef ctxs = @iseq_method_to_ctxs[mdef] next unless ctxs ctx = ctxs.find {|ctx| ctx.mid == mid } || ctxs.first next if Config.current.check_dir_filter(ctx.iseq.absolute_path) == :exclude method_name = mid method_name = "self.#{ method_name }" if singleton key = [:iseq, method_name] visibilities[key] ||= mdef.pub_meth source_locations[key] ||= ctx.iseq.source_location(0) (methods[key] ||= []) << @scratch.show_method_signature(ctx) when AliasMethodDef next if mdef.def_ep && Config.current.check_dir_filter(mdef.def_ep.source_location) == :exclude alias_name, orig_name = mid, mdef.orig_mid if singleton alias_name = "self.#{ alias_name }" orig_name = "self.#{ orig_name }" end key = [:alias, alias_name] visibilities[key] ||= mdef.pub_meth source_locations[key] ||= mdef.def_ep&.source_location methods[key] = orig_name when ExecutedAttrMethodDef absolute_path = mdef.def_ep.ctx.iseq.absolute_path next if !absolute_path || Config.current.check_dir_filter(absolute_path) == :exclude mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=") method_name = mid method_name = "self.#{ mid }" if singleton method_name = [method_name, :"@#{ mid }" != mdef.ivar] key = [:attr, method_name] visibilities[key] ||= mdef.pub_meth source_locations[key] ||= mdef.def_ep.source_location if methods[key] if methods[key][0] != mdef.kind methods[key][0] = :accessor end else entry = ivars[[singleton, mdef.ivar]] ty = entry ? entry.type : Type.any methods[key] = [mdef.kind, ty.screen_name(@scratch), ty.include_untyped?(@scratch)] end when TypedMethodDef if mdef.rbs_source method_name, sigs = mdef.rbs_source key = [:rbs, method_name] methods[key] = sigs visibilities[key] ||= mdef.pub_meth source_locations[key] ||= mdef.iseq&.source_location(0) end when TypedAttrMethodDef if mdef.rbs_source mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=") method_name = mid method_name = [method_name, :"@#{ mid }" != mdef.ivar] key = [:rbs_attr, method_name] visibilities[key] ||= mdef.pub_meth if methods[key] if methods[key][0] != mdef.kind methods[key][0] = :accessor end else entry = ivars[[singleton, mdef.ivar]] ty = entry ? entry.type : Type.any methods[key] = [mdef.kind, ty.screen_name(@scratch), ty.include_untyped?(@scratch)] end end end end end superclass_ivars = {} while (superclass_def = (superclass_def || class_def).superclass) superclass_ivars.merge!(superclass_def.ivars.dump) end ivars = ivars.map do |(singleton, var), entry| next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude } ty = entry.type next unless var.to_s.start_with?("@") if (_, existing = superclass_ivars.find {|((s, v), _)| s == singleton && v == var }) existing_types = existing.type.is_a?(Type::Union) ? existing.type.types : [existing.type] entry_types = entry.type.is_a?(Type::Union) ? entry.type.types : [entry.type] if entry_types.all? { |t| existing_types.include?(t) } # This type is a subset of the parent type next end end var = "self.#{ var }" if singleton next if methods[[:attr, [singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]] next if entry.rbs_declared [var, ty.screen_name(@scratch)] end.compact cvars = cvars.map do |var, entry| next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude } next if entry.rbs_declared [var, entry.type.screen_name(@scratch)] end.compact if !class_def.absolute_path || Config.current.check_dir_filter(class_def.absolute_path) == :exclude if methods.keys.all? {|type,| type == :rbs } return nil if consts.empty? && modules[:before][true].empty? && modules[:before][false].empty? && modules[:after][true].empty? && modules[:after][false].empty? && ivars.empty? && cvars.empty? && inner_classes.empty? end end @scratch.namespace = nil ClassData.new( kind: class_def.kind, name: class_def.name, superclass: superclass, consts: consts, modules: modules, ivars: ivars, cvars: cvars, methods: methods, visibilities: visibilities, source_locations: source_locations, inner_classes: inner_classes, ) end
#conv_class_lsp(namespace, class_def)
[ GitHub ]# File 'lib/typeprof/export.rb', line 268
def conv_class_lsp(namespace, class_def) @scratch.namespace = namespace if class_def.klass_obj.superclass != :__root__ && class_def.klass_obj.superclass omit = class_def.klass_obj.superclass == Type::Builtin[:obj] || class_def.klass_obj == Type::Builtin[:obj] superclass = omit ? nil : @scratch.get_class_name(class_def.klass_obj.superclass) type_args = class_def.klass_obj.superclass_type_args if type_args && !type_args.empty? superclass += "[#{ type_args.map {|ty| ty.screen_name(@scratch) }.join(", ") }]" end end @scratch.namespace = class_def.name consts = {} class_def.consts.each do |name, (ty, loc)| next unless loc next if ty.is_a?(Type::Class) next if Config.current.check_dir_filter(loc[0]) == :exclude consts[name] = ty.screen_name(@scratch) end modules = class_def.modules.to_h do |kind, mods| mods = mods.to_h do |singleton, mods| mods = mods.filter_map do |mod_def, _type_args, absolute_paths| next if absolute_paths.all? {|path| !path || Config.current.check_dir_filter(path) == :exclude } Type::Instance.new(mod_def.klass_obj).screen_name(@scratch) end [singleton, mods] end [kind, mods] end visibilities = {} source_locations = {} methods = {} ivars = class_def.ivars.dump cvars = class_def.cvars.dump class_def.methods.each do |(singleton, mid), mdefs| mdefs.each do |mdef| case mdef when ISeqMethodDef ctxs = @iseq_method_to_ctxs[mdef] next unless ctxs ctx = ctxs.find {|ctx| ctx.mid == mid } || ctxs.first next if Config.current.check_dir_filter(ctx.iseq.absolute_path) == :exclude method_name = mid method_name = "self.#{ method_name }" if singleton key = [:iseq, method_name] visibilities[key] ||= mdef.pub_meth source_locations[key] ||= [ctx.iseq.source_location(0)] sig = @scratch.show_method_signature(ctx) (methods[key] ||= []) << sig if sig when AliasMethodDef alias_name, orig_name = mid, mdef.orig_mid if singleton alias_name = "self.#{ alias_name }" orig_name = "self.#{ orig_name }" end key = [:alias, alias_name] visibilities[key] ||= mdef.pub_meth source_locations[key] ||= [mdef.def_ep&.source_location] methods[key] = orig_name when ExecutedAttrMethodDef next if !mdef.def_ep absolute_path = mdef.def_ep.ctx.iseq.absolute_path next if !absolute_path || Config.current.check_dir_filter(absolute_path) == :exclude mid = mid.to_s[0..-2].to_sym if mid.to_s.end_with?("=") method_name = mid method_name = "self.#{ mid }" if singleton method_name = [method_name, :"@#{ mid }" != mdef.ivar] key = [:attr, method_name] visibilities[key] ||= mdef.pub_meth source_locations[key] ||= [mdef.def_ep.source_location] if methods[key] if methods[key][0] != mdef.kind methods[key][0] = :accessor end else entry = ivars[[singleton, mdef.ivar]] ty = entry ? entry.type : Type.any methods[key] = [mdef.kind, ty.screen_name(@scratch), ty.include_untyped?(@scratch)] end when TypedMethodDef if mdef.rbs_source method_name, sigs, rbs_code_range = mdef.rbs_source key = [:rbs, method_name] methods[key] = sigs visibilities[key] ||= mdef.pub_meth source_locations[key] ||= [mdef.iseq&.source_location(0), rbs_code_range] end end end end ivars = ivars.map do |(singleton, var), entry| next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude } ty = entry.type next unless var.to_s.start_with?("@") var = "self.#{ var }" if singleton next if methods[[:attr, [singleton ? "self.#{ var.to_s[1..] }" : var.to_s[1..].to_sym, false]]] next if entry.rbs_declared [var, ty.screen_name(@scratch)] end.compact cvars = cvars.map do |var, entry| next if entry.absolute_paths.all? {|path| Config.current.check_dir_filter(path) == :exclude } next if entry.rbs_declared [var, entry.type.screen_name(@scratch)] end.compact if !class_def.absolute_path || Config.current.check_dir_filter(class_def.absolute_path) == :exclude if methods.keys.all? {|type,| type == :rbs } return nil if consts.empty? && modules[:before][true].empty? && modules[:before][false].empty? && modules[:after][true].empty? && modules[:after][false].empty? && ivars.empty? && cvars.empty? end end @scratch.namespace = nil ClassData.new( kind: class_def.kind, name: class_def.name, superclass: superclass, consts: consts, modules: modules, ivars: ivars, cvars: cvars, methods: methods, visibilities: visibilities, source_locations: source_locations, ) end
#show(stat_eps, output)
[ GitHub ]# File 'lib/typeprof/export.rb', line 444
def show(stat_eps, output) # make the class hierarchy root = {} @class_defs.each_value do |class_def| h = root class_def.name.each do |name| h = h[name] ||= {} end h[:class_def] = class_def end hierarchy = build_class_hierarchy([], root) output.puts "# Classes" # and Modules prev_nil = true show_class_hierarchy(0, hierarchy).each do |line| if line == nil output.puts line unless prev_nil prev_nil = true else output.puts line prev_nil = false end end if ENV["TP_STAT"] output.puts "" output.puts "# TypeProf statistics:" output.puts "# %d execution points" % stat_eps.size end if ENV["TP_COVERAGE"] coverage = {} stat_eps.each do |ep| path = ep.ctx.iseq.path lineno = ep.ctx.iseq.insns[ep.pc].lineno - 1 (coverage[path] ||= [])[lineno] ||= 0 (coverage[path] ||= [])[lineno] += 1 end File.binwrite("typeprof-analysis-coverage.dump", Marshal.dump(coverage)) end end
#show_class_data(depth, class_data)
[ GitHub ]# File 'lib/typeprof/export.rb', line 512
def show_class_data(depth, class_data) indent = " " * depth name = class_data.name.last superclass = " < " + class_data.superclass if class_data.superclass first_line = indent + "#{ class_data.kind } #{ name }#{ superclass }" lines = [] class_data.consts.each do |name, ty| lines << (indent + " #{ name }: #{ ty }") end class_data.modules.each do |kind, mods| mods.each do |singleton, mods| case when kind == :before && singleton then directive = nil when kind == :before && !singleton then directive = "prepend" when kind == :after && singleton then directive = "extend" when kind == :after && !singleton then directive = "include" end mods.each do |mod| lines << (indent + " #{ directive } #{ mod }") if directive end end end class_data.ivars.each do |var, ty| lines << (indent + " #{ var }: #{ ty }") unless var.start_with?("_") end class_data.cvars.each do |var, ty| lines << (indent + " #{ var }: #{ ty }") end lines << nil prev_vis = true class_data.methods.each do |key, arg| vis = class_data.visibilities[key] if prev_vis != vis lines << nil lines << (indent + " #{ vis ? "public" : "private" }") prev_vis = vis end source_location = class_data.source_locations[key] if Config.current. [:show_source_locations] && source_location lines << nil lines << (indent + " # #{ source_location }") end type, (method_name, hidden) = key case type when :rbs_attr kind, ty, untyped = *arg lines << (indent + "# attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }") when :attr kind, ty, untyped = *arg exclude = Config.current. [:exclude_untyped] && untyped ? "#" : " " # XXX lines << (indent + "#{ exclude } attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }") when :rbs arg = arg.map { |a| a.split("\n").join("\n" + indent + "#" + " " * (method_name.size + 5)) } sigs = arg.sort.join("\n" + indent + "#" + " " * (method_name.size + 5) + "| ") lines << (indent + "# def #{ method_name }: #{ sigs }") when :iseq sigs = [] untyped = false arg.each do |sig, untyped0| sigs << sig untyped ||= untyped0 end sigs = sigs.sort.join("\n" + indent + " " * (method_name.size + 6) + "| ") exclude = Config.current. [:exclude_untyped] && untyped ? "#" : " " # XXX lines << (indent + "#{ exclude } def #{ method_name }: #{ sigs }") when :alias orig_name = arg lines << (indent + " alias #{ method_name } #{ orig_name }") end end lines.concat show_class_hierarchy(depth + 1, class_data.inner_classes) lines.shift until lines.empty? || lines.first lines.pop until lines.empty? || lines.last lines.unshift first_line lines << (indent + "end") end
#show_class_hierarchy(depth, hierarchy)
[ GitHub ]# File 'lib/typeprof/export.rb', line 496
def show_class_hierarchy(depth, hierarchy) lines = [] hierarchy.each do |class_data| lines << nil lines.concat show_class_data(depth, class_data) end lines end
#show_const(namespace, path)
[ GitHub ]# File 'lib/typeprof/export.rb', line 505
def show_const(namespace, path) return path.last.to_s if namespace == path i = 0 i += 1 while namespace[i] && namespace[i] == path[i] path[i..].join("::") end
#show_lsp
[ GitHub ]# File 'lib/typeprof/export.rb', line 408
def show_lsp res = [] @class_defs.each_value do |class_def| class_data = conv_class_lsp([], class_def) next unless class_data class_data.methods.each do |key, arg| source_location, rbs_code_range = class_data.source_locations[key] type, (method_name, hidden) = key case type when :attr, :rbs_attr kind, ty, untyped = *arg line = "attr_#{ kind } #{ method_name }#{ hidden ? "()" : "" }: #{ ty }" when :rbs sigs = arg.sort.join(" | ") line = "# def #{ method_name }: #{ sigs }" when :iseq sigs = [] untyped = false arg.each do |sig, untyped0| sigs << sig untyped ||= untyped0 end sigs = sigs.sort.join(" | ") line = "def #{ method_name }: #{ sigs }" when :alias orig_name = arg line = "alias #{ method_name } #{ orig_name }" end if source_location =~ /:(\d+)$/ res << [$`, $1.to_i, line, rbs_code_range, class_data.kind, class_data.name] end end end res end