rubymirrors 0.0.1

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.
Files changed (60) hide show
  1. data/Gemfile +7 -0
  2. data/README.md +30 -0
  3. data/Rakefile +13 -0
  4. data/lib/abstract_reflection.rb +112 -0
  5. data/lib/abstract_reflection/class_mirror.rb +150 -0
  6. data/lib/abstract_reflection/compiler_mirror.rb +31 -0
  7. data/lib/abstract_reflection/field_mirror.rb +35 -0
  8. data/lib/abstract_reflection/gc_mirror.rb +19 -0
  9. data/lib/abstract_reflection/method_mirror.rb +253 -0
  10. data/lib/abstract_reflection/mirror.rb +108 -0
  11. data/lib/abstract_reflection/object_mirror.rb +48 -0
  12. data/lib/abstract_reflection/stack_frame_mirror.rb +61 -0
  13. data/lib/abstract_reflection/thread_mirror.rb +64 -0
  14. data/lib/maglev/reflection.rb +49 -0
  15. data/lib/maglev/reflection/class_mirror.rb +157 -0
  16. data/lib/maglev/reflection/core_ext/class_organizer.rb +20 -0
  17. data/lib/maglev/reflection/core_ext/maglev.rb +5 -0
  18. data/lib/maglev/reflection/core_ext/method.rb +154 -0
  19. data/lib/maglev/reflection/core_ext/module.rb +41 -0
  20. data/lib/maglev/reflection/core_ext/object.rb +4 -0
  21. data/lib/maglev/reflection/core_ext/thread.rb +226 -0
  22. data/lib/maglev/reflection/field_mirror.rb +39 -0
  23. data/lib/maglev/reflection/field_mirror/fixed_instance_variable_mirror.rb +25 -0
  24. data/lib/maglev/reflection/method_mirror.rb +149 -0
  25. data/lib/maglev/reflection/mirror.rb +6 -0
  26. data/lib/maglev/reflection/object_mirror.rb +18 -0
  27. data/lib/maglev/reflection/stack_frame_mirror.rb +104 -0
  28. data/lib/maglev/reflection/thread_mirror.rb +116 -0
  29. data/lib/rubinius/reflection.rb +6 -0
  30. data/lib/ruby/reflection.rb +74 -0
  31. data/lib/ruby/reflection/class_mirror.rb +89 -0
  32. data/lib/ruby/reflection/field_mirror.rb +32 -0
  33. data/lib/ruby/reflection/field_mirror/class_variable_mirror.rb +25 -0
  34. data/lib/ruby/reflection/field_mirror/constant_mirror.rb +36 -0
  35. data/lib/ruby/reflection/field_mirror/instance_variable_mirror.rb +25 -0
  36. data/lib/ruby/reflection/method_mirror.rb +122 -0
  37. data/lib/ruby/reflection/mirror.rb +12 -0
  38. data/lib/ruby/reflection/object_mirror.rb +25 -0
  39. data/lib/ruby/reflection/stack_frame_mirror.rb +49 -0
  40. data/lib/ruby/reflection/support/shift_reset.rb +29 -0
  41. data/lib/ruby/reflection/thread_mirror.rb +47 -0
  42. data/rubymirrors.gemspec +12 -0
  43. data/spec/class_spec.rb +92 -0
  44. data/spec/field_spec.rb +119 -0
  45. data/spec/fixtures/class_spec.rb +29 -0
  46. data/spec/fixtures/field_spec.rb +9 -0
  47. data/spec/fixtures/method_spec.rb +14 -0
  48. data/spec/fixtures/object_spec.rb +5 -0
  49. data/spec/fixtures/reflect_spec.rb +14 -0
  50. data/spec/fixtures/stack_frame_spec.rb +5 -0
  51. data/spec/fixtures/thread_spec.rb +5 -0
  52. data/spec/frame_spec.rb +80 -0
  53. data/spec/method_spec.rb +166 -0
  54. data/spec/object_spec.rb +18 -0
  55. data/spec/reflection_spec.rb +74 -0
  56. data/spec/spec_helper.rb +16 -0
  57. data/spec/spec_helper/mspec_patch.rb +29 -0
  58. data/spec/spec_helper/multiple_reflections.rb +14 -0
  59. data/spec/thread_spec.rb +142 -0
  60. metadata +173 -0
@@ -0,0 +1,80 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'fixtures/stack_frame_spec'
3
+
4
+ describe "StackFrameMirror" do
5
+ before(:each) do
6
+ @r = reflection
7
+ @t = Thread.start do
8
+ t = FrameFixture.new
9
+ t.my_stop("argument_value")
10
+ t.my_return
11
+ end
12
+ Thread.pass until @t.stop?
13
+ @m = @r.reflect(@t)
14
+ @s = @m.stack
15
+ @f = @s.detect {|frame| frame.name == "my_stop" }
16
+ end
17
+
18
+ it "should return StackFrameMirrors when reflecting on a Threads stack" do
19
+ @s.each {|a| a.should be_kind_of Reflection::StackFrameMirror }
20
+ end
21
+
22
+ it "the step offset" do
23
+ @f.step_offset.should be_kind_of Fixnum
24
+ end
25
+
26
+ it "the source offset" do
27
+ offs = @f.source_offset
28
+ offs.should be_kind_of Fixnum
29
+ offs.should < @f.method.source.size
30
+ end
31
+
32
+ it "the receiver" do
33
+ @f.receiver.target_class.name.should == "FrameFixture"
34
+ end
35
+
36
+ it "the self should be the receiver for a regular method" do
37
+ @f.self.reflectee.should == @f.receiver.reflectee
38
+ end
39
+
40
+ it "the arguments" do
41
+ @f.arguments.keys.should == ["argument"]
42
+ @f.arguments.values.collect(&:name).should == ["argument_value".inspect]
43
+ end
44
+
45
+ it "the locals" do
46
+ @f.locals.keys.should include "local"
47
+ @f.locals.values.collect(&:name).should include "local_value".inspect
48
+ end
49
+
50
+ it "the variable context" do
51
+ pending
52
+ end
53
+
54
+ it "restart the frame" do
55
+ prev_step_offset = @f.step_offset
56
+ @f.restart
57
+ @f.step_offset.should < prev_step_offset
58
+ @m.stack.first.should == @f
59
+ end
60
+
61
+ it "pop the frame" do
62
+ frame_before_f = @s[@s.index(@f) + 1]
63
+ @f.pop
64
+ @m.stack.first.should == frame_before_f
65
+ end
66
+
67
+ it "step over the current call" do
68
+ @f.restart
69
+ prev_step_offset = @f.step_offset
70
+ @f.step(:over)
71
+ @f.step_offset.should == prev_step_offset + 1
72
+ end
73
+
74
+ it "step into the next call" do
75
+ @f.restart
76
+ @f.step(:into)
77
+ @m.stack[0].should_not == @f
78
+ @m.stack[1].should == @f
79
+ end
80
+ end
@@ -0,0 +1,166 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'fixtures/method_spec'
3
+
4
+ describe "MethodMirror" do
5
+ describe "runtime reflection" do
6
+ describe "structural queries" do
7
+ before(:each) do
8
+ @r = Reflection.new(nil)
9
+ @f = MethodSpecFixture
10
+ m = MethodSpecFixture.instance_method(:source_location)
11
+ @m = @r.reflect(m)
12
+ end
13
+
14
+ it "file" do
15
+ @m.file.should == @f.new.source_location[0]
16
+ end
17
+
18
+ it "line" do
19
+ @m.line.should == (@f.new.source_location[1] - 1)
20
+ end
21
+
22
+ it "selector" do
23
+ @m.selector.should == @f.new.source_location[2]
24
+ end
25
+
26
+ it "defining class" do
27
+ @m.defining_class.name.should == @f.new.source_location[3].name
28
+ end
29
+
30
+ it "source" do
31
+ @m.source.should =~ /[__FILE__, __LINE__, __method__.to_s, self.class]/
32
+ end
33
+
34
+ it "line=" do
35
+ @m.line = 12
36
+ @m.line.should == 12
37
+ end
38
+
39
+ it "file=" do
40
+ @m.file = "no_file.rb"
41
+ @m.file.should == "no_file.rb"
42
+ end
43
+
44
+ it "source=" do
45
+ @m.source = @m.source.sub("__FILE__", "__FILE__.to_s")
46
+ @m.source.should =~ /[__FILE__.to_s, __LINE__, __method__.to_s, self.class]/
47
+ @m.defining_class.name.should == MethodSpecFixture.name
48
+ m = @r.reflect MethodSpecFixture.instance_method(:source_location)
49
+ m.source.should =~ /[__FILE__.to_s, __LINE__, __method__.to_s, self.class]/
50
+ end
51
+ end
52
+
53
+ describe "runtime behavior queries" do
54
+ def method_b(a, aa, b = 1, bb = 2, *args, &block)
55
+ to_s
56
+ super
57
+ end
58
+
59
+ before do
60
+ @m = @r.reflect(method(:method_b))
61
+ end
62
+
63
+ describe "arguments" do
64
+ it "argument list" do
65
+ @m.arguments.should include("a", "aa", "b", "bb", "args", "block")
66
+ end
67
+
68
+ it "block argument" do
69
+ @m.block_argument.should == "block"
70
+ end
71
+
72
+ it "required arguments" do
73
+ @m.required_arguments.should include("a", "aa")
74
+ end
75
+
76
+ it "optional arguments" do
77
+ @m.optional_arguments.should include("b", "bb")
78
+ end
79
+
80
+ it "splat argument" do
81
+ @m.splat_argument.should == "args"
82
+ end
83
+ end
84
+
85
+ it "step_locations" do
86
+ @m.step_offsets.each do |l|
87
+ l.should be_kind_of(Fixnum)
88
+ l.should < @m.source.length
89
+ end
90
+ end
91
+
92
+ it "message sends and their offsets" do
93
+ @m.send_offsets.should be_kind_of(Hash)
94
+ @m.send_offsets.keys.should include "to_s"
95
+ @m.send_offsets.values.first.should be_kind_of(Fixnum)
96
+ end
97
+
98
+ it "ast" do
99
+ pending
100
+ end
101
+
102
+ it "bytecodes" do
103
+ pending
104
+ end
105
+
106
+ describe "protection" do
107
+ before do
108
+ @cm = @r.reflect(MethodSpecFixture)
109
+ end
110
+
111
+ it "is public" do
112
+ m = @cm.method(:method_p_public)
113
+ m.public?.should.be_true
114
+ m.protected?.should.be_false
115
+ m.private?.should.be_false
116
+ end
117
+
118
+ it "is private" do
119
+ m = @cm.method(:method_p_private)
120
+ m.public?.should.be_false
121
+ m.protected?.should.be_false
122
+ m.private?.should.be_true
123
+ end
124
+
125
+ it "is protected" do
126
+ m = @cm.method(:method_p_protected)
127
+ m.public?.should.be_false
128
+ m.protected?.should.be_true
129
+ m.private?.should.be_false
130
+ end
131
+ end
132
+
133
+ it "returns native code if it was JITted" do
134
+ pending
135
+ end
136
+
137
+ it "returns average execution time" do
138
+ @m.execution_time_average.should be_kind_of(Time)
139
+ end
140
+
141
+ it "can return an approximation about the overall execution time it has been on the stack for" do
142
+ @m.execution_time.should be_kind_of(Time)
143
+ end
144
+
145
+ it "returns how many percent of the total process' execution time this method was active" do
146
+ @m.execution_time_share.should be_kind_of(Number)
147
+ @m.execution_time_share.should < 1
148
+ @m.execution_time_share.should > 0
149
+ end
150
+
151
+ it "returns invocation count" do
152
+ ic = @m.invocation_count
153
+ MethodSpecFixture.new.send(@m.selector)
154
+ @m.invocation_count.should == (ic + 1)
155
+ end
156
+
157
+ it "can delete a method from its home class" do
158
+ c = MethodSpecFixture
159
+ m = @r.reflect c.instance_method(:removeable_method)
160
+ c.instance_methods(false).map(&:to_s).should include("removeable_method")
161
+ m.delete
162
+ c.instance_methods(false).map(&:to_s).should_not include("removeable_method")
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'fixtures/object_spec'
3
+
4
+ describe "ObjectMirror" do
5
+ before do
6
+ @r = reflection
7
+ end
8
+
9
+ before(:each) do
10
+ @o = ObjectFixture.new
11
+ @m = @r.reflect(@o)
12
+ end
13
+
14
+ it "can query instance variables" do
15
+ vars = @m.variables
16
+ vars.collect(&:name).should == ["@ivar"]
17
+ end
18
+ end
@@ -0,0 +1,74 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'fixtures/reflect_spec'
3
+
4
+ describe Reflection do
5
+ it "returns the type of codebase it can work on" do
6
+ Reflection.codebase.should be_kind_of(Class)
7
+ end
8
+
9
+ describe "queries" do
10
+ before do
11
+ @r = reflection
12
+ end
13
+
14
+ it "finds known modules" do
15
+ modules = @r.modules.collect(&:name)
16
+ modules.should include("ReflectModule")
17
+ modules.should_not include("ReflectClass")
18
+ end
19
+
20
+ it "finds known classes" do
21
+ classes = @r.classes.collect(&:name)
22
+ classes.should include("ReflectClass")
23
+ classes.should_not include("ReflectModule")
24
+ end
25
+
26
+ it "finds known instances of something" do
27
+ class MyObj; end
28
+ class My2Obj < MyObj; end
29
+ a = MyObj.new
30
+ b = My2Obj.new
31
+ instances = @r.instances_of(MyObj).collect(&:name)
32
+ instances.should include(a.inspect)
33
+ instances.should_not include(b.inspect)
34
+ end
35
+
36
+ it "can get vm objects id" do
37
+ o = Object.new
38
+ @r.object_by_id(o.object_id).name.should == o.inspect
39
+ end
40
+
41
+ it "can find implementors of a method" do
42
+ l = @r.implementations_of("unique_reflect_fixture_method")
43
+ l.should be_kind_of Array
44
+ l.size.should == 1
45
+ l.first.selector.should == "unique_reflect_fixture_method"
46
+ l.first.defining_class.name.should == "ReflectClass"
47
+ end
48
+
49
+ it "can find senders of a method" do
50
+ l = @r.senders_of("unique_reflect_sent_method")
51
+ l.should be_kind_of Array
52
+ l.size.should == 1
53
+ l.first.selector.should == "unique_reflect_fixture_method"
54
+ l.first.defining_class.name.should == "ReflectClass"
55
+ end
56
+
57
+ it "returns all available threads" do
58
+ t = Thread.start {}
59
+ @r.threads.collect(&:name).should include(t.inspect)
60
+ end
61
+
62
+ it "reports a platform" do
63
+ @r.platform.should == RUBY_PLATFORM
64
+ end
65
+
66
+ it "reports a ruby implementation" do
67
+ @r.engine.should == RUBY_ENGINE
68
+ end
69
+
70
+ it "reports the implementation's version" do
71
+ @r.version.should == Object.const_get(:"#{RUBY_ENGINE.upcase}_VERSION")
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,16 @@
1
+ RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
2
+ mirror_api = ENV["MIRRORS"] || RUBY_ENGINE
3
+ mirror_api = 'rubinius' if mirror_api == 'rbx'
4
+
5
+ $:.push File.expand_path("..", __FILE__)
6
+ $:.push File.expand_path("../../lib", __FILE__)
7
+
8
+ require "#{mirror_api}/reflection"
9
+ include Object.const_get(mirror_api.capitalize)
10
+
11
+ require 'spec_helper/mspec_patch'
12
+ require 'spec_helper/multiple_reflections'
13
+
14
+ class PendingError < StandardError; end
15
+ MSpec.store :guarding_exceptions, [Reflection::CapabilitiesExceeded, PendingError]
16
+ def pending; raise PendingError; end
@@ -0,0 +1,29 @@
1
+ class GuardException
2
+ def initialize(example, exception)
3
+ @message = example && example.description || "<no description>"
4
+ @exception = exception
5
+ @method = exception.backtrace.first
6
+ end
7
+
8
+ def finish(*args)
9
+ print "Skipped '#{@message}'\n\t#{@exception}\n\tin #{@method}).\n\n"
10
+ end
11
+ end
12
+
13
+ module MSpec
14
+ class << self
15
+ alias mspec_protect protect
16
+
17
+ def protect(location, &block)
18
+ wrapped_block = proc do
19
+ begin
20
+ instance_eval(&block)
21
+ rescue *MSpec.retrieve(:guarding_exceptions)
22
+ MSpec.expectation
23
+ MSpec.register :finish, GuardException.new(MSpec.current.state, $!)
24
+ end
25
+ end
26
+ mspec_protect(location, &wrapped_block)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ require 'uri'
2
+
3
+ class Object
4
+ def reflection
5
+ map = { File => proc { Reflection.new("fixtures/reflect") },
6
+ NilClass => proc { Reflection.new(nil) },
7
+ URI => proc do
8
+ run_drb_vm
9
+ Reflection.new(URI::Generic.new("drb", "127.0.0.1", "9128"))
10
+ end }
11
+ map[Reflection.codebase][]
12
+ end
13
+ end
14
+
@@ -0,0 +1,142 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'fixtures/thread_spec'
3
+
4
+ describe "ThreadMirror" do
5
+ before(:each) do
6
+ @r = reflection
7
+ @t = Thread.start do
8
+ t = ThreadFixture.new
9
+ t.stop
10
+ t.return
11
+ end
12
+ @m = reflection.reflect(@t)
13
+ sleep 0.1 until @t.stop?
14
+ end
15
+
16
+ after(:each) do
17
+ @t.kill
18
+ end
19
+
20
+ describe "should query" do
21
+ it "the thread state" do
22
+ @m.status.should == "sleep"
23
+ end
24
+
25
+ it "the thread return value" do
26
+ @m.run
27
+ sleep 0.1 until @t.stop?
28
+ @m.return_value.should == ThreadFixture.new.return
29
+ end
30
+
31
+ it "the stack" do
32
+ @m.stack.collect(&:name).should include "stop"
33
+ end
34
+ end
35
+
36
+ describe "should manipulate" do
37
+ it "should be able to resume the thread" do
38
+ @m.run
39
+ sleep 0.1 while @t.status
40
+ @m.status.should == "dead"
41
+ end
42
+ end
43
+
44
+ it "should install exception blocks" do
45
+ @t = Thread.start do
46
+ t = ThreadFixture.new
47
+ t.stop
48
+ t.t_raise
49
+ end
50
+ @m = @r.reflect(@t)
51
+ handled = false
52
+ @m.handle_exception Exception do |e|
53
+ handled = true
54
+ end
55
+ @m.run
56
+ begin @t.join rescue RuntimeError end
57
+ handled.should == true
58
+ end
59
+
60
+ it "should handle the right thread exception blocks" do
61
+ t1 = Thread.start do
62
+ t = ThreadFixture.new
63
+ t.stop
64
+ t.t_raise
65
+ end
66
+ t2 = Thread.start do
67
+ t = ThreadFixture.new
68
+ t.stop
69
+ t.t_raise
70
+ end
71
+ m1 = @r.reflect(t1)
72
+ m2 = @r.reflect(t2)
73
+ handled = false
74
+ m1.handle_exception Exception do |e|
75
+ handled = true
76
+ end
77
+ m2.run
78
+ begin t2.join rescue RuntimeError end
79
+ handled.should == false
80
+ m1.run
81
+ begin t1.join rescue RuntimeError end
82
+ handled.should == true
83
+ end
84
+
85
+ it "should handle the many thread exception blocks" do
86
+ t1 = Thread.start do
87
+ t = ThreadFixture.new
88
+ t.stop
89
+ t.t_raise
90
+ end
91
+ t2 = Thread.start do
92
+ t = ThreadFixture.new
93
+ t.stop
94
+ t.t_raise
95
+ end
96
+ m1 = @r.reflect(t1)
97
+ m2 = @r.reflect(t2)
98
+ handles = []
99
+ m1.handle_exception Exception do |e|
100
+ handles << "m1"
101
+ end
102
+ m2.handle_exception Exception do |e|
103
+ handles << "m2"
104
+ end
105
+ m1.run
106
+ begin t1.join rescue RuntimeError end
107
+ handles.should include "m1"
108
+ handles.should_not include "m2"
109
+ m2.run
110
+ begin t2.join rescue RuntimeError end
111
+ handles.should include "m1"
112
+ handles.should include "m2"
113
+ end
114
+
115
+ describe "implement shift/reset" do
116
+ it "can be used to return what you want" do
117
+ retval = @r.reflect(Thread.current).reset do
118
+ @r.reflect(Thread.current).shift {|cc| cc.call "shifted" } + " test"
119
+ end
120
+
121
+ retval.should == "shifted test"
122
+ end
123
+
124
+ it "can drop the continuation" do
125
+ retval = @r.reflect(Thread.current).reset do
126
+ @r.reflect(Thread.current).shift {|cc| 1 } + 1
127
+ end
128
+
129
+ retval.should == 1
130
+ end
131
+
132
+ it "can be run and re-run" do
133
+ retval = @r.reflect(Thread.current).reset do
134
+ @r.reflect(Thread.current).shift do |cc|
135
+ cc.call(cc.call(1))
136
+ end + 1
137
+ end
138
+
139
+ retval.should == 3
140
+ end
141
+ end
142
+ end