Module: Police::DataFlow::Proxying
- Defined in:
- lib/police/dataflow/proxying.rb
Class Method Summary collapse
-
.add_instance_method(proxy_class, method_def, access) ⇒ Object
Adds a method to a proxy class.
-
.add_instance_methods(proxy_class, klass) ⇒ NilClass
Creates proxies for a class’ instance methods.
-
.proxy(proxied, label_set) ⇒ Police::DataFlow::ProxyBase
Creates a label-holding proxy for an object.
-
.proxy_argument_list(method_def, capture_block) ⇒ String
The list of arguments used to define a proxy for the given method.
-
.proxy_call_argument_list(method_def) ⇒ String
The list of arguments used to call a proxied method.
-
.proxy_low_level_call_argument_list(method_def) ⇒ String
The list of arguments used to call a proxied low-level method.
-
.proxy_method_call(method_def, access) ⇒ String
The proxying call to a method.
-
.proxy_method_definition(label_classes, method_def, access) ⇒ String
The full definition of a proxy method.
-
.proxy_return_decorating(label_classes, method_def) ⇒ String
Code that labels return value of a decorated method.
- .proxy_return_sticky_decorating(method_def) ⇒ Object
-
.proxy_sticky_fastpath_check(method_def) ⇒ String
Boolean expression deciding if a proxied method received labeled arguments.
-
.proxy_sticky_gathering(method_def) ⇒ String
Code for computing the union of the arguments’ sticky labels.
-
.proxy_yield_args_decorating(label_classes, method_def) ⇒ String
Code that labels the values yielded by a decorated method to its block.
-
.proxy_yield_sticky_decorating(method_def) ⇒ String
Code for applying argument sticky labels to a method’s yielded arguments.
Class Method Details
.add_instance_method(proxy_class, method_def, access) ⇒ Object
Adds a method to a proxy class.
57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/police/dataflow/proxying.rb', line 57 def self.add_instance_method(proxy_class, method_def, access) # Avoid redefining methods, because that blows up VM caches. if proxy_class.method_defined?(method_def.name) || proxy_class.private_method_defined?(method_def.name) return end # Define the method. proxy_class.class_eval proxy_method_definition( proxy_class.__police_classes__, method_def, access) # Set its access level. proxy_class.__send__ access, method_def.name end |
.add_instance_methods(proxy_class, klass) ⇒ NilClass
Creates proxies for a class’ instance methods.
The proxy methods are defined as instance methods for the proxying class, because all the proxied objects that have the same class will need the same proxies.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/police/dataflow/proxying.rb', line 35 def self.add_instance_methods(proxy_class, klass) # NOTE: this is thread-safe because, at worst, the effort of adding methods # will be re-duplicated klass.public_instance_methods(true).each do |method| add_instance_method proxy_class, klass.instance_method(method), :public end klass.protected_instance_methods(true).each do |method| add_instance_method proxy_class, klass.instance_method(method), :protected end klass.private_instance_methods(true).each do |method| add_instance_method proxy_class, klass.instance_method(method), :private end nil end |
.proxy(proxied, label_set) ⇒ Police::DataFlow::ProxyBase
Creates a label-holding proxy for an object.
20 21 22 23 |
# File 'lib/police/dataflow/proxying.rb', line 20 def self.proxy(proxied, label_set) proxy_class = Police::DataFlow::Proxies.for proxied.class, label_set proxy_class.new proxied, proxy_class, label_set end |
.proxy_argument_list(method_def, capture_block) ⇒ String
The list of arguments used to define a proxy for the given method.
188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/police/dataflow/proxying.rb', line 188 def self.proxy_argument_list(method_def, capture_block) arg_list = if method_def.arity >= 0 # Fixed number of arguments. (1..method_def.arity).map { |i| "arg#{i}" } else # Variable number of arguments. ((1..(-method_def.arity - 1)).map { |i| "arg#{i}" } << '*args') end arg_list << '&block' if capture_block arg_list.join ', ' end |
.proxy_call_argument_list(method_def) ⇒ String
The list of arguments used to call a proxied method.
This assumes that the proxy method definition uses the code retuned by proxy_argument_list.
209 210 211 212 213 214 215 216 |
# File 'lib/police/dataflow/proxying.rb', line 209 def self.proxy_call_argument_list(method_def) source = Police::VmInfo.method_source method_def if source == :native || source == :kernel proxy_low_level_call_argument_list method_def else proxy_argument_list method_def, false end end |
.proxy_low_level_call_argument_list(method_def) ⇒ String
The list of arguments used to call a proxied low-level method.
Low-level methods don’t use Ruby methods to manipulate their arguments, so they can’t work on proxies, and need to receive the proxied objects as arguments.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/police/dataflow/proxying.rb', line 228 def self.proxy_low_level_call_argument_list(method_def) arg_list = if method_def.arity >= 0 # Fixed number of arguments. (1..method_def.arity).map do |i| "(nil == arg#{i}.__police_labels__) ? arg#{i} : " + "arg#{i}.__police_proxied__" end else # Variable number of arguments. args_mapper = '*(args.map { |a| (nil == a.__police_labels__) ? a : ' + 'a.__police_proxied__ })' (1..(-method_def.arity - 1)).map { |i| "(nil == arg#{i}.__police_labels__) ? arg#{i} : " + "arg#{i}.__police_proxied__" } << args_mapper end arg_list.join ', ' end |
.proxy_method_call(method_def, access) ⇒ String
The proxying call to a method.
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/police/dataflow/proxying.rb', line 115 def self.proxy_method_call(method_def, access) arg_list = proxy_call_argument_list method_def if access == :public "@__police_proxied__.#{method_def.name}(#{arg_list})" else if arg_list.empty? "@__police_proxied__.__send__(:#{method_def.name})" else "@__police_proxied__.__send__(:#{method_def.name}, #{arg_list})" end end end |
.proxy_method_definition(label_classes, method_def, access) ⇒ String
The full definition of a proxy method.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/police/dataflow/proxying.rb', line 80 def self.proxy_method_definition(label_classes, method_def, access) # NOTE: it might be tempting to attempt to pass a block to the proxied # method at all times, and try to yield to the original block when # our block is invoked; this would work most of the time, but it # would break methods such as Enumerable#map and String#scan, whose # behavior changes depending on whether or not a block is passed to # them ["def #{method_def.name}(#{proxy_argument_list(method_def, true)})", proxy_sticky_fastpath_check(method_def), proxy_sticky_gathering(method_def), "return_value = if block", proxy_method_call(method_def, access) + " do |*yield_args|", (method_def), (label_classes, method_def), "block_return = yield(*yield_args)", # TODO(pwnall): consider adding a yield value filter "next block_return", "end", "else", proxy_method_call(method_def, access), "end", (method_def), (label_classes, method_def), "return return_value", "end"].join ';' end |
.proxy_return_decorating(label_classes, method_def) ⇒ String
Code that labels return value of a decorated method.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/police/dataflow/proxying.rb', line 162 def self.(label_classes, method_def) method_name = method_def.name arg_list = proxy_argument_list method_def, false code_lines = ['labels = @__police_labels__'] label_classes.each do |label_class| if hook = label_class.return_hook(method_name) label_key = label_class.__id__ code_lines << "labels[#{label_key}].each { |label, _| " \ "return_value = label.#{hook}(return_value, self, #{arg_list}) }" elsif label_class.sticky? label_key = label_class.__id__ code_lines << "labels[#{label_key}].each { |label, _| " \ "return_value = ::Police::DataFlow.label(return_value, label) }" end end (code_lines.length > 1) ? code_lines.join('; ') : '' end |
.proxy_return_sticky_decorating(method_def) ⇒ Object
341 342 343 344 345 346 347 348 349 |
# File 'lib/police/dataflow/proxying.rb', line 341 def self.(method_def) # Don't generate anything for zero-argument methods. return '' if method_def.arity == 0 'unless fast_sticky; ' + 'return_value = ::Police::DataFlow::Labeling.bulk_sticky_label(' + 'return_value, sticky_labels); ' + 'end' end |
.proxy_sticky_fastpath_check(method_def) ⇒ String
Boolean expression deciding if a proxied method received labeled arguments.
If none of the method’s arguments is labeled, the sticky label propagation logic can be completely bypassed.
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/police/dataflow/proxying.rb', line 257 def self.proxy_sticky_fastpath_check(method_def) # Don't generate anything for zero-argument methods. return '' if method_def.arity == 0 boolean_list = if method_def.arity > 0 # Fixed number of arguments. (1..method_def.arity).map do |i| "(nil == arg#{i}.__police_stickies__)" end else # Variable number of arguments. args_boolean = '(args.all? { |a| nil == a.__police_stickies__ })' (1..(-method_def.arity - 1)).map { |i| "(nil == arg#{i}.__police_stickies__)" } << args_boolean end 'fast_sticky = ' + boolean_list.join(' && ') end |
.proxy_sticky_gathering(method_def) ⇒ String
Code for computing the union of the arguments’ sticky labels.
The code is wrapped in a check that assumes the code returned by #proxy_sticky_fastpath_check was already executed.
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/police/dataflow/proxying.rb', line 287 def self.proxy_sticky_gathering(method_def) # Don't generate anything for zero-argument methods. return '' if method_def.arity == 0 code_lines = ['unless fast_sticky', 'sticky_labels = {}'] if method_def.arity > 0 # Fixed number of arguments. 1.upto method_def.arity do |i| code_lines << "unless nil == arg#{i}.__police_stickies__" code_lines << '::Police::DataFlow::Labeling.merge_sets!(' + "sticky_labels, arg#{i}.__police_stickies__)" code_lines << 'end' end else # Variable number of arguments. 1.upto(-method_def.arity - 1) do |i| code_lines << "unless nil == arg#{i}.__police_stickies__" code_lines << '::Police::DataFlow::Labeling.merge_sets!(' + "sticky_labels, arg#{i}.__police_stickies__)" code_lines << 'end' end code_lines << 'args.each do |a|' code_lines << 'unless nil == a.__police_stickies__' code_lines << '::Police::DataFlow::Labeling.merge_sets!(' + 'sticky_labels, a.__police_stickies__)' code_lines << 'end' code_lines << 'end' end code_lines << 'end' code_lines.join '; ' end |
.proxy_yield_args_decorating(label_classes, method_def) ⇒ String
Code that labels the values yielded by a decorated method to its block.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/police/dataflow/proxying.rb', line 136 def self.(label_classes, method_def) method_name = method_def.name arg_list = proxy_argument_list method_def, false code_lines = ['labels = @__police_labels__'] label_classes.each do |label_class| if hook = label_class.yield_args_hook(method_name) label_key = label_class.__id__ code_lines << "labels[#{label_key}].each { |label, _| " \ "label.#{hook}(self, yield_args, #{arg_list}) }" elsif label_class.sticky? label_key = label_class.__id__ code_lines << "labels[#{label_key}].each { |label, _| " \ "yield_args.map! { |arg| ::Police::DataFlow.label(arg, label) } " \ "}" end end (code_lines.length > 1) ? code_lines.join('; ') : '' end |
.proxy_yield_sticky_decorating(method_def) ⇒ String
Code for applying argument sticky labels to a method’s yielded arguments.
This code assumes that the code returned by #proxy_sticky_fastpath_check and #proxy_sticky_gathering was already executed.
330 331 332 333 334 335 336 337 338 339 |
# File 'lib/police/dataflow/proxying.rb', line 330 def self.(method_def) # Don't generate anything for zero-argument methods. return '' if method_def.arity == 0 'unless fast_sticky; ' + 'yield_args.map! do |a|; ' + '::Police::DataFlow::Labeling.bulk_sticky_label(a, sticky_labels); ' + 'end; ' + 'end' end |