Class: Opal::Nodes::ScopeNode
  
  
  
  
  
    - Inherits:
- 
      Base
      
        
          - Object
- Base
- Opal::Nodes::ScopeNode
 show all
    - Defined in:
- opal/lib/opal/nodes/scope.rb
 
  Instance Attribute Summary collapse
  
  
  
  Attributes inherited from Base
  #compiler, #sexp, #type
  
  
  
  #closure
  
    
      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, #compile_to_fragments, #error, #expr, #expr?, #expr_or_empty, #expr_or_nil, #fragment, handle, handlers, #helper, #process, #push, #recv, #recv?, #s, #scope, #source_location, #stmt, #stmt?, #top_scope, truthy_optimize?, #unshift, #while_loop, #with_temp, #wrap
  
  
  
  
  
  
  
  
  
  #closure_is?, #compile_catcher, #generate_thrower, #generate_thrower_without_catcher, #in_closure, #pop_closure, #push_closure, #select_closure, #thrower
  
  
  
  
  
  
  
  
  Methods included from Helpers
  #current_indent, #empty_line, #indent, #js_truthy, #js_truthy_optimize, #line, #mid_to_jsid, #property, #valid_name?
  Constructor Details
  
    
  
  
    Returns a new instance of ScopeNode.
   
 
  
  
    | 
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 | # File 'opal/lib/opal/nodes/scope.rb', line 34
def initialize(*)
  super
  @locals   = []
  @temps    = []
  @args     = []
  @ivars    = []
  @gvars    = []
  @parent   = nil
  @queue    = []
  @unique   = 'a'
  @while_stack = []
  @identity = nil
  @defs     = nil
  @methods = []
  @uses_block = false
  @in_ensure = false
    @proto_ivars = []
end | 
 
  
 
  
    Instance Attribute Details
    
      
      
      
  
  
    #await_encountered  ⇒ Object 
  
  
  
  
    Returns the value of attribute await_encountered.
   
 
  
  
    | 
419
420
421 | # File 'opal/lib/opal/nodes/scope.rb', line 419
def await_encountered
  @await_encountered
end | 
 
    
      
      
      
  
  
    #block_name  ⇒ Object 
  
  
  
  
    The given block name for a def scope
   
 
  
  
    | 
15
16
17 | # File 'opal/lib/opal/nodes/scope.rb', line 15
def block_name
  @block_name
end | 
 
    
      
      
      
  
  
    #catch_return  ⇒ Object 
  
  
  
  
    Returns the value of attribute catch_return.
   
 
  
  
    | 
30
31
32 | # File 'opal/lib/opal/nodes/scope.rb', line 30
def catch_return
  @catch_return
end | 
 
    
      
      
      
  
  
    #defs  ⇒ Object 
  
  
  
  
    true if singleton def, false otherwise
   
 
  
  
    | 
25
26
27 | # File 'opal/lib/opal/nodes/scope.rb', line 25
def defs
  @defs
end | 
 
    
      
      
      
  
  
    #gvars  ⇒ Object  
  
  
  
  
    Returns the value of attribute gvars.
   
 
  
  
    | 
20
21
22 | # File 'opal/lib/opal/nodes/scope.rb', line 20
def gvars
  @gvars
end | 
 
    
      
      
      
  
  
    #has_break  ⇒ Object 
  
  
  
  
    Returns the value of attribute has_break.
   
 
  
  
    | 
30
31
32 | # File 'opal/lib/opal/nodes/scope.rb', line 30
def has_break
  @has_break
end | 
 
    
      
      
      
  
  
    #has_retry  ⇒ Object 
  
  
  
  
    Returns the value of attribute has_retry.
   
 
  
  
    | 
30
31
32 | # File 'opal/lib/opal/nodes/scope.rb', line 30
def has_retry
  @has_retry
end | 
 
    
      
      
      
  
  
    #identity  ⇒ Object  
  
  
  
  
    Returns the value of attribute identity.
   
 
  
  
    | 
277
278
279 | # File 'opal/lib/opal/nodes/scope.rb', line 277
def identity
  @identity
end | 
 
    
      
      
      
  
  
    #ivars  ⇒ Object  
  
  
  
  
    Returns the value of attribute ivars.
   
 
  
  
    | 
19
20
21 | # File 'opal/lib/opal/nodes/scope.rb', line 19
def ivars
  @ivars
end | 
 
    
      
      
      
  
  
    #locals  ⇒ Object  
  
  
  
  
    Returns the value of attribute locals.
   
 
  
  
    | 
18
19
20 | # File 'opal/lib/opal/nodes/scope.rb', line 18
def locals
  @locals
end | 
 
    
      
      
      
  
  
    #methods  ⇒ Object  
  
  
  
  
    used by modules to know what methods to donate to includees
   
 
  
  
    | 
28
29
30 | # File 'opal/lib/opal/nodes/scope.rb', line 28
def methods
  @methods
end | 
 
    
      
      
      
  
  
    #mid  ⇒ Object 
  
  
  
  
    Returns the value of attribute mid.
   
 
  
  
    | 
22
23
24 | # File 'opal/lib/opal/nodes/scope.rb', line 22
def mid
  @mid
end | 
 
    
      
      
      
  
  
    #name  ⇒ Object 
  
  
  
  
    The class or module name if this scope is a class scope
   
 
  
  
    | 
12
13
14 | # File 'opal/lib/opal/nodes/scope.rb', line 12
def name
  @name
end | 
 
    
      
      
      
  
  
    #parent  ⇒ Object 
  
  
  
  
    Every scope can have a parent scope
   
 
  
  
    | 
9
10
11 | # File 'opal/lib/opal/nodes/scope.rb', line 9
def parent
  @parent
end | 
 
    
      
      
      
  
  
    #rescue_else_sexp  ⇒ Object 
  
  
  
  
    Returns the value of attribute rescue_else_sexp.
   
 
  
  
    | 
32
33
34 | # File 'opal/lib/opal/nodes/scope.rb', line 32
def rescue_else_sexp
  @rescue_else_sexp
end | 
 
    
      
      
      
  
  
    #scope_name  ⇒ Object  
  
  
  
  
    Returns the value of attribute scope_name.
   
 
  
  
    | 
17
18
19 | # File 'opal/lib/opal/nodes/scope.rb', line 17
def scope_name
  @scope_name
end | 
 
    
   
  
    Instance Method Details
    
      
  
  
    #accepts_using?  ⇒ Boolean 
  
  
  
 
    
      
  
  
    #add_arg(arg)  ⇒ Object 
  
  
  
  
    | 
172
173
174
175 | # File 'opal/lib/opal/nodes/scope.rb', line 172
def add_arg(arg)
  @args << arg unless @args.include? arg
  arg
end | 
 
    
      
  
  
    #add_proto_ivar(ivar)  ⇒ Object 
  
  
  
  
    | 
168
169
170 | # File 'opal/lib/opal/nodes/scope.rb', line 168
def add_proto_ivar(ivar)
  @proto_ivars << ivar unless @proto_ivars.include? ivar
end | 
 
    
      
  
  
    #add_scope_gvar(gvar)  ⇒ Object 
  
  
  
  
    | 
164
165
166 | # File 'opal/lib/opal/nodes/scope.rb', line 164
def add_scope_gvar(gvar)
  @gvars << gvar unless @gvars.include? gvar
end | 
 
    
      
  
  
    #add_scope_ivar(ivar)  ⇒ Object 
  
  
  
  
    | 
156
157
158
159
160
161
162 | # File 'opal/lib/opal/nodes/scope.rb', line 156
def add_scope_ivar(ivar)
  if def_in_class?
    @parent.add_proto_ivar ivar
  else
    @ivars << ivar unless @ivars.include? ivar
  end
end | 
 
    
      
  
  
    #add_scope_local(local)  ⇒ Object 
  
  
  
  
    | 
177
178
179
180
181 | # File 'opal/lib/opal/nodes/scope.rb', line 177
def add_scope_local(local)
  return if has_local? local
  @locals << local
end | 
 
    
      
  
  
    #add_scope_temp(tmp)  ⇒ Object 
  
  
  
  
    | 
194
195
196
197
198 | # File 'opal/lib/opal/nodes/scope.rb', line 194
def add_scope_temp(tmp)
  return if has_temp?(tmp)
  @temps.push(tmp)
end | 
 
    
      
  
  
    #class?  ⇒ Boolean 
  
  
  
  
    Returns true if this is strictly a class scope
   
 
  
    | 
73
74
75 | # File 'opal/lib/opal/nodes/scope.rb', line 73
def class?
  @type == :class
end | 
 
    
      
  
  
    #class_scope?  ⇒ Boolean 
  
  
  
  
    Returns true if this scope is a class/module body scope
   
 
  
    | 
68
69
70 | # File 'opal/lib/opal/nodes/scope.rb', line 68
def class_scope?
  @type == :class || @type == :module
end | 
 
    
      
  
  
    #collect_refinements_temps(temps = [])  ⇒ Object 
  
  
  
  
    | 
371
372
373
374
375 | # File 'opal/lib/opal/nodes/scope.rb', line 371
def collect_refinements_temps(temps = [])
  temps << @refinements_temp if @refinements_temp
  return parent.collect_refinements_temps(temps) if parent
  temps
end | 
 
    
      
  
  
    #current_rescue  ⇒ Object 
  
  
  
  
    | 
328
329
330 | # File 'opal/lib/opal/nodes/scope.rb', line 328
def current_rescue
  @rescues.last
end | 
 
    
      
  
  
    #def?  ⇒ Boolean 
  
  
  
  
    | 
96
97
98 | # File 'opal/lib/opal/nodes/scope.rb', line 96
def def?
  @type == :def || @type == :defs
end | 
 
    
      
  
  
    #def_in_class?  ⇒ Boolean 
  
  
  
  
    Is this a normal def method directly inside a class? This is
used for optimizing ivars as we can set them to nil in the
class body
   
 
  
    | 
121
122
123 | # File 'opal/lib/opal/nodes/scope.rb', line 121
def def_in_class?
  !@defs && @type == :def && @parent && @parent.class?
end | 
 
    
      
  
  
    #defines_lambda  ⇒ Object 
  
  
  
  
    | 
108
109
110
111
112 | # File 'opal/lib/opal/nodes/scope.rb', line 108
def defines_lambda
  @lambda_definition = true
  yield
  @lambda_definition = false
end | 
 
    
      
  
  
    #find_parent_def  ⇒ Object 
  
  
  
  
    | 
279
280
281
282
283
284
285
286
287
288 | # File 'opal/lib/opal/nodes/scope.rb', line 279
def find_parent_def
  scope = self
  while scope = scope.parent
    if scope.def? || scope.lambda?
      return scope
    end
  end
  nil
end | 
 
    
      
  
  
    #gen_retry_id  ⇒ Object 
  
  
  
  
    | 
360
361
362
363 | # File 'opal/lib/opal/nodes/scope.rb', line 360
def gen_retry_id
  @next_retry_id ||= 'retry_0'
  @next_retry_id = @next_retry_id.succ
end | 
 
    
      
  
  
    #has_local?(local)  ⇒ Boolean 
  
  
  
  
    | 
183
184
185
186
187 | # File 'opal/lib/opal/nodes/scope.rb', line 183
def has_local?(local)
  return true if @locals.include?(local) || @args.include?(local) || @temps.include?(local)
  return @parent.has_local?(local) if @parent && @type == :iter
  false
end | 
 
    
      
  
  
    #has_rescue_else?  ⇒ Boolean 
  
  
  
  
    | 
314
315
316 | # File 'opal/lib/opal/nodes/scope.rb', line 314
def has_rescue_else?
  !rescue_else_sexp.nil?
end | 
 
    
      
  
  
    #has_temp?(tmp)  ⇒ Boolean 
  
  
  
  
    | 
206
207
208 | # File 'opal/lib/opal/nodes/scope.rb', line 206
def has_temp?(tmp)
  @temps.include? tmp
end | 
 
    
      
  
  
    #identify!(name = nil)  ⇒ Object 
  
  
  
  
    | 
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275 | # File 'opal/lib/opal/nodes/scope.rb', line 255
def identify!(name = nil)
  return @identity if @identity
  if valid_name? mid
                                    @identity = "$$#{mid}"
  else
        name ||= [(parent && (parent.name || parent.scope_name)), mid].compact.join('_')
    @identity = @compiler.unique_temp(name)
  end
  @identity
end | 
 
    
      
  
  
    #in_ensure  ⇒ Object 
  
  
  
  
    | 
346
347
348
349
350
351
352
353
354 | # File 'opal/lib/opal/nodes/scope.rb', line 346
def in_ensure
  return unless block_given?
  @in_ensure = true
  result = yield
  @in_ensure = false
  result
end | 
 
    
      
  
  
    #in_ensure?  ⇒ Boolean 
  
  
  
  
    | 
356
357
358 | # File 'opal/lib/opal/nodes/scope.rb', line 356
def in_ensure?
  @in_ensure
end | 
 
    
      
  
  
    #in_resbody  ⇒ Object 
  
  
  
  
    | 
332
333
334
335
336
337
338
339
340 | # File 'opal/lib/opal/nodes/scope.rb', line 332
def in_resbody
  return unless block_given?
  @in_resbody = true
  result = yield
  @in_resbody = false
  result
end | 
 
    
      
  
  
    #in_resbody?  ⇒ Boolean 
  
  
  
  
    | 
342
343
344 | # File 'opal/lib/opal/nodes/scope.rb', line 342
def in_resbody?
  @in_resbody
end | 
 
    
      
  
  
    #in_rescue(node)  ⇒ Object 
  
  
  
  
    | 
318
319
320
321
322
323
324
325
326 | # File 'opal/lib/opal/nodes/scope.rb', line 318
def in_rescue(node)
  @rescues ||= []
  @rescues.push(node)
  result = yield
  @rescues.pop
  result
end | 
 
    
      
  
  
    #in_scope  ⇒ Object 
  
  
  
  
    | 
58
59
60
61
62
63
64
65 | # File 'opal/lib/opal/nodes/scope.rb', line 58
def in_scope
  indent do
    @parent = compiler.scope
    compiler.scope = self
    yield self
    compiler.scope = @parent
  end
end | 
 
    
      
  
  
    #in_while?  ⇒ Boolean 
  
  
  
  
    | 
242
243
244 | # File 'opal/lib/opal/nodes/scope.rb', line 242
def in_while?
  !@while_stack.empty?
end | 
 
    
      
  
  
    #is_lambda!  ⇒ Object 
  
  
  
  
    rubocop:disable Naming/PredicateName
   
 
  
  
    | 
104
105
106 | # File 'opal/lib/opal/nodes/scope.rb', line 104
def is_lambda!   @is_lambda = true
end | 
 
    
      
  
  
    #iter?  ⇒ Boolean 
  
  
  
  
    True if a block/iter scope
   
 
  
    | 
92
93
94 | # File 'opal/lib/opal/nodes/scope.rb', line 92
def iter?
  @type == :iter
end | 
 
    
      
  
  
    #lambda?  ⇒ Boolean 
  
  
  
  
    | 
100
101
102 | # File 'opal/lib/opal/nodes/scope.rb', line 100
def lambda?
  iter? && @is_lambda
end | 
 
    
      
  
  
    #lambda_definition?  ⇒ Boolean 
  
  
  
  
    | 
114
115
116 | # File 'opal/lib/opal/nodes/scope.rb', line 114
def lambda_definition?
  @lambda_definition
end | 
 
    
      
  
  
    #module?  ⇒ Boolean 
  
  
  
  
    True if this is a module scope
   
 
  
    | 
78
79
80 | # File 'opal/lib/opal/nodes/scope.rb', line 78
def module?
  @type == :module
end | 
 
    
      
  
  
    #nesting  ⇒ Object 
  
  
  
  
    Returns '$nesting', but also ensures we compile the nesting chain
   
 
  
  
    | 
396
397
398
399 | # File 'opal/lib/opal/nodes/scope.rb', line 396
def nesting
  @define_nesting = true
  '$nesting'
end | 
 
    
      
  
  
    #new_refinements_temp  ⇒ Object 
  
  
  
  
    | 
377
378
379
380
381 | # File 'opal/lib/opal/nodes/scope.rb', line 377
def new_refinements_temp
  var = compiler.unique_temp("$refn")
  add_scope_local(var)
  var
end | 
 
    
      
  
  
    #new_temp  ⇒ Object 
  
  
  
  
    | 
210
211
212
213
214
215
216 | # File 'opal/lib/opal/nodes/scope.rb', line 210
def new_temp
  return @queue.pop unless @queue.empty?
  tmp = next_temp
  @temps << tmp
  tmp
end | 
 
    
      
  
  
    #next_temp  ⇒ Object 
  
  
  
  
    | 
218
219
220
221
222
223
224
225
226 | # File 'opal/lib/opal/nodes/scope.rb', line 218
def next_temp
  tmp = nil
  loop do
    tmp = "$#{@unique}"
    @unique = @unique.succ
    break unless has_local?(tmp)
  end
  tmp
end | 
 
    
      
  
  
    #pop_while  ⇒ Object 
  
  
  
  
    | 
238
239
240 | # File 'opal/lib/opal/nodes/scope.rb', line 238
def pop_while
  @while_stack.pop
end | 
 
    
      
  
  
    #prepare_block(block_name = nil)  ⇒ Object 
  
  
  
  
    | 
407
408
409
410
411
412
413
414
415
416
417 | # File 'opal/lib/opal/nodes/scope.rb', line 407
def prepare_block(block_name = nil)
  scope_name = scope.identity
  self.block_name = block_name if block_name
  add_temp "#{self.block_name} = #{scope_name}.$$p || nil"
  unless @block_prepared
    line "#{scope_name}.$$p = null;"
    @block_prepared = true
  end
end | 
 
    
      
  
  
    #prepend_scope_temp(tmp)  ⇒ Object 
  
  
  
  
    | 
200
201
202
203
204 | # File 'opal/lib/opal/nodes/scope.rb', line 200
def prepend_scope_temp(tmp)
  return if has_temp?(tmp)
  @temps.unshift(tmp)
end | 
 
    
      
  
  
    #push_while  ⇒ Object 
  
  
  
  
    | 
232
233
234
235
236 | # File 'opal/lib/opal/nodes/scope.rb', line 232
def push_while
  info = {}
  @while_stack.push info
  info
end | 
 
    
      
  
  
    #queue_temp(name)  ⇒ Object 
  
  
  
  
    | 
228
229
230 | # File 'opal/lib/opal/nodes/scope.rb', line 228
def queue_temp(name)
  @queue << name
end | 
 
    
      
  
  
    #refinements_temp  ⇒ Object 
  
  
  
  
    | 
383
384
385
386
387 | # File 'opal/lib/opal/nodes/scope.rb', line 383
def refinements_temp
  prev, curr = @refinements_temp, new_refinements_temp
  @refinements_temp = curr
  [prev, curr]
end | 
 
    
      
  
  
    #relative_access  ⇒ Object 
  
  
  
  
    Returns '$$', but also ensures we compile it
   
 
  
  
    | 
402
403
404
405 | # File 'opal/lib/opal/nodes/scope.rb', line 402
def relative_access
  @define_relative_access = @define_nesting = true
  '$$'
end | 
 
    
      
  
  
    #sclass?  ⇒ Boolean 
  
  
  
  
    | 
82
83
84 | # File 'opal/lib/opal/nodes/scope.rb', line 82
def sclass?
  @type == :sclass
end | 
 
    
      
  
  
    #scope_locals  ⇒ Object 
  
  
  
  
    | 
189
190
191
192 | # File 'opal/lib/opal/nodes/scope.rb', line 189
def scope_locals
  locals = @locals | @args | (@parent && @type == :iter ? @parent.scope_locals : [])
  locals.reject { |i| i.to_s.start_with?('$') }
end | 
 
    
      
  
  
    #self  ⇒ Object 
  
  
  
  
    Returns 'self', but also ensures that the self variable is set
   
 
  
  
    | 
390
391
392
393 | # File 'opal/lib/opal/nodes/scope.rb', line 390
def self
  @define_self = true
  'self'
end | 
 
    
      
  
  
    #super_chain  ⇒ Object 
  
  
  
  
    | 
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 | # File 'opal/lib/opal/nodes/scope.rb', line 290
def super_chain
  chain, scope, defn, mid = [], self, 'null', 'null'
  while scope
    if scope.type == :iter
      chain << scope.identify!
      scope = scope.parent if scope.parent
    elsif %i[def defs].include?(scope.type)
      defn = scope.identify!
      mid  = "'#{scope.mid}'"
      break
    else
      break
    end
  end
  [chain, defn, mid]
end | 
 
    
      
  
  
    #to_vars  ⇒ Object 
  
  
  
  
    Vars to use inside each scope
   
 
  
  
    | 
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 | # File 'opal/lib/opal/nodes/scope.rb', line 127
def to_vars
  vars = @temps.dup
  vars.push(*@locals.map { |l| "#{l} = nil" })
  iv = ivars.map do |ivar|
    "if (self#{ivar} == null) self#{ivar} = nil;\n"
  end
  gv = gvars.map do |gvar|
    "if ($gvars#{gvar} == null) $gvars#{gvar} = nil;\n"
  end
  if class? && !@proto_ivars.empty?
    vars << '$proto = self.$$prototype'
  end
  indent = @compiler.parser_indent
  str  = vars.empty? ? '' : "var #{vars.join ', '};\n"
  str += "#{indent}#{iv.join indent}" unless ivars.empty?
  str += "#{indent}#{gv.join indent}" unless gvars.empty?
  if class? && !@proto_ivars.empty?
    pvars = @proto_ivars.map { |i| "$proto#{i}" }.join(' = ')
    str = "#{str}\n#{indent}#{pvars} = nil;"
  end
  fragment(str)
end | 
 
    
      
  
  
    #top?  ⇒ Boolean 
  
  
  
  
    Returns true if this is a top scope (main file body)
   
 
  
    | 
87
88
89 | # File 'opal/lib/opal/nodes/scope.rb', line 87
def top?
  @type == :top
end | 
 
    
      
  
  
    #uses_block!  ⇒ Object 
  
  
  
  
    | 
246
247
248
249
250
251
252
253 | # File 'opal/lib/opal/nodes/scope.rb', line 246
def uses_block!
  if @type == :iter && @parent
    @parent.uses_block!
  else
    @uses_block = true
    identify!
  end
end | 
 
    
      
  
  
    #uses_block?  ⇒ Boolean 
  
  
  
  
    | 
310
311
312 | # File 'opal/lib/opal/nodes/scope.rb', line 310
def uses_block?
  @uses_block
end |