Class: Necropsy::Analyzers::Static::RTA
- Inherits:
-
Necropsy::Analyzer
- Object
- Necropsy::Analyzer
- Necropsy::Analyzers::Static::RTA
- Defined in:
- lib/necropsy/analyzers/static/rta.rb
Constant Summary collapse
- ENUMERABLE_MESSAGES =
%w[ all? any? chunk collect count cycle detect drop drop_while each_cons each_entry each_slice each_with_index each_with_object entries filter find find_all flat_map grep group_by include? inject map max min none? one? partition reduce reject reverse_each select sort sort_by take take_while to_a to_h ].freeze
- COMPARISON_MESSAGES =
%w[< <= > >= between? clamp sort sort_by max min].freeze
- CORE_ENUMERABLE_RECEIVERS =
%w[Array Enumerator Hash Range Set].freeze
- CORE_COMPARISON_RECEIVERS =
%w[Array].freeze
- KERNEL_OUTPUT_MESSAGES =
%w[print puts warn].freeze
- REFLECTION_HOOK_MESSAGES =
%w[method respond_to?].freeze
- BLOCK_DEPENDENT_ENUMERABLE_MESSAGES =
%w[ all? any? chunk collect cycle detect drop_while each_cons each_entry each_slice each_with_index each_with_object filter find find_all flat_map group_by map none? one? partition reject reverse_each select sort_by take_while ].freeze
- BLOCK_COMPARISON_MESSAGES =
%w[max min sort].freeze
Instance Attribute Summary collapse
-
#emit_redundant_edges ⇒ Object
readonly
Returns the value of attribute emit_redundant_edges.
-
#pruning ⇒ Object
readonly
Returns the value of attribute pruning.
Instance Method Summary collapse
- #analyze(graph, _project) ⇒ Object
- #capabilities ⇒ Object
- #expanded_call_sites(graph) ⇒ Object
- #implicit_messages(site, graph: nil) ⇒ Object
- #implicit_sites(site, graph: nil) ⇒ Object
-
#initialize(pruning: :rank_only, emit_redundant_edges: true) ⇒ RTA
constructor
A new instance of RTA.
- #profile ⇒ Object
- #with_pruning(mode, emit_redundant_edges: @emit_redundant_edges) ⇒ Object
- #without_redundant_edges ⇒ Object
Constructor Details
#initialize(pruning: :rank_only, emit_redundant_edges: true) ⇒ RTA
Returns a new instance of RTA.
29 30 31 32 33 34 35 36 37 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 29 def initialize(pruning: :rank_only, emit_redundant_edges: true) mode = pruning.to_s unless Configuration::RTA_PRUNING_MODES.include?(mode) raise Error, "RTA pruning must be one of: #{Configuration::RTA_PRUNING_MODES.join(', ')}" end @pruning = mode.to_sym @emit_redundant_edges = emit_redundant_edges == true end |
Instance Attribute Details
#emit_redundant_edges ⇒ Object (readonly)
Returns the value of attribute emit_redundant_edges.
27 28 29 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 27 def emit_redundant_edges @emit_redundant_edges end |
#pruning ⇒ Object (readonly)
Returns the value of attribute pruning.
27 28 29 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 27 def pruning @pruning end |
Instance Method Details
#analyze(graph, _project) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 47 def analyze(graph, _project) sites = (graph) original_call_site_ids = graph.call_sites.to_set(&:call_site_id) derived_sites = sites.reject { |site| original_call_site_ids.include?(site.call_site_id) } = (graph) analyses = sites.map do |site| targets = rta_candidates(graph, site) site_edges = targets.filter_map do |candidate| # In rank-only mode CHA/name resolution already materialize the # same conservative edge. Keep the RTA resolution record, but # avoid allocating a duplicate evidence object for an edge that # cannot change reachability. Legacy pruning still emits every # candidate because reconciliation uses those edge identities. next if pruning == :rank_only && !emit_redundant_edges && graph.edge_present?(site.caller_id, candidate.graph_id) EdgeEvidence.new( caller_id: site.caller_id, callee_id: candidate.graph_id, evidence: evidence( kind: :call_edge, details: "RTA candidate at #{site.file}:#{site.line}", metadata: site.to_h.merge().merge('target_definition_id' => candidate.graph_id), grade: :heuristic, relation: :call_edge, source: call_site_evidence_source(site).merge('target_definition_id' => candidate.graph_id), scope: call_site_evidence_scope(site).merge('target_definition_id' => candidate.graph_id) ) ) end [site, targets, site_edges] end edge_evidences = analyses.flat_map(&:last) analyses_by_call_site_id = analyses.to_h { |site, targets, edges| [site.call_site_id, [targets, edges]] } resolutions = sites.map do |site| targets, edges = analyses_by_call_site_id.fetch(site.call_site_id) resolution_record(site, targets, edges, unknown_scope: graph.residual_scope_for(site)) end AnalyzerResult.new( edge_evidences: edge_evidences, alive_evidences: [], uncertainties: {}, observation: { 'rta' => { 'pruning' => pruning.to_s, 'analyzed_sites' => sites.map(&:to_h) } }, resolutions: resolutions, evidences: result_evidences(edge_evidences), derived_call_sites: derived_sites ) end |
#capabilities ⇒ Object
113 114 115 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 113 def capabilities [:complete_resolution].freeze end |
#expanded_call_sites(graph) ⇒ Object
117 118 119 120 121 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 117 def (graph) graph.call_sites.flat_map do |site| [site, *implicit_sites(site, graph: graph)] end end |
#implicit_messages(site, graph: nil) ⇒ Object
134 135 136 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 134 def (site, graph: nil) implicit_sites(site, graph: graph).map(&:message) end |
#implicit_sites(site, graph: nil) ⇒ Object
123 124 125 126 127 128 129 130 131 132 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 123 def implicit_sites(site, graph: nil) summaries = [] summaries << ['each', :same_receiver] if core_enumerable_receiver?(site) && enumerable_protocol?(site) summaries.concat(comparison_summaries(site)) if core_comparison_receiver?(site) summaries.concat(output_summaries(site)) if kernel_output_call?(site, graph) summaries << ['respond_to_missing?', :same_receiver] if reflection_hook_call?(site, graph) summaries.map.with_index do |(, receiver), index| derived_protocol_site(site, , receiver, index: index) end end |
#profile ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 97 def profile description = if pruning == :legacy 'RTA pruning mode legacy removes static candidates without scanned construction evidence.' else 'RTA pruning mode rank_only records constructed-class hints without removing static edges.' end AnalyzerProfile.new( name: :rta, kind: :static, soundness: :partial, description: description, version: Necropsy::VERSION, assumptions: ['scanned_allocations', "pruning=#{pruning}"] ) end |
#with_pruning(mode, emit_redundant_edges: @emit_redundant_edges) ⇒ Object
39 40 41 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 39 def with_pruning(mode, emit_redundant_edges: @emit_redundant_edges) self.class.new(pruning: mode, emit_redundant_edges: emit_redundant_edges) end |
#without_redundant_edges ⇒ Object
43 44 45 |
# File 'lib/necropsy/analyzers/static/rta.rb', line 43 def without_redundant_edges self.class.new(pruning: pruning, emit_redundant_edges: false) end |