rubymirrors 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +7 -0
- data/README.md +30 -0
- data/Rakefile +13 -0
- data/lib/abstract_reflection.rb +112 -0
- data/lib/abstract_reflection/class_mirror.rb +150 -0
- data/lib/abstract_reflection/compiler_mirror.rb +31 -0
- data/lib/abstract_reflection/field_mirror.rb +35 -0
- data/lib/abstract_reflection/gc_mirror.rb +19 -0
- data/lib/abstract_reflection/method_mirror.rb +253 -0
- data/lib/abstract_reflection/mirror.rb +108 -0
- data/lib/abstract_reflection/object_mirror.rb +48 -0
- data/lib/abstract_reflection/stack_frame_mirror.rb +61 -0
- data/lib/abstract_reflection/thread_mirror.rb +64 -0
- data/lib/maglev/reflection.rb +49 -0
- data/lib/maglev/reflection/class_mirror.rb +157 -0
- data/lib/maglev/reflection/core_ext/class_organizer.rb +20 -0
- data/lib/maglev/reflection/core_ext/maglev.rb +5 -0
- data/lib/maglev/reflection/core_ext/method.rb +154 -0
- data/lib/maglev/reflection/core_ext/module.rb +41 -0
- data/lib/maglev/reflection/core_ext/object.rb +4 -0
- data/lib/maglev/reflection/core_ext/thread.rb +226 -0
- data/lib/maglev/reflection/field_mirror.rb +39 -0
- data/lib/maglev/reflection/field_mirror/fixed_instance_variable_mirror.rb +25 -0
- data/lib/maglev/reflection/method_mirror.rb +149 -0
- data/lib/maglev/reflection/mirror.rb +6 -0
- data/lib/maglev/reflection/object_mirror.rb +18 -0
- data/lib/maglev/reflection/stack_frame_mirror.rb +104 -0
- data/lib/maglev/reflection/thread_mirror.rb +116 -0
- data/lib/rubinius/reflection.rb +6 -0
- data/lib/ruby/reflection.rb +74 -0
- data/lib/ruby/reflection/class_mirror.rb +89 -0
- data/lib/ruby/reflection/field_mirror.rb +32 -0
- data/lib/ruby/reflection/field_mirror/class_variable_mirror.rb +25 -0
- data/lib/ruby/reflection/field_mirror/constant_mirror.rb +36 -0
- data/lib/ruby/reflection/field_mirror/instance_variable_mirror.rb +25 -0
- data/lib/ruby/reflection/method_mirror.rb +122 -0
- data/lib/ruby/reflection/mirror.rb +12 -0
- data/lib/ruby/reflection/object_mirror.rb +25 -0
- data/lib/ruby/reflection/stack_frame_mirror.rb +49 -0
- data/lib/ruby/reflection/support/shift_reset.rb +29 -0
- data/lib/ruby/reflection/thread_mirror.rb +47 -0
- data/rubymirrors.gemspec +12 -0
- data/spec/class_spec.rb +92 -0
- data/spec/field_spec.rb +119 -0
- data/spec/fixtures/class_spec.rb +29 -0
- data/spec/fixtures/field_spec.rb +9 -0
- data/spec/fixtures/method_spec.rb +14 -0
- data/spec/fixtures/object_spec.rb +5 -0
- data/spec/fixtures/reflect_spec.rb +14 -0
- data/spec/fixtures/stack_frame_spec.rb +5 -0
- data/spec/fixtures/thread_spec.rb +5 -0
- data/spec/frame_spec.rb +80 -0
- data/spec/method_spec.rb +166 -0
- data/spec/object_spec.rb +18 -0
- data/spec/reflection_spec.rb +74 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/spec_helper/mspec_patch.rb +29 -0
- data/spec/spec_helper/multiple_reflections.rb +14 -0
- data/spec/thread_spec.rb +142 -0
- metadata +173 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
module Ruby
|
2
|
+
class Reflection
|
3
|
+
class StackFrameMirror < Mirror
|
4
|
+
include AbstractReflection::StackFrameMirror
|
5
|
+
reflect! ThreadMirror::Frame
|
6
|
+
|
7
|
+
attr_reader :name
|
8
|
+
attr_reader :method
|
9
|
+
|
10
|
+
def initialize(obj)
|
11
|
+
super
|
12
|
+
@name = obj.method
|
13
|
+
@index = obj.index
|
14
|
+
@method = find_method_for(obj.file, obj.line)
|
15
|
+
@thread = obj.thread
|
16
|
+
end
|
17
|
+
|
18
|
+
def step_offset
|
19
|
+
@method.step_offsets.index(source_offset)
|
20
|
+
end
|
21
|
+
|
22
|
+
def source_offset
|
23
|
+
if next_frame = @thread.stack[@index - 1]
|
24
|
+
s = @method.send_offsets[next_frame.name]
|
25
|
+
end
|
26
|
+
s || raise(CapabilitiesExceeded)
|
27
|
+
end
|
28
|
+
|
29
|
+
def selector
|
30
|
+
@name
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def find_method_for(file, line)
|
35
|
+
# Find all methods that are in the same file and start before
|
36
|
+
# or on the current line of execution
|
37
|
+
possible_methods = reflection.implementations_of(@name).select do |m|
|
38
|
+
f, l = m.send(:source_location)
|
39
|
+
f == file && l.to_i <= line.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sort by source location. The last method will be the method
|
43
|
+
# that starts closest to the current line of execution. This
|
44
|
+
# should (for most purposes) be the method we're looking for
|
45
|
+
possible_methods.sort_by {|m| m.send(:source_location).last }.last
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Modeled after Andrzej Filinski's article "Representing
|
3
|
+
# Monads" at POPL'94, and a Scheme implementation of it.
|
4
|
+
# http://citeseer.ist.psu.edu/filinski94representing.html
|
5
|
+
#
|
6
|
+
# Copyright 2004–2011 Christian Neukirchen
|
7
|
+
module ShiftReset
|
8
|
+
@@metacont = lambda do |x|
|
9
|
+
raise RuntimeError, "You forgot the top-level reset..."
|
10
|
+
end
|
11
|
+
|
12
|
+
def reset(&block)
|
13
|
+
mc = @@metacont
|
14
|
+
callcc do |k|
|
15
|
+
@@metacont = lambda do |v|
|
16
|
+
@@metacont = mc
|
17
|
+
k.call v
|
18
|
+
end
|
19
|
+
x = block.call
|
20
|
+
@@metacont.call x
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def shift(&block)
|
25
|
+
callcc do |k|
|
26
|
+
@@metacont.call block.call(lambda {|*v| reset { k.call *v } })
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'ruby/reflection/support/shift_reset'
|
2
|
+
require 'continuation' unless defined? callcc
|
3
|
+
|
4
|
+
module Ruby
|
5
|
+
class Reflection
|
6
|
+
class ThreadMirror < ObjectMirror
|
7
|
+
include AbstractReflection::ThreadMirror
|
8
|
+
include ShiftReset
|
9
|
+
reflect! Thread
|
10
|
+
Frame = Struct.new :method, :index, :file, :line, :thread
|
11
|
+
|
12
|
+
def status
|
13
|
+
s = @subject.status
|
14
|
+
if s.respond_to? :to_str
|
15
|
+
s.to_str
|
16
|
+
elsif s.nil?
|
17
|
+
"aborted"
|
18
|
+
else
|
19
|
+
"dead"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
@subject.run
|
25
|
+
end
|
26
|
+
|
27
|
+
def stack
|
28
|
+
if bt = @subject.backtrace
|
29
|
+
bt.each_with_index.collect do |str, idx|
|
30
|
+
file, line, method_spec = str.split(':')
|
31
|
+
method_spec =~ /\`([^']+)'/
|
32
|
+
method = $1
|
33
|
+
frame = Frame.new method, idx, file, line, self
|
34
|
+
reflection.reflect frame
|
35
|
+
end
|
36
|
+
else
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def return_value
|
42
|
+
return nil if @subject.alive?
|
43
|
+
@subject.value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/rubymirrors.gemspec
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "rubymirrors"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.authors = ["Tim Felgentreff"]
|
6
|
+
s.email = ["timfelgentreff@gmail.com"]
|
7
|
+
s.summary = "Mirror API for Ruby"
|
8
|
+
s.description = File.read(File.expand_path("../README.md", __FILE__))
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
11
|
+
s.require_paths = ["lib"]
|
12
|
+
end
|
data/spec/class_spec.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'fixtures/class_spec'
|
3
|
+
|
4
|
+
describe "ClassMirror" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@r = reflection
|
8
|
+
@m = reflection.reflect(ClassFixture)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "queries" do
|
12
|
+
it "name" do
|
13
|
+
@m.name.should == ClassFixture.name
|
14
|
+
end
|
15
|
+
|
16
|
+
it "known instance variables" do
|
17
|
+
names = @m.instance_variables.collect(&:name)
|
18
|
+
names.should include("@a", "@b")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "known class variables" do
|
22
|
+
names = @m.class_variables.collect(&:name)
|
23
|
+
names.should include "@@cva"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "known class instance variables" do
|
27
|
+
names = @m.class_instance_variables.collect(&:name)
|
28
|
+
names.should include "@civa"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "known constants" do
|
32
|
+
names = @m.constants.collect(&:name)
|
33
|
+
names.should include "Foo"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can return a mirror on a particular constant" do
|
37
|
+
@m.constant("Foo").name.should == "Foo"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can find a nested constant" do
|
41
|
+
cname = ClassFixture::ClassFixtureNested::ClassFixtureNestedNested.name
|
42
|
+
ct = @m.constant(cname)
|
43
|
+
ct.name.should == "ClassFixtureNestedNested"
|
44
|
+
ct.value.name.should == cname
|
45
|
+
end
|
46
|
+
|
47
|
+
it "known inner classes" do
|
48
|
+
@m.nested_classes.first.name.should == ClassFixture::ClassFixtureNested.name
|
49
|
+
end
|
50
|
+
|
51
|
+
it "known instance methods" do
|
52
|
+
@m.methods.size.should == ClassFixture.instance_methods(false).size
|
53
|
+
end
|
54
|
+
|
55
|
+
it "can return one particular method" do
|
56
|
+
n = ClassFixture.instance_methods.first
|
57
|
+
@m.method(n).mirrors?(ClassFixture.instance_method(n)).should == true
|
58
|
+
end
|
59
|
+
|
60
|
+
it "ancestors" do
|
61
|
+
ancestorsnames = [*ClassFixture.ancestors.collect(&:name)]
|
62
|
+
@m.ancestors.collect(&:name).should include *ancestorsnames
|
63
|
+
end
|
64
|
+
|
65
|
+
it "superclass" do
|
66
|
+
@m.superclass.name.should == ClassFixture.superclass.name
|
67
|
+
end
|
68
|
+
|
69
|
+
it "known subclasses" do
|
70
|
+
@m.subclasses.size.should == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
it "mixins" do
|
74
|
+
@m.mixins.first.name.should == ClassFixtureModule.name
|
75
|
+
end
|
76
|
+
|
77
|
+
it "nesting" do
|
78
|
+
m = @r.reflect ClassFixture::ClassFixtureNested
|
79
|
+
nesting = m.nesting
|
80
|
+
nesting.should == [ClassFixture::ClassFixtureNested, ClassFixture]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "source locations" do
|
84
|
+
@m.source_files.any? {|l| l =~ /spec\/class_spec.rb/ }.should.be_true
|
85
|
+
@m.source_files.any? {|l| l =~ /fixtures\/class_spec.rb/ }.should.be_true
|
86
|
+
end
|
87
|
+
|
88
|
+
it "value of a known constant" do
|
89
|
+
@m.constant("Foo").value.name.should == "Bar".inspect
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/spec/field_spec.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'fixtures/field_spec'
|
3
|
+
|
4
|
+
describe "FieldMirror" do
|
5
|
+
describe "variables", :shared => true do
|
6
|
+
it "reports the ivar name as name" do
|
7
|
+
@m.name.should == @nom
|
8
|
+
end
|
9
|
+
|
10
|
+
it "reports the var value" do
|
11
|
+
@m.value.name.should == @nom.sub(/@@?/, '').inspect
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can change the value" do
|
15
|
+
old_value = @o.send(:"#{@class_side}_variable_get", @nom)
|
16
|
+
@m.value = "changed"
|
17
|
+
@o.send(:"#{@class_side}_variable_get", @nom).should == "changed"
|
18
|
+
@m.value = old_value
|
19
|
+
end
|
20
|
+
|
21
|
+
it "always shows the current value" do
|
22
|
+
@m.value.name.should == @nom.sub(/@@?/, '').inspect
|
23
|
+
@o.send(:"#{@class_side}_variable_set", @nom, "changed")
|
24
|
+
@m.value.name.should == "changed".inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
it "reports vars as private only" do
|
28
|
+
@m.private?.should be_true
|
29
|
+
@m.protected?.should be_false
|
30
|
+
@m.public?.should be_false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "instance variables" do
|
35
|
+
before(:each) do
|
36
|
+
@o = FieldFixture.new
|
37
|
+
@om = reflection.reflect(@o)
|
38
|
+
@m = @om.variables.first
|
39
|
+
@nom = "@ivar"
|
40
|
+
@class_side = "instance"
|
41
|
+
end
|
42
|
+
|
43
|
+
it_behaves_like "variables", "instance variables"
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "class instance variables" do
|
47
|
+
before(:each) do
|
48
|
+
@o = FieldFixture
|
49
|
+
@om = reflection.reflect(@o)
|
50
|
+
@m = @om.variables.first
|
51
|
+
@nom = "@civar"
|
52
|
+
@class_side = "instance"
|
53
|
+
end
|
54
|
+
|
55
|
+
it_behaves_like "variables", "class instance variables"
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "class variables" do
|
59
|
+
before(:each) do
|
60
|
+
@o = FieldFixture
|
61
|
+
@om = reflection.reflect(@o)
|
62
|
+
@m = @om.class_variables.first
|
63
|
+
@nom = "@@cvar"
|
64
|
+
@class_side = "class"
|
65
|
+
end
|
66
|
+
|
67
|
+
it_behaves_like "variables", "class variables"
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "constants" do
|
71
|
+
before(:each) do
|
72
|
+
@o = FieldFixture
|
73
|
+
@om = reflection.reflect(@o)
|
74
|
+
@m = @om.constants.first
|
75
|
+
@name = "CONSTANT"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "reports the constant name as name" do
|
79
|
+
@m.name.should == @name
|
80
|
+
end
|
81
|
+
|
82
|
+
it "reports the ivar value" do
|
83
|
+
@m.value.name.should == @name.downcase.inspect
|
84
|
+
end
|
85
|
+
|
86
|
+
it "can change the value" do
|
87
|
+
old_value = @m.value.reflectee
|
88
|
+
@m.value = "changed"
|
89
|
+
@o.const_get(@name).should == "changed"
|
90
|
+
@m.value = old_value
|
91
|
+
end
|
92
|
+
|
93
|
+
it "always shows the current value" do
|
94
|
+
@m.value.name.should == @name.downcase.inspect
|
95
|
+
@o.const_set(@name, "changed")
|
96
|
+
@m.value.name.should == "changed".inspect
|
97
|
+
end
|
98
|
+
|
99
|
+
it "reports constants as public" do
|
100
|
+
@m.private?.should be_false
|
101
|
+
@m.protected?.should be_false
|
102
|
+
@m.public?.should be_true
|
103
|
+
end
|
104
|
+
|
105
|
+
it "can delete a constant" do
|
106
|
+
@m.delete
|
107
|
+
@om.constants.should_not include @m
|
108
|
+
|
109
|
+
@m = @om.reflectee.const_set(@m.name, @name)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "can add a constant" do
|
113
|
+
cst = @om.constant("MyNewlyAddedConstant")
|
114
|
+
@om.constants.collect(&:name).should_not include("MyNewlyAddedConstant")
|
115
|
+
cst.value = "MyNewlyAddedConstant"
|
116
|
+
@om.constants.collect(&:name).should include("MyNewlyAddedConstant")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ClassFixtureModule
|
2
|
+
end
|
3
|
+
|
4
|
+
class ClassFixture
|
5
|
+
Foo = "Bar"
|
6
|
+
|
7
|
+
class ClassFixtureNested
|
8
|
+
class ClassFixtureNestedNested
|
9
|
+
end
|
10
|
+
end
|
11
|
+
include ClassFixtureModule
|
12
|
+
|
13
|
+
attr_accessor :b
|
14
|
+
def a
|
15
|
+
@a = 1
|
16
|
+
@@cvb = 1
|
17
|
+
end
|
18
|
+
|
19
|
+
@@cva = 1
|
20
|
+
@civa = 1
|
21
|
+
|
22
|
+
def self.b
|
23
|
+
@@cvc = 1
|
24
|
+
@civb = 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ClassFixtureSubclass < ClassFixture; end
|
29
|
+
class ClassFixtureSubclassSubclass < ClassFixtureSubclass; end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class MethodSpecFixture
|
2
|
+
def source_location
|
3
|
+
[__FILE__, __LINE__, __method__.to_s, self.class]
|
4
|
+
end
|
5
|
+
|
6
|
+
def removeable_method
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_p_public; end
|
10
|
+
def method_p_private; end
|
11
|
+
private :method_p_private
|
12
|
+
def method_p_protected; end
|
13
|
+
protected :method_p_protected
|
14
|
+
end
|