mirrors 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 (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