mirrors 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +2 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +23 -0
  6. data/LICENSE.txt +59 -0
  7. data/README.md +1 -0
  8. data/asdfasdf.rb +53 -0
  9. data/bin/bundler +17 -0
  10. data/bin/byebug +17 -0
  11. data/bin/testunit +8 -0
  12. data/circle.yml +6 -0
  13. data/dev.yml +6 -0
  14. data/lib/mirrors.rb +150 -0
  15. data/lib/mirrors/class_mirror.rb +197 -0
  16. data/lib/mirrors/class_mixin.rb +11 -0
  17. data/lib/mirrors/field_mirror.rb +24 -0
  18. data/lib/mirrors/field_mirror/class_variable_mirror.rb +23 -0
  19. data/lib/mirrors/field_mirror/constant_mirror.rb +34 -0
  20. data/lib/mirrors/field_mirror/instance_variable_mirror.rb +23 -0
  21. data/lib/mirrors/hook.rb +33 -0
  22. data/lib/mirrors/index/indexer.rb +6 -0
  23. data/lib/mirrors/index/marker.rb +40 -0
  24. data/lib/mirrors/invoke.rb +29 -0
  25. data/lib/mirrors/method_mirror.rb +206 -0
  26. data/lib/mirrors/mirror.rb +37 -0
  27. data/lib/mirrors/object_mirror.rb +25 -0
  28. data/lib/mirrors/package_inference.rb +164 -0
  29. data/lib/mirrors/package_inference/class_to_file_resolver.rb +66 -0
  30. data/lib/mirrors/package_mirror.rb +33 -0
  31. data/lib/mirrors/visitors/disasm_visitor.rb +11 -0
  32. data/lib/mirrors/visitors/iseq_visitor.rb +84 -0
  33. data/lib/mirrors/visitors/references_visitor.rb +58 -0
  34. data/lib/mirrors/visitors/yasmdata.rb +212 -0
  35. data/lol.rb +35 -0
  36. data/mirrors.gemspec +19 -0
  37. data/test/fixtures/class.rb +29 -0
  38. data/test/fixtures/field.rb +9 -0
  39. data/test/fixtures/method.rb +15 -0
  40. data/test/fixtures/object.rb +5 -0
  41. data/test/fixtures/reflect.rb +14 -0
  42. data/test/mirrors/class_mirror_test.rb +87 -0
  43. data/test/mirrors/field_mirror_test.rb +125 -0
  44. data/test/mirrors/iseq_visitor_test.rb +56 -0
  45. data/test/mirrors/marker_test.rb +48 -0
  46. data/test/mirrors/method_mirror_test.rb +62 -0
  47. data/test/mirrors/object_mirror_test.rb +16 -0
  48. data/test/mirrors/package_inference_test.rb +31 -0
  49. data/test/mirrors/references_visitor_test.rb +30 -0
  50. data/test/mirrors_test.rb +38 -0
  51. data/test/test_helper.rb +12 -0
  52. metadata +137 -0
@@ -0,0 +1,212 @@
1
+ # -*-ruby-*-
2
+ #
3
+
4
+ class VM
5
+ class InstructionSequence
6
+ class Instruction
7
+ InsnID2NO = {
8
+ nop: 0,
9
+ getlocal: 1,
10
+ setlocal: 2,
11
+ getspecial: 3,
12
+ setspecial: 4,
13
+ getinstancevariable: 5,
14
+ setinstancevariable: 6,
15
+ getclassvariable: 7,
16
+ setclassvariable: 8,
17
+ getconstant: 9,
18
+ setconstant: 10,
19
+ getglobal: 11,
20
+ setglobal: 12,
21
+ putnil: 13,
22
+ putself: 14,
23
+ putobject: 15,
24
+ putspecialobject: 16,
25
+ putiseq: 17,
26
+ putstring: 18,
27
+ concatstrings: 19,
28
+ tostring: 20,
29
+ freezestring: 21,
30
+ toregexp: 22,
31
+ newarray: 23,
32
+ duparray: 24,
33
+ expandarray: 25,
34
+ concatarray: 26,
35
+ splatarray: 27,
36
+ newhash: 28,
37
+ newrange: 29,
38
+ pop: 30,
39
+ dup: 31,
40
+ dupn: 32,
41
+ swap: 33,
42
+ reverse: 34,
43
+ reput: 35,
44
+ topn: 36,
45
+ setn: 37,
46
+ adjuststack: 38,
47
+ defined: 39,
48
+ checkmatch: 40,
49
+ checkkeyword: 41,
50
+ trace: 42,
51
+ defineclass: 43,
52
+ send: 44,
53
+ opt_str_freeze: 45,
54
+ opt_newarray_max: 46,
55
+ opt_newarray_min: 47,
56
+ opt_send_without_block: 48,
57
+ invokesuper: 49,
58
+ invokeblock: 50,
59
+ leave: 51,
60
+ throw: 52,
61
+ jump: 53,
62
+ branchif: 54,
63
+ branchunless: 55,
64
+ branchnil: 56,
65
+ getinlinecache: 57,
66
+ setinlinecache: 58,
67
+ once: 59,
68
+ opt_case_dispatch: 60,
69
+ opt_plus: 61,
70
+ opt_minus: 62,
71
+ opt_mult: 63,
72
+ opt_div: 64,
73
+ opt_mod: 65,
74
+ opt_eq: 66,
75
+ opt_neq: 67,
76
+ opt_lt: 68,
77
+ opt_le: 69,
78
+ opt_gt: 70,
79
+ opt_ge: 71,
80
+ opt_ltlt: 72,
81
+ opt_aref: 73,
82
+ opt_aset: 74,
83
+ opt_aset_with: 75,
84
+ opt_aref_with: 76,
85
+ opt_length: 77,
86
+ opt_size: 78,
87
+ opt_empty_p: 79,
88
+ opt_succ: 80,
89
+ opt_not: 81,
90
+ opt_regexpmatch1: 82,
91
+ opt_regexpmatch2: 83,
92
+ opt_call_c_function: 84,
93
+ bitblt: 85,
94
+ answer: 86,
95
+ getlocal_OP__WC__0: 87,
96
+ getlocal_OP__WC__1: 88,
97
+ setlocal_OP__WC__0: 89,
98
+ setlocal_OP__WC__1: 90,
99
+ putobject_OP_INT2FIX_O_0_C_: 91,
100
+ putobject_OP_INT2FIX_O_1_C_: 92,
101
+
102
+ }
103
+
104
+ def self.id2insn_no(id)
105
+ if InsnID2NO.key? id
106
+ InsnID2NO[id]
107
+ end
108
+ end
109
+
110
+ InsnNO2Size = [
111
+ 1, # nop => 0
112
+ 3, # getlocal => 1
113
+ 3, # setlocal => 2
114
+ 3, # getspecial => 3
115
+ 2, # setspecial => 4
116
+ 3, # getinstancevariable => 5
117
+ 3, # setinstancevariable => 6
118
+ 2, # getclassvariable => 7
119
+ 2, # setclassvariable => 8
120
+ 2, # getconstant => 9
121
+ 2, # setconstant => 10
122
+ 2, # getglobal => 11
123
+ 2, # setglobal => 12
124
+ 1, # putnil => 13
125
+ 1, # putself => 14
126
+ 2, # putobject => 15
127
+ 2, # putspecialobject => 16
128
+ 2, # putiseq => 17
129
+ 2, # putstring => 18
130
+ 2, # concatstrings => 19
131
+ 1, # tostring => 20
132
+ 2, # freezestring => 21
133
+ 3, # toregexp => 22
134
+ 2, # newarray => 23
135
+ 2, # duparray => 24
136
+ 3, # expandarray => 25
137
+ 1, # concatarray => 26
138
+ 2, # splatarray => 27
139
+ 2, # newhash => 28
140
+ 2, # newrange => 29
141
+ 1, # pop => 30
142
+ 1, # dup => 31
143
+ 2, # dupn => 32
144
+ 1, # swap => 33
145
+ 2, # reverse => 34
146
+ 1, # reput => 35
147
+ 2, # topn => 36
148
+ 2, # setn => 37
149
+ 2, # adjuststack => 38
150
+ 4, # defined => 39
151
+ 2, # checkmatch => 40
152
+ 3, # checkkeyword => 41
153
+ 2, # trace => 42
154
+ 4, # defineclass => 43
155
+ 4, # send => 44
156
+ 2, # opt_str_freeze => 45
157
+ 2, # opt_newarray_max => 46
158
+ 2, # opt_newarray_min => 47
159
+ 3, # opt_send_without_block => 48
160
+ 4, # invokesuper => 49
161
+ 2, # invokeblock => 50
162
+ 1, # leave => 51
163
+ 2, # throw => 52
164
+ 2, # jump => 53
165
+ 2, # branchif => 54
166
+ 2, # branchunless => 55
167
+ 2, # branchnil => 56
168
+ 3, # getinlinecache => 57
169
+ 2, # setinlinecache => 58
170
+ 3, # once => 59
171
+ 3, # opt_case_dispatch => 60
172
+ 3, # opt_plus => 61
173
+ 3, # opt_minus => 62
174
+ 3, # opt_mult => 63
175
+ 3, # opt_div => 64
176
+ 3, # opt_mod => 65
177
+ 3, # opt_eq => 66
178
+ 5, # opt_neq => 67
179
+ 3, # opt_lt => 68
180
+ 3, # opt_le => 69
181
+ 3, # opt_gt => 70
182
+ 3, # opt_ge => 71
183
+ 3, # opt_ltlt => 72
184
+ 3, # opt_aref => 73
185
+ 3, # opt_aset => 74
186
+ 4, # opt_aset_with => 75
187
+ 4, # opt_aref_with => 76
188
+ 3, # opt_length => 77
189
+ 3, # opt_size => 78
190
+ 3, # opt_empty_p => 79
191
+ 3, # opt_succ => 80
192
+ 3, # opt_not => 81
193
+ 2, # opt_regexpmatch1 => 82
194
+ 3, # opt_regexpmatch2 => 83
195
+ 2, # opt_call_c_function => 84
196
+ 1, # bitblt => 85
197
+ 1, # answer => 86
198
+ 2, # getlocal_OP__WC__0 => 87
199
+ 2, # getlocal_OP__WC__1 => 88
200
+ 2, # setlocal_OP__WC__0 => 89
201
+ 2, # setlocal_OP__WC__1 => 90
202
+ 1, # putobject_OP_INT2FIX_O_0_C_ => 91
203
+ 1, # putobject_OP_INT2FIX_O_1_C_ => 92
204
+
205
+ ]
206
+
207
+ def self.insn_no2size(ins_no)
208
+ InsnNO2Size[ins_no]
209
+ end
210
+ end
211
+ end
212
+ end
data/lol.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'mirrors'
2
+ require 'byebug'
3
+
4
+ module ClassFixtureModule
5
+ end
6
+
7
+ class ClassFixture
8
+ Foo = "Bar"
9
+
10
+ class ClassFixtureNested
11
+ class ClassFixtureNestedNested
12
+ end
13
+ end
14
+ include ClassFixtureModule
15
+
16
+ attr_accessor :b
17
+ def a
18
+ @a = 1
19
+ @@cvb = 1
20
+ end
21
+
22
+ @@cva = 1
23
+ @civa = 1
24
+
25
+ def self.b
26
+ @@cvc = 1
27
+ @civb = 1
28
+ end
29
+ end
30
+
31
+ class ClassFixtureSubclass < ClassFixture; end
32
+ class ClassFixtureSubclassSubclass < ClassFixtureSubclass; end
33
+
34
+ @m = Mirrors.reflect(ClassFixture)
35
+ puts "Foo" == @m.constant("Foo").name
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'mirrors'
3
+ s.version = '0.0.1'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.licenses = ['MIT']
6
+ s.authors = ['Burke Libbey']
7
+ s.email = ['burke.libbey@shopify.com']
8
+ s.homepage = 'https://github.com/Shopify/mirrors'
9
+ s.summary = 'Mirror API for Ruby'
10
+ s.description = 'Provides a number of specs and classes that document a mirror API for Ruby.'
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- spec/*`.split("\n")
13
+ s.require_paths = ['lib']
14
+
15
+ s.add_development_dependency 'minitest', '~> 5.0'
16
+ s.add_development_dependency 'byebug', '~> 9.0.6'
17
+
18
+ s.add_runtime_dependency 'method_source', '~> 0.8'
19
+ 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,9 @@
1
+ class FieldFixture
2
+ attr_accessor :ivar
3
+ def initialize
4
+ @ivar = "ivar"
5
+ end
6
+ CONSTANT = "constant"
7
+ @@cvar = "cvar"
8
+ @civar = "civar"
9
+ end
@@ -0,0 +1,15 @@
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
+
11
+ def method_p_private; end
12
+ private :method_p_private
13
+ def method_p_protected; end
14
+ protected :method_p_protected
15
+ end
@@ -0,0 +1,5 @@
1
+ class ObjectFixture
2
+ def initialize
3
+ @ivar = "ivar"
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+
2
+ class ReflectClass
3
+ def unique_reflect_fixture_method
4
+ unique_reflect_sent_method
5
+ end
6
+
7
+ def unique_reflect_sent_method
8
+ end
9
+ end
10
+
11
+ module ReflectModule
12
+ end
13
+
14
+ reflect_string = "string"
@@ -0,0 +1,87 @@
1
+ require 'test_helper'
2
+
3
+ module Mirrors
4
+ class ClassMirrorTest < MiniTest::Test
5
+ def setup
6
+ @m = Mirrors.reflect(ClassFixture)
7
+ super
8
+ end
9
+
10
+ def test_name
11
+ assert_equal(ClassFixture.name, @m.name)
12
+ end
13
+
14
+ def test_class_variables
15
+ names = @m.class_variables.collect(&:name)
16
+ assert_includes(names, "@@cva")
17
+ end
18
+
19
+ def test_class_instance_variables
20
+ names = @m.class_instance_variables.collect(&:name)
21
+ assert_includes(names, "@civa")
22
+ end
23
+
24
+ def test_constants
25
+ names = @m.constants.collect(&:name)
26
+ assert_includes(names, "Foo")
27
+ end
28
+
29
+ def test_constant
30
+ assert_equal("Foo", @m.constant("Foo").name)
31
+ end
32
+
33
+ def test_nested_constant
34
+ cname = ClassFixture::ClassFixtureNested::ClassFixtureNestedNested.name
35
+ ct = @m.constant(cname)
36
+ assert_equal("ClassFixtureNestedNested", ct.name)
37
+ assert_equal(cname, ct.value.name)
38
+ end
39
+
40
+ def test_nested_classes
41
+ assert_equal(ClassFixture::ClassFixtureNested.name, @m.nested_classes.first.name)
42
+ end
43
+
44
+ def test_instance_methods
45
+ assert_equal(ClassFixture.instance_methods(false).size, @m.methods.size)
46
+ end
47
+
48
+ def test_instance_method
49
+ n = ClassFixture.instance_methods.first
50
+ assert(@m.method(n).mirrors?(ClassFixture.instance_method(n)))
51
+ end
52
+
53
+ def test_ancestors
54
+ act = @m.ancestors.map(&:name)
55
+ exp = ClassFixture.ancestors.map(&:name)
56
+ exp.each do |name|
57
+ assert_includes(act, name)
58
+ end
59
+ end
60
+
61
+ def test_superclass
62
+ assert_equal(ClassFixture.superclass.name, @m.superclass.name)
63
+ end
64
+
65
+ def test_subclasses
66
+ assert_equal(1, @m.subclasses.size)
67
+ end
68
+
69
+ def test_mixins
70
+ assert_equal(ClassFixtureModule.name, @m.mixins.first.name)
71
+ end
72
+
73
+ def test_nesting
74
+ m = Mirrors.reflect(ClassFixture::ClassFixtureNested)
75
+ nesting = m.nesting
76
+ assert_equal([ClassFixture::ClassFixtureNested, ClassFixture], nesting)
77
+ end
78
+
79
+ def test_source_locations
80
+ assert(@m.source_files.any? { |l| l.include?('fixtures/class.rb') })
81
+ end
82
+
83
+ def test_constant_value
84
+ assert_equal("Bar", @m.constant("Foo").value.name)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,125 @@
1
+ require 'test_helper'
2
+
3
+ module Mirrors
4
+ class FieldMirrorTest < MiniTest::Test
5
+ module FieldMirrorTests
6
+ def test_name
7
+ assert_equal(@nom, @m.name)
8
+ end
9
+
10
+ def test_value
11
+ assert_equal(@nom.sub(/@@?/, ''), @m.value.name)
12
+ end
13
+
14
+ def test_set_value
15
+ old_value = @o.send(:"#{@class_side}_variable_get", @nom)
16
+ @m.value = "changed"
17
+ assert_equal("changed", @o.send(:"#{@class_side}_variable_get", @nom))
18
+ assert_equal("changed", @m.value.name)
19
+ @m.value = old_value
20
+ end
21
+
22
+ def test_reports_vars_as_private
23
+ assert(@m.private?)
24
+ refute(@m.protected?)
25
+ refute(@m.public?)
26
+ end
27
+ end
28
+
29
+ class InstanceVariableMirrorTest < MiniTest::Test
30
+ def setup
31
+ @o = FieldFixture.new
32
+ @om = Mirrors.reflect(@o)
33
+ @m = @om.variables.first
34
+ @nom = "@ivar"
35
+ @class_side = "instance"
36
+ super
37
+ end
38
+
39
+ include FieldMirrorTests
40
+ end
41
+
42
+ class ClassInstanceVariableMirrorTest < MiniTest::Test
43
+ def setup
44
+ @o = FieldFixture
45
+ @om = Mirrors.reflect(@o)
46
+ @m = @om.variables.first
47
+ @nom = "@civar"
48
+ @class_side = "instance"
49
+ super
50
+ end
51
+
52
+ include FieldMirrorTests
53
+ end
54
+
55
+ class ClassVariableMirrorTest < MiniTest::Test
56
+ def setup
57
+ @o = FieldFixture
58
+ @om = Mirrors.reflect(@o)
59
+ @m = @om.class_variables.first
60
+ @nom = "@@cvar"
61
+ @class_side = "class"
62
+ super
63
+ end
64
+
65
+ include FieldMirrorTests
66
+ end
67
+ end
68
+
69
+ class ConstantMirrorTest < MiniTest::Test
70
+ def setup
71
+ @o = FieldFixture
72
+ @om = Mirrors.reflect(@o)
73
+ @m = @om.constants.first
74
+ @name = "CONSTANT"
75
+ super
76
+ end
77
+
78
+ def test_name
79
+ assert_equal(@name, @m.name)
80
+ end
81
+
82
+ def test_value
83
+ assert_equal(@name.downcase, @m.value.name)
84
+ end
85
+
86
+ def test_set_value
87
+ silence do
88
+ old_value = @m.value.reflectee
89
+ @m.value = "changed"
90
+ assert_equal("changed", @o.const_get(@name))
91
+ assert_equal("changed", @m.value.name)
92
+ @m.value = old_value
93
+ end
94
+ end
95
+
96
+ def test_public
97
+ refute(@m.private?)
98
+ refute(@m.protected?)
99
+ assert(@m.public?)
100
+ end
101
+
102
+ def test_delete
103
+ @m.delete
104
+ refute_includes(@om.constants, @m)
105
+ @m = @om.reflectee.const_set(@m.name, @name)
106
+ silence do
107
+ @om.constant(@name).value = "constant"
108
+ end
109
+ end
110
+
111
+ def test_add
112
+ cst = @om.constant("MyNewlyAddedConstant")
113
+ refute_includes(@om.constants.map(&:name), "MyNewlyAddedConstant")
114
+ cst.value = "MyNewlyAddedConstant"
115
+ assert_includes(@om.constants.map(&:name), "MyNewlyAddedConstant")
116
+ cst.delete
117
+ end
118
+
119
+ private
120
+
121
+ def silence(&block)
122
+ capture_subprocess_io(&block)
123
+ end
124
+ end
125
+ end