Class: Opal::Nodes::ScopeNode

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

Instance Attribute Summary collapse

Attributes inherited from Base

#compiler, #sexp, #type

Attributes included from Closure::NodeSupport

#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

Methods included from Closure::NodeSupport

#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

#initializeScopeNode

Returns a new instance of ScopeNode.

[View source]

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

  # used by classes to store all ivars used in direct def methods
  @proto_ivars = []
end

Instance Attribute Details

#await_encounteredObject

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_nameObject

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_returnObject

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

#defsObject

true if singleton def, false otherwise


25
26
27
# File 'opal/lib/opal/nodes/scope.rb', line 25

def defs
  @defs
end

#gvarsObject (readonly)

Returns the value of attribute gvars.


20
21
22
# File 'opal/lib/opal/nodes/scope.rb', line 20

def gvars
  @gvars
end

#has_breakObject

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_retryObject

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

#identityObject (readonly)

Returns the value of attribute identity.


277
278
279
# File 'opal/lib/opal/nodes/scope.rb', line 277

def identity
  @identity
end

#ivarsObject (readonly)

Returns the value of attribute ivars.


19
20
21
# File 'opal/lib/opal/nodes/scope.rb', line 19

def ivars
  @ivars
end

#localsObject (readonly)

Returns the value of attribute locals.


18
19
20
# File 'opal/lib/opal/nodes/scope.rb', line 18

def locals
  @locals
end

#methodsObject (readonly)

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

#midObject

Returns the value of attribute mid.


22
23
24
# File 'opal/lib/opal/nodes/scope.rb', line 22

def mid
  @mid
end

#nameObject

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

#parentObject

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_sexpObject

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_nameObject (readonly)

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

Returns:

  • (Boolean)
[View source]

365
366
367
368
369
# File 'opal/lib/opal/nodes/scope.rb', line 365

def accepts_using?
  # IterNode of a special kind of Module.new {} is accepted...
  # though we don't check for it that thoroughly.
  [TopNode, ModuleNode, ClassNode, IterNode].include? self.class
end

#add_arg(arg) ⇒ Object

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

Returns:

  • (Boolean)
[View source]

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

Returns:

  • (Boolean)
[View source]

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

[View source]

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_rescueObject

[View source]

328
329
330
# File 'opal/lib/opal/nodes/scope.rb', line 328

def current_rescue
  @rescues.last
end

#def?Boolean

Returns:

  • (Boolean)
[View source]

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

Returns:

  • (Boolean)
[View source]

121
122
123
# File 'opal/lib/opal/nodes/scope.rb', line 121

def def_in_class?
  !@defs && @type == :def && @parent && @parent.class?
end

#defines_lambdaObject

[View source]

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_defObject

[View source]

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_idObject

[View source]

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

Returns:

  • (Boolean)
[View source]

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

Returns:

  • (Boolean)
[View source]

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

Returns:

  • (Boolean)
[View source]

206
207
208
# File 'opal/lib/opal/nodes/scope.rb', line 206

def has_temp?(tmp)
  @temps.include? tmp
end

#identify!(name = nil) ⇒ Object

[View source]

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
    # There are some special utf8 chars that can be used as valid JS
    # identifiers, some examples:
    #
    # utf8_pond = 'ⵌ'
    # utf8_question = 'ʔ̣'
    # utf8_exclamation 'ǃ'
    #
    # For now we're just using $$, to maintain compatibility with older IEs.
    @identity = "$$#{mid}"
  else
    # Parent scope is the defining module/class
    name ||= [(parent && (parent.name || parent.scope_name)), mid].compact.join('_')
    @identity = @compiler.unique_temp(name)
  end

  @identity
end

#in_ensureObject

[View source]

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

Returns:

  • (Boolean)
[View source]

356
357
358
# File 'opal/lib/opal/nodes/scope.rb', line 356

def in_ensure?
  @in_ensure
end

#in_resbodyObject

[View source]

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

Returns:

  • (Boolean)
[View source]

342
343
344
# File 'opal/lib/opal/nodes/scope.rb', line 342

def in_resbody?
  @in_resbody
end

#in_rescue(node) ⇒ Object

[View source]

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_scopeObject

[View source]

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

Returns:

  • (Boolean)
[View source]

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

[View source]

104
105
106
# File 'opal/lib/opal/nodes/scope.rb', line 104

def is_lambda! # rubocop:disable Naming/PredicateName
  @is_lambda = true
end

#iter?Boolean

True if a block/iter scope

Returns:

  • (Boolean)
[View source]

92
93
94
# File 'opal/lib/opal/nodes/scope.rb', line 92

def iter?
  @type == :iter
end

#lambda?Boolean

Returns:

  • (Boolean)
[View source]

100
101
102
# File 'opal/lib/opal/nodes/scope.rb', line 100

def lambda?
  iter? && @is_lambda
end

#lambda_definition?Boolean

Returns:

  • (Boolean)
[View source]

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

Returns:

  • (Boolean)
[View source]

78
79
80
# File 'opal/lib/opal/nodes/scope.rb', line 78

def module?
  @type == :module
end

#nestingObject

Returns '$nesting', but also ensures we compile the nesting chain

[View source]

396
397
398
399
# File 'opal/lib/opal/nodes/scope.rb', line 396

def nesting
  @define_nesting = true
  '$nesting'
end

#new_refinements_tempObject

[View source]

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_tempObject

[View source]

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_tempObject

[View source]

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_whileObject

[View source]

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

[View source]

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

[View source]

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_whileObject

[View source]

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

[View source]

228
229
230
# File 'opal/lib/opal/nodes/scope.rb', line 228

def queue_temp(name)
  @queue << name
end

#refinements_tempObject

[View source]

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_accessObject

Returns '$$', but also ensures we compile it

[View source]

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

Returns:

  • (Boolean)
[View source]

82
83
84
# File 'opal/lib/opal/nodes/scope.rb', line 82

def sclass?
  @type == :sclass
end

#scope_localsObject

[View source]

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

#selfObject

Returns 'self', but also ensures that the self variable is set

[View source]

390
391
392
393
# File 'opal/lib/opal/nodes/scope.rb', line 390

def self
  @define_self = true
  'self'
end

#super_chainObject

[View source]

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_varsObject

Vars to use inside each scope

[View source]

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)

Returns:

  • (Boolean)
[View source]

87
88
89
# File 'opal/lib/opal/nodes/scope.rb', line 87

def top?
  @type == :top
end

#uses_block!Object

[View source]

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

Returns:

  • (Boolean)
[View source]

310
311
312
# File 'opal/lib/opal/nodes/scope.rb', line 310

def uses_block?
  @uses_block
end