lib/opal/nodes/defined.rb



require 'opal/nodes/base'

module Opal
  module Nodes
    class DefinedNode < Base
      handle :defined

      children :value

      def compile
        type = value.type

        case type
        when :self, :nil, :false, :true
          push type.to_s.inspect
        when :lasgn, :iasgn, :gasgn, :cvdecl, :masgn, :op_asgn_or, :op_asgn_and
          push "'assignment'"
        when :paren, :not
          push expr(s(:defined, value[1]))
        when :lvar
          push "'local-variable'"
        else
          if respond_to? "compile_#{type}"
            __send__ "compile_#{type}"
          else
            push "'expression'"
          end
        end
      end

      def compile_call
        mid = mid_to_jsid value[2].to_s
        recv = value[1] ? expr(value[1]) : 'self'

        with_temp do |tmp|
          push "(((#{tmp} = ", recv, "#{mid}) && !#{tmp}.$$stub) || ", recv
          push "['$respond_to_missing?']('#{value[2].to_s}') ? 'method' : nil)"
        end
      end

      def compile_ivar
        # FIXME: this check should be positive for ivars initialized as nil too.
        # Since currently all known ivars are inialized to nil in the constructor
        # we can't tell if it was the user that put nil and made the ivar #defined?
        # or not.
        with_temp do |tmp|
          name = value[1].to_s[1..-1]

          push "((#{tmp} = self['#{name}'], #{tmp} != null && #{tmp} !== nil) ? "
          push "'instance-variable' : nil)"
        end
      end

      def compile_super
        push expr(s(:defined_super, value))
      end

      def compile_yield
        push compiler.handle_block_given_call(@sexp)
        wrap '((',  ') != null ? "yield" : nil)'
      end

      def compile_xstr
        push expr(value)
        wrap '(typeof(', ') !== "undefined")'
      end
      alias compile_dxstr compile_xstr

      def compile_const
        push "($scope.#{value[1]} != null)"
      end

      def compile_colon2
        # TODO: avoid try/catch, probably a #process_colon2 alternative that
        # does not raise errors is needed
        push "(function(){ try { return (("
        push expr(value)
        push ") != null ? 'constant' : nil); } catch (err) { if (err.$$class"
        push " === Opal.NameError) { return nil; } else { throw(err); }}; })()"
      end

      def compile_colon3
        push "(Opal.Object.$$scope.#{value[1]} == null ? nil : 'constant')"
      end

      def compile_cvar
        push "(Opal.cvars['#{value[1]}'] != null ? 'class variable' : nil)"
      end

      def compile_gvar
        name = value[1].to_s[1..-1]

        if %w[~ !].include? name
          push "'global-variable'"
        elsif %w[` ' + &].include? name
          with_temp do |tmp|
            push "((#{tmp} = $gvars['~'], #{tmp} != null && #{tmp} !== nil) ? "
            push "'global-variable' : nil)"
          end
        else
          push "($gvars[#{name.inspect}] != null ? 'global-variable' : nil)"
        end
      end

      def compile_nth_ref
        with_temp do |tmp|
          push "((#{tmp} = $gvars['~'], #{tmp} != null && #{tmp} != nil) ? "
          push "'global-variable' : nil)"
        end
      end
    end
  end
end