Class: Opal::Nodes::IfNode

Inherits:
Base
  • Object
show all
Defined in:
opal/lib/opal/nodes/if.rb

Instance Attribute Summary

Attributes inherited from Base

#compiler, #sexp, #type

Instance Method Summary collapse

Methods inherited from Base

#add_gvar, #add_ivar, #add_local, #add_temp, #children, children, #class_variable_owner, #class_variable_owner_nesting_level, #comments, #compile_to_fragments, #error, #expr, #expr?, #expr_or_nil, #fragment, handle, handlers, #has_rescue_else?, #helper, #in_ensure, #in_ensure?, #in_resbody, #in_resbody?, #in_rescue, #in_while?, #initialize, #process, #push, #recv, #recv?, #s, #scope, #source_location, #stmt, #stmt?, #top_scope, truthy_optimize?, #unshift, #while_loop, #with_temp, #wrap

Methods included from Helpers

#conditional_send, #current_indent, #empty_line, #indent, #js_truthy, #js_truthy_optimize, #line, #mid_to_jsid, #property, #valid_name?

Constructor Details

This class inherits a constructor from Opal::Nodes::Base

Instance Method Details

#compileObject



12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'opal/lib/opal/nodes/if.rb', line 12

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
  else
    compile_with_if
  end
end

#compile_with_binary_andObject



117
118
119
120
121
122
123
124
125
126
127
128
# File 'opal/lib/opal/nodes/if.rb', line 117

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_orObject



130
131
132
133
134
135
136
137
138
139
140
141
# File 'opal/lib/opal/nodes/if.rb', line 130

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_ifObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'opal/lib/opal/nodes/if.rb', line 26

def compile_with_if
  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

  if expects_expression?
    if scope.await_encountered
      wrap '(await (async function() {', '})())'
    else
      wrap '(function() {', '})()'
    end
  end
end

#compile_with_ternaryObject



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'opal/lib/opal/nodes/if.rb', line 99

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

#expects_expression?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'opal/lib/opal/nodes/if.rb', line 85

def expects_expression?
  expr? || recv?
end

#falsyObject



73
74
75
# File 'opal/lib/opal/nodes/if.rb', line 73

def falsy
  returnify(false_body)
end

#returnify(body) ⇒ Object



77
78
79
80
81
82
83
# File 'opal/lib/opal/nodes/if.rb', line 77

def returnify(body)
  if expects_expression? && body
    compiler.returns(body)
  else
    body
  end
end

#should_compile_as_simple_expression?Boolean

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?

Returns:

  • (Boolean)


95
96
97
# File 'opal/lib/opal/nodes/if.rb', line 95

def should_compile_as_simple_expression?
  expects_expression? && simple?(true_body) && simple?(false_body)
end

#simple?(body) ⇒ Boolean

Let's ensure there are no control flow statements inside.

Returns:

  • (Boolean)


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'opal/lib/opal/nodes/if.rb', line 144

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

#truthyObject



69
70
71
# File 'opal/lib/opal/nodes/if.rb', line 69

def truthy
  returnify(true_body)
end