require 'rrb/scriptfile'
require 'rrb/script'
require 'rrb/node.rb'
require 'rrb/parser.rb'
require 'rrb/common_visitor.rb'
module RRB
class PushdownMethodCheckVisitor < Visitor
def initialize(dumped_info, method_name, new_namespace)
@dumped_info = dumped_info
@method_name = method_name
@old_namespace = method_name.namespace
@new_namespace = new_namespace
@result = true
end
attr_reader :result
def check_condition(namespace, node)
return false if @method_name.match_node?( namespace, node )
return false unless @dumped_info[namespace].subclass_of?(@old_namespace)
return false if @dumped_info[namespace].subclass_of?(@new_namespace)
return false unless node.fcalls.any?{|fcall| fcall.name == @method_name.bare_name}
return true
end
def called_method(namespace, node)
node.method_factory.new(namespace, @method_name.bare_name)
end
def check_node(namespace, node)
if @dumped_info.real_method(called_method(namespace, node)) == @method_name
@result = false
@error_message = "#{namespace.name} calls #{@method_name.name}"
end
end
def visit_method(namespace, node)
return unless @method_name.instance_method?
return unless check_condition(namespace, node)
check_node(namespace, node)
end
def visit_class_method(namespace, node)
return unless @method_name.class_method?
return unless check_condition(namespace, node)
check_node(namespace, node)
end
end
class ScriptFile
def pushdown_method( method_name, new_namespace,
pushdowned_method,
lineno)
if method_name.class_method?
pushdowned_method.gsub!(/^((\s)*def\s+)(.*)\./) {|s| $1 + new_namespace.name + '.'}
end
visitor = MoveMethodVisitor.new( method_name, lineno )
@tree.accept( visitor )
pushdowned_method =
RRB.reindent_str_node( pushdowned_method, visitor.inserted )
@new_script = RRB.insert_str(@input, lineno,
visitor.delete_range, pushdowned_method )
end
def pushdown_method?(dumped_info, method_name, new_namespace)
visitor = PushdownMethodCheckVisitor.new(dumped_info,
method_name, new_namespace)
@tree.accept(visitor)
@error_message = visitor.error_message unless visitor.result
return visitor.result
end
end
class Script
def pushdown_method(method_name, new_namespace,
path, lineno)
pushdowned_method = get_string_of_method( method_name)
@files.each do |scriptfile|
scriptfile.pushdown_method(method_name,
new_namespace, pushdowned_method,
(scriptfile.path == path) ? lineno : nil )
end
end
def pushdown_method?(method_name, new_namespace,
path, lineno)
old_namespace = method_name.namespace
unless get_dumped_info.exist?(method_name)
@error_message = "#{method_name.name}: no definition"
return false
end
unless get_dumped_info[new_namespace].subclass_of?(method_name.namespace)
@error_message = "#{new_namespace.name} is not the subclass of #{old_namespace.name}"
return false
end
new_method = method_name.ns_replaced(new_namespace)
if get_dumped_info.exist?(new_method, false)
@error_message = "#{new_method.name}: already defined"
return false
end
target_class = class_on(path, lineno)
unless target_class && new_namespace == target_class
@error_message = "Specify which definition to push down method to"
return false
end
@files.each do |scriptfile|
unless scriptfile.pushdown_method?(get_dumped_info,
method_name, new_namespace)
@error_message = scriptfile.error_message
return false
end
end
return true
end
end
end
syntax highlighted by Code2HTML, v. 0.9.1