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, #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, #compile_to_fragments, #error, #expr, #expr?, #expr_or_nil, #fragment, handle, handlers, #helper, #process, #push, #recv, #recv?, #s, #scope, #stmt, #stmt?, truthy_optimize?, #unshift, #while_loop, #with_temp, #wrap

Methods included from Helpers

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

Constructor Details

#initializeScopeNode

Returns a new instance of ScopeNode.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'opal/lib/opal/nodes/scope.rb', line 38

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

#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.



34
35
36
# File 'opal/lib/opal/nodes/scope.rb', line 34

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.



34
35
36
# File 'opal/lib/opal/nodes/scope.rb', line 34

def has_break
  @has_break
end

#identityObject (readonly)

Returns the value of attribute identity.



239
240
241
# File 'opal/lib/opal/nodes/scope.rb', line 239

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.



36
37
38
# File 'opal/lib/opal/nodes/scope.rb', line 36

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

#uses_superObject

uses parents super method



31
32
33
# File 'opal/lib/opal/nodes/scope.rb', line 31

def uses_super
  @uses_super
end

#uses_zuperObject

Returns the value of attribute uses_zuper.



32
33
34
# File 'opal/lib/opal/nodes/scope.rb', line 32

def uses_zuper
  @uses_zuper
end

Instance Method Details

#add_arg(arg) ⇒ Object



156
157
158
159
# File 'opal/lib/opal/nodes/scope.rb', line 156

def add_arg(arg)
  @args << arg unless @args.include? arg
  arg
end

#add_proto_ivar(ivar) ⇒ Object



152
153
154
# File 'opal/lib/opal/nodes/scope.rb', line 152

def add_proto_ivar(ivar)
  @proto_ivars << ivar unless @proto_ivars.include? ivar
end

#add_scope_gvar(gvar) ⇒ Object



148
149
150
# File 'opal/lib/opal/nodes/scope.rb', line 148

def add_scope_gvar(gvar)
  @gvars << gvar unless @gvars.include? gvar
end

#add_scope_ivar(ivar) ⇒ Object



140
141
142
143
144
145
146
# File 'opal/lib/opal/nodes/scope.rb', line 140

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



161
162
163
164
165
# File 'opal/lib/opal/nodes/scope.rb', line 161

def add_scope_local(local)
  return if has_local? local

  @locals << local
end

#add_scope_temp(tmp) ⇒ Object



173
174
175
176
177
# File 'opal/lib/opal/nodes/scope.rb', line 173

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)


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

def class?
  @type == :class
end

#class_scope?Boolean

Returns true if this scope is a class/module body scope

Returns:

  • (Boolean)


72
73
74
# File 'opal/lib/opal/nodes/scope.rb', line 72

def class_scope?
  @type == :class || @type == :module
end

#def?Boolean

Returns:

  • (Boolean)


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

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)


107
108
109
# File 'opal/lib/opal/nodes/scope.rb', line 107

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

#find_parent_defObject



241
242
243
244
245
246
247
248
249
250
# File 'opal/lib/opal/nodes/scope.rb', line 241

def find_parent_def
  scope = self
  while scope = scope.parent
    if scope.def?
      return scope
    end
  end

  nil
end

#has_local?(local) ⇒ Boolean

Returns:

  • (Boolean)


167
168
169
170
171
# File 'opal/lib/opal/nodes/scope.rb', line 167

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)


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

def has_rescue_else?
  !rescue_else_sexp.nil?
end

#has_temp?(tmp) ⇒ Boolean

Returns:

  • (Boolean)


179
180
181
# File 'opal/lib/opal/nodes/scope.rb', line 179

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

#identify!(name = nil) ⇒ Object



228
229
230
231
232
233
234
235
236
237
# File 'opal/lib/opal/nodes/scope.rb', line 228

def identify!(name = nil)
  return @identity if @identity

  # Parent scope is the defining module/class
  name ||= [(parent && (parent.name || parent.scope_name)), mid].compact.join('_')
  @identity = @compiler.unique_temp(name)
  @parent.add_scope_temp @identity if @parent

  @identity
end

#in_ensureObject



280
281
282
283
284
285
286
287
288
# File 'opal/lib/opal/nodes/scope.rb', line 280

def in_ensure
  return unless block_given?

  @in_ensure = true
  result = yield
  @in_ensure = false

  result
end

#in_ensure?Boolean

Returns:

  • (Boolean)


290
291
292
# File 'opal/lib/opal/nodes/scope.rb', line 290

def in_ensure?
  @in_ensure
end

#in_scopeObject



62
63
64
65
66
67
68
69
# File 'opal/lib/opal/nodes/scope.rb', line 62

def in_scope
  indent do
    @parent = compiler.scope
    compiler.scope = self
    yield self
    compiler.scope = @parent
  end
end

#in_while?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'opal/lib/opal/nodes/scope.rb', line 215

def in_while?
  !@while_stack.empty?
end

#iter?Boolean

True if a block/iter scope

Returns:

  • (Boolean)


96
97
98
# File 'opal/lib/opal/nodes/scope.rb', line 96

def iter?
  @type == :iter
end

#module?Boolean

True if this is a module scope

Returns:

  • (Boolean)


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

def module?
  @type == :module
end

#new_tempObject



183
184
185
186
187
188
189
# File 'opal/lib/opal/nodes/scope.rb', line 183

def new_temp
  return @queue.pop unless @queue.empty?

  tmp = next_temp
  @temps << tmp
  tmp
end

#next_tempObject



191
192
193
194
195
196
197
198
199
# File 'opal/lib/opal/nodes/scope.rb', line 191

def next_temp
  tmp = nil
  loop do
    tmp = "$#{@unique}"
    @unique = @unique.succ
    break unless has_local?(tmp)
  end
  tmp
end

#pop_whileObject



211
212
213
# File 'opal/lib/opal/nodes/scope.rb', line 211

def pop_while
  @while_stack.pop
end

#push_whileObject



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

def push_while
  info = {}
  @while_stack.push info
  info
end

#queue_temp(name) ⇒ Object



201
202
203
# File 'opal/lib/opal/nodes/scope.rb', line 201

def queue_temp(name)
  @queue << name
end

#sclass?Boolean

Returns:

  • (Boolean)


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

def sclass?
  @type == :sclass
end

#super_chainObject



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'opal/lib/opal/nodes/scope.rb', line 252

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



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'opal/lib/opal/nodes/scope.rb', line 113

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

  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| "self.$$prototype#{i}" }.join(' = ')
    result = "#{str}\n#{indent}#{pvars} = nil;"
  else
    result = str
  end

  fragment(result)
end

#top?Boolean

Returns true if this is a top scope (main file body)

Returns:

  • (Boolean)


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

def top?
  @type == :top
end

#uses_block!Object



219
220
221
222
223
224
225
226
# File 'opal/lib/opal/nodes/scope.rb', line 219

def uses_block!
  if @type == :iter && @parent
    @parent.uses_block!
  else
    @uses_block = true
    identify!
  end
end

#uses_block?Boolean

Returns:

  • (Boolean)


272
273
274
# File 'opal/lib/opal/nodes/scope.rb', line 272

def uses_block?
  @uses_block
end