require 'maglev/reflection/core_ext/method' module Maglev class Reflection class MethodMirror < Mirror include AbstractReflection::MethodMirror reflect! Method, UnboundMethod, GsNMethod # Adding support for on-demand wrapping of GsNMethods def initialize(obj) super if @subject.kind_of? GsNMethod @subject = wrap_gsmeth(@subject) end end def file (@subject.source_location || [])[0] end def file=(string) raise CapabilitiesExceeded unless regular_method? reload_after do defining_class.reflectee.class_eval(source, string, line) end end def line (@subject.source_location || [])[1] end def line=(num) raise CapabilitiesExceeded unless regular_method? reload_after do defining_class.reflectee.class_eval(source, file, num) end end def source gsmeth.__source_string end def source=(str) raise CapabilitiesExceeded unless regular_method? reload_after do if file.nil? && line.nil? # Smalltalk method defining_class.reflectee.__compile_method_category_environment_id(str, '*maglev-dynamic-compile-unclassified', 1) else # Ruby method defining_class.reflectee.class_eval(str, file, line) end end end def selector @subject.name.to_s end def defining_class reflection.reflect gsmeth.__in_class end def arguments gsmeth.__args_and_temps.to_a[0...gsmeth.__num_args].collect(&:to_s) end def block_argument return nil unless gsmeth.__selector.to_s[-1] == ?& arguments.last end def optional_arguments opt_arg_offset = gsmeth.__ruby_opt_args_bits.to_s(2).reverse.index(?1) argsize = gsmeth.__num_args unless opt_arg_offset if block_argument && splat_argument opt_arg_offset = argsize - 2 elsif block_argument || splat_argument opt_arg_offset = argsize - 1 else opt_arg_offset = -2 end end arguments[opt_arg_offset..-1] end def required_arguments arguments - optional_arguments end def splat_argument return nil unless gsmeth.__selector.to_s[-2] == ?* block_argument ? arguments[-2] : arguments[-1] end def step_offsets gsmeth.__source_offsets end def send_offsets offs = gsmeth.__source_offsets_of_sends.to_a offs = offs.each_slice(2).collect do |offset, selector| [prefix_if_ruby_selector(selector), offset] end Hash[*offs.flatten] end def bytecodes gsmeth.__ip_steps.to_a.collect {|ip| gsmeth.__opcode_info_at(ip) } end def delete raise CapabilitiesExceeded unless regular_method? gsmeth.__in_class.remove_method(selector) end def breakpoints gsmeth.__all_breakpoints_source_offsets end def break(source_offset = 1) step_point = find_step_point_just_before(source_offset) if gsmeth.__is_method_for_block gsmeth.__set_break_at_step_point(step_point) else @subject.__nonbridge_meth.__set_break_at_step_point(step_point) end end def clear_break(source_offset) gsmeth.__clear_break_at(find_step_point_just_before(source_offset)) end private def find_step_point_just_before(source_offset) nxt = step_offsets.detect {|o| o > source_offset } [step_offsets.index(nxt) - 1, 1].max end def wrap_gsmeth(gsmethod) label = gsmethod.__name.to_s cls = gsmethod.__in_class if cls.instance_methods.include?(label) cls.instance_method(label) else GsNMethodWrapper.new(gsmethod) end end def gsmeth @subject.__st_gsmeth end # Removes the Ruby suffix from the GsNMethod selector def prefix_if_ruby_selector(sym) selector = sym.to_s selector[0...(selector.rindex(?#) || -1)] end def regular_method? not (gsmeth.__is_method_for_block || gsmeth.__in_class.nil?) end def reload_after(&block) cls = defining_class.reflectee sel = selector.to_sym yield @subject = cls.instance_method(sel) end end end end