lib/safemode/parser.rb in safemode-1.3.8 vs lib/safemode/parser.rb in safemode-1.4.0

- old
+ new

@@ -24,23 +24,26 @@ def parser=(parser) @@parser = parser end end - def jail(str, parentheses = false) - str = parentheses ? "(#{str})." : "#{str}." if str + def jail(str, parentheses = false, safe_call: false) + str = if str + dot = safe_call ? "&." : "." + parentheses ? "(#{str})#{dot}" : "#{str}#{dot}" + end "#{str}to_jail" end # split up #process_call. see below ... - def process_call(exp) - exp.shift # remove ":call" symbol - receiver = jail process_call_receiver(exp) - name = exp.shift - args = process_call_args(exp) + def process_call(exp, safe_call = false) + _, recv, name, *args = exp - process_call_code(receiver, name, args) + receiver = jail(process_call_receiver(recv), safe_call: safe_call) + arguments = process_call_args(name, args) + + process_call_code(receiver, name, arguments, safe_call) end def process_fcall(exp) # using haml we probably never arrive here because :lasgn'ed :fcalls # somehow seem to change to :calls somewhere during processing @@ -138,47 +141,70 @@ end # split up Ruby2Ruby#process_call monster method so we can hook into it # in a more readable manner - def process_call_receiver(exp) - receiver_node_type = exp.first.nil? ? nil : exp.first.first - receiver = process exp.shift - receiver = "(#{receiver})" if - Ruby2Ruby::ASSIGN_NODES.include? receiver_node_type + def process_call_receiver(recv) + receiver_node_type = recv && recv.sexp_type + receiver = process recv + receiver = "(#{receiver})" if ASSIGN_NODES.include? receiver_node_type receiver end - def process_call_args(exp) - args = [] - while not exp.empty? do - args_exp = exp.shift - if args_exp && args_exp.first == :array # FIX - processed = "#{process(args_exp)[1..-2]}" - else - processed = process args_exp - end - args << processed unless (processed.nil? or processed.empty?) + def process_call_args(name, args) + in_context :arglist do + max = args.size - 1 + args = args.map.with_index { |arg, i| + arg_type = arg.sexp_type + is_empty_hash = arg == s(:hash) + arg = process arg + + next if arg.empty? + + strip_hash = (arg_type == :hash and + not BINARY.include? name and + not is_empty_hash and + (i == max or args[i + 1].sexp_type == :splat)) + wrap_arg = Ruby2Ruby::ASSIGN_NODES.include? arg_type + + arg = arg[2..-3] if strip_hash + arg = "(#{arg})" if wrap_arg + + arg + }.compact end - args.empty? ? nil : args.join(", ") end - def process_call_code(receiver, name, args) + def process_call_code(receiver, name, args, safe_call) case name - when :<=>, :==, "!=".to_sym, :<, :>, :<=, :>=, :-, :+, :*, :/, :%, :<<, :>>, :** then - "(#{receiver} #{name} #{args})" + when *BINARY then + if safe_call + "#{receiver}&.#{name}(#{args.join(", ")})" + elsif args.length > 1 + "#{receiver}.#{name}(#{args.join(", ")})" + else + "(#{receiver} #{name} #{args.join(", ")})" + end when :[] then - "#{receiver}[#{args}]" + receiver ||= "self" + "#{receiver}[#{args.join(", ")}]" + when :[]= then + receiver ||= "self" + rhs = args.pop + "#{receiver}[#{args.join(", ")}] = #{rhs}" + when :"!" then + "(not #{receiver})" when :"-@" then "-#{receiver}" when :"+@" then "+#{receiver}" else - unless receiver.nil? then - "#{receiver}.#{name}#{args ? "(#{args})" : args}" - else - "#{name}#{args ? "(#{args})" : args}" - end + args = nil if args.empty? + args = "(#{args.join(", ")})" if args + receiver = "#{receiver}." if receiver and not safe_call + receiver = "#{receiver}&." if receiver and safe_call + + "#{receiver}#{name}#{args}" end end # Ruby2Ruby process_if rewrites if and unless statements in a way that # makes the result unusable for evaluation in, e.g. ERB which appends a