class Opal::Nodes::ScopeNode

Attributes

await_encountered[RW]
block_name[RW]

The given block name for a def scope

catch_return[RW]
defs[RW]

true if singleton def, false otherwise

gvars[R]
has_break[RW]
has_retry[RW]
identity[R]
ivars[R]
locals[R]
methods[R]

used by modules to know what methods to donate to includees

mid[RW]
name[RW]

The class or module name if this scope is a class scope

parent[RW]

Every scope can have a parent scope

rescue_else_sexp[RW]
scope_name[R]

Public Class Methods

new(*) click to toggle source
Calls superclass method Opal::Nodes::Base::new
# File 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

Public Instance Methods

accepts_using?() click to toggle source
# File 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) click to toggle source
# File lib/opal/nodes/scope.rb, line 172
def add_arg(arg)
  @args << arg unless @args.include? arg
  arg
end
add_proto_ivar(ivar) click to toggle source
# File 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) click to toggle source
# File lib/opal/nodes/scope.rb, line 164
def add_scope_gvar(gvar)
  @gvars << gvar unless @gvars.include? gvar
end
add_scope_ivar(ivar) click to toggle source
# File 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) click to toggle source
# File lib/opal/nodes/scope.rb, line 177
def add_scope_local(local)
  return if has_local? local

  @locals << local
end
add_scope_temp(tmp) click to toggle source
# File lib/opal/nodes/scope.rb, line 194
def add_scope_temp(tmp)
  return if has_temp?(tmp)

  @temps.push(tmp)
end
class?() click to toggle source

Returns true if this is strictly a class scope

# File lib/opal/nodes/scope.rb, line 73
def class?
  @type == :class
end
class_scope?() click to toggle source

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

# File lib/opal/nodes/scope.rb, line 68
def class_scope?
  @type == :class || @type == :module
end
collect_refinements_temps(temps = []) click to toggle source
# File 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() click to toggle source
# File lib/opal/nodes/scope.rb, line 328
def current_rescue
  @rescues.last
end
def?() click to toggle source
# File lib/opal/nodes/scope.rb, line 96
def def?
  @type == :def || @type == :defs
end
def_in_class?() click to toggle source

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

# File lib/opal/nodes/scope.rb, line 121
def def_in_class?
  !@defs && @type == :def && @parent && @parent.class?
end
defines_lambda() { || ... } click to toggle source
# File lib/opal/nodes/scope.rb, line 108
def defines_lambda
  @lambda_definition = true
  yield
  @lambda_definition = false
end
find_parent_def() click to toggle source
# File 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() click to toggle source
# File 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) click to toggle source
# File 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?() click to toggle source
# File lib/opal/nodes/scope.rb, line 314
def has_rescue_else?
  !rescue_else_sexp.nil?
end
has_temp?(tmp) click to toggle source
# File lib/opal/nodes/scope.rb, line 206
def has_temp?(tmp)
  @temps.include? tmp
end
identify!(name = nil) click to toggle source
# File 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_ensure() { || ... } click to toggle source
# File 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?() click to toggle source
# File lib/opal/nodes/scope.rb, line 356
def in_ensure?
  @in_ensure
end
in_resbody() { || ... } click to toggle source
# File 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?() click to toggle source
# File lib/opal/nodes/scope.rb, line 342
def in_resbody?
  @in_resbody
end
in_rescue(node) { || ... } click to toggle source
# File lib/opal/nodes/scope.rb, line 318
def in_rescue(node)
  @rescues ||= []

  @rescues.push(node)
  result = yield
  @rescues.pop

  result
end
in_scope() { |self| ... } click to toggle source
# File 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?() click to toggle source
# File lib/opal/nodes/scope.rb, line 242
def in_while?
  !@while_stack.empty?
end
is_lambda!() click to toggle source
# File lib/opal/nodes/scope.rb, line 104
def is_lambda! # rubocop:disable Naming/PredicateName
  @is_lambda = true
end
iter?() click to toggle source

True if a block/iter scope

# File lib/opal/nodes/scope.rb, line 92
def iter?
  @type == :iter
end
lambda?() click to toggle source
# File lib/opal/nodes/scope.rb, line 100
def lambda?
  iter? && @is_lambda
end
lambda_definition?() click to toggle source
# File lib/opal/nodes/scope.rb, line 114
def lambda_definition?
  @lambda_definition
end
module?() click to toggle source

True if this is a module scope

# File lib/opal/nodes/scope.rb, line 78
def module?
  @type == :module
end
nesting() click to toggle source

Returns ‘$nesting’, but also ensures we compile the nesting chain

# File lib/opal/nodes/scope.rb, line 396
def nesting
  @define_nesting = true
  '$nesting'
end
new_refinements_temp() click to toggle source
# File lib/opal/nodes/scope.rb, line 377
def new_refinements_temp
  var = compiler.unique_temp("$refn")
  add_scope_local(var)
  var
end
new_temp() click to toggle source
# File 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() click to toggle source
# File 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() click to toggle source
# File lib/opal/nodes/scope.rb, line 238
def pop_while
  @while_stack.pop
end
prepare_block(block_name = nil) click to toggle source
# File 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) click to toggle source
# File lib/opal/nodes/scope.rb, line 200
def prepend_scope_temp(tmp)
  return if has_temp?(tmp)

  @temps.unshift(tmp)
end
push_while() click to toggle source
# File lib/opal/nodes/scope.rb, line 232
def push_while
  info = {}
  @while_stack.push info
  info
end
queue_temp(name) click to toggle source
# File lib/opal/nodes/scope.rb, line 228
def queue_temp(name)
  @queue << name
end
refinements_temp() click to toggle source
# File 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() click to toggle source

Returns ‘$$’, but also ensures we compile it

# File lib/opal/nodes/scope.rb, line 402
def relative_access
  @define_relative_access = @define_nesting = true
  '$$'
end
sclass?() click to toggle source
# File lib/opal/nodes/scope.rb, line 82
def sclass?
  @type == :sclass
end
scope_locals() click to toggle source
# File 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() click to toggle source

Returns ‘self’, but also ensures that the self variable is set

# File lib/opal/nodes/scope.rb, line 390
def self
  @define_self = true
  'self'
end
super_chain() click to toggle source
# File 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() click to toggle source

Vars to use inside each scope

# File 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?() click to toggle source

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

# File lib/opal/nodes/scope.rb, line 87
def top?
  @type == :top
end
uses_block!() click to toggle source
# File 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?() click to toggle source
# File lib/opal/nodes/scope.rb, line 310
def uses_block?
  @uses_block
end