police-dataflow 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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