Module: CastOff::Compiler

Includes:
Util
Included in:
CastOff
Defined in:
lib/cast_off/compile.rb,
lib/cast_off/compile/cfg.rb,
lib/cast_off/compile/iseq.rb,
lib/cast_off/compile/stack.rb,
lib/cast_off/compile/ir/sub_ir.rb,
lib/cast_off/compile/basicblock.rb,
lib/cast_off/compile/dependency.rb,
lib/cast_off/compile/ir/call_ir.rb,
lib/cast_off/compile/ir/jump_ir.rb,
lib/cast_off/compile/ir/operand.rb,
lib/cast_off/compile/translator.rb,
lib/cast_off/compile/information.rb,
lib/cast_off/compile/instruction.rb,
lib/cast_off/compile/ir/guard_ir.rb,
lib/cast_off/compile/ir/param_ir.rb,
lib/cast_off/compile/code_manager.rb,
lib/cast_off/compile/ir/return_ir.rb,
lib/cast_off/compile/ir/simple_ir.rb,
lib/cast_off/compile/configuration.rb,
lib/cast_off/compile/method_information.rb

Defined Under Namespace

Modules: Instruction, SimpleIR Classes: CodeManager, Configuration, Dependency, Iseq, MethodInformation, ReCompilation, SingletonClass, Translator

Constant Summary collapse

DefaultSuggestionIO =
Object.new
@@suggestion_io =
DefaultSuggestionIO
@@autoload_running_p =
false
@@compilation_threshold =
100
@@autocompile_proc =
nil
@@original_instance_method_iseq =
{}
@@original_singleton_method_iseq =
{}
@@loaded_binary =
{}
@@mls =
{}

Instance Method Summary collapse

Methods included from Util

#bug, #dlog, #todo, #vlog

Instance Method Details

#autocompileObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/cast_off/compile.rb', line 40

def autocompile()
  return false if autoload_running?
  return true if autocompile_running?
  class_table = {}
  number = 0
  @@autocompile_proc = lambda {|klass, mid, bind|
    # trace method invocation
    # TODO should handle singleton class

    method_table = (class_table[klass] ||= Hash.new(-1))
    count = (method_table[mid] += 1)
    #count = @@compilation_threshold if contain_loop?(klass, mid) if count == 0
    if count == @@compilation_threshold
      bug() unless bind.nil?
      return true # re-call this proc with binding
    end
    __autocompile(klass, mid, bind, (number += 1)) if bind
    false
  }
  hook_method_invocation(@@autocompile_proc)
  true
end

#autocompile_running?Boolean

Returns:

  • (Boolean)


271
272
273
# File 'lib/cast_off/compile.rb', line 271

def autocompile_running?
  !!@@autocompile_proc
end

#autoloadObject



26
27
28
29
30
# File 'lib/cast_off/compile.rb', line 26

def autoload()
  return false if autocompile_running?
  @@autoload_running_p = true
  true
end

#autoload_running?Boolean

Returns:

  • (Boolean)


275
276
277
# File 'lib/cast_off/compile.rb', line 275

def autoload_running?
  !!@@autoload_running_p
end

#clearObject



21
22
23
# File 'lib/cast_off/compile.rb', line 21

def clear()
  CodeManager.clear()
end

#compilation_threshold=(num) ⇒ Object

Raises:

  • (ArgumentError)


33
34
35
36
37
# File 'lib/cast_off/compile.rb', line 33

def compilation_threshold=(num)
  raise(ArgumentError.new("first argument should be Integer")) unless num.is_a?(Integer)
  raise(ArgumentError.new("threshold should be more than 0")) unless num >= 0
  @@compilation_threshold = num
end

#compile(target, mid, bind_or_typemap = nil, typemap = nil, force_compilation = false) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/cast_off/compile.rb', line 87

def compile(target, mid, bind_or_typemap = nil, typemap = nil, force_compilation = false)
  execute_no_hook() do
    case target
    when Class, Module
      # ok
    else
      raise(ArgumentError.new("first argument should be Class or Module"))
    end
    mid, bind, typemap = parse_arguments(mid, bind_or_typemap, typemap)
    t = override_target(target, mid)
    iseq = @@original_instance_method_iseq[[t, mid]] || get_iseq(target, mid, false)
    manager, configuration, suggestion = compile_iseq(iseq, mid, typemap, false, bind, force_compilation)
    manager.compilation_target_is_a(t, mid, false)
    set_direct_call(target, mid, target.instance_of?(Class) ? :class : :module, manager, configuration)
    load_binary(manager, configuration, suggestion, iseq)
    t = override_target(target, mid)
    dlog("override target of #{target}##{mid} is #{t}")
    replace_method(t, manager, false)
    @@original_instance_method_iseq[[t, mid]] = iseq
    @@manager_table[manager.signiture] = manager
  end
  true
end

#compile_instance_methods(klass, bind = nil, skip = []) ⇒ Object

Raises:

  • (ArgumentError)


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/cast_off/compile.rb', line 63

def compile_instance_methods(klass, bind = nil, skip = [])
  raise ArgumentError.new("first argument should be Class") unless klass.instance_of?(Class)
  raise ArgumentError.new("second argument should be Binding") unless !bind || bind.instance_of?(Binding)
  logger = self
  klass.class_eval do
    instance_methods(false).each_with_index do |mid, idx|
      next if skip.include?(mid)
      args = [klass, mid, bind]
      begin
        CastOff.compile(*args.compact())
        logger.vlog("#{idx}: compile #{mid}")
      rescue UnsupportedError => e
        logger.vlog("#{idx}: failed to compile #{self}##{mid} (#{e.message})")
      end
    end
  end
end

#compile_singleton_method(obj, mid, bind_or_typemap = nil, typemap = nil, force_compilation = false) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/cast_off/compile.rb', line 116

def compile_singleton_method(obj, mid, bind_or_typemap = nil, typemap = nil, force_compilation = false)
  execute_no_hook() do
    mid, bind, typemap = parse_arguments(mid, bind_or_typemap, typemap)
    iseq = @@original_singleton_method_iseq[[obj, mid]] || get_iseq(obj, mid, true)
    manager, configuration, suggestion = compile_iseq(iseq, mid, typemap, false, bind, force_compilation)
    manager.compilation_target_is_a(obj, mid, true)
    set_direct_call(obj, mid, :singleton, manager, configuration)
    load_binary(manager, configuration, suggestion, iseq)
    replace_method(obj, manager, true)
    @@original_singleton_method_iseq[[obj, mid]] = iseq
    @@manager_table[manager.signiture] = manager
  end
  true
end

#compiler_running?Boolean

Returns:

  • (Boolean)


279
280
281
# File 'lib/cast_off/compile.rb', line 279

def compiler_running?
  !!Thread.current[COMPILER_RUNNING_KEY]
end

#create_method_invocation_callbackObject



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
# File 'lib/cast_off/compile.rb', line 215

def create_method_invocation_callback()
  proc{|*args, &block|
    hook_proc = CastOff.current_bmethod_proc()
    current_method_id = CastOff.current_method_id()
    target = CastOff.override_target_of_current_method(self)
    me = @@mls[hook_proc]
    CastOff.bug() unless me.instance_of?(UnboundMethod)
    if target && CastOff.singleton_class?(target)
      compilation_method_id = :compile_singleton_method
      separator = '.'
      singleton = true
    else
      compilation_method_id = :compile
      separator = '#'
      singleton = false
      unless target
        # from method object
        methods = ObjectSpace.each_object(Method).select{|mobj| CastOff.rewrite_method_object(mobj, me, hook_proc)}
        me = methods.find{|m| m.receiver.__id__ == self.__id__}
        CastOff.bug() unless me.instance_of?(Method)
        CastOff.vlog("rewrite method object #{me}")
        return me.call(*args, &block)
      end
    end
    CastOff.bug() unless CastOff.instance_bmethod_proc(target.instance_method(current_method_id)) == hook_proc
    CastOff.restore_method(target, current_method_id, me)
    CastOff.bug() unless CastOff.get_compilable_iseq(target, current_method_id, false)
    begin
      CastOff.__send__(compilation_method_id, singleton ? self : target, current_method_id)
    rescue
      CastOff.vlog("failed to load #{target}#{separator}#{current_method_id}")
    end
    target.instance_method(current_method_id).bind(self).call(*args, &block)
  }
end

#delete_original_instance_method_iseq(target, mid) ⇒ Object



82
83
84
85
# File 'lib/cast_off/compile.rb', line 82

def delete_original_instance_method_iseq(target, mid)
  t = override_target(target, mid)
  @@original_instance_method_iseq.delete([t, mid])
end

#delete_original_singleton_method_iseq(obj, mid) ⇒ Object



112
113
114
# File 'lib/cast_off/compile.rb', line 112

def delete_original_singleton_method_iseq(obj, mid)
  @@original_singleton_method_iseq.delete([obj, mid])
end

#do_aliasObject



187
188
189
190
191
192
193
194
195
196
# File 'lib/cast_off/compile.rb', line 187

def do_alias()
  bug() unless block_given?
  begin
    bug() if method_aliasing?
    method_aliasing(true)
    yield
  ensure
    method_aliasing(false)
  end
end

#execute(typemap = nil, &block) ⇒ Object

Raises:

  • (ArgumentError)


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/cast_off/compile.rb', line 132

def execute(typemap = nil, &block)
  raise(ArgumentError.new('no block given')) unless block
  iseq = get_iseq_from_block(block)
  key = iseq.__id__
  if !@@loaded_binary[key]
    execute_no_hook() do
      bind = block.binding
      manager, configuration, suggestion = compile_iseq(iseq, nil, typemap, false, bind, false)
      load_binary(manager, configuration, suggestion, iseq)
      @@loaded_binary[key] = manager.signiture
    end
  end
  sign = @@loaded_binary[key]
  recv = get_caller()
  __send__(sign, recv)
end

#hook_method_definitionObject



173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/cast_off/compile.rb', line 173

def hook_method_definition()
  Module.class_eval do
    def method_added(mid)
      return unless CastOff.autoload_running? && !CastOff.compiler_running? && !CastOff.method_aliasing?
      CastOff.replace_method_invocation(self, mid, false)
    end

    def singleton_method_added(mid)
      return unless CastOff.autoload_running? && !CastOff.compiler_running? && !CastOff.method_aliasing?
      CastOff.replace_method_invocation(self, mid, true)
    end
  end
end

#method_aliasing?Boolean

Returns:

  • (Boolean)


287
288
289
# File 'lib/cast_off/compile.rb', line 287

def method_aliasing?
  !!Thread.current[METHOD_ALIASING_KEY]
end

#method_replacing?Boolean

Returns:

  • (Boolean)


283
284
285
# File 'lib/cast_off/compile.rb', line 283

def method_replacing?
  !!Thread.current[METHOD_REPLACING_KEY]
end

#re_compile_allObject



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/cast_off/compile.rb', line 149

def re_compile_all()
  all = @@original_singleton_method_iseq.map{|(target, mid), v|
    [target, mid, true]
  } + @@original_instance_method_iseq.map{|(target, mid), v|
    [target, mid, false]
  }
  all.each do |(target, mid, singleton)|
    compilation_method_name = "compile#{singleton ? '_singleton_method' : ''}"
    separator = singleton ? '.' : '#'
    begin
      CastOff.__send__(compilation_method_name, target, mid)
    rescue ArgumentError, NameError, CompileError => e
      # ArgumentError: dependency の Marshal.load に失敗
      # NameError: prefetch constant での定数参照に失敗
      # CompileError: メソッドが undef されて未定義になってしまった場合
      vlog("Skip: #{target}#{separator}#{mid}")
    rescue UnsupportedError => e
      vlog("Unsupported: #{target}#{separator}#{mid} => #{e}")
    rescue => e
      vlog("Catch exception #{e.class}: #{e}\n#{e.backtrace.join("\n")}")
    end
  end
end

#replace_method_invocation(obj, mid, singleton_p) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/cast_off/compile.rb', line 251

def replace_method_invocation(obj, mid, singleton_p)
  iseq = get_compilable_iseq(obj, mid, singleton_p)
  return unless iseq
  filepath = get_iseq_filepath(iseq)
  line_no = get_iseq_line(iseq)
  return unless CodeManager.compiled_method_exist?(filepath, line_no)
  target = singleton_p ? obj.singleton_class : override_target(obj, mid)
  begin
    original_method = target.instance_method(mid)
  rescue => e
    CastOff.bug(e)
  end
  do_alias do
    hook_proc = create_method_invocation_callback()
    target.__send__(:define_method, mid, &hook_proc)
    CastOff.bug() if @@mls[hook_proc]
    @@mls[hook_proc] = original_method
  end
end

#restore_method(target, mid, me) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/cast_off/compile.rb', line 198

def restore_method(target, mid, me)
  do_alias do
    begin
      target.__send__(:define_method, mid, me)
    rescue
      me = me.clone
      CastOff.rewrite_rclass_of_unbound_method_object(me, target)
      begin
        target.__send__(:define_method, mid, me)
      rescue => e
        CastOff.bug("#{target}, #{mid}, #{e}:\n#{e.backtrace}")
      end
    end
  end
end

#set_suggestion_io(io) ⇒ Object



13
14
15
# File 'lib/cast_off/compile.rb', line 13

def set_suggestion_io(io)
  @@suggestion_io = io
end

#verbose(b) ⇒ Object



17
18
19
# File 'lib/cast_off/compile.rb', line 17

def verbose(b)
  CastOff::Util.set_verbose_mode(b)
end