Class: CastOff::Compiler::Dependency

Inherits:
Object
  • Object
show all
Extended by:
Util
Includes:
Util
Defined in:
lib/cast_off/compile/dependency.rb

Constant Summary collapse

@@instance_method_dependency =
{}
@@instance_method_dependency_initializers =
{}
@@singleton_method_dependency =
{}
@@singleton_method_dependency_initializers =
{}
@@instance_method_strong_dependency =
{}
@@singleton_method_strong_dependency =
{}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

bug, dlog, todo, vlog

Constructor Details

#initializeDependency

Returns a new instance of Dependency.



7
8
9
10
# File 'lib/cast_off/compile/dependency.rb', line 7

def initialize
  @dependency = {}
  @strong_dependency = []
end

Class Method Details

.copy(dst, src, singleton_p) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/cast_off/compile/dependency.rb', line 165

def self.copy(dst, src, singleton_p)
  bug() unless dst.is_a?(Module)
  dst = ModuleWrapper.new(dst)
  deps  = singleton_p ? @@singleton_method_dependency : @@instance_method_dependency
  funcs = singleton_p ? @@singleton_method_dependency_initializers : @@instance_method_dependency_initializers
  (deps[src] || []).each do |mid|
    finits = funcs[[src, mid]]
    if finits && !finits.empty?
      finits.each{|f| instance_method_depend(dst, mid, f)}
    else
      bug() unless singleton_p
      case mid
      when :singleton_method_added, :method_added, :include, :extend
        # nothing to do
      else
        bug()
      end
    end
  end
end

.get_class_or_module(km) ⇒ Object



19
20
21
22
23
24
25
26
27
28
# File 'lib/cast_off/compile/dependency.rb', line 19

def self.get_class_or_module(km)
  case km
  when ClassWrapper
    bug() if  km.singleton?
    return km.contain_class
  when ModuleWrapper
    return km.contain_module
  end
  bug()
end

.hook(o) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/cast_off/compile/dependency.rb', line 186

def self.hook(o)
  s = class << o
    self
  end
  s.class_eval do
    def override_singleton_method(obj, mid, flag)
      CastOff.dlog("singleton method added #{obj}.#{mid}")
      CastOff.delete_original_singleton_method_iseq(self, mid)
      if initializers = Dependency.singleton_method_depend?(obj, mid)
        if Dependency.singleton_method_strongly_depend?(obj, mid) && !CastOff.method_replacing?
          raise(ExecutionError.new("Should not be override #{obj}.#{mid}"))
        end
        # TODO Array.each の上書きチェック
        initializers.each do |init|
          CastOff.dlog("update function pointer #{obj}.#{mid}")
          CastOff.__send__(init)
        end
      end
    end

    def override_method(obj, mid, flag)
      CastOff.dlog("method added #{obj}##{mid}")
      CastOff.delete_original_instance_method_iseq(self, mid)
      if initializers = Dependency.instance_method_depend?(obj, mid)
        if Dependency.instance_method_strongly_depend?(obj, mid) && !CastOff.method_replacing?
          raise(ExecutionError.new("Should not be override #{obj}##{mid}"))
        end
        # TODO Array.each の上書きチェック
        initializers.each do |init|
          CastOff.dlog("update function pointer #{obj}##{mid}")
          CastOff.__send__(init)
        end
      end
    end

    begin
      singleton_method_added = o.method(:singleton_method_added).unbind
    rescue NameError
      singleton_method_added = nil
    end

    begin
      method_added = o.method(:method_added).unbind
    rescue NameError
      method_added = nil
    end

    begin
      m_include = o.method(:include).unbind
      m_extend  = o.method(:extend).unbind
    rescue NameError
      raise(ExecutionError.new("#{o}: include or extend not defined"))
    end

    define_method(:method_added) do |*args|
      ignore_overridden_p = Dependency.ignore_overridden?
      current_mid = CastOff.current_method_id
      case current_mid
      when :extend, :include
        mods = args
        extend_p = current_mid == :extend
        CastOff.dlog("#{self} #{extend_p ? 'extends' : 'includes'} #{mods}")
        bug() unless m_include && m_extend
        m = extend_p ? m_extend : m_include
        # include, extend の対象は self なので、self 以外が include, extend することはない。
        m.bind(self).call(*mods)
        return unless o == self
        mods.each do |mod|
          next unless mod.is_a?(Module)
          Dependency.copy(mod, self, extend_p)
          Dependency.hook(mod) unless mod.singleton_methods(false).include?(:override_singleton_method)
          methods = mod.instance_methods(false) + mod.private_instance_methods(false)
          override_callback = extend_p ? :override_singleton_method : :override_method
          methods.each{|mid| o.__send__(override_callback, o, mid, extend_p ? :extend : :include)}
        end
      when :singleton_method_added
        mid = args.first
        CastOff.bug() unless mid.instance_of?(Symbol)
        if self == o && !ignore_overridden_p
          override_singleton_method(o, mid, :added) 
          singleton_method_added.bind(self).call(mid) if singleton_method_added
        elsif o == Module && singleton_method_added
          singleton_method_added.bind(self).call(mid)
        end
        super(mid) rescue NoMethodError
      when :method_added
        mid = args.first
        CastOff.bug() unless mid.instance_of?(Symbol)
        if self == o && !ignore_overridden_p
          override_method(o, mid, :added)
          method_added.bind(self).call(mid) if method_added
        elsif o == Module && method_added
          method_added.bind(self).call(mid)
        end
        super(mid) rescue NoMethodError
      else
        raise(ExecutionError.new("CastOff expected include, extend, method_added or singleton_method_added but #{current_mid} was called"))
      end
    end
    alias extend method_added
    alias include method_added
    alias singleton_method_added method_added
    CastOff.dlog("hook #{o}")
  end
  @@singleton_method_dependency[o] ||= []
  @@singleton_method_dependency[o] |= [:method_added, :singleton_method_added, :include, :extend]
  @@singleton_method_strong_dependency[o] ||= []
  @@singleton_method_strong_dependency[o] |= [:method_added, :singleton_method_added, :include, :extend]
end

.instance_method_depend(klass, mid, function_pointer_initializer) ⇒ Object



30
31
32
33
34
35
36
# File 'lib/cast_off/compile/dependency.rb', line 30

def self.instance_method_depend(klass, mid, function_pointer_initializer)
  c = get_class_or_module(klass)
  a = (@@instance_method_dependency[c] ||= [])
  a << mid unless a.include?(mid)
  b = (@@instance_method_dependency_initializers[[c, mid]] ||= [])
  b << function_pointer_initializer unless b.include?(function_pointer_initializer)
end

.instance_method_depend?(klass, mid) ⇒ Boolean

Returns:

  • (Boolean)


45
46
47
48
49
50
51
52
# File 'lib/cast_off/compile/dependency.rb', line 45

def self.instance_method_depend?(klass, mid)
  bug() unless klass.instance_of?(Class) || klass.instance_of?(Module)
  dep = @@instance_method_dependency[klass]
  return false unless dep
  return false unless dep.include?(mid)
  bug() unless @@instance_method_dependency_initializers[[klass, mid]]
  @@instance_method_dependency_initializers[[klass, mid]]
end

.instance_method_strongly_depend(klass, mid) ⇒ Object



38
39
40
41
42
43
# File 'lib/cast_off/compile/dependency.rb', line 38

def self.instance_method_strongly_depend(klass, mid)
  c = get_class_or_module(klass)
  @@instance_method_strong_dependency[c] ||= []
  a = @@instance_method_strong_dependency[c]
  a << mid unless a.include?(mid)
end

.instance_method_strongly_depend?(obj, mid) ⇒ Boolean

Returns:

  • (Boolean)


54
55
56
57
58
# File 'lib/cast_off/compile/dependency.rb', line 54

def self.instance_method_strongly_depend?(obj, mid)
  bug() unless obj.instance_of?(Class) || obj.instance_of?(Module)
  dep = @@instance_method_strong_dependency[obj]
  dep.instance_of?(Array) ? dep.include?(mid) : false
end

.load(str) ⇒ Object



115
116
117
118
119
# File 'lib/cast_off/compile/dependency.rb', line 115

def self.load(str)
  dep = Marshal.load(str)
  bug() unless dep.instance_of?(Dependency)
  dep
end

.singleton_method_depend(klass, mid, function_pointer_initializer) ⇒ Object



60
61
62
63
64
65
66
67
68
# File 'lib/cast_off/compile/dependency.rb', line 60

def self.singleton_method_depend(klass, mid, function_pointer_initializer)
  bug() unless klass.instance_of?(ClassWrapper)
  bug() unless klass.singleton?
  o = klass.contain_object
  a = (@@singleton_method_dependency[o] ||= [])
  a << mid unless a.include?(mid)
  b = (@@singleton_method_dependency_initializers[[o, mid]] ||= [])
  b << function_pointer_initializer unless b.include?(function_pointer_initializer)
end

.singleton_method_depend?(obj, mid) ⇒ Boolean

Returns:

  • (Boolean)


79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/cast_off/compile/dependency.rb', line 79

def self.singleton_method_depend?(obj, mid)
  bug() unless ClassWrapper.support?(obj, false)
  dep = @@singleton_method_dependency[obj]
  return false unless dep
  return false unless dep.include?(mid)
  unless @@singleton_method_dependency_initializers[[obj, mid]]
    if mid == :method_added || mid == :singleton_method_added
      return []
    else
      bug()
    end
  end
  @@singleton_method_dependency_initializers[[obj, mid]]
end

.singleton_method_strongly_depend(klass, mid) ⇒ Object



70
71
72
73
74
75
76
77
# File 'lib/cast_off/compile/dependency.rb', line 70

def self.singleton_method_strongly_depend(klass, mid)
  bug() unless klass.instance_of?(ClassWrapper)
  bug() unless klass.singleton?
  o = klass.contain_object
  @@singleton_method_strong_dependency[o] ||= []
  a = @@singleton_method_strong_dependency[o]
  a << mid unless a.include?(mid)
end

.singleton_method_strongly_depend?(obj, mid) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
98
# File 'lib/cast_off/compile/dependency.rb', line 94

def self.singleton_method_strongly_depend?(obj, mid)
  bug() unless ClassWrapper.support?(obj, false)
  dep = @@singleton_method_strong_dependency[obj]
  dep.instance_of?(Array) ? dep.include?(mid) : false
end

Instance Method Details

#add(klass, mid, strong_p) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/cast_off/compile/dependency.rb', line 129

def add(klass, mid, strong_p)
  bug() unless klass.instance_of?(ClassWrapper)
  targets = [klass]
  klass.each_method_search_target(mid) do |cm|
    next if klass.singleton? ? (cm == klass) : (cm == klass.contain_class)
    case cm
    when Class
      targets << ClassWrapper.new(cm, true)
    when Module
      targets << ModuleWrapper.new(cm)
    when ClassWrapper
      bug() unless cm.singleton?
      targets << cm
    else
      bug()
    end
  end
  targets.each do |t|
    @dependency[t] ||= []
    @dependency[t] |= [mid]
    @strong_dependency |= [[t, mid]] if strong_p
  end
end

#check(configuration) ⇒ Object



157
158
159
160
161
162
163
# File 'lib/cast_off/compile/dependency.rb', line 157

def check(configuration)
  @dependency.each do |(klass, mids)|
    bug() unless klass.instance_of?(ClassWrapper) || klass.instance_of?(ModuleWrapper)
    # TODO ブロックインライニングしたメソッドに対して
    #      コンパイル時のメソッドと等しいものかどうかをチェック
  end
end

#check_failed(msg = '') ⇒ Object

Raises:

  • (LoadError)


153
154
155
# File 'lib/cast_off/compile/dependency.rb', line 153

def check_failed(msg = '')
  raise(LoadError.new("failed to check method dependency: #{msg}"))
end

#dump(io) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/cast_off/compile/dependency.rb', line 100

def dump(io)
  begin
    Marshal.dump(self, io)
  rescue TypeError => e
    raise(UnsupportedError.new(<<-EOS))

Failed to marshal dump method dependency.
Dependency object should be able to marshal dump.
Currently, CastOff doesn't support object, which cannot marshal dump (e.g. STDIN).
--- Marshal.dump error message ---
#{e.message}
    EOS
  end
end

#hook(function_pointer_initializer) ⇒ Object



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/cast_off/compile/dependency.rb', line 296

def hook(function_pointer_initializer)
  @dependency.keys.each do |klass|
    m = @dependency[klass]
    if klass.instance_of?(ClassWrapper) && klass.singleton?
      m.each{|mid| self.class.singleton_method_depend(klass, mid, function_pointer_initializer)}
      m.each{|mid| self.class.singleton_method_strongly_depend(klass, mid) if @strong_dependency.include?([klass, mid])}
      o = klass.contain_object
      bug() unless ClassWrapper.support?(o, false)
    else
      m.each{|mid| self.class.instance_method_depend(klass, mid, function_pointer_initializer)}
      m.each{|mid| self.class.instance_method_strongly_depend(klass, mid) if @strong_dependency.include?([klass, mid])}
      case klass
      when ClassWrapper
        o = klass.contain_class
      when ModuleWrapper
        o = klass.contain_module
      else
        bug()
      end
    end
    next if o.singleton_methods(false).include?(:override_singleton_method)
    Dependency.hook(o)
  end
end

#marshal_dumpObject



121
122
123
# File 'lib/cast_off/compile/dependency.rb', line 121

def marshal_dump()
  [@dependency, @strong_dependency]
end

#marshal_load(obj) ⇒ Object



125
126
127
# File 'lib/cast_off/compile/dependency.rb', line 125

def marshal_load(obj)
  @dependency, @strong_dependency = obj
end