class Opal::Nodes::IfNode
Constants
- SWITCH_BRANCH_TEST_MATCH
Matches: ‘when 456` (from `case foo; when 123; when 456; end`) Captures: [s(:int, 456), “$ret_or_1”]
- SWITCH_BRANCH_TEST_MATCH_CONTINUED
Matches: ‘when 456` Captures: [
s(:int, 789), "$ret_or_1", …here we delegate to either SWITCH_BRANCH_TEST_MATCH or SWITCH_BRANCH_TEST_MATCH_CONTINUED
]
- SWITCH_TEST_MATCH
Matches: ‘case some_value_or_expression; when 123` Captures: [s(:int, 123), “$ret_or_1”, s(:send, nil, :some_value_or_expression))]
- SWITCH_TEST_MATCH_CONTINUED
Matches: case some_value_or_expression; when 123, 456; end Captures: [
s(:int, 123), "$ret_or_1", s(:send, nil, :some_value_or_expression)), …here we delegate to either SWITCH_BRANCH_TEST_MATCH or SWITCH_BRANCH_TEST_MATCH_CONTINUED
]
Public Instance Methods
compile()
click to toggle source
# File lib/opal/nodes/if.rb, line 13 def compile if should_compile_as_simple_expression? if true_body == s(:true) compile_with_binary_or elsif false_body == s(:false) compile_with_binary_and else compile_with_ternary end elsif could_become_switch? compile_with_switch else compile_with_if end end
compile_switch_case(test)
click to toggle source
# File lib/opal/nodes/if.rb, line 365 def compile_switch_case(test) line "case ", expr(test), ":" if @switch_additional_rules @switch_additional_rules.each do |rule| line "case ", expr(rule), ":" end end indent do line stmt(true_body) line "break;" if !true_body || !returning?(true_body) end if false_body if false_body.meta[:switch_default] compile_switch_default elsif false_body.meta[:switch_child] push stmt(false_body) end else push stmt(s(:nil)) end end
compile_switch_default()
click to toggle source
# File lib/opal/nodes/if.rb, line 387 def compile_switch_default line "default:" indent do line stmt(false_body) end end
compile_with_binary_and()
click to toggle source
# File lib/opal/nodes/if.rb, line 130 def compile_with_binary_and if sexp.meta[:do_js_truthy_on_true_body] truthy = js_truthy(true_body || s(:nil)) else truthy = expr(true_body || s(:nil)) end push '(' push js_truthy(test), ' && ' push '(', truthy, ')' push ')' end
compile_with_binary_or()
click to toggle source
# File lib/opal/nodes/if.rb, line 143 def compile_with_binary_or if sexp.meta[:do_js_truthy_on_false_body] falsy = js_truthy(false_body || s(:nil)) else falsy = expr(false_body || s(:nil)) end push '(' push js_truthy(test), ' || ' push '(', falsy, ')' push ')' end
compile_with_if()
click to toggle source
# File lib/opal/nodes/if.rb, line 29 def compile_with_if push_closure if expects_expression? truthy = self.truthy falsy = self.falsy if falsy && !truthy # Let's optimize a little bit `unless` calls. push 'if (!', js_truthy(test), ') {' falsy, truthy = truthy, falsy else push 'if (', js_truthy(test), ') {' end # skip if-body if no truthy sexp indent { line stmt(truthy) } if truthy if falsy if falsy.type == :if line '} else ', stmt(falsy) else line '} else {' indent do line stmt(falsy) end line '}' end else line '}' # This resolution isn't finite. Let's ensure this block # always return something if we expect a return line 'return nil;' if expects_expression? end pop_closure if expects_expression? if expects_expression? return_kw = 'return ' if returning_if? if scope.await_encountered wrap "#{return_kw}(await (async function() {", '})())' else wrap "#{return_kw}(function() {", '})()' end end end
compile_with_switch()
click to toggle source
# File lib/opal/nodes/if.rb, line 346 def compile_with_switch if sexp.meta[:switch_child] @switch_variable = sexp.meta[:switch_variable] @switch_additional_rules = sexp.meta[:switch_additional_rules] compile_switch_case(sexp.meta[:switch_test]) else line "switch (", expr(@switch_first_test), ".valueOf()) {" indent do compile_switch_case(@switch_test) end line "}" end end
compile_with_ternary()
click to toggle source
# File lib/opal/nodes/if.rb, line 112 def compile_with_ternary truthy = true_body falsy = false_body push '(' push js_truthy(test), ' ? ' push '(', expr(truthy || s(:nil)), ') : ' if !falsy || falsy.type == :if push expr(falsy || s(:nil)) else push '(', expr(falsy || s(:nil)), ')' end push ')' end
could_become_switch?()
click to toggle source
# File lib/opal/nodes/if.rb, line 254 def could_become_switch? return false if expects_expression? return true if sexp.meta[:switch_child] test_match = SWITCH_TEST_MATCH.match(test) || SWITCH_TEST_MATCH_CONTINUED.match(test) return false unless test_match @switch_test, @switch_variable, @switch_first_test, additional_rules = *test_match additional_rules = handle_additional_switch_rules(additional_rules) return false unless additional_rules # It's ok for them to be empty, but false denotes a mismatch @switch_additional_rules = additional_rules return false unless valid_switch_body?(true_body) could_become_switch_branch?(false_body) end
could_become_switch_branch?(body)
click to toggle source
# File lib/opal/nodes/if.rb, line 286 def could_become_switch_branch?(body) if !body return true elsif body.type != :if if valid_switch_body?(body) body.meta[:switch_default] = true return true end return false end test, true_body, false_body = *body test_match = SWITCH_BRANCH_TEST_MATCH.match(test) || SWITCH_BRANCH_TEST_MATCH_CONTINUED.match(test) unless test_match if valid_switch_body?(body, true) body.meta[:switch_default] = true return true end end switch_test, switch_variable, additional_rules = *test_match switch_additional_rules = handle_additional_switch_rules(additional_rules) return false unless switch_additional_rules # It's ok for them to be empty, but false denotes a mismatch return false unless switch_variable == @switch_variable return false unless valid_switch_body?(true_body) return false unless could_become_switch_branch?(false_body) body.meta.merge!(switch_child: true, switch_test: switch_test, switch_variable: @switch_variable, switch_additional_rules: switch_additional_rules ) true end
expects_expression?()
click to toggle source
# File lib/opal/nodes/if.rb, line 98 def expects_expression? expr? || recv? end
falsy()
click to toggle source
# File lib/opal/nodes/if.rb, line 86 def falsy returnify(false_body) end
handle_additional_switch_rules(additional_rules)
click to toggle source
# File lib/opal/nodes/if.rb, line 272 def handle_additional_switch_rules(additional_rules) switch_additional_rules = [] while additional_rules match = SWITCH_BRANCH_TEST_MATCH.match(additional_rules) || SWITCH_BRANCH_TEST_MATCH_CONTINUED.match(additional_rules) return false unless match switch_test, switch_variable, additional_rules = *match return false unless switch_variable == @switch_variable switch_additional_rules << switch_test end switch_additional_rules end
returnify(body)
click to toggle source
# File lib/opal/nodes/if.rb, line 90 def returnify(body) if expects_expression? && body compiler.returns(body) else body end end
returning?(body)
click to toggle source
# File lib/opal/nodes/if.rb, line 360 def returning?(body) %i[return js_return next].include?(body.type) || (body.type == :begin && %i[return js_return next].include?(body.children.last.type)) end
returning_if?()
click to toggle source
# File lib/opal/nodes/if.rb, line 78 def returning_if? @sexp.meta[:returning] end
should_compile_as_simple_expression?()
click to toggle source
There was a particular case in the past, that when we expected an expression from if, we always had to closure it. This produced an ugly code that was hard to minify. This addition tries to make a few cases compiled with a ternary operator instead and possibly a binary operator even?
# File lib/opal/nodes/if.rb, line 108 def should_compile_as_simple_expression? expects_expression? && simple?(true_body) && simple?(false_body) end
simple?(body)
click to toggle source
Let’s ensure there are no control flow statements inside.
# File lib/opal/nodes/if.rb, line 157 def simple?(body) case body when AST::Node case body.type when :return, :js_return, :break, :next, :redo, :retry false when :xstr XStringNode.single_line?( XStringNode.strip_empty_children(body.children) ) else body.children.all? { |i| simple?(i) } end else true end end
truthy()
click to toggle source
# File lib/opal/nodes/if.rb, line 82 def truthy returnify(true_body) end
valid_switch_body?(body, check_variable = false)
click to toggle source
# File lib/opal/nodes/if.rb, line 325 def valid_switch_body?(body, check_variable = false) case body when AST::Node case body.type when :break, :redo, :retry false when :iter, :while # Don't traverse the iters or whiles! true else body.children.all? { |i| valid_switch_body?(i, check_variable) } end when @switch_variable # Perhaps we ended abruptly and we lack a $ret_or variable... but sometimes # we can ignore this. !check_variable else true end end