Class: Laser::Analysis::DispatchResults

Inherits:
Object
  • Object
show all
Defined in:
lib/laser/analysis/bootstrap/dispatch_results.rb

Overview

Collects the results of attempted dispatches and calculates the return type, raise type, and raise frequency.

Is used whenever a dispatch is discovered that needs analysis. This includes, for example, performing CPA and analyzing a call to send/public_send.

Is responsible for noting that a method has been used, as this is the central place for the logic pertaining to success/failure on dispatch. Keep in mind: a method isn't used if it is called with incorrect arity, and that arity checking should occur here.

Defined Under Namespace

Classes: ArityError, PrivacyError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDispatchResults

Returns a new instance of DispatchResults



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 32

def initialize
  @raise_type  = Types::EMPTY
  @result_type = Types::EMPTY
  @privacy_failures = 0
  @privacy_samples  = 0
  @privacy_errors   = Set[]
  @arity_failures   = 0
  @arity_samples    = 0
  @arity_errors     = Set[]
  @normal_failures  = 0
  @normal_samples   = 0
end

Instance Attribute Details

#result_typeObject (readonly)

Returns the value of attribute result_type



30
31
32
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 30

def result_type
  @result_type
end

Instance Method Details

#add_samples_from_dispatch(methods, self_type, cartesian, ignore_privacy) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 45

def add_samples_from_dispatch(methods, self_type, cartesian, ignore_privacy)
  if methods.empty?
    @privacy_failures += 1
    @privacy_samples += 1
    @raise_type |= ClassRegistry['NoMethodError'].as_type
  end
  methods.each do |method|
    next unless check_privacy(method, self_type, ignore_privacy)
    cartesian.each do |*type_list, block_type|
      next unless check_arity(method, self_type, type_list.size)
      method.been_used!
      normal_dispatch(method, self_type, cartesian)
    end
  end
end

#arity_failure_frequencyObject

Arity-related dispatch issues ############



124
125
126
127
128
129
130
131
132
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 124

def arity_failure_frequency
  if @arity_failures == 0
    Frequency::NEVER
  elsif @arity_failures == @arity_samples
    Frequency::ALWAYS
  else
    Frequency::MAYBE
  end
end

#check_arity(method, self_type, proposed_arity) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 79

def check_arity(method, self_type, proposed_arity)
  result = false
  self_type.possible_classes.each do |self_class|
    if method.valid_arity?(proposed_arity)
      passes_arity
      result = true
    else
      fails_arity(self_class, method.name, proposed_arity, method.arity)
    end
  end
  result
end

#check_privacy(method, self_type, ignore_privacy) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 61

def check_privacy(method, self_type, ignore_privacy)
  result = false
  if ignore_privacy
    passes_privacy
    result = true
  else
    self_type.possible_classes.each do |self_class|
      if self_class.visibility_for(method.name) == :public
        passes_privacy
        result = true
      else
        fails_privacy(self_class, method.name)
      end
    end
  end
  result
end

#fails_arity(receiver_class, method_name, provided, expected) ⇒ Object



138
139
140
141
142
143
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 138

def fails_arity(receiver_class, method_name, provided, expected)
  @arity_errors << ArityError.new(receiver_class, method_name, provided, expected)
  @arity_samples += 1
  @arity_failures += 1
  @raise_type |= ClassRegistry['ArgumentError'].as_type
end

#fails_dispatch(raise_type) ⇒ Object



185
186
187
188
189
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 185

def fails_dispatch(raise_type)
  @raise_type |= raise_type
  @normal_failures += 1
  @normal_samples += 1
end

#fails_privacy(receiver_class, method_name) ⇒ Object



161
162
163
164
165
166
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 161

def fails_privacy(receiver_class, method_name)
  @privacy_errors << PrivacyError.new(receiver_class, method_name)
  @privacy_samples += 1
  @privacy_failures += 1
  @raise_type |= ClassRegistry['NoMethodError'].as_type
end

#normal_dispatch(method, self_type, cartesian) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 92

def normal_dispatch(method, self_type, cartesian)
  cartesian.each do |*type_list, block_type|
    raise_frequency = method.raise_frequency_for_types(self_type, type_list, block_type)
    if raise_frequency > Frequency::NEVER
      fails_dispatch(method.raise_type_for_types(self_type, type_list, block_type))
    end
    if raise_frequency < Frequency::ALWAYS
      passes_dispatch(method.return_type_for_types(self_type, type_list, block_type))
    end
  end
end

#normal_failure_frequencyObject

Calculated dispatch issues ###########



170
171
172
173
174
175
176
177
178
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 170

def normal_failure_frequency
  if @normal_failures == 0
    Frequency::NEVER
  elsif @normal_failures == @normal_samples
    Frequency::ALWAYS
  else
    Frequency::MAYBE
  end
end

#passes_arityObject



134
135
136
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 134

def passes_arity
  @arity_samples += 1
end

#passes_dispatch(return_type) ⇒ Object



180
181
182
183
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 180

def passes_dispatch(return_type)
  @result_type |= return_type
  @normal_samples += 1
end

#passes_privacyObject



157
158
159
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 157

def passes_privacy
  @privacy_samples += 1
end

#privacy_failure_frequencyObject

Privacy-related dispatch issues ############



147
148
149
150
151
152
153
154
155
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 147

def privacy_failure_frequency
  if @privacy_failures == 0
    Frequency::NEVER
  elsif @privacy_failures == @privacy_samples
    Frequency::ALWAYS
  else
    Frequency::MAYBE
  end
end

#raise_frequencyObject



114
115
116
117
118
119
120
121
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 114

def raise_frequency
  if @privacy_samples.zero?
    Frequency::ALWAYS
  else
    [arity_failure_frequency, privacy_failure_frequency,
     normal_failure_frequency].max
  end
end

#raise_typeObject

Result Accessors ##############



106
107
108
109
110
111
112
# File 'lib/laser/analysis/bootstrap/dispatch_results.rb', line 106

def raise_type
  if @privacy_samples.zero?
    ClassRegistry['NoMethodError'].as_type
  else
    @raise_type
  end
end