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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d3d02baa921913fcc9ac830bba816d1a9f47339
4
+ data.tar.gz: 71253ea57e8ae471532753da621496811112da9b
5
+ SHA512:
6
+ metadata.gz: 6922717b62cd336253f51223ff28b3c7be93471c15f7117d92e6b61796a562c613c027ac0185275aa6947c7631e24e7b586113850652ef80f13ff6420db86fa2
7
+ data.tar.gz: 4f3ffb84b4cada62a5f67b47be3484dbb832b62831c886a73554d12c0f6f9641e69466e51cb5abc0393b62ab16dd2e3a4ace12d14a855ebb63b836c87dcbe764
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ .rubocop-http*
4
+ .byebug_history
5
+ .vscode/
@@ -0,0 +1,2 @@
1
+ inherit_from:
2
+ - http://shopify.github.io/ruby-style-guide/rubocop.yml
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,23 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mirrors (0.0.1)
5
+ method_source (~> 0.8)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ byebug (9.0.6)
11
+ method_source (0.8.2)
12
+ minitest (5.9.1)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ byebug (~> 9.0.6)
19
+ minitest (~> 5.0)
20
+ mirrors!
21
+
22
+ BUNDLED WITH
23
+ 1.13.6
@@ -0,0 +1,59 @@
1
+ Copyright (c) 2016 Burke Libbey, Shopify
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ 3. The names of the contributors may not be used
15
+ to endorse or promote products derived from this software without
16
+ specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+
30
+ --- most code derived from github.com/timfel/rubymirrors ---
31
+
32
+
33
+ Copyright (c) Tim Felgentreff and individual contributors.
34
+ All rights reserved.
35
+
36
+ Redistribution and use in source and binary forms, with or without modification,
37
+ are permitted provided that the following conditions are met:
38
+
39
+ 1. Redistributions of source code must retain the above copyright notice,
40
+ this list of conditions and the following disclaimer.
41
+
42
+ 2. Redistributions in binary form must reproduce the above copyright
43
+ notice, this list of conditions and the following disclaimer in the
44
+ documentation and/or other materials provided with the distribution.
45
+
46
+ 3. The names of the contributors may not be used
47
+ to endorse or promote products derived from this software without
48
+ specific prior written permission.
49
+
50
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
51
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
52
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
54
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
55
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
56
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
57
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ Use `bin/frontend` and `bin/backend`.
@@ -0,0 +1,53 @@
1
+ class Resolver
2
+ def resolve(klass)
3
+ @files ||= {}
4
+
5
+ klass.instance_methods(false).each do |name|
6
+ meth = klass.instance_method(name)
7
+
8
+ file = begin
9
+ meth.source_location[0]
10
+ rescue # ???
11
+ next
12
+ end
13
+
14
+ contents = (@files[file] ||= File.open(file, 'r') { |f| f.readpartial(4096) })
15
+ n = klass.name.sub(/.*::/, '') # last component of module name
16
+ return file if contents =~ /^\s+(class|module) ([\S]+::)?#{Regexp.quote(n)}\s/
17
+ end
18
+ nil
19
+ end
20
+ end
21
+
22
+ def infer_class_file_from_methods(klass)
23
+ if m = Resolver.new.resolve(klass)
24
+ return m
25
+ end
26
+
27
+ methods = klass
28
+ .instance_methods(false)
29
+ .map { |n| klass.instance_method(n) }
30
+
31
+ defined_directly_on_class = methods
32
+ .select do |meth|
33
+ # aliased methods can show up with instance_methods(false)
34
+ # but their source_location and owner point to the module they came from.
35
+ meth.owner == klass &&
36
+ meth.source =~ /\A\s+def #{Regexp.quote(meth.name)}/
37
+ # as a mostly-useful heuristic, we just eliminate everything that was
38
+ # defined using a template eval or define_method.
39
+ end
40
+
41
+ files = Hash.new(0)
42
+
43
+ defined_directly_on_class.each do |meth|
44
+ begin
45
+ files[meth.source_location[0]] += 1
46
+ rescue # which class?
47
+ raise
48
+ end
49
+ end
50
+
51
+ file = files.max_by { |k, v| v }
52
+ file ? file[0] : nil
53
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'bundler' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("bundler", "bundler")
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'byebug' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("byebug", "byebug")
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ if [ $# -eq 0 ]; then
4
+ ruby -I"test" -e 'Dir.glob("./test/**/*_test.rb").each { |f| require f }' -- "$@"
5
+ else
6
+ path=$1
7
+ ruby -I"test" -e "require '${path#test/}'" -- "$@"
8
+ fi
@@ -0,0 +1,6 @@
1
+ machine:
2
+ ruby:
3
+ version: ruby-2.3.1
4
+ test:
5
+ override:
6
+ - bundle exec bin/testunit
data/dev.yml ADDED
@@ -0,0 +1,6 @@
1
+ up:
2
+ - ruby: 2.3.1
3
+ - bundler
4
+ commands:
5
+ test: bundle exec bin/testunit
6
+ console: bundle exec irb -r mirrors
@@ -0,0 +1,150 @@
1
+ require 'logger'
2
+ require 'mirrors/mirror'
3
+ require 'mirrors/object_mirror'
4
+ require 'mirrors/class_mirror'
5
+ require 'mirrors/field_mirror'
6
+ require 'mirrors/method_mirror'
7
+ require 'mirrors/package_mirror'
8
+ require 'mirrors/package_inference'
9
+ require 'mirrors/class_mixin'
10
+ require 'mirrors/index/indexer'
11
+
12
+ module Mirrors
13
+ extend self
14
+
15
+ @class_mirrors = {}
16
+ @constant_mirrors = {}
17
+ @watches = {}
18
+ @logger = Logger.new(STDOUT)
19
+
20
+ def packages
21
+ packages = {}
22
+ # Object is the top-level.
23
+ Object.constants.each do |const|
24
+ pkg = PackageInference.infer_from_toplevel(const)
25
+ packages[pkg] = true
26
+ end
27
+ toplevel_packages = packages.keys.map { |pkg| pkg.sub(/:.*/, '') }.sort
28
+ package_mirrors(toplevel_packages)
29
+ end
30
+
31
+ # This method can be used to query the system for known modules. It
32
+ # is not guaranteed that all possible modules are returned.
33
+ #
34
+ # @return [Array<ClassMirror>] a list of class mirrors
35
+ def modules
36
+ instances_of(Module).sort! { |a, b| a.name <=> b.name }
37
+ end
38
+
39
+ # This method can be used to query the system for known classes. It
40
+ # is not guaranteed that all possible classes are returned.
41
+ #
42
+ # @return [Array<ClassMirror>] a list of class mirrors
43
+ def classes
44
+ instances_of(Class).sort! { |a, b| a.name <=> b.name }
45
+ end
46
+
47
+ # Query the system for objects that are direct instances of the
48
+ # given class.
49
+ # @param [Class]
50
+ # @return [Array<ObjectMirror>] a list of appropriate mirrors for the requested objects
51
+ def instances_of(klass)
52
+ mirrors(ObjectSpace.each_object(klass).select { |obj| obj.class == klass })
53
+ end
54
+
55
+ # Ask the system to find the object with the given object id
56
+ # @param [Numeric] object id
57
+ # @return [ObjectMirror, NilClass] the object mirror or nil
58
+ def object_by_id(id)
59
+ obj = ObjectSpace._id2ref(id)
60
+ obj ? reflect(obj) : nil
61
+ end
62
+
63
+ # Query the system for implementors of a particular message
64
+ # @param [String] the message name
65
+ # @return [Array<MethodMirror>] the implementing methods
66
+ def implementations_of(str)
67
+ methods = ObjectSpace.each_object(Module).collect do |m|
68
+ ims = m.instance_methods(false).collect { |s| m.instance_method(s) }
69
+ cms = m.methods(false).collect { |s| m.method(s) }
70
+ ims + cms
71
+ end.flatten
72
+
73
+ mirrors(methods.select { |m| m.name.to_s == str.to_s })
74
+ end
75
+
76
+ def references_to(str)
77
+ filtered = {}
78
+ Mirrors.classes.each do |klass|
79
+ klass.methods.each do |m|
80
+ refs = m.references.select { |marker| marker.message.match(str) }
81
+ filtered[m] = refs unless refs.empty?
82
+ end
83
+ end
84
+ filtered
85
+ end
86
+
87
+ # Create a mirror for a given object in the system under
88
+ # observation. This is *the* factory method for all mirror
89
+ # instances, interning and cache invalidation will be added here.
90
+ #
91
+ # @param [Object]
92
+ # @return [Mirror]
93
+ def reflect(obj)
94
+ klass = basic_class(obj)
95
+ mirror =
96
+ if klass == FieldMirror::Field || klass == Symbol
97
+ case obj.name.to_s
98
+ when /^@@/
99
+ intern_field_mirror(ClassVariableMirror.new(obj))
100
+ when /^@/
101
+ # instance variables not interned as they are not guaranteed to be
102
+ # present in all instances
103
+ InstanceVariableMirror.new(obj)
104
+ else
105
+ intern_field_mirror(ConstantMirror.new(obj))
106
+ end
107
+ elsif klass == Method || klass == UnboundMethod
108
+ intern_method_mirror(MethodMirror.new(obj))
109
+ elsif klass == Class || klass == Module
110
+ intern_class_mirror(ClassMirror.new(obj))
111
+ else
112
+ # TODO: revisit if ObjectMirror delivers value
113
+ ObjectMirror.new(obj)
114
+ end
115
+ raise "badness" unless mirror.is_a?(Mirror)
116
+ mirror
117
+ end
118
+
119
+ private
120
+
121
+ # find the class of obj
122
+ def basic_class(obj)
123
+ Kernel.instance_method(:class).bind(obj).call
124
+ end
125
+
126
+ # find the class name of obj
127
+ def basic_class_name(klass)
128
+ Class.instance_method(:name).bind(klass).call
129
+ end
130
+
131
+ def intern_class_mirror(mirror)
132
+ interned = @class_mirrors[mirror.name] ||= mirror
133
+ end
134
+
135
+ def intern_method_mirror(mirror)
136
+ mirror.defining_class.intern_method_mirror(mirror)
137
+ end
138
+
139
+ def intern_field_mirror(mirror)
140
+ mirror.defining_class.intern_field_mirror(mirror)
141
+ end
142
+
143
+ def mirrors(list)
144
+ list.map { |e| reflect(e) }
145
+ end
146
+
147
+ def package_mirrors(list)
148
+ list.map { |e| PackageMirror.reflect(e) }
149
+ end
150
+ end
@@ -0,0 +1,197 @@
1
+ module Mirrors
2
+ # A specific mirror for a class, that includes all the capabilites
3
+ # and information we can gather about classes.
4
+ class ClassMirror < ObjectMirror
5
+ def initialize(obj)
6
+ super(obj)
7
+ @field_mirrors = {}
8
+ @method_mirrors = {}
9
+ end
10
+
11
+ def is_class
12
+ @subject.is_a?(Class)
13
+ end
14
+
15
+ def package
16
+ # TODO(burke)
17
+ end
18
+
19
+ def fields
20
+ [constants, class_variables, class_instance_variables, instance_variables].flatten
21
+ end
22
+
23
+ # The known class variables.
24
+ # @see #instance_variables
25
+ # @return [Array<FieldMirror>]
26
+ def class_variables
27
+ field_mirrors(@subject.class_variables)
28
+ end
29
+
30
+ # The known class variables.
31
+ # @see #instance_variables
32
+ # @return [Array<FieldMirror>]
33
+ def class_instance_variables
34
+ field_mirrors(@subject.instance_variables)
35
+ end
36
+
37
+ # The source files this class is defined and/or extended in.
38
+ #
39
+ # @return [Array<String,File>]
40
+ def source_files
41
+ locations = @subject.instance_methods(false).collect do |name|
42
+ method = @subject.instance_method(name)
43
+ sl = method.source_location
44
+ sl.first if sl
45
+ end
46
+ locations.compact.uniq
47
+ end
48
+
49
+ # The singleton class of this class
50
+ #
51
+ # @return [ClassMirror]
52
+ def singleton_class
53
+ Mirrors.reflect(@subject.singleton_class)
54
+ end
55
+
56
+ # Predicate to determine whether the subject is a singleton class
57
+ #
58
+ # @return [true,false]
59
+ def singleton_class?
60
+ name.match(/^\#<Class:.*>$/)
61
+ end
62
+
63
+ # The mixins included in the ancestors of this class.
64
+ #
65
+ # @return [Array<ClassMirror>]
66
+ def mixins
67
+ mirrors(@subject.ancestors.reject { |m| m.is_a?(Class) })
68
+ end
69
+
70
+ # The direct superclass
71
+ #
72
+ # @return [ClassMirror]
73
+ def superclass
74
+ Mirrors.reflect(@subject.superclass)
75
+ end
76
+
77
+ # The known subclasses
78
+ #
79
+ # @return [Array<ClassMirror>]
80
+ def subclasses
81
+ mirrors(ObjectSpace.each_object(Class).select { |a| a.superclass == @subject })
82
+ end
83
+
84
+ # The list of ancestors
85
+ #
86
+ # @return [Array<ClassMirror>]
87
+ def ancestors
88
+ mirrors(@subject.ancestors)
89
+ end
90
+
91
+ # The constants defined within this class. This includes nested
92
+ # classes and modules, but also all other kinds of constants.
93
+ #
94
+ # @return [Array<FieldMirror>]
95
+ def constants
96
+ field_mirrors(@subject.constants)
97
+ end
98
+
99
+ # Searches for the named constant in the mirrored namespace. May
100
+ # include a colon (::) separated constant path. This _may_ trigger
101
+ # an autoload!
102
+ #
103
+ # @return [ClassMirror, nil] the requested constant, or nil
104
+ def constant(str)
105
+ path = str.to_s.split("::")
106
+ c = path[0..-2].inject(@subject) { |klass, s| klass.const_get(s) }
107
+ field_mirror (c || @subject), path.last
108
+ rescue NameError => e
109
+ p e
110
+ nil
111
+ end
112
+
113
+ # The full nesting.
114
+ #
115
+ # @return [Array<ClassMirror>]
116
+ def nesting
117
+ ary = []
118
+ @subject.name.split('::').inject(Object) do |klass, str|
119
+ ary << klass.const_get(str)
120
+ ary.last
121
+ end
122
+ ary.reverse
123
+ rescue NameError
124
+ [@subject]
125
+ end
126
+
127
+ # The classes nested within the subject. Should _not_ trigger
128
+ # autloads!
129
+ #
130
+ # @return [Array<ClassMirror>]
131
+ def nested_classes
132
+ nc = @subject.constants.collect do |c|
133
+ # do not trigger autoloads
134
+ if @subject.const_defined?(c) && !@subject.autoload?(c)
135
+ @subject.const_get(c)
136
+ end
137
+ end
138
+ mirrors(nc.compact.select { |c| c.is_a?(Module) }.sort_by(&:name))
139
+ end
140
+
141
+ def nested_class_count
142
+ nested_classes.count
143
+ end
144
+
145
+ # The instance methods of this class. To get to the class methods,
146
+ # ask the #singleton_class for its methods.
147
+ #
148
+ # @return [Array<MethodMirror>]
149
+ def methods
150
+ pub_names = @subject.public_instance_methods(false)
151
+ prot_names = @subject.protected_instance_methods(false)
152
+ priv_names = @subject.private_instance_methods(false)
153
+
154
+ mirrors = []
155
+ pub_names.sort.each do |n|
156
+ mirrors << Mirrors.reflect(@subject.instance_method(n))
157
+ end
158
+ prot_names.sort.each do |n|
159
+ mirrors << Mirrors.reflect(@subject.instance_method(n))
160
+ end
161
+ priv_names.sort.each do |n|
162
+ mirrors << Mirrors.reflect(@subject.instance_method(n))
163
+ end
164
+ mirrors
165
+ end
166
+
167
+ # The instance method of this class or any of its superclasses
168
+ # that has the specified selector
169
+ #
170
+ # @return [MethodMirror, nil] the method or nil, if none was found
171
+ def method(name)
172
+ Mirrors.reflect @subject.instance_method(name)
173
+ end
174
+
175
+ # to work around overridden `name` methods
176
+ MODULE_INSPECT = Module.instance_method(:inspect)
177
+ def name
178
+ MODULE_INSPECT.bind(@subject).call
179
+ rescue
180
+ puts @subject.inspect
181
+ puts @subject.class
182
+ raise
183
+ end
184
+
185
+ def demodulized_name
186
+ name.split('::').last
187
+ end
188
+
189
+ def intern_method_mirror(mirror)
190
+ @method_mirrors[mirror.name] ||= mirror
191
+ end
192
+
193
+ def intern_field_mirror(mirror)
194
+ @field_mirrors[mirror.name] ||= mirror
195
+ end
196
+ end
197
+ end