police-dataflow 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,19 +6,22 @@ module DataFlow
6
6
  #
7
7
  # ProxyBase is the superclass for all proxy classes, which makes it visible to
8
8
  # application code. For this reason, we avoid defining any methods there.
9
+ require 'police/vminfo'
10
+
9
11
  module Proxying
10
12
  # Creates a label-holding proxy for an object.
11
13
  #
12
14
  # @param [Object] proxied the object to be proxied
13
- # @param [Array<Integer>] label_keys
15
+ # @param [Hash<Integer,Hash<Police::DataFlow::Label,Boolean>>] label_set the
16
+ # set of all labels that will be held by the object's proxy
14
17
  # @return [Police::DataFlow::ProxyBase] an object that can carry labels, and
15
18
  # performs label-propagation as it redirects received messages to the
16
19
  # proxied object
17
- def self.proxy(proxied, label_set, autoflow_set)
20
+ def self.proxy(proxied, label_set)
18
21
  proxy_class = Police::DataFlow::Proxies.for proxied.class, label_set
19
- proxy_class.new proxied, proxy_class, label_set, autoflow_set
22
+ proxy_class.new proxied, proxy_class, label_set
20
23
  end
21
-
24
+
22
25
  # Creates proxies for a class' instance methods.
23
26
  #
24
27
  # The proxy methods are defined as instance methods for the proxying class,
@@ -29,17 +32,18 @@ module Proxying
29
32
  # receive the new proxy method definitions
30
33
  # @param [Class] klass the class whose instance methods will be proxied
31
34
  # @return [NilClass] nil
32
- def self.add_class_methods(proxy_class, klass)
35
+ def self.add_instance_methods(proxy_class, klass)
33
36
  # NOTE: this is thread-safe because, at worst, the effort of adding methods
34
37
  # will be re-duplicated
35
38
  klass.public_instance_methods(true).each do |method|
36
- add_class_method proxy_class, klass.instance_method(method), :public
39
+ add_instance_method proxy_class, klass.instance_method(method), :public
37
40
  end
38
41
  klass.protected_instance_methods(true).each do |method|
39
- add_class_method proxy_class, klass.instance_method(method), :protected
42
+ add_instance_method proxy_class, klass.instance_method(method),
43
+ :protected
40
44
  end
41
45
  klass.private_instance_methods(true).each do |method|
42
- add_class_method proxy_class, klass.instance_method(method), :private
46
+ add_instance_method proxy_class, klass.instance_method(method), :private
43
47
  end
44
48
  nil
45
49
  end
@@ -50,20 +54,20 @@ module Proxying
50
54
  # @param [Method] method_def the definition of the method to be proxied
51
55
  # @param [Symbol] access the proxied method's access level (:public,
52
56
  # :protected, or :private)
53
- def self.add_class_method(proxy_class, method_def, access)
57
+ def self.add_instance_method(proxy_class, method_def, access)
54
58
  # Avoid redefining methods, because that blows up VM caches.
55
59
  if proxy_class.method_defined?(method_def.name) ||
56
60
  proxy_class.private_method_defined?(method_def.name)
57
61
  return
58
62
  end
59
-
63
+
60
64
  # Define the method.
61
65
  proxy_class.class_eval proxy_method_definition(
62
66
  proxy_class.__police_classes__, method_def, access)
63
67
  # Set its access level.
64
68
  proxy_class.__send__ access, method_def.name
65
69
  end
66
-
70
+
67
71
  # The full definition of a proxy method.
68
72
  #
69
73
  # @param [Array<Police::DataFlow::Label>] label_classes the label classes
@@ -75,46 +79,54 @@ module Proxying
75
79
  # proxy class to define a proxy for the given method
76
80
  def self.proxy_method_definition(label_classes, method_def, access)
77
81
  # NOTE: it might be tempting to attempt to pass a block to the proxied
78
- # method at all times, and try to yield to the original block when our
79
- # block is invoked; this would work most of the time, but it would
80
- # break methods such as Enumerable#map and String#scan, whose behavior
81
- # changes depending on whether or not a block is passed to them
82
+ # method at all times, and try to yield to the original block when
83
+ # our block is invoked; this would work most of the time, but it
84
+ # would break methods such as Enumerable#map and String#scan, whose
85
+ # behavior changes depending on whether or not a block is passed to
86
+ # them
82
87
  ["def #{method_def.name}(#{proxy_argument_list(method_def, true)})",
88
+ proxy_sticky_fastpath_check(method_def),
89
+ proxy_sticky_gathering(method_def),
83
90
  "return_value = if block",
84
- proxy_method_call(method_def, access, false) + " do |*yield_args|",
91
+ proxy_method_call(method_def, access) + " do |*yield_args|",
92
+ proxy_yield_sticky_decorating(method_def),
85
93
  proxy_yield_args_decorating(label_classes, method_def),
86
94
  "block_return = yield(*yield_args)",
87
95
  # TODO(pwnall): consider adding a yield value filter
88
96
  "next block_return",
89
97
  "end",
90
98
  "else",
91
- proxy_method_call(method_def, access, false),
99
+ proxy_method_call(method_def, access),
92
100
  "end",
93
- proxy_return_decorating(label_classes, method_def),
101
+
102
+ proxy_return_sticky_decorating(method_def),
103
+ proxy_return_decorating(label_classes, method_def),
94
104
  "return return_value",
95
105
  "end"].join ';'
96
106
  end
97
-
107
+
98
108
  # The proxying call to a method.
99
109
  #
100
110
  # @param [Method] method_def the definition of the method to be proxied
101
111
  # @param [Symbol] access the proxied method's access level (:public,
102
112
  # :protected, or :private)
103
- # @param [Boolean] include_block if true, the method call passes the block
104
- # that the proxy has received; if false, the block is ignored
105
113
  # @return [String] a chunk of Ruby that can be used to call the given method
106
114
  # when defining a proxy for it
107
- def self.proxy_method_call(method_def, access, include_block)
108
- arg_list = proxy_argument_list method_def, include_block
115
+ def self.proxy_method_call(method_def, access)
116
+ arg_list = proxy_call_argument_list method_def
109
117
 
110
118
  if access == :public
111
119
  "@__police_proxied__.#{method_def.name}(#{arg_list})"
112
120
  else
113
- "@__police_proxied__.__send__(:#{method_def.name}, #{arg_list})"
121
+ if arg_list.empty?
122
+ "@__police_proxied__.__send__(:#{method_def.name})"
123
+ else
124
+ "@__police_proxied__.__send__(:#{method_def.name}, #{arg_list})"
125
+ end
114
126
  end
115
127
  end
116
-
117
- # Code that labels the values yielded by a decorated method to its block.
128
+
129
+ # Code that labels the values yielded by a decorated method to its block.
118
130
  #
119
131
  # @param [Array<Police::DataFlow::Label>] label_classes the label classes
120
132
  # supported by the proxy class
@@ -126,39 +138,51 @@ module Proxying
126
138
  arg_list = proxy_argument_list method_def, false
127
139
  code_lines = ['labels = @__police_labels__']
128
140
  label_classes.each do |label_class|
129
- next unless hook = label_class.yield_args_hook(method_name)
130
- label_key = label_class.__id__
131
- code_lines << "labels[#{label_key}].each { |label, _| " \
132
- "label.#{hook}(self, yield_args, #{arg_list}) }"
141
+ if hook = label_class.yield_args_hook(method_name)
142
+ label_key = label_class.__id__
143
+ code_lines << "labels[#{label_key}].each { |label, _| " \
144
+ "label.#{hook}(self, yield_args, #{arg_list}) }"
145
+ elsif label_class.sticky?
146
+ label_key = label_class.__id__
147
+ code_lines << "labels[#{label_key}].each { |label, _| " \
148
+ "yield_args.map! { |arg| ::Police::DataFlow.label(arg, label) } " \
149
+ "}"
150
+ end
133
151
  end
134
152
  (code_lines.length > 1) ? code_lines.join('; ') : ''
135
153
  end
136
-
154
+
137
155
  # Code that labels return value of a decorated method.
138
156
  #
139
157
  # @param [Array<Police::DataFlow::Label>] label_classes the label classes
140
158
  # supported by the proxy class
141
159
  # @param [Method] method_def the definition of the method to be proxied
142
- # @return [String] a chunk of Ruby that can be used to invoke the return value
143
- # decorators of the labels held by a labeled object's proxy
160
+ # @return [String] a chunk of Ruby that can be used to invoke the return
161
+ # value decorators of the labels held by a labeled object's proxy
144
162
  def self.proxy_return_decorating(label_classes, method_def)
145
163
  method_name = method_def.name
146
164
  arg_list = proxy_argument_list method_def, false
147
165
  code_lines = ['labels = @__police_labels__']
148
166
  label_classes.each do |label_class|
149
- next unless hook = label_class.return_hook(method_name)
150
- label_key = label_class.__id__
151
- code_lines << "labels[#{label_key}].each { |label, _| " \
152
- "return_value = label.#{hook}(return_value, self, #{arg_list}) }"
167
+ if hook = label_class.return_hook(method_name)
168
+ label_key = label_class.__id__
169
+ code_lines << "labels[#{label_key}].each { |label, _| " \
170
+ "return_value = label.#{hook}(return_value, self, #{arg_list}) }"
171
+ elsif label_class.sticky?
172
+ label_key = label_class.__id__
173
+ code_lines << "labels[#{label_key}].each { |label, _| " \
174
+ "return_value = ::Police::DataFlow.label(return_value, label) }"
175
+ end
153
176
  end
154
177
  (code_lines.length > 1) ? code_lines.join('; ') : ''
155
178
  end
156
-
179
+
157
180
  # The list of arguments used to define a proxy for the given method.
158
181
  #
159
182
  # @param [Method] method_def the definition of the method to be proxied
160
- # @param [Boolean] captue_block if true, the method captures the block that it
161
- # receives
183
+ # @param [Boolean] captue_block if true, the method captures the block that
184
+ # it receives; this should be true when the returned code is used in
185
+ # method definitions, and false when it is used in method calls
162
186
  # @return [String] a chunk of Ruby that can be used as the argument list when
163
187
  # defining a proxy for the given method
164
188
  def self.proxy_argument_list(method_def, capture_block)
@@ -172,6 +196,157 @@ module Proxying
172
196
  arg_list << '&block' if capture_block
173
197
  arg_list.join ', '
174
198
  end
199
+
200
+ # The list of arguments used to call a proxied method.
201
+ #
202
+ # This assumes that the proxy method definition uses the code retuned by
203
+ # proxy_argument_list.
204
+ #
205
+ # @param [Method] method_def the definition of the method to be proxied;
206
+ # should match the value passed to proxy_argument_list
207
+ # @return [String] a chunk of Ruby that can be used as the argument list when
208
+ # defining a proxy for the given method
209
+ def self.proxy_call_argument_list(method_def)
210
+ source = Police::VmInfo.method_source method_def
211
+ if source == :native || source == :kernel
212
+ proxy_low_level_call_argument_list method_def
213
+ else
214
+ proxy_argument_list method_def, false
215
+ end
216
+ end
217
+
218
+ # The list of arguments used to call a proxied low-level method.
219
+ #
220
+ # Low-level methods don't use Ruby methods to manipulate their arguments, so
221
+ # they can't work on proxies, and need to receive the proxied objects
222
+ # as arguments.
223
+ #
224
+ # @param [Method] method_def the definition of the method to be proxied;
225
+ # should match the value passed to {#proxy_argument_list}
226
+ # @return [String] a chunk of Ruby that can be used as the argument list when
227
+ # defining a proxy for the given method
228
+ def self.proxy_low_level_call_argument_list(method_def)
229
+ arg_list = if method_def.arity >= 0
230
+ # Fixed number of arguments.
231
+ (1..method_def.arity).map do |i|
232
+ "(nil == arg#{i}.__police_labels__) ? arg#{i} : " +
233
+ "arg#{i}.__police_proxied__"
234
+ end
235
+ else
236
+ # Variable number of arguments.
237
+ args_mapper = '*(args.map { |a| (nil == a.__police_labels__) ? a : ' +
238
+ 'a.__police_proxied__ })'
239
+ (1..(-method_def.arity - 1)).map { |i|
240
+ "(nil == arg#{i}.__police_labels__) ? arg#{i} : " +
241
+ "arg#{i}.__police_proxied__"
242
+ } << args_mapper
243
+ end
244
+ arg_list.join ', '
245
+ end
246
+
247
+ # Boolean expression deciding if a proxied method received labeled arguments.
248
+ #
249
+ # If none of the method's arguments is labeled, the sticky label propagation
250
+ # logic can be completely bypassed.
251
+ #
252
+ # @param [Method] method_def the definition of the method to be proxied;
253
+ # should match the value passed to {#proxy_argument_list}
254
+ # @return [String] a chunk of Ruby that sets the 'fast_sticky' local variable
255
+ # to a truthy value if none of the proxied method's arguments is labeled,
256
+ # and to a falsey value if at least one argument has a label
257
+ def self.proxy_sticky_fastpath_check(method_def)
258
+ # Don't generate anything for zero-argument methods.
259
+ return '' if method_def.arity == 0
260
+
261
+ boolean_list = if method_def.arity > 0
262
+ # Fixed number of arguments.
263
+ (1..method_def.arity).map do |i|
264
+ "(nil == arg#{i}.__police_stickies__)"
265
+ end
266
+ else
267
+ # Variable number of arguments.
268
+ args_boolean = '(args.all? { |a| nil == a.__police_stickies__ })'
269
+ (1..(-method_def.arity - 1)).map { |i|
270
+ "(nil == arg#{i}.__police_stickies__)"
271
+ } << args_boolean
272
+ end
273
+ 'fast_sticky = ' + boolean_list.join(' && ')
274
+ end
275
+
276
+ # Code for computing the union of the arguments' sticky labels.
277
+ #
278
+ # The code is wrapped in a check that assumes the code returned by
279
+ # {#proxy_sticky_fastpath_check} was already executed.
280
+ #
281
+ # @param [Method] method_def the definition of the method to be proxied;
282
+ # should match the value passed to {#proxy_argument_list} and
283
+ # {#proxy_sticky_fastpath_check}
284
+ # @return [String] a chunk of Ruby that sets the 'sticky_labels' local
285
+ # variable to a label set that contains all the sticky labels in the
286
+ # method's arguments
287
+ def self.proxy_sticky_gathering(method_def)
288
+ # Don't generate anything for zero-argument methods.
289
+ return '' if method_def.arity == 0
290
+
291
+ code_lines = ['unless fast_sticky', 'sticky_labels = {}']
292
+ if method_def.arity > 0
293
+ # Fixed number of arguments.
294
+ 1.upto method_def.arity do |i|
295
+ code_lines << "unless nil == arg#{i}.__police_stickies__"
296
+ code_lines << '::Police::DataFlow::Labeling.merge_sets!(' +
297
+ "sticky_labels, arg#{i}.__police_stickies__)"
298
+ code_lines << 'end'
299
+ end
300
+ else
301
+ # Variable number of arguments.
302
+ 1.upto(-method_def.arity - 1) do |i|
303
+ code_lines << "unless nil == arg#{i}.__police_stickies__"
304
+ code_lines << '::Police::DataFlow::Labeling.merge_sets!(' +
305
+ "sticky_labels, arg#{i}.__police_stickies__)"
306
+ code_lines << 'end'
307
+ end
308
+ code_lines << 'args.each do |a|'
309
+ code_lines << 'unless nil == a.__police_stickies__'
310
+ code_lines << '::Police::DataFlow::Labeling.merge_sets!(' +
311
+ 'sticky_labels, a.__police_stickies__)'
312
+ code_lines << 'end'
313
+ code_lines << 'end'
314
+ end
315
+ code_lines << 'end'
316
+ code_lines.join '; '
317
+ end
318
+
319
+ # Code for applying argument sticky labels to a method's yielded arguments.
320
+ #
321
+ # This code assumes that the code returned by {#proxy_sticky_fastpath_check}
322
+ # and {#proxy_sticky_gathering} was already executed.
323
+ #
324
+ # @param [Method] method_def the definition of the method to be proxied;
325
+ # should match the value passed to {#proxy_argument_list} and
326
+ # {#proxy_sticky_fastpath_check}
327
+ # @return [String] a chunk of Ruby that sets the 'sticky_labels' local
328
+ # variable to a label set that contains all the sticky labels in the
329
+ # method's arguments
330
+ def self.proxy_yield_sticky_decorating(method_def)
331
+ # Don't generate anything for zero-argument methods.
332
+ return '' if method_def.arity == 0
333
+
334
+ 'unless fast_sticky; ' +
335
+ 'yield_args.map! do |a|; ' +
336
+ '::Police::DataFlow::Labeling.bulk_sticky_label(a, sticky_labels); ' +
337
+ 'end; ' +
338
+ 'end'
339
+ end
340
+
341
+ def self.proxy_return_sticky_decorating(method_def)
342
+ # Don't generate anything for zero-argument methods.
343
+ return '' if method_def.arity == 0
344
+
345
+ 'unless fast_sticky; ' +
346
+ 'return_value = ::Police::DataFlow::Labeling.bulk_sticky_label(' +
347
+ 'return_value, sticky_labels); ' +
348
+ 'end'
349
+ end
175
350
  end # namespace Police::DataFlow::Proxying
176
351
 
177
352
  end # namespace Police::DataFlow
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "police-dataflow"
8
- s.version = "0.0.1"
8
+ s.version = "0.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Victor Costan"]
12
- s.date = "2012-03-28"
12
+ s.date = "2013-09-24"
13
13
  s.description = "Pure Ruby implementtion of data flow label propagation."
14
14
  s.email = "victor@costan.us"
15
15
  s.extra_rdoc_files = [
@@ -27,36 +27,32 @@ Gem::Specification.new do |s|
27
27
  "lib/police-dataflow.rb",
28
28
  "lib/police/dataflow.rb",
29
29
  "lib/police/dataflow/core_extensions.rb",
30
- "lib/police/dataflow/guarding.rb",
30
+ "lib/police/dataflow/gate_profiles/ruby1.9.3",
31
+ "lib/police/dataflow/gating.rb",
31
32
  "lib/police/dataflow/label.rb",
32
33
  "lib/police/dataflow/labeling.rb",
33
34
  "lib/police/dataflow/proxies.rb",
34
35
  "lib/police/dataflow/proxy_base.rb",
36
+ "lib/police/dataflow/proxy_numeric.rb",
35
37
  "lib/police/dataflow/proxying.rb",
36
38
  "police-dataflow.gemspec",
39
+ "tasks/info.rake",
37
40
  "test/dataflow/core_extensions_test.rb",
38
41
  "test/dataflow/labeling_test.rb",
39
42
  "test/dataflow/proxies_test.rb",
40
43
  "test/dataflow/proxy_base_test.rb",
44
+ "test/dataflow/proxy_numeric_test.rb",
41
45
  "test/dataflow/proxying_test.rb",
42
46
  "test/helper.rb",
43
- "test/helpers/auto_flow_fixture.rb",
47
+ "test/helpers/hooks_flow_fixture.rb",
44
48
  "test/helpers/no_flow_fixture.rb",
45
- "test/helpers/proxying_fixture.rb"
49
+ "test/helpers/proxying_fixture.rb",
50
+ "test/helpers/sticky_fixture.rb"
46
51
  ]
47
52
  s.homepage = "http://github.com/csail/police"
48
53
  s.licenses = ["MIT"]
49
54
  s.require_paths = ["lib"]
50
- s.rubygems_version = "1.8.21"
55
+ s.rubygems_version = "2.0.7"
51
56
  s.summary = "Data flow label propagation"
52
-
53
- if s.respond_to? :specification_version then
54
- s.specification_version = 3
55
-
56
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
- else
58
- end
59
- else
60
- end
61
57
  end
62
58
 
@@ -0,0 +1,43 @@
1
+ task :vminfo do
2
+ require 'police/vminfo'
3
+ require 'yaml'
4
+
5
+ modules = Set.new
6
+ class_methods = {}
7
+ instance_methods = {}
8
+ Police::VmInfo.core_modules.sort_by(&:to_s).each do |module_object|
9
+ methods = []
10
+ Police::VmInfo.core_class_methods(module_object).sort_by(&:to_s).
11
+ each do |method|
12
+ source = Police::VmInfo.method_source(method)
13
+ next unless source == :native || source == :kernel
14
+ next if method.arity == 0
15
+ methods << method.name
16
+ end
17
+ unless methods.empty?
18
+ class_methods[module_object.to_s] = methods.sort
19
+ modules << module_object.to_s
20
+ end
21
+
22
+ methods = []
23
+ Police::VmInfo.core_instance_methods(module_object).sort_by(&:to_s).
24
+ each do |method|
25
+ source = Police::VmInfo.method_source(method)
26
+ next unless source == :native || source == :kernel
27
+ next if method.arity == 0
28
+ methods << method.name
29
+ end
30
+ unless methods.empty?
31
+ instance_methods[module_object.to_s] = methods.sort
32
+ modules << module_object.to_s
33
+ end
34
+ end
35
+
36
+ profile_path = File.expand_path(
37
+ "../lib/police/dataflow/gate_profiles/#{Police::VmInfo.signature}",
38
+ File.dirname(__FILE__))
39
+ File.open(profile_path, 'w') do |f|
40
+ f.write YAML.dump(core: modules.to_a.sort, :class => class_methods,
41
+ instance: instance_methods)
42
+ end
43
+ end