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, #closest_module_node, #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

#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



164
165
166
167
# File 'opal/lib/opal/nodes/scope.rb', line 164

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

#add_proto_ivar(ivar) ⇒ Object



160
161
162
# File 'opal/lib/opal/nodes/scope.rb', line 160

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

#add_scope_gvar(gvar) ⇒ Object



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

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

#add_scope_ivar(ivar) ⇒ Object



148
149
150
151
152
153
154
# File 'opal/lib/opal/nodes/scope.rb', line 148

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



169
170
171
172
173
# File 'opal/lib/opal/nodes/scope.rb', line 169

def add_scope_local(local)
  return if has_local? local

  @locals << local
end

#add_scope_temp(tmp) ⇒ Object



181
182
183
184
185
# File 'opal/lib/opal/nodes/scope.rb', line 181

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 or @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



252
253
254
255
256
257
258
259
260
261
# File 'opal/lib/opal/nodes/scope.rb', line 252

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

  nil
end

#get_super_chainObject



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'opal/lib/opal/nodes/scope.rb', line 263

def get_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 [:def, :defs].include?(scope.type)
      defn = scope.identify!
      mid  = "'#{scope.mid}'"
      break
    else
      break
    end
  end

  [chain, defn, mid]
end

#has_local?(local) ⇒ Boolean

Returns:

  • (Boolean)


175
176
177
178
179
# File 'opal/lib/opal/nodes/scope.rb', line 175

def has_local?(local)
  return true if @locals.include? local or @args.include? local or @temps.include? local
  return @parent.has_local?(local) if @parent and @type == :iter
  false
end

#has_rescue_else?Boolean

Returns:

  • (Boolean)


287
288
289
# File 'opal/lib/opal/nodes/scope.rb', line 287

def has_rescue_else?
  !!rescue_else_sexp
end

#has_temp?(tmp) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
# File 'opal/lib/opal/nodes/scope.rb', line 187

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

#identify!(name = nil) ⇒ Object



237
238
239
240
241
242
243
244
245
246
# File 'opal/lib/opal/nodes/scope.rb', line 237

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

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

  @identity
end

#identityObject



248
249
250
# File 'opal/lib/opal/nodes/scope.rb', line 248

def identity
  @identity
end

#in_ensureObject



291
292
293
294
295
296
297
298
299
# File 'opal/lib/opal/nodes/scope.rb', line 291

def in_ensure
  return unless block_given?

  @in_ensure = true
  result = yield
  @in_ensure = false

  result
end

#in_ensure?Boolean

Returns:

  • (Boolean)


301
302
303
# File 'opal/lib/opal/nodes/scope.rb', line 301

def in_ensure?
  !!@in_ensure
end

#in_scope(&block) ⇒ Object



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

def in_scope(&block)
  indent do
    @parent = compiler.scope
    compiler.scope = self
    block.call self
    compiler.scope = @parent
  end
end

#in_while?Boolean

Returns:

  • (Boolean)


224
225
226
# File 'opal/lib/opal/nodes/scope.rb', line 224

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



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

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

  tmp = next_temp
  @temps << tmp
  tmp
end

#next_tempObject



199
200
201
202
203
204
205
206
207
208
# File 'opal/lib/opal/nodes/scope.rb', line 199

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

#pop_whileObject



220
221
222
# File 'opal/lib/opal/nodes/scope.rb', line 220

def pop_while
  @while_stack.pop
end

#protoObject

Inside a class or module scope, the javascript variable name returned by this function points to the classes' prototype. This is the target to where methods are actually added inside a class body.



114
115
116
# File 'opal/lib/opal/nodes/scope.rb', line 114

def proto
  "def"
end

#push_whileObject



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

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

#queue_temp(name) ⇒ Object



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

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

#to_varsObject

Vars to use inside each scope



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'opal/lib/opal/nodes/scope.rb', line 120

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? and !@proto_ivars.empty?
    #raise "FIXME to_vars"
    pvars = @proto_ivars.map { |i| "#{proto}#{i}"}.join(' = ')
    result = "%s\n%s%s = nil;" % [str, indent, pvars]
  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



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

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

#uses_block?Boolean

Returns:

  • (Boolean)


283
284
285
# File 'opal/lib/opal/nodes/scope.rb', line 283

def uses_block?
  @uses_block
end