Class: Laser::Analysis::ControlFlow::GraphBuilder
- Inherits:
-
Object
- Object
- Laser::Analysis::ControlFlow::GraphBuilder
- Defined in:
- lib/laser/analysis/control_flow/cfg_builder.rb
Overview
This class builds a control flow graph. The algorithm used is derived from Robert Morgan's "Building an Optimizing Compiler".
Instance Attribute Summary (collapse)
-
- (Object) current_block
readonly
Returns the value of attribute current_block.
-
- (Object) enter
readonly
Returns the value of attribute enter.
-
- (Object) exit
readonly
Returns the value of attribute exit.
-
- (Object) graph
readonly
Returns the value of attribute graph.
-
- (Object) self_register
readonly
Returns the value of attribute self_register.
-
- (Object) sexp
readonly
Returns the value of attribute sexp.
-
- (Object) temporary_counter
readonly
Returns the value of attribute temporary_counter.
Instance Method Summary (collapse)
- - (Object) build
-
- (Object) build_block_with_jump(target = nil, name = nil)
Creates a new block that jumps to the given target upon completion.
- - (Object) build_exception_blocks
- - (Object) build_formal_args(formals, opts = {})
- - (Object) build_prologue
- - (Object) copy_positionals(args)
- - (Object) copy_positionals_with_offset(args, offset)
- - (Object) current_namespace
- - (Object) current_scope
- - (Object) current_self
- - (Object) formal_arg_at(idx)
- - (Object) formal_arg_range(start, size)
-
- (GraphBuilder) initialize(sexp, formals = [], scope = Scope::GlobalScope)
constructor
A new instance of GraphBuilder.
-
- (Object) novalue_walk(node)
Walks the node expecting that the expression's return value will be discarded.
- - (Object) observe_just_raised_exception
- - (Object) pop_scope
- - (Object) pop_self
- - (Object) push_scope(scope)
- - (Object) push_self(obj)
- - (Object) query_self
- - (Object) reobserve_current_exception
- - (Object) reset_visibility_stack
-
- (Object) value_walk(node)
Walks the node with the expectation that the return value will be used.
-
- (Object) walk_node(node, opts = {})
Walks the node differently based on whether the value is needed.
-
- (Object) with_current_basic_block(basic_block)
yields with the current basic block set to the provided basic block.
- - (Object) with_current_node(node)
- - (Object) with_scope(scope)
- - (Object) with_self(namespace)
Constructor Details
- (GraphBuilder) initialize(sexp, formals = [], scope = Scope::GlobalScope)
A new instance of GraphBuilder
10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 10 def initialize(sexp, formals=[], scope=Scope::GlobalScope) @sexp = sexp @formals = formals @graph = @enter = @exit = nil @scope_stack = [scope] @self_stack = [] @temporary_counter = 0 @temporary_table = Hash.new do |hash, keys| @temporary_counter += 1 hash[keys] = Bindings::TemporaryBinding.new("%t#{@temporary_counter}", nil) end end |
Instance Attribute Details
- (Object) current_block (readonly)
Returns the value of attribute current_block
7 8 9 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 7 def current_block @current_block end |
- (Object) enter (readonly)
Returns the value of attribute enter
7 8 9 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 7 def enter @enter end |
- (Object) exit (readonly)
Returns the value of attribute exit
7 8 9 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 7 def exit @exit end |
- (Object) graph (readonly)
Returns the value of attribute graph
7 8 9 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 7 def graph @graph end |
- (Object) self_register (readonly)
Returns the value of attribute self_register
8 9 10 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 8 def self_register @self_register end |
- (Object) sexp (readonly)
Returns the value of attribute sexp
7 8 9 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 7 def sexp @sexp end |
- (Object) temporary_counter (readonly)
Returns the value of attribute temporary_counter
7 8 9 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 7 def temporary_counter @temporary_counter end |
Instance Method Details
- (Object) build
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 23 def build initialize_graph @current_node = @sexp @self_register = Bindings::TemporaryBinding.new('self', current_scope.self_ptr) build_prologue result = walk_node @sexp, value: true if @sexp.type == :program uncond_instruct @current_return else return_uncond_jump_instruct result end @graph.prune_totally_useless_blocks @graph end |
- (Object) build_block_with_jump(target = nil, name = nil)
Creates a new block that jumps to the given target upon completion. Very useful for building branches.
262 263 264 265 266 267 268 269 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 262 def build_block_with_jump(target = nil, name = nil) new_block = name ? create_block(name) : create_block with_current_basic_block(new_block) do yield uncond_instruct target if target end new_block end |
- (Object) build_exception_blocks
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 102 def build_exception_blocks @return_register = create_temporary('t#return_value') @graph.final_return = create_temporary('t#final_return') @exception_register = create_temporary('t#exception_value') @graph.final_exception = create_temporary('t#exit_exception') @current_return = create_block(ControlFlowGraph::RETURN_POSTDOMINATOR_NAME) @current_rescue = create_block(ControlFlowGraph::EXCEPTION_POSTDOMINATOR_NAME) @current_yield_fail = create_block(ControlFlowGraph::YIELD_POSTDOMINATOR_NAME) joined = create_block(ControlFlowGraph::FAILURE_POSTDOMINATOR_NAME) with_current_basic_block(@current_rescue) do uncond_instruct joined, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL end with_current_basic_block(@current_yield_fail) do uncond_instruct joined, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL end with_current_basic_block(joined) do copy_instruct(@graph.final_exception, @exception_register) add_instruction(:raise, @graph.final_exception) uncond_instruct @exit, flags: RGL::ControlFlowGraph::EDGE_ABNORMAL, jump_instruct: false end with_current_basic_block(@current_return) do copy_instruct(@graph.final_return, @return_register) add_instruction(:return, @graph.final_return) uncond_instruct @exit, jump_instruct: false end end |
- (Object) build_formal_args(formals, opts = {})
179 180 181 182 183 184 185 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 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 179 def build_formal_args(formals, opts={}) formals.each { |formal| current_scope.add_binding!(formal) } nb_formals = formals.last.is_block? ? formals[0..-2] : formals has_rest = rest_arg = nb_formals.find(&:is_rest?) min_arity = nb_formals.count(&:is_positional?) optionals = nb_formals.select(&:is_optional?) num_optionals = optionals.size min_nonrest_arity = min_arity + num_optionals # zero dynamic args = easy and more efficient case if !rest_arg && optionals.empty? copy_positionals(nb_formals) else # pre-dynamic positionals cur_arity = call_instruct(ClassRegistry['Laser#Magic'].binding, :current_arity, value: true, raise: false) pre_dynamic_positional = nb_formals.take_while(&:is_positional?) num_pre_dynamics = pre_dynamic_positional.size copy_positionals(pre_dynamic_positional) # optional args previous_optional_block = nil optionals.each_with_index do |argument, index| max_arity_indicating_missing = const_instruct(index + min_arity) has_arg, no_arg = create_blocks 2 if previous_optional_block with_current_basic_block(previous_optional_block) do uncond_instruct no_arg end end cond_result = call_instruct(cur_arity, :<, max_arity_indicating_missing, value: true, raise: false) cond_instruct cond_result, no_arg, has_arg start_block no_arg arg_value = walk_node(argument.default_value_sexp, value: true) copy_instruct(argument, arg_value) previous_optional_block = @current_block start_block has_arg copy_instruct(argument, formal_arg_at(const_instruct(index + num_pre_dynamics))) end optionals_done = create_block # rest args if has_rest rest_start = const_instruct(num_pre_dynamics + num_optionals) rest_size = call_instruct(cur_arity, :-, const_instruct(min_nonrest_arity), value: true, raise: false) copy_instruct(rest_arg, formal_arg_range(rest_start, rest_size)) end uncond_instruct optionals_done if previous_optional_block with_current_basic_block(previous_optional_block) do # at this point, if there was a rest arg, it's empty. if has_rest empty_rest = call_instruct(ClassRegistry['Array'].binding, :[], value: true, raise: false) copy_instruct(rest_arg, empty_rest) end uncond_instruct optionals_done end end start_block optionals_done # post-dynamic conditionals post_dynamic_positional = nb_formals[num_pre_dynamics..-1].select(&:is_positional?) post_dynamic_start = call_instruct(cur_arity, :-, const_instruct(post_dynamic_positional.size), value: true, raise: false) copy_positionals_with_offset(post_dynamic_positional, post_dynamic_start) end block_arg = formals.find(&:is_block?) if block_arg if opts[:block_arg] the_block = opts[:block_arg] else the_block = call_instruct(ClassRegistry['Laser#Magic'].binding, :current_block, value: true, raise: false) end copy_instruct(block_arg, the_block) end end |
- (Object) build_prologue
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 134 def build_prologue uncond_instruct create_block if @sexp.type == :program push_self(Scope::GlobalScope.self_ptr) else push_self(query_self) end reset_visibility_stack build_exception_blocks if @sexp.type != :program dynamic_context = ClosedScope.new(current_scope, current_self) @scope_stack = [dynamic_context] @block_arg = call_instruct(ClassRegistry['Laser#Magic'].binding, :current_block, value: true, raise: false) @block_arg.name = 't#current_block' @graph.block_register = @block_arg reobserve_current_exception build_formal_args(@formals, block_arg: @block_arg) unless @formals.empty? end end |
- (Object) copy_positionals(args)
165 166 167 168 169 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 165 def copy_positionals(args) args.each_with_index do |pos, idx| copy_instruct(pos, formal_arg_at(const_instruct(idx))) end end |
- (Object) copy_positionals_with_offset(args, offset)
171 172 173 174 175 176 177 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 171 def copy_positionals_with_offset(args, offset) args.each_with_index do |pos, idx| dynamic_idx = idx.zero? ? offset : call_instruct(const_instruct(idx), :+, offset, value: true, raise: false) copy_instruct(pos, formal_arg_at(dynamic_idx)) end end |
- (Object) current_namespace
39 40 41 42 43 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 39 def current_namespace scope = current_scope scope = scope.parent if @scope_stack.size == 1 && @sexp.type != :program scope.lexical_target end |
- (Object) current_scope
49 50 51 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 49 def current_scope @scope_stack.last end |
- (Object) current_self
69 70 71 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 69 def current_self @self_stack.last end |
- (Object) formal_arg_at(idx)
160 161 162 163 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 160 def formal_arg_at(idx) call_instruct(ClassRegistry['Laser#Magic'].binding, :current_argument, idx, value: true, raise: false) end |
- (Object) formal_arg_range(start, size)
155 156 157 158 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 155 def formal_arg_range(start, size) call_instruct(ClassRegistry['Laser#Magic'].binding, :current_argument_range, start, size, value: true, raise: false) end |
- (Object) novalue_walk(node)
Walks the node expecting that the expression's return value will be discarded. Since everything is an expression in Ruby, knowing when to ignore return values is nice.
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 461 def novalue_walk(node) with_current_node(node) do case node.type when :void_stmt # Do nothing. when :massign lhs, rhs = node.children multiple_assign_instruct(lhs, rhs, value: false) when :opassign lhs, op, rhs = node.children op = op.[0..-2].to_sym if lhs.type == :field receiver = walk_node lhs[1], value: true method_name = lhs[3]. # Receiver is ONLY EVALUATED ONCE # (on ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]) current_val = call_instruct(receiver, method_name.to_sym, block: false, value: true) if op == :||" false_block, after = create_blocks 2 cond_instruct(current_val, after, false_block) start_block false_block rhs_value = walk_node rhs, value: true call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false) uncond_instruct after start_block after elsif op == :&&" true_block, after = create_blocks 2 cond_instruct(current_val, true_block, after) start_block true_block rhs_value = walk_node rhs, value: true call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false) uncond_instruct after start_block after else rhs_value = walk_node rhs, value: true temp_result = call_instruct(current_val, op, rhs_value, block: false, value: true) call_instruct(receiver, "#{method_name}=".to_sym, temp_result, block: false, value: false) end # TODO(adgar): aref_field else result = binary_instruct(lhs, op, rhs, value: true) single_assign_instruct(lhs, result) end when :case after = create_block argument, body = node.children argument_value = walk_node argument, value: true while body && body.type == :when when_opts, when_body, body = body.children when_body_block = create_block when_opts.each do |opt| after_fail = create_block condition_result = call_instruct(walk_node(opt, value: true), :===, argument_value, value: true) cond_instruct(condition_result, when_body_block, after_fail) start_block after_fail end all_fail = @current_block start_block when_body_block walk_body when_body, value: false uncond_instruct after start_block all_fail end if body && body.type == :else walk_body body[1], value: false end uncond_instruct after when :var_ref nil when :for lhs, receiver, body = node.children receiver_value = walk_node receiver, value: true if Symbol === lhs[0] # field or var_ref/const_ref case lhs.type when :field # TODO(adgar): generate calls else # just get the value arg_bindings = [lhs.binding] call_method_with_block(receiver_value, :each, [], arg_bindings, body, value: false) end else # TODO(adgar): multiple assign end when :string_embexpr node[1].each { |elt| walk_node(elt, value: false) } when :@CHAR, :@tstring_content, :@int, :@float, :@regexp_end, :symbol, :@label, :symbol_literal, :defined # do nothing when :string_literal content_nodes = node[1].children content_nodes.each do |node| walk_node node, value: false end when :xstring_literal body = build_string_instruct(node[1]) call_instruct(self_register, :`, body, value: false) when :regexp_literal node[1].each { |part| walk_node node, value: false } when :dyna_symbol content_nodes = node[1].children content_nodes.each { |node| walk_node node, value: false } when :array receiver = ClassRegistry['Array'].binding generic_call_instruct(receiver, :[], node[1], false, value: false) when :hash if node[1] walk_node node[1], value: false else const_instruct({}, value: false) end when :assoclist_from_args, :bare_assoc_hash pairs = node[1] key_value_paired = pairs.map {|a, b| [walk_node(a, value: true), walk_node(b, value: true)] }.flatten receiver = ClassRegistry['Hash'].binding call_instruct(receiver, :[], *key_value_paired, block: false, value: false, raise: false) else raise ArgumentError.new("Unknown AST node type #{node.type.inspect}") end end end |
- (Object) observe_just_raised_exception
96 97 98 99 100 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 96 def observe_just_raised_exception cur_exception = call_instruct(ClassRegistry['Laser#Magic'].binding, :get_just_raised_exception, value: true, raise: false) copy_instruct(@exception_register, cur_exception) end |
- (Object) pop_scope
53 54 55 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 53 def pop_scope @scope_stack.pop end |
- (Object) pop_self
73 74 75 76 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 73 def pop_self @self_stack.pop copy_instruct(@self_register, current_self) end |
- (Object) push_scope(scope)
45 46 47 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 45 def push_scope(scope) @scope_stack.push scope end |
- (Object) push_self(obj)
64 65 66 67 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 64 def push_self(obj) copy_instruct(@self_register, obj) @self_stack.push obj end |
- (Object) query_self
85 86 87 88 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 85 def query_self call_instruct(ClassRegistry['Laser#Magic'].binding, :current_self, value: true, raise: false) end |
- (Object) reobserve_current_exception
90 91 92 93 94 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 90 def reobserve_current_exception cur_exception = call_instruct(ClassRegistry['Laser#Magic'].binding, :current_exception, value: true, raise: false) copy_instruct(@exception_register, cur_exception) end |
- (Object) reset_visibility_stack
129 130 131 132 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 129 def reset_visibility_stack initial = call_instruct(ClassRegistry['Array'].binding, :[], const_instruct(:private), value: true, raise: false) copy_instruct(Bootstrap::VISIBILITY_STACK, initial) end |
- (Object) value_walk(node)
Walks the node with the expectation that the return value will be used.
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 591 def value_walk(node) with_current_node(node) do case node.type when :opassign lhs, op, rhs = node.children op = op.[0..-2].to_sym if lhs.type == :field receiver = walk_node lhs[1], value: true method_name = lhs[3]. # Receiver is ONLY EVALUATED ONCE # (on ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]) current_val = call_instruct(receiver, method_name.to_sym, block: false, value: true) if op == :||" result = create_temporary true_block, false_block, after = create_blocks 3 cond_instruct(current_val, true_block, false_block) start_block true_block copy_instruct result, current_val uncond_instruct after start_block false_block rhs_value = walk_node rhs, value: true call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false) copy_instruct result, rhs_value uncond_instruct after start_block after result elsif op == :&&" result = create_temporary true_block, false_block, after = create_blocks 3 cond_instruct(current_val, true_block, false_block) start_block true_block rhs_value = walk_node rhs, value: true call_instruct(receiver, "#{method_name}=".to_sym, rhs_value, block: false, value: false) copy_instruct result, rhs_value uncond_instruct after start_block false_block copy_instruct result, current_val uncond_instruct after start_block after result else rhs_value = walk_node rhs, value: true temp_result = call_instruct(current_val, op, rhs_value, block: false, value: true) call_instruct(receiver, "#{method_name}=".to_sym, temp_result, block: false, value: false) temp_result end # TODO(adgar): aref_field else result = binary_instruct(lhs, op, rhs, value: true) single_assign_instruct(lhs, result) result end when :var_field variable_instruct(node) when :var_ref if node[1].type == :@const const_lookup(node[1].) elsif node[1].type == :@ident || node[1]. == 'self' variable_instruct(node) elsif node[1].type == :@kw const_instruct(node.constant_value) elsif node[1].type == :@ivar call_instruct(current_self, :instance_variable_get, const_instruct(node.), value: true, ignore_privacy: true) elsif node[1].type == :@gvar call_instruct(ClassRegistry['Laser#Magic'].binding, :get_global, const_instruct(node.), raise: false, value: true) end when :top_const_ref const = node[1] ident = const_instruct(const.) call_instruct(ClassRegistry['Object'].binding, :const_get, ident, value: true) when :for lhs, receiver, body = node.children receiver_value = walk_node receiver, value: true if Symbol === lhs[0] # field or var_ref/const_ref case lhs.type when :field # call else # just get the value arg_bindings = [lhs.binding] call_method_with_block(receiver_value, :each, [], arg_bindings, body, value: true) end # TODO(adgar): aref_field else # TODO(adgar): multiple assign end when :case after = create_block result = create_temporary argument, body = node.children argument_value = walk_node argument, value: true while body && body.type == :when when_opts, when_body, body = body.children when_body_block = create_block when_opts.each do |opt| after_fail = create_block condition_result = call_instruct(walk_node(opt, value: true), :===, argument_value, value: true) cond_instruct(condition_result, when_body_block, after_fail) start_block after_fail end all_fail = @current_block start_block when_body_block when_body_result = walk_body when_body, value: true copy_instruct(result, when_body_result) uncond_instruct after start_block all_fail end if body.nil? copy_instruct(result, nil) uncond_instruct after elsif body.type == :else else_body_result = walk_body body[1], value: true copy_instruct(result, else_body_result) uncond_instruct after end start_block after result when :@CHAR, :@tstring_content, :@int, :@float, :@regexp_end, :symbol, :@label, :symbol_literal const_instruct(node.constant_value) when :string_literal content_nodes = node[1].children build_string_instruct(content_nodes) when :string_embexpr final = walk_body node[1], value: true call_instruct(final, :to_s, value: true) when :xstring_literal body = build_string_instruct(node[1]) call_instruct(self_register, :`, body, value: true) when :regexp_literal body = build_string_instruct(node[1]) = const_instruct(node[2].constant_value) receiver = ClassRegistry['Regexp'].binding call_instruct(receiver, :new, body, , value: true) when :dyna_symbol content_nodes = node[1].children string_version = build_string_instruct(content_nodes) call_instruct(string_version, :to_sym, value: true, raise: false) when :array receiver = ClassRegistry['Array'].binding generic_call_instruct(receiver, :[], node[1], false, value: true) when :hash if node[1] walk_node node[1], value: true else const_instruct({}) end when :assoclist_from_args, :bare_assoc_hash pairs = node[1].map { |_, k, v| [k, v] } key_value_paired = pairs.map {|a, b| [walk_node(a, value: true), walk_node(b, value: true)] }.flatten receiver = ClassRegistry['Hash'].binding call_instruct(receiver, :[], *key_value_paired, block: false, value: true) when :defined defined_op_instruct(node[1]) else raise ArgumentError.new("Unknown AST node type #{node.type.inspect}") end end end |
- (Object) walk_node(node, opts = {})
Walks the node differently based on whether the value is needed.
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 290 def walk_node(node, opts={}) with_current_node(node) do case node.type when :bodystmt bodystmt_walk node when :class class_name, superclass, body = node.children class_instruct(class_name, superclass, body, opts) when :module module_name, body = node.children module_instruct(module_name, body, opts) when :sclass receiver, body = node.children singleton_class_instruct receiver, body, opts when :def name, args, body = node.children name = const_instruct(name..to_sym) parsed_args = Signature.arg_list_for_arglist(args) def_opts = opts.merge(line_number: node.line_number) def_instruct(current_namespace, name, parsed_args, body, def_opts) when :defs recv, _, name, args, body = node.children name = const_instruct(name..to_sym) receiver = walk_node(recv, value: true) singleton = call_instruct(receiver, :singleton_class, value: true) parsed_args = Signature.arg_list_for_arglist(args) def_opts = opts.merge(line_number: node.line_number) def_instruct(singleton, name, parsed_args, body, def_opts) when :alias lhs, rhs = node.children lhs_val = const_instruct(lhs[1]..to_sym) rhs_val = const_instruct(rhs[1]..to_sym) call_instruct(current_namespace, :alias_method, lhs_val, rhs_val, value: false, ignore_privacy: true) when :undef undeffed = node.children.first undeffed.each do |method| method_name = const_instruct(method[1]..to_sym) call_instruct(current_namespace, :undef_method, method_name, value: false, raise: true) end nil when :assign lhs, rhs = node.children single_assign_instruct(lhs, rhs, opts) when :massign lhs, rhs = node.children multiple_assign_instruct(lhs, rhs, opts) when :begin walk_node node[1], opts when :paren walk_body node[1], opts when :while condition, body = node.children while_instruct(condition, body, opts) when :while_mod condition, body_stmt = node.children while_instruct(condition, [body_stmt], opts) when :until condition, body = node.children until_instruct(condition, body, opts) when :until_mod condition, body_stmt = node.children until_instruct(condition, [body_stmt], opts) when :if if_instruct(node, false, opts) when :unless condition, body, else_block = node.children unless_instruct(condition, body, else_block, opts) when :if_mod if_instruct(node, true, opts) when :unless_mod condition, body = node.children unless_instruct(condition, [body], nil, opts) when :rescue_mod rescue_expr, guarded_expr = node.children rescue_mod_instruct(rescue_expr, guarded_expr, opts) when :unary op, receiver = node.children receiver = walk_node(receiver, value: true) call_instruct(receiver, op, opts) when :binary # If someone makes an overloaded operator that mutates something.... # we have to run it (maybe), even if we hate them. lhs, op, rhs = node.children binary_instruct(lhs, op, rhs, opts) when :ifop cond, if_true, if_false = node.children ternary_instruct(cond, if_true, if_false, opts) when :const_path_ref lhs, const = node.children lhs_value = walk_node lhs, value: true ident = const_instruct(const.) call_instruct(lhs_value, :const_get, ident, opts) when :call, :command, :command_call, :aref, :method_add_arg, :vcall issue_call node, opts when :method_add_block # need: the receiver, the method name, the arguments, and the block body method_call = node.method_call receiver = if method_call.receiver_node then walk_node(method_call.receiver_node, value: true) else self_instruct end arg_node = method_call.arg_node arg_node = arg_node[1] if arg_node && arg_node.type == :arg_paren block_arg_bindings = node[2][1] ? Signature.arg_list_for_arglist(node[2][1][1]) : [] body_sexp = node[2][2] case node[1].type when :super arg_node = arg_node[1] if arg_node.type == :args_add_block call_method_with_block( receiver, method_call.method_name, arg_node, block_arg_bindings, body_sexp, opts) when :zsuper call_zsuper_with_block(node[1], block_arg_bindings, body_sexp, opts) else opts = opts.merge(ignore_privacy: true) if method_call.implicit_receiver? call_method_with_block( receiver, method_call.method_name, arg_node, block_arg_bindings, body_sexp, opts) end when :super issue_super_call(node) when :zsuper # TODO(adgar): blocks in args & style block = rb_check_convert_type(@block_arg, ClassRegistry['Proc'].binding, :to_proc) invoke_super_with_block(*compute_zsuper_arguments, block, opts) when :yield yield_instruct(node[1], opts) when :yield0 yield_instruct(nil, opts) when :return return_instruct node const_instruct(nil) if opts[:value] when :return0 return0_instruct const_instruct(nil) if opts[:value] when :break break_instruct(node[1]) const_instruct(nil) if opts[:value] when :next next_instruct(node[1]) const_instruct(nil) if opts[:value] when :redo redo_instruct const_instruct(nil) if opts[:value] when :void_stmt const_instruct(nil) if opts[:value] when :program uncond_instruct create_block walk_body node[1], value: false when :dot3 start, stop = node.children start_val = walk_node(start, value: true) stop_val = walk_node(stop, value: true) true_val = const_instruct(true) call_instruct(ClassRegistry['Range'].binding, :new, start_val, stop_val, true_val, opts) when :dot2 start, stop = node.children start_val = walk_node(start, value: true) stop_val = walk_node(stop, value: true) false_val = const_instruct(false) call_instruct(ClassRegistry['Range'].binding, :new, start_val, stop_val, false_val, opts) else opts[:value] ? value_walk(node) : novalue_walk(node) end end end |
- (Object) with_current_basic_block(basic_block)
yields with the current basic block set to the provided basic block. useful for quickly adding an edge without directly touching the graph object.
274 275 276 277 278 279 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 274 def with_current_basic_block(basic_block) old_block, @current_block = @current_block, basic_block yield ensure @current_block = old_block end |
- (Object) with_current_node(node)
281 282 283 284 285 286 287 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 281 def with_current_node(node) old_node, @current_node = @current_node, node @current_node.scope = current_scope yield ensure @current_node = old_node end |
- (Object) with_scope(scope)
57 58 59 60 61 62 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 57 def with_scope(scope) push_scope scope yield ensure pop_scope end |
- (Object) with_self(namespace)
78 79 80 81 82 83 |
# File 'lib/laser/analysis/control_flow/cfg_builder.rb', line 78 def with_self(namespace) push_self namespace yield ensure pop_self end |