# TODO: Finish adding some layer structure to the core library. This file is a
# complete mess at the moment.
#
# A proper layered structure for the core library would enable and encourage
# composition and make visible where pieces are loosely or tightly coupled. A
# reasonable layer for something as complex as the Ruby core library would be:
#
# 1. Simple values like Fixnum, Bignum, Float;
# 2. Basic data structures like Tuple, ByteArray, Array, Hash;
# 3. More complicated values like String, Regexp, Complex, Rational;
# 4. Fairly simple system-dependent facilities like IO, File, Process;
# 5. Even more complex facilities like FFI;
# 6. Something like Socket that composes multiple lower layers;
# 7. A complex facility like CodeLoader or RubyGems.


# The virtual concatenation file of the files given on command line (or
# from $stdin if no files were given.) Usable like an IO.
ARGF = Rubinius::ARGFClass.new

class Module
  def alias_method(new_name, current_name)
    new_name = Rubinius::Type.coerce_to_symbol(new_name)
    current_name = Rubinius::Type.coerce_to_symbol(current_name)
    mod, entry = lookup_method(current_name, true, false)

    if entry
      method_visibility = visibility_for_aliased_method(new_name, entry.visibility)

      # If we're aliasing a method we contain, just reference it directly, no
      # need for the alias wrapper
      #
      # The 'and entry.get_method' is there to force us to use the alias code
      # when the original method exists only to change the visibility of a
      # parent method.
      if mod == self and entry.get_method
        @method_table.store new_name, entry.method_id, entry.method,
          entry.scope, entry.serial, method_visibility
      else
        @method_table.alias new_name, method_visibility, current_name,
                            entry.get_method, mod
      end

      Rubinius::VM.reset_method_cache self, new_name

      if ai = Rubinius::Type.singleton_class_object(self)
        Rubinius.privately do
          ai.singleton_method_added new_name
        end
      else
        method_added new_name
      end

      self
    else
      if ai = Rubinius::Type.singleton_class_object(self)
        raise NameError, "Unable to find '#{current_name}' for object #{ai.inspect}"
      else
        thing = Rubinius::Type.object_kind_of?(self, Class) ? "class" : "module"
        raise NameError, "undefined method `#{current_name}' for #{thing} `#{self.name}'"
      end
    end
  end
  private :alias_method

  def const_set(name, value)
    name = Rubinius::Type.coerce_to_constant_name name
    Rubinius.check_frozen

    if Rubinius::Type.object_kind_of? value, Module
      Rubinius::Type.set_module_name value, name, self

      if Object.equal? self
        value.constant_table.each do |n, c, _|
          if Rubinius::Type.object_kind_of? c, Module and !c.name
            Rubinius::Type.set_module_name c, n, value
          end
        end
      end
    end

    @constant_table.store(name, value, :public)
    Rubinius.inc_global_serial

    return value
  end

  def module_function(*args)
    if kind_of? Class
      raise TypeError, "invalid receiver class #{__class__}, expected Module"
    end

    if args.empty?
      vs = Rubinius::VariableScope.of_sender
      vs.method_visibility = :module
    else
      sc = Rubinius::Type.object_singleton_class(self)
      args.each do |meth|
        method_name = Rubinius::Type.coerce_to_symbol meth
        mod, entry = lookup_method(method_name)
        sc.method_table.store method_name, entry.method_id, entry.method,
          entry.scope, entry.serial, :public
        Rubinius::VM.reset_method_cache self, method_name
        set_visibility method_name, :private
      end
    end

    return self
  end
  private :module_function

  def public(*args)
    if args.empty?
      vs = Rubinius::VariableScope.of_sender
      vs.method_visibility = nil
    else
      args.each { |meth| set_visibility(meth, :public) }
    end

    self
  end
  private :public

  def private(*args)
    if args.empty?
      vs = Rubinius::VariableScope.of_sender
      vs.method_visibility = :private
    else
      args.each { |meth| set_visibility(meth, :private) }
    end

    self
  end
  private :private

  def protected(*args)
    if args.empty?
      vs = Rubinius::VariableScope.of_sender
      vs.method_visibility = :protected
    else
      args.each { |meth| set_visibility(meth, :protected) }
    end

    self
  end
  private :protected

  def private_class_method(*args)
    args.each do |meth|
      set_class_visibility(meth, :private)
    end
    self
  end

  def public_class_method(*args)
    args.each do |meth|
      set_class_visibility(meth, :public)
    end
    self
  end

  # Invokes <code>Module#append_features</code> and
  # <code>Module#included</code> on each argument, passing in self.
  #
  def include(*modules)
    modules.reverse_each do |mod|
      if !mod.kind_of?(Module) or mod.kind_of?(Class)
        raise TypeError, "wrong argument type #{mod.class} (expected Module)"
      end

      Rubinius.privately do
        mod.append_features self
      end

      Rubinius.privately do
        mod.included self
      end
    end
    self
  end

  # Add all constants, instance methods and module variables
  # of this Module and all Modules that this one includes to +klass+
  #
  # This method is aliased as append_features as the default implementation
  # for that method. Kernel#extend calls this method directly through
  # Module#extend_object, because Kernel#extend should not use append_features.
  def include_into(klass)
    unless klass.kind_of? Module
      raise TypeError, "invalid argument class #{klass.class}, expected Module"
    end

    if kind_of? Class
      raise TypeError, "invalid receiver class #{__class__}, expected Module"
    end

    Rubinius::Type.include_modules_from(self, klass.origin)
    Rubinius::Type.infect(klass, self)

    self
  end

  # Called when this Module is being included in another Module.
  # This may be overridden for custom behaviour. The default
  # is to add constants, instance methods and module variables
  # of this Module and all Modules that this one includes to +klass+.
  #
  # See also #include.
  #
  alias_method :append_features, :include_into
  private :append_features

  def attr_reader(*names)
    vis = Rubinius::VariableScope.of_sender.method_visibility

    names.each { |name| Rubinius.add_reader name, self, vis }

    return nil
  end
  private :attr_reader

  def attr_writer(*names)
    vis = Rubinius::VariableScope.of_sender.method_visibility

    names.each { |name| Rubinius::add_writer name, self, vis }

    return nil
  end
  private :attr_writer

  def attr_accessor(*names)
    vis = Rubinius::VariableScope.of_sender.method_visibility

    names.each do |name|
      Rubinius.add_reader name, self, vis
      Rubinius.add_writer name, self, vis
    end

    return nil
  end
  private :attr_accessor

  def undef_method(*names)
    return self if names.empty?
    names.map!{ |name| Rubinius::Type.coerce_to_symbol name }
    Rubinius.check_frozen

    names.each do |name|
      # Will raise a NameError if the method doesn't exist.
      instance_method name
      undef_method! name
    end

    self
  end
  private :undef_method

  # Like undef_method, but doesn't even check that the method exists. Used
  # mainly to implement rb_undef_method.
  def undef_method!(name)
    Rubinius.check_frozen
    name = Rubinius::Type.coerce_to_symbol(name)
    @method_table.store name, nil, nil, nil, 0, :undef
    Rubinius::VM.reset_method_cache self, name
    if obj = Rubinius::Type.singleton_class_object(self)
      Rubinius.privately do
        obj.singleton_method_undefined(name)
      end
    else
      method_undefined(name)
    end

    name
  end

  def remove_method(*names)
    names.each do |name|
      name = Rubinius::Type.coerce_to_symbol(name)
      Rubinius.check_frozen

      unless @method_table.lookup(name)
        raise NameError.new("method `#{name}' not defined in #{self.name}", name)
      end
      @method_table.delete name

      Rubinius::VM.reset_method_cache self, name

      if obj = Rubinius::Type.singleton_class_object(self)
        Rubinius.privately do
          obj.singleton_method_removed(name)
        end
      else
        method_removed(name)
      end
    end

    self
  end
  private :remove_method

  def name
    Rubinius::Type.module_name self
  end

  def to_s
    Rubinius::Type.module_inspect self
  end

  def inspect
    to_s
  end
end

module Rubinius
  Config = Configuration.new
  Globals = GlobalVariables.new

  @add_method_hook = Rubinius::Hook.new

  def self.open_class(name, sup, scope)
    if scope
      under = scope.module
    else
      under = Object
    end

    open_class_under name, sup, under
  end

  def self.open_class_under(name, sup, mod)
    unless Type.object_kind_of? mod, Module
      raise TypeError, "'#{mod.inspect}' is not a class/module"
    end

    tbl = mod.constant_table
    if !tbl.has_name?(name)
      # Create the class
      sup = Object unless sup
      obj = Class.new sup, name, mod
    else
      entry = tbl.lookup(name)
      obj = entry.constant
      if Type.object_kind_of? obj, Autoload
        obj = begin
          obj.call(mod, true)
        rescue NameError # Constant not found in loaded file
          nil
        end

        # nil is returned if the autoload was abort, usually because
        # the file to be required has already been loaded. In which case
        # act like the autoload wasn't there.
        unless obj
          supr = sup ? sup : Object
          obj = Class.new supr, name, mod
        end
      end

      if Type.object_kind_of? obj, Class
        if sup and obj.superclass != sup
          raise TypeError, "Superclass mismatch: #{obj.superclass} != #{sup}"
        end
      else
        raise TypeError, "#{name} is not a class"
      end
    end

    return obj
  end

  def self.open_module(name, scope)
    if scope
      under = scope.module
    else
      under = Object
    end

    open_module_under name, under
  end

  def self.open_module_under(name, mod)
    unless Type.object_kind_of? mod, Module
      raise TypeError, "'#{mod.inspect}' is not a class/module"
    end

    tbl = mod.constant_table
    found = tbl.has_name?(name)

    # Object has special behavior, we check it's included
    # modules also
    if !found and mod.equal? Object
      check = mod.direct_superclass

      while check
        tbl = check.constant_table
        found = tbl.has_name?(name)
        break if found
        check = check.direct_superclass
      end
    end

    if !found
      # Create the module
      obj = Module.new
      mod.const_set name, obj
    else
      entry = tbl.lookup(name)
      obj = entry.constant

      if Type.object_kind_of? obj, Autoload
        obj = obj.call(mod, true)

        # See comment above about autoload returning nil
        unless obj
          obj = Module.new
          mod.const_set name, obj
        end
      end

      if Type.object_kind_of?(obj, Class) || !Type.object_kind_of?(obj, Module)
        raise TypeError, "#{name} is not a module, but a #{obj.class}"
      end
    end
    return obj
  end

  def self.add_defn_method(name, executable, lexical_scope, vis)
    # TODO: puts serial on MethodTable entry
    unless Type.object_kind_of? executable, String
    executable.serial = 1
    if executable.respond_to? :scope=
      # If we're adding a method inside ane eval, dup it so that
      # we don't share the CompiledCode with the eval, since
      # we're going to mutate it.
      if lexical_scope and script = lexical_scope.current_script
        if script.eval?
          executable = executable.dup
        end
      end
      executable.scope = lexical_scope
    end
    end

    mod = lexical_scope.for_method_definition

    if ai = Type.singleton_class_object(mod)
      if Type.object_kind_of? ai, Numeric

        # Such a weird protocol. If :singleton_method_added exists, allow this.
        # le sigh.
        unless Type.object_respond_to? ai, :singleton_method_added
          raise TypeError, "Unable to define singleton methods on Numerics"
        end
      end
    end

    add_method name, executable, mod, lexical_scope, 1, vis
    name
  end

  # Must be AFTER add_method, because otherwise we'll run this attach_method to add
  # add_method itself and fail.
  def self.attach_method(name, executable, lexical_scope, recv)
    # TODO: puts serial on MethodTable entry
    unless Type.object_kind_of? executable, String
    executable.serial = 1
    if executable.respond_to? :scope=
      # If we're adding a method inside ane eval, dup it so that
      # we don't share the CompiledCode with the eval, since
      # we're going to mutate it.
      if lexical_scope and script = lexical_scope.current_script
        if script.eval?
          executable = executable.dup
        end
      end
      executable.scope = lexical_scope
    end
    end

    mod = Rubinius::Type.object_singleton_class recv

    add_method name, executable, mod, lexical_scope, 1, :public
    name
  end

  def self.add_reader(name, mod, vis)
    normalized = Rubinius::Type.coerce_to_symbol(name)
    add_method normalized, AccessVariable.get_ivar(normalized), mod, nil, 0, vis
  end

  def self.add_writer(name, mod, vis)
    normalized = Rubinius::Type.coerce_to_symbol(name)
    writer_name = "#{normalized}=".to_sym
    add_method writer_name, AccessVariable.set_ivar(normalized), mod, nil, 0, vis
  end

  class << self
    attr_reader :add_method_hook
  end

  module CAPI
    INTEGER_PACK_MSWORD_FIRST       = 0x01
    INTEGER_PACK_LSWORD_FIRST       = 0x02
    INTEGER_PACK_MSBYTE_FIRST       = 0x10
    INTEGER_PACK_LSBYTE_FIRST       = 0x20
    INTEGER_PACK_NATIVE_BYTE_ORDER  = 0x40
    INTEGER_PACK_2COMP              = 0x80
    INTEGER_PACK_FORCE_BIGNUM       = 0x100
    INTEGER_PACK_NEGATIVE           = 0x200

    INTEGER_PACK_LITTLE_ENDIAN  = INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_LSBYTE_FIRST
    INTEGER_PACK_BIG_ENDIAN     = INTEGER_PACK_MSWORD_FIRST|INTEGER_PACK_MSBYTE_FIRST

    INTEGER_PACK_DIGITS = [
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ]
  end

  class CodeLoader
    Lock = Object.new

    # Hook support. This allows code to be told when a file was just compiled,
    # or when it has finished loading.
    @compiled_hook = Rubinius::Hook.new
    @loaded_hook = Rubinius::Hook.new
    @loaded_features_index = Hash.new(false)
  end

  module CType
    Printed = Rubinius::Tuple.new 256
    i = 0
    while i < 256
      Printed[i] = toprint(i)
      i += 1
    end

    Lowered = Rubinius::Tuple.new 256

    i = 0
    while i < 256
      Lowered[i] = tolower(i).chr
      i += 1
    end
  end

  module FFI
    # Shorthand for the current process, i.e. all code that
    # the process image itself contains. In addition to the
    # Rubinius codebase, this also includes libc etc.
    #
    # Use this constant instead of nil directly.
    #
    CURRENT_PROCESS = nil

    TypeDefs = Rubinius::LookupTable.new

    # Converts a char
    add_typedef TYPE_CHAR,    :char

    # Converts an unsigned char
    add_typedef TYPE_UCHAR,   :uchar

    # The C++ boolean type
    add_typedef TYPE_BOOL,    :bool

    # Converts a short
    add_typedef TYPE_SHORT,   :short

    # Converts an unsigned short
    add_typedef TYPE_USHORT,  :ushort
    add_typedef TYPE_USHORT,  :mode_t
    add_typedef TYPE_USHORT,  :nlink_t

    # Converts an int
    add_typedef TYPE_INT,     :int
    add_typedef TYPE_INT,     :dev_t
    add_typedef TYPE_INT,     :blksize_t
    add_typedef TYPE_INT,     :time_t

    # Converts an unsigned int
    add_typedef TYPE_UINT,    :uint
    add_typedef TYPE_UINT,    :uid_t
    add_typedef TYPE_UINT,    :gid_t
    add_typedef TYPE_UINT,    :id_t

    # Converts a long
    add_typedef TYPE_LONG,    :long
    add_typedef TYPE_LONG,    :ssize_t

    # Converts an unsigned long
    add_typedef TYPE_ULONG,   :ulong

    # Converts a size_t
    add_typedef TYPE_ULONG,   :size_t

    # Converts a long long
    add_typedef TYPE_LL,      :long_long
    add_typedef TYPE_LL,      :blkcnt_t
    add_typedef TYPE_LL,      :off_t

    # Converts an unsigned long long
    add_typedef TYPE_ULL,     :ulong_long
    add_typedef TYPE_ULL,     :ino64_t

    # Converts a float
    add_typedef TYPE_FLOAT,   :float

    # Converts a double
    add_typedef TYPE_DOUBLE,  :double

    # Converts a pointer to opaque data
    add_typedef TYPE_PTR,     :pointer

    # For when a function has no return value
    add_typedef TYPE_VOID,    :void

    # Converts NULL-terminated C strings
    add_typedef TYPE_STRING,  :string

    # Use strptr when you need to free the result of some operation.
    add_typedef TYPE_STRPTR,  :strptr
    add_typedef TYPE_STRPTR,  :string_and_pointer

    # Use for a C struct with a char [] embedded inside.
    add_typedef TYPE_CHARARR, :char_array

    # A set of unambigious integer types
    add_typedef TYPE_CHAR,   :int8
    add_typedef TYPE_UCHAR,  :uint8
    add_typedef TYPE_SHORT,  :int16
    add_typedef TYPE_USHORT, :uint16
    add_typedef TYPE_INT,    :int32
    add_typedef TYPE_UINT,   :uint32

    # Converts a varargs argument
    add_typedef TYPE_VARARGS, :varargs

    if Rubinius::L64
      add_typedef TYPE_LONG,  :int64
      add_typedef TYPE_ULONG, :uint64
    else
      add_typedef TYPE_LL,    :int64
      add_typedef TYPE_ULL,   :uint64
    end

    TypeSizes = Rubinius::LookupTable.new
    TypeSizes[1] = :char
    TypeSizes[2] = :short
    TypeSizes[4] = :int
    TypeSizes[8] = Rubinius::L64 ? :long : :long_long

    # Load all the platform dependent types

    Rubinius::Config.section("rbx.platform.typedef.") do |key, value|
      add_typedef(find_type(value.to_sym), key.substring(21, key.length).to_sym)
    end

    class DynamicLibrary
      extend FFI::Library

      # Bootstrap dlsym, dlopen, and dlerror
      pointer_as_function :find_symbol, FFI::Pointer::DLSYM, [:pointer, :string], :pointer
      pointer_as_function :open_library, FFI::Pointer::DLOPEN, [:string, :int], :pointer
      pointer_as_function :last_error, FFI::Pointer::DLERROR, [], :string

      if Rubinius.windows?
        RTLD_LAZY   = 0
        RTLD_NOW    = 0
        RTLD_GLOBAL = 0
        RTLD_LOCAL  = 0
      else
        RTLD_LAZY   = Rubinius::Config['rbx.platform.dlopen.RTLD_LAZY']
        RTLD_NOW    = Rubinius::Config['rbx.platform.dlopen.RTLD_NOW']
        RTLD_GLOBAL = Rubinius::Config['rbx.platform.dlopen.RTLD_GLOBAL']
        RTLD_LOCAL  = Rubinius::Config['rbx.platform.dlopen.RTLD_LOCAL']
      end

      class << self
        alias_method :open, :new
      end

      def initialize(name, flags=nil)
        # Accept nil and check for ruby-ffi API compat
        flags ||= RTLD_LAZY | RTLD_GLOBAL

        unless name
          @name = "[current process]"
          @handle = FFI::Pointer::CURRENT_PROCESS
          return
        end

        @name = name
        @handle = DynamicLibrary.open_library name, flags

        unless @handle
          orig_error = last_error

          libnames = FFI::LIB_SUFFIXES.map {|suffix| "#{name}.#{suffix}" }
          libnames << "lib#{name}"
          libnames += FFI::LIB_SUFFIXES.map {|suffix| "lib#{name}.#{suffix}" }

          libnames.detect do |libname|
            @name = libname
            @handle = DynamicLibrary.open_library libname, flags
          end
        end

        unless @handle
          orig_error = orig_error.split("\n").first
          # API Compat. LoadError is wrong here.
          raise LoadError, "Could not open library #{name} - #{orig_error}"
        end
      end

      attr_reader :name

      def find_symbol(name)
        ptr = DynamicLibrary.find_symbol @handle, name
        return nil unless ptr
        # defined in core/pointer.rb
        FFI::DynamicLibrary::Symbol.new(self, ptr, name)
      end

      alias_method :find_function, :find_symbol
      alias_method :find_variable, :find_symbol

      def last_error
        DynamicLibrary.last_error
      end

      CURRENT_PROCESS = DynamicLibrary.new(nil)
    end

    module Platform
      ARCH = Rubinius::CPU

      # ruby-ffi compatible
      LONG_SIZE = Rubinius::SIZEOF_LONG * 8
      ADDRESS_SIZE = Rubinius::WORDSIZE

      case
      when Rubinius.windows?
        LIBSUFFIX = "dll"
        IS_WINDOWS = true
        OS = 'windows'
      when Rubinius.darwin?
        LIBSUFFIX = "dylib"
        IS_WINDOWS = false
        OS = 'darwin'
      else
        LIBSUFFIX = "so"
        IS_WINDOWS = false
        OS = 'linux'
      end

      module File
        SEPARATOR = '/'
        ALT_SEPARATOR = nil
        PATH_SEPARATOR = ':'
      end

      module Math
        extend FFI::Library

        attach_function :fabs,  [:double], :double
        attach_function :atan2, [:double, :double], :double
        attach_function :cos,   [:double], :double
        attach_function :sin,   [:double], :double
        attach_function :tan,   [:double], :double
        attach_function :acos,  [:double], :double
        attach_function :asin,  [:double], :double
        attach_function :atan,  [:double], :double
        attach_function :cosh,  [:double], :double
        attach_function :sinh,  [:double], :double
        attach_function :tanh,  [:double], :double
        attach_function :acosh, [:double], :double
        attach_function :asinh, [:double], :double
        attach_function :atanh, [:double], :double
        attach_function :exp,   [:double], :double
        attach_function :log,   [:double], :double
        attach_function :log10, [:double], :double
        attach_function :sqrt,  [:double], :double
        attach_function :frexp, [:double, :pointer], :double
        attach_function :ldexp, [:double, :int], :double
        attach_function :hypot, [:double, :double], :double
        attach_function :erf,   [:double], :double
        attach_function :erfc,  [:double], :double
        attach_function :cbrt, [:double], :double
        attach_function :tgamma, [:double], :double
        attach_function :lgamma_r, [:double, :pointer], :double

        # Rubinius-specific, used in Marshal
        attach_function :modf,  [:double, :pointer], :double

        # TODO: we need a way to determine whether a function
        # is defined for a particular platform.
        #
        # attach_function :log2,  [:double], :double
        def self.log2(x)
          log10(x) / log10(2.0)
        end
      end

      module POSIX
        extend FFI::Library

        # errors
        attach_function :errno,    'ffi_errno',     [],     :int
        attach_function :errno=,   'ffi_set_errno', [:int], :void

        # memory
        attach_function :malloc,   [:size_t], :pointer
        attach_function :realloc,  [:pointer, :size_t], :pointer
        attach_function :free,     [:pointer], :void
        attach_function :memset,   [:pointer, :int, :size_t], :pointer
        attach_function :memcpy,   [:pointer, :pointer, :size_t], :pointer

        # file system
        attach_function :access,   [:string, :int], :int
        attach_function :chmod,    [:string, :mode_t], :int
        attach_function :fchmod,   [:int, :mode_t], :int
        attach_function :lchmod,   [:string, :mode_t], :int
        attach_function :chown,    [:string, :uid_t, :gid_t], :int
        attach_function :fchown,   [:int, :uid_t, :gid_t], :int
        attach_function :lchown,   [:string, :uid_t, :gid_t], :int
        attach_function :unlink,   [:string], :int
        attach_function :getcwd,   [:string, :size_t], :string
        attach_function :umask,    [:mode_t], :int
        attach_function :link,     [:string, :string], :int
        attach_function :symlink,  [:string, :string], :int
        attach_function :readlink, [:string, :pointer, :size_t], :ssize_t
        attach_function :rename,   [:string, :string], :int
        attach_function :utimes,   [:string, :pointer], :int
        attach_function :mkfifo,   [:string, :mode_t], :int

        # directories
        attach_function :chdir,     [:string], :int
        attach_function :mkdir,     [:string, :mode_t], :int
        attach_function :rmdir,     [:string], :int
        attach_function :chroot,    [:string], :int

        # File/IO
        attach_function :fcntl,       [:int, :int, :long], :int
        attach_function :ioctl,       [:int, :ulong, :long], :int
        attach_function :fsync,       [:int], :int
        attach_function :dup,         [:int], :int
        attach_function :dup2,        [:int, :int], :int
        attach_function :open,        [:string, :int, :mode_t], :int
        attach_function :close,       [:int], :int
        attach_function :lseek,       [:int, :off_t, :int], :off_t
        attach_function :read,        [:int, :pointer, :size_t], :ssize_t
        attach_function :ftruncate,   [:int, :off_t], :int
        attach_function :truncate,    [:string, :off_t], :int
        attach_function :write,       [:int, :pointer, :size_t], :ssize_t
        attach_function :select,      [:int, :pointer, :pointer, :pointer, :pointer], :int

        # Other I/O
        attach_function :pipe,        [:pointer], :int
        attach_function :mmap,        [:pointer, :size_t, :int, :int, :int, :off_t], :pointer
        attach_function :msync,       [:pointer, :size_t, :int], :int
        attach_function :munmap,      [:pointer, :size_t], :int
        attach_function :getpagesize, [], :int
        attach_function :shutdown,    [:int, :int], :int
        attach_function :posix_fadvise, [:int, :off_t, :off_t, :int], :int

        #   inspecting
        attach_function :isatty,   [:int], :int
        attach_function :ttyname,  [:int], :string

        #   locking
        attach_function :flock, [:int, :int], :int

        # UID/GID
        attach_function :getuid,  [], :uid_t
        attach_function :getgid,  [], :gid_t
        attach_function :geteuid, [], :uid_t
        attach_function :getegid, [], :gid_t

        attach_function :setgid,  [:gid_t], :int
        attach_function :setuid,  [:uid_t], :int
        attach_function :setegid, [:gid_t], :int
        attach_function :seteuid, [:uid_t], :int

        attach_function :setregid, [:gid_t, :gid_t], :int
        attach_function :setreuid, [:uid_t, :uid_t], :int

        attach_function :setresgid, [:gid_t, :gid_t, :gid_t], :int
        attach_function :setresuid, [:uid_t, :uid_t, :uid_t], :int

        attach_function :getpriority, [:int, :id_t], :int
        attach_function :setpriority, [:int, :id_t, :int], :int

        attach_function :getgroups,  [:int, :pointer], :int
        attach_function :setgroups,  [:int, :pointer], :int
        attach_function :initgroups, [:string, :gid_t], :int

        attach_function :setrlimit, [:int, :pointer], :int
        attach_function :getrlimit, [:int, :pointer], :int

        # password and group file access
        attach_function :getpwnam, [:string], :pointer
        attach_function :getpwuid, [:uid_t], :pointer
        attach_function :setpwent, [], :void
        attach_function :getpwent, [], :pointer
        attach_function :endpwent, [], :void

        attach_function :getgrnam, [:string], :pointer
        attach_function :getgrgid, [:gid_t], :pointer
        attach_function :setgrent, [], :void
        attach_function :getgrent, [], :pointer
        attach_function :endgrent, [], :void

        # processes and signals
        attach_function :kill,    [:pid_t, :int], :int
        attach_function :getpgid, [:pid_t], :pid_t
        attach_function :setpgid, [:pid_t, :pid_t], :int
        attach_function :getpid,  [], :pid_t
        attach_function :getppid, [], :pid_t
        attach_function :getpgrp, [], :pid_t
        attach_function :setsid,  [], :pid_t

        # related to stat()
        attach_function :major, 'ffi_major', [:dev_t], :dev_t
        attach_function :minor, 'ffi_minor', [:dev_t], :dev_t

        # time
        attach_function :gettimeofday, [:pointer, :pointer], :int

        #--
        # Internal class for accessing timevals
        #++
        class TimeVal < FFI::Struct
          config 'rbx.platform.timeval', :tv_sec, :tv_usec
        end
      end
    end
  end

  module EnvironmentAccess
    extend FFI::Library

    attach_function :getenv,   [:string], :string
    attach_function :putenv,   [:string], :int
    attach_function :setenv,   [:string,  :string, :int], :int
    attach_function :unsetenv, [:string], :int
    attach_function :environ,  'ffi_environ', [], :pointer
  end

  class EnvironmentVariables
    include EnvironmentAccess
  end

  ::ENV = EnvironmentVariables.new

  module Metrics
    class Data
      MetricsHash = Hash.new
    end
  end

  class Stat
    S_IRUSR  = Rubinius::Config['rbx.platform.file.S_IRUSR']
    S_IWUSR  = Rubinius::Config['rbx.platform.file.S_IWUSR']
    S_IXUSR  = Rubinius::Config['rbx.platform.file.S_IXUSR']
    S_IRGRP  = Rubinius::Config['rbx.platform.file.S_IRGRP']
    S_IWGRP  = Rubinius::Config['rbx.platform.file.S_IWGRP']
    S_IXGRP  = Rubinius::Config['rbx.platform.file.S_IXGRP']
    S_IROTH  = Rubinius::Config['rbx.platform.file.S_IROTH']
    S_IWOTH  = Rubinius::Config['rbx.platform.file.S_IWOTH']
    S_IXOTH  = Rubinius::Config['rbx.platform.file.S_IXOTH']

    S_IRUGO  = S_IRUSR | S_IRGRP | S_IROTH
    S_IWUGO  = S_IWUSR | S_IWGRP | S_IWOTH
    S_IXUGO  = S_IXUSR | S_IXGRP | S_IXOTH

    S_IFMT   = Rubinius::Config['rbx.platform.file.S_IFMT']
    S_IFIFO  = Rubinius::Config['rbx.platform.file.S_IFIFO']
    S_IFCHR  = Rubinius::Config['rbx.platform.file.S_IFCHR']
    S_IFDIR  = Rubinius::Config['rbx.platform.file.S_IFDIR']
    S_IFBLK  = Rubinius::Config['rbx.platform.file.S_IFBLK']
    S_IFREG  = Rubinius::Config['rbx.platform.file.S_IFREG']
    S_IFLNK  = Rubinius::Config['rbx.platform.file.S_IFLNK']
    S_IFSOCK = Rubinius::Config['rbx.platform.file.S_IFSOCK']
    S_IFWHT  = Rubinius::Config['rbx.platform.file.S_IFWHT']
    S_ISUID  = Rubinius::Config['rbx.platform.file.S_ISUID']
    S_ISGID  = Rubinius::Config['rbx.platform.file.S_ISGID']
    S_ISVTX  = Rubinius::Config['rbx.platform.file.S_ISVTX']
  end
end

code = Class.method_table.lookup(:new).get_method
code.serial = Rubinius::CompiledCode::KernelMethodSerial

class Class
  undef_method :append_features
  undef_method :extend_object
  undef_method :module_function
  undef_method :prepend_features
end

class Complex < Numeric
  I = Complex(0, 1)
end

class NilClass
  include ImmediateValue
end

class FalseClass
  include ImmediateValue
end

class TrueClass
  include ImmediateValue
end

class Symbol
  include ImmediateValue
end

class Fixnum
  include ImmediateValue
end

# Default Ruby Record Separator
# Used in this file and by various methods that need to ignore $/
DEFAULT_RECORD_SEPARATOR = "\n"

class Dir
  # This seems silly, I know. But we do this to make Dir more resistent to people
  # screwing with ::File later (ie, fakefs)
  PrivateFile = ::File

  FFI = Rubinius::FFI

  module Glob
    total = Rubinius::Config['glob.cache']

    case total
    when Fixnum
      if total == 0
        @glob_cache = nil
      else
        @glob_cache = Rubinius::LRUCache.new(total)
      end
    when false
      @glob_cache = nil
    else
      @glob_cache = Rubinius::LRUCache.new(50)
    end
  end
end

class Encoding
  class Converter
    class TranscodingPath
      @paths = {}
      @load_cache = true
      @cache_valid = false
      @transcoders_count = TranscodingMap.size
    end
  end
end

Enumerator = Enumerable::Enumerator

module Errno
  FFI = Rubinius::FFI
end

class File < IO
  # these will be necessary when we run on Windows
  DOSISH = false # !!(RUBY_PLATFORM =~ /mswin/)
  CASEFOLD_FILESYSTEM = DOSISH
  FNM_SYSCASE = CASEFOLD_FILESYSTEM ? FNM_CASEFOLD : 0

  module Constants
    F_GETFL  = Rubinius::Config['rbx.platform.fcntl.F_GETFL']
    F_SETFL  = Rubinius::Config['rbx.platform.fcntl.F_SETFL']

    # O_ACCMODE is /undocumented/ for fcntl() on some platforms
    ACCMODE  = Rubinius::Config['rbx.platform.fcntl.O_ACCMODE']
    O_ACCMODE = Rubinius::Config['rbx.platform.fcntl.O_ACCMODE']

    F_GETFD  = Rubinius::Config['rbx.platform.fcntl.F_GETFD']
    F_SETFD  = Rubinius::Config['rbx.platform.fcntl.F_SETFD']
    FD_CLOEXEC = Rubinius::Config['rbx.platform.fcntl.FD_CLOEXEC']
    O_CLOEXEC = Rubinius::Config['rbx.platform.file.O_CLOEXEC']
    O_NONBLOCK = Rubinius::Config['rbx.platform.file.O_NONBLOCK']

    RDONLY   = Rubinius::Config['rbx.platform.file.O_RDONLY']
    WRONLY   = Rubinius::Config['rbx.platform.file.O_WRONLY']
    RDWR     = Rubinius::Config['rbx.platform.file.O_RDWR']

    CREAT    = Rubinius::Config['rbx.platform.file.O_CREAT']
    EXCL     = Rubinius::Config['rbx.platform.file.O_EXCL']
    NOCTTY   = Rubinius::Config['rbx.platform.file.O_NOCTTY']
    TRUNC    = Rubinius::Config['rbx.platform.file.O_TRUNC']
    APPEND   = Rubinius::Config['rbx.platform.file.O_APPEND']
    NONBLOCK = Rubinius::Config['rbx.platform.file.O_NONBLOCK']
    SYNC     = Rubinius::Config['rbx.platform.file.O_SYNC']

    # TODO: these flags should probably be imported from Platform
    LOCK_SH  = 0x01
    LOCK_EX  = 0x02
    LOCK_NB  = 0x04
    LOCK_UN  = 0x08
    BINARY   = 0x04

    # TODO: "OK" constants aren't in File::Constants in MRI
    F_OK = 0 # test for existence of file
    X_OK = 1 # test for execute or search permission
    W_OK = 2 # test for write permission
    R_OK = 4 # test for read permission

    FNM_NOESCAPE = 0x01
    FNM_PATHNAME = 0x02
    FNM_DOTMATCH = 0x04
    FNM_CASEFOLD = 0x08
    FNM_EXTGLOB  = 0x10
    FNM_SYSCASE  = File::FNM_SYSCASE

    if Rubinius.windows?
      NULL = 'NUL'
    else
      NULL = '/dev/null'
    end
  end

  Stat = Rubinius::Stat

  class Stat
    @module_name = :"File::Stat"

    def world_readable?
      if mode & S_IROTH == S_IROTH
        tmp = mode & (S_IRUGO | S_IWUGO | S_IXUGO)
        return Rubinius::Type.coerce_to tmp, Fixnum, :to_int
      end
    end

    def world_writable?
      if mode & S_IWOTH == S_IWOTH
        tmp = mode & (S_IRUGO | S_IWUGO | S_IXUGO)
        return Rubinius::Type.coerce_to tmp, Fixnum, :to_int
      end
    end
  end

  FFI = Rubinius::FFI

  SEPARATOR = FFI::Platform::File::SEPARATOR
  Separator = FFI::Platform::File::SEPARATOR
  ALT_SEPARATOR = FFI::Platform::File::ALT_SEPARATOR
  PATH_SEPARATOR = FFI::Platform::File::PATH_SEPARATOR
  POSIX = FFI::Platform::POSIX
end

class Float
  FFI = Rubinius::FFI
end

class IO
  include File::Constants

  FFI = Rubinius::FFI

  SEEK_SET = Rubinius::Config['rbx.platform.io.SEEK_SET']
  SEEK_CUR = Rubinius::Config['rbx.platform.io.SEEK_CUR']
  SEEK_END = Rubinius::Config['rbx.platform.io.SEEK_END']

  # Not available on all platforms, so these constants may be nil
  POSIX_FADV_NORMAL     = Rubinius::Config['rbx.platform.advise.POSIX_FADV_NORMAL']
  POSIX_FADV_SEQUENTIAL = Rubinius::Config['rbx.platform.advise.POSIX_FADV_SEQUENTIAL']
  POSIX_FADV_RANDOM     = Rubinius::Config['rbx.platform.advise.POSIX_FADV_RANDOM']
  POSIX_FADV_WILLNEED   = Rubinius::Config['rbx.platform.advise.POSIX_FADV_WILLNEED']
  POSIX_FADV_DONTNEED   = Rubinius::Config['rbx.platform.advise.POSIX_FADV_DONTNEED']
  POSIX_FADV_NOREUSE    = Rubinius::Config['rbx.platform.advise.POSIX_FADV_NOREUSE']

  class FileDescriptor
    @@max_descriptors = Rubinius::AtomicReference.new(2)

    include File::Constants

    O_RDONLY   = Rubinius::Config['rbx.platform.file.O_RDONLY']
    O_WRONLY   = Rubinius::Config['rbx.platform.file.O_WRONLY']
    O_RDWR     = Rubinius::Config['rbx.platform.file.O_RDWR']
  end

  class Select
    FD_SETSIZE = Rubinius::Config['rbx.platform.select.FD_SETSIZE']
  end
end

class NilClass
  include ImmediateValue
end

class FalseClass
  include ImmediateValue
end

class TrueClass
  include ImmediateValue
end

class Symbol
  include ImmediateValue
end

class Fixnum
  include ImmediateValue
end

module Rubinius
  class Mirror
    module Process
      class Execute
        # Mapping of string open modes to integer oflag versions.
        OFLAGS = {
          "r"  => ::File::RDONLY,
          "r+" => ::File::RDWR   | ::File::CREAT,
          "w"  => ::File::WRONLY | ::File::CREAT  | ::File::TRUNC,
          "w+" => ::File::RDWR   | ::File::CREAT  | ::File::TRUNC,
          "a"  => ::File::WRONLY | ::File::APPEND | ::File::CREAT,
          "a+" => ::File::RDWR   | ::File::APPEND | ::File::CREAT
        }
      end
    end
  end
end

class Regexp
  IGNORECASE         = 1
  EXTENDED           = 2
  MULTILINE          = 4
  FIXEDENCODING      = 16
  NOENCODING         = 32
  DONT_CAPTURE_GROUP = 128
  CAPTURE_GROUP      = 256

  KCODE_NONE = (1 << 9)
  KCODE_EUC  = (2 << 9)
  KCODE_SJIS = (3 << 9)
  KCODE_UTF8 = (4 << 9)
  KCODE_MASK = KCODE_NONE | KCODE_EUC | KCODE_SJIS | KCODE_UTF8

  OPTION_MASK = IGNORECASE | EXTENDED | MULTILINE | FIXEDENCODING | NOENCODING | DONT_CAPTURE_GROUP | CAPTURE_GROUP

  ESCAPE_TABLE = Rubinius::Tuple.new(256)

  # Seed it with direct replacements
  i = 0
  while i < 256
    ESCAPE_TABLE[i] = i.chr
    i += 1
  end

  # The character literals (?x) are Fixnums in 1.8 and Strings in 1.9
  # so we use literal values instead so this is 1.8/1.9 compatible.
  ESCAPE_TABLE[9]   = '\\t'
  ESCAPE_TABLE[10]  = '\\n'
  ESCAPE_TABLE[11]  = '\\v'
  ESCAPE_TABLE[12]  = '\\f'
  ESCAPE_TABLE[13]  = '\\r'
  ESCAPE_TABLE[32]  = '\\ '
  ESCAPE_TABLE[35]  = '\\#'
  ESCAPE_TABLE[36]  = '\\$'
  ESCAPE_TABLE[40]  = '\\('
  ESCAPE_TABLE[41]  = '\\)'
  ESCAPE_TABLE[42]  = '\\*'
  ESCAPE_TABLE[43]  = '\\+'
  ESCAPE_TABLE[45]  = '\\-'
  ESCAPE_TABLE[46]  = '\\.'
  ESCAPE_TABLE[63]  = '\\?'
  ESCAPE_TABLE[91]  = '\\['
  ESCAPE_TABLE[92]  = '\\\\'
  ESCAPE_TABLE[93]  = '\\]'
  ESCAPE_TABLE[94]  = '\\^'
  ESCAPE_TABLE[123] = '\\{'
  ESCAPE_TABLE[124] = '\\|'
  ESCAPE_TABLE[125] = '\\}'

  class SourceParser
    class Part
      OPTIONS_MAP = {
        'm' => Regexp::MULTILINE,
        'i' => Regexp::IGNORECASE,
        'x' => Regexp::EXTENDED
      }
    end

    PossibleOptions = [[MULTILINE, "m"], [IGNORECASE, "i"], [EXTENDED, "x"]]
  end
end

class String
  class Rationalizer
    SPACE = "\\s*"
    DIGITS = "(?:[0-9](?:_[0-9]|[0-9])*)"
    NUMERATOR = "(?:#{DIGITS}?\\.)?#{DIGITS}(?:[eE][-+]?#{DIGITS})?"
    DENOMINATOR = DIGITS
    RATIONAL = "\\A#{SPACE}([-+])?(#{NUMERATOR})(?:\\/(#{DENOMINATOR}))?#{SPACE}"
    PATTERN = Regexp.new RATIONAL
  end

  class Complexifier
    SPACE = Rationalizer::SPACE
    NUMERATOR = Rationalizer::NUMERATOR
    DENOMINATOR = Rationalizer::DENOMINATOR
    NUMBER = "[-+]?#{NUMERATOR}(?:\\/#{DENOMINATOR})?"
    NUMBERNOS = "#{NUMERATOR}(?:\\/#{DENOMINATOR})?"
    PATTERN0 = Regexp.new "\\A#{SPACE}(#{NUMBER})@(#{NUMBER})#{SPACE}"
    PATTERN1 = Regexp.new "\\A#{SPACE}([-+])?(#{NUMBER})?[iIjJ]#{SPACE}"
    PATTERN2 = Regexp.new "\\A#{SPACE}(#{NUMBER})(([-+])(#{NUMBERNOS})?[iIjJ])?#{SPACE}"
  end
end

module Signal
  Names = {
    "EXIT" => 0
  }

  Numbers = {
    0 => "EXIT"
  }

  @handlers = {}

  Rubinius::Config.section 'rbx.platform.signal.' do |key, value|
    name = key[23, key.length]
    number = value.to_i
    Names[name] = number
    Numbers[number] = name
  end

  # special case of signal.c
  if Names["CHLD"]
    Names["CLD"] = Names["CHLD"]
  end
end

class Integer < Numeric
  alias_method :magnitude, :abs
end

module Kernel
  def extend(*modules)
    raise ArgumentError, "wrong number of arguments (0 for 1+)" if modules.empty?
    Rubinius.check_frozen

    modules.reverse_each do |mod|
      Rubinius.privately do
        mod.extend_object self
      end

      Rubinius.privately do
        mod.extended self
      end
    end
    self
  end

  alias_method :__extend__, :extend

  def method_missing(meth, *args)
    cls = NoMethodError

    case Rubinius.method_missing_reason
    when :private
      msg = "private method `#{meth}' called"
    when :protected
      msg = "protected method `#{meth}' called"
    when :super
      msg = "no superclass method `#{meth}'"
    when :vcall
      msg = "undefined local variable or method `#{meth}'"
      cls = NameError
    else
      msg = "undefined method `#{meth}'"
    end

    object_class = Rubinius::Type.object_class(self)

    if Rubinius::Type.object_kind_of?(self, Module)
      msg << " on #{self} (#{object_class})"

    # A separate case for nil, because people like to patch methods to
    # nil, so we can't call methods on it reliably.
    elsif nil.equal?(self)
      msg << " on nil:NilClass."
    elsif ImmediateValue === self
      msg << " on #{self}:#{object_class}."
    else
      msg << " on an instance of #{object_class}."
    end

    Kernel.raise(cls.new(msg, meth, args, receiver: self))
  end

  private :method_missing

  def raise(exc=undefined, msg=undefined, ctx=nil, cause: nil)
    if undefined.equal? exc
      exc = $!
      exc = RuntimeError.new("No current exception") unless exc
      exc.cause = cause unless exc.cause or exc == cause
    else
      if exc.respond_to? :exception
        if undefined.equal? msg
          exc = exc.exception
        else
          exc = exc.exception msg
        end
        raise ::TypeError, 'exception class/object expected' unless exc.kind_of?(::Exception)
      elsif exc.kind_of? String
        exc = ::RuntimeError.exception exc
      else
        raise ::TypeError, 'exception class/object expected'
      end

      exc.cause = cause unless exc.cause or exc == cause
      exc.cause = $! unless exc.cause or exc == $!
    end

    if ctx.kind_of? Array
      exc.set_backtrace ctx
    else
      exc.capture_backtrace!(2) unless exc.backtrace?
    end

    if $DEBUG and $VERBOSE != nil
      if loc = exc.locations and loc[1]
        pos = loc[1].position
      else
        pos = Rubinius::VM.backtrace(1)[0].position
      end

      STDERR.puts "Exception: `#{exc.class}' #{pos} - #{exc.message}"
    end

    Rubinius.raise_exception exc
  end
  module_function :raise

  alias_method :fail, :raise
  module_function :fail

  def to_s
    Rubinius::Type.infect("#<#{self.class}:0x#{self.__id__.to_s(16)}>", self)
  end

  # Add in $! in as a hook, to just do $!. This is for accesses to $!
  # that the compiler can't see.
  Rubinius::Globals.set_hook(:$!) { $! }

  get = proc do
    # We raise an exception here because Regexp.last_match won't work
    raise TypeError, "Unable to handle $~ in this context"
  end
  set = proc do |key, val|
    if val.nil? || val.kind_of?(MatchData)
      Rubinius.invoke_primitive :regexp_set_last_match, val
    else
      raise TypeError, "Cannot assign #{val.class}, expexted nil or instance MatchData."
    end
  end
  Rubinius::Globals.set_hook(:$~, get, set)

  Rubinius::Globals.set_hook(:$*) { ARGV }

  Rubinius::Globals.set_hook(:$@) { $! ? $!.backtrace : nil }

  Rubinius::Globals.set_hook(:$$) { Process.pid }

  prc = proc { ARGF.filename }
  Rubinius::Globals.set_hook(:$FILENAME, prc, :raise_readonly)

  write_filter = proc do |key, io|
    unless io.respond_to? :write
      raise ::TypeError, "#{key} must have write method, #{io.class} given"
    end
    io
  end

  get = proc { |key| Thread.current[:$_] }
  set = proc { |key, val| Thread.current[:$_] = val }
  Rubinius::Globals.set_hook(:$_, get, set)

  Rubinius::Globals.add_alias :$stdout, :$>
  Rubinius::Globals.set_filter(:$stdout, write_filter)
  Rubinius::Globals.set_filter(:$stderr, write_filter)

  get = proc do
    warn "$KCODE is unused in Ruby 1.9 and later"
    nil
  end

  set = proc do |key, io|
    warn "$KCODE is unused in Ruby 1.9 and later, changes are ignored"
    nil
  end

  Rubinius::Globals.set_hook(:$KCODE, get, set)

  set = proc do |key, val|
    val = Rubinius::Type.coerce_to val, String, :to_str
    Rubinius.invoke_primitive :vm_set_process_title, val
  end
  Rubinius::Globals.set_hook(:$0, :[], set)

  set = proc { |key, val| STDERR.puts("WARNING: $SAFE is not supported on Rubinius."); val }
  Rubinius::Globals.set_hook(:$SAFE, :[], set)

  # Alias $0 $PROGRAM_NAME
  Rubinius::Globals.add_alias(:$0, :$PROGRAM_NAME)

  Rubinius::Globals.read_only :$:, :$LOAD_PATH, :$-I
  Rubinius::Globals.read_only :$", :$LOADED_FEATURES
  Rubinius::Globals.read_only :$<

  Rubinius::Globals[:$-a] = false
  Rubinius::Globals[:$-l] = false
  Rubinius::Globals[:$-p] = false
  Rubinius::Globals.read_only :$-a, :$-l, :$-p

  Rubinius::Globals.add_alias :$DEBUG,   :$-d
  Rubinius::Globals.add_alias :$VERBOSE, :$-v
  Rubinius::Globals.add_alias :$VERBOSE, :$-w

  set_string = proc do |key, value|
    unless value.nil? or value.kind_of? String
      raise TypeError, "value of #{key} must be a String"
    end

    Rubinius::Globals.set! key, value
  end

  Rubinius::Globals.set_filter :$/, set_string
  Rubinius::Globals.add_alias :$/, :$-0

  Rubinius::Globals.set_filter :$,, set_string
end

module Math
  FFI = Rubinius::FFI

  # Constants
  PI = 3.14159_26535_89793_23846
  E  = 2.71828_18284_59045_23536

  FactorialTable = [
    1.0,
    1.0,
    2.0,
    6.0,
    24.0,
    120.0,
    720.0,
    5040.0,
    40320.0,
    362880.0,
    3628800.0,
    39916800.0,
    479001600.0,
    6227020800.0,
    87178291200.0,
    1307674368000.0,
    20922789888000.0,
    355687428096000.0,
    6402373705728000.0,
    121645100408832000.0,
    2432902008176640000.0,
    51090942171709440000.0,
    1124000727777607680000.0
  ]

  if Errno.const_defined? :EDOM
    DomainError = Errno::EDOM
  elsif Errno.const_defined? :ERANGE
    DomainError = Errno::ERANGE
  else
    class DomainError < SystemCallError
    end
  end
end

module Process
  module Constants
    EXIT_SUCCESS = Rubinius::Config['rbx.platform.process.EXIT_SUCCESS'] || 0
    EXIT_FAILURE = Rubinius::Config['rbx.platform.process.EXIT_FAILURE'] || 1

    PRIO_PGRP    = Rubinius::Config['rbx.platform.process.PRIO_PGRP']
    PRIO_PROCESS = Rubinius::Config['rbx.platform.process.PRIO_PROCESS']
    PRIO_USER    = Rubinius::Config['rbx.platform.process.PRIO_USER']

    RLIM_INFINITY  = Rubinius::Config['rbx.platform.process.RLIM_INFINITY']
    RLIM_SAVED_MAX = Rubinius::Config['rbx.platform.process.RLIM_SAVED_MAX']
    RLIM_SAVED_CUR = Rubinius::Config['rbx.platform.process.RLIM_SAVED_CUR']

    RLIMIT_AS      = Rubinius::Config['rbx.platform.process.RLIMIT_AS']
    RLIMIT_CORE    = Rubinius::Config['rbx.platform.process.RLIMIT_CORE']
    RLIMIT_CPU     = Rubinius::Config['rbx.platform.process.RLIMIT_CPU']
    RLIMIT_DATA    = Rubinius::Config['rbx.platform.process.RLIMIT_DATA']
    RLIMIT_FSIZE   = Rubinius::Config['rbx.platform.process.RLIMIT_FSIZE']
    RLIMIT_MEMLOCK = Rubinius::Config['rbx.platform.process.RLIMIT_MEMLOCK']
    RLIMIT_NOFILE  = Rubinius::Config['rbx.platform.process.RLIMIT_NOFILE']
    RLIMIT_NPROC   = Rubinius::Config['rbx.platform.process.RLIMIT_NPROC']
    RLIMIT_RSS     = Rubinius::Config['rbx.platform.process.RLIMIT_RSS']
    RLIMIT_SBSIZE  = Rubinius::Config['rbx.platform.process.RLIMIT_SBSIZE']
    RLIMIT_STACK   = Rubinius::Config['rbx.platform.process.RLIMIT_STACK']

    RLIMIT_RTPRIO     = Rubinius::Config['rbx.platform.process.RLIMIT_RTPRIO']
    RLIMIT_RTTIME     = Rubinius::Config['rbx.platform.process.RLIMIT_RTTIME']
    RLIMIT_SIGPENDING = Rubinius::Config['rbx.platform.process.RLIMIT_SIGPENDING']
    RLIMIT_MSGQUEUE   = Rubinius::Config['rbx.platform.process.RLIMIT_MSGQUEUE']
    RLIMIT_NICE       = Rubinius::Config['rbx.platform.process.RLIMIT_NICE']

    WNOHANG = 1
    WUNTRACED = 2
  end
  include Constants

  FFI = Rubinius::FFI

  class Rlimit < FFI::Struct
    config "rbx.platform.rlimit", :rlim_cur, :rlim_max
  end

  Rubinius::Globals.read_only :$?
  Rubinius::Globals.set_hook(:$?) { Thread.current[:$?] }
end


STDIN  = Rubinius::IOUtility.redefine_io(0, :read_only)
STDOUT = Rubinius::IOUtility.redefine_io(1, :write_only)
STDERR = Rubinius::IOUtility.redefine_io(2, :write_only)

Rubinius::Globals.set!(:$stdin, STDIN)
Rubinius::Globals.set!(:$stdout, STDOUT)
Rubinius::Globals.set!(:$stderr, STDERR)

module Rubinius
  begin
    is_tty = STDIN.tty?
  rescue IOError
    is_tty = false
  end

  Terminal = is_tty
  AtExit = []

  class Sprinter
    total = Rubinius::Config['printf.cache']

    case total
    when Fixnum
      if total == 0
        @cache = nil
      else
        @cache = Rubinius::LRUCache.new(total)
      end
    when false
      @cache = nil
    else
      @cache = Rubinius::LRUCache.new(50)
    end

    class Builder
      RADIX = { 'u' => 10, 'x' => 16, 'X' => 16, 'o' => 8, 'b' => 2, 'B' => 2 }
      PREFIX = { 'o' => '0', 'x' => '0x', 'X' => '0X', 'b' => '0b', 'B' => '0B' }

      AtomMap = Rubinius::LookupTable.new

      AtomMap[?c] = CharAtom
      AtomMap[?s] = StringAtom
      AtomMap[?p] = InspectAtom
      AtomMap[?e] = FloatAtom
      AtomMap[?E] = FloatAtom
      AtomMap[?g] = FloatAtom
      AtomMap[?G] = FloatAtom
      AtomMap[?f] = FloatAtom
      AtomMap[?d] = IntegerAtom
      AtomMap[?i] = IntegerAtom
      AtomMap[?u] = IntegerAtom
      AtomMap[?b] = ExtIntegerAtom
      AtomMap[?B] = ExtIntegerAtom
      AtomMap[?x] = ExtIntegerAtom
      AtomMap[?X] = ExtIntegerAtom
      AtomMap[?o] = ExtIntegerAtom

      RE = /
        ([^%]+|%(?:[\n\0]|\z)) # 1
        |
        %
        ( # 2
          (<[^>]+>)? # 3
          ([0# +-]*) # 4
          (?:([0-9]+)\$)? # 5
          ([0# +-]*) # 6
          (?:
            (\*(?:([0-9]+)\$)?|([1-9][0-9]*))? # 7 8 9
            (?:\.(\*(?:([0-9]+)\$)?|([0-9][0-9]*))?)? # 10 11 12
          )
          (?:([0-9]+)\$)? # 13
          ([BbcdEefGgiopsuXx]) # 14
        )
        |
        (%)(?:%|[-+0-9# *.$]+\$[0-9.]*\z) # 15
        |
        (%{[^}]+}) #16
        |
        (%) # 17
      /x
    end
  end
end

class Struct
  Struct.new 'Tms', :utime, :stime, :cutime, :cstime, :tutime, :tstime

  class Tms
    def initialize(utime=nil, stime=nil, cutime=nil, cstime=nil,
                   tutime=nil, tstime=nil)
      @utime = utime
      @stime = stime
      @cutime = cutime
      @cstime = cstime
      @tutime = tutime
      @tstime = tstime
    end
  end

  def self._specialize(attrs)
    # Because people are crazy, they subclass Struct directly, ie.
    #  class Craptastic < Struct
    #
    # NOT
    #
    #  class Fine < Struct.new(:x, :y)
    #
    # When they do this craptastic act, they'll sometimes define their
    # own #initialize and super into Struct#initialize.
    #
    # When they do this and then do Craptastic.new(:x, :y), this code
    # will accidentally shadow their #initialize. So for now, only run
    # the specialize if we're trying new Struct's directly from Struct itself,
    # not a craptastic Struct subclass.

    return unless superclass.equal? Struct

    # To allow for optimization, we generate code with normal ivar
    # references for all attributes whose names can be written as
    # tIVAR tokens. For example, of the following struct attributes
    #
    #   Struct.new(:a, :@b, :c?, :'d-e')
    #
    # only the first, :a, can be written as a valid tIVAR token:
    #
    #   * :a can be written as @a
    #   * :@b becomes @@b and would be interpreted as a tCVAR
    #   * :c? becomes @c? and be interpreted as the beginning of
    #     a ternary expression
    #   * :'d-e' becomes @d-e and would be interpreted as a method
    #     invocation
    #
    # Attribute names that cannot be written as tIVAR tokens will
    # fall back to using #instance_variable_(get|set).

    args, assigns, hashes, vars = [], [], [], []

    attrs.each_with_index do |name, i|
      name = "@#{name}"

      if name =~ /^@[a-z_]\w*$/i
        assigns << "#{name} = a#{i}"
        vars    << name
      else
        assigns << "instance_variable_set(:#{name.inspect}, a#{i})"
        vars    << "instance_variable_get(:#{name.inspect})"
      end

      args   << "a#{i} = nil"
      hashes << "#{vars[-1]}.hash"
    end

    code = <<-CODE
      def initialize(#{args.join(", ")})
        #{assigns.join(';')}
        self
      end

      def hash
        hash = #{hashes.size}

        return hash if Thread.detect_outermost_recursion(self) do
          hash = hash ^ #{hashes.join(' ^ ')}
        end

        hash
      end

      def to_a
        [#{vars.join(', ')}]
      end

      def length
        #{vars.size}
      end
    CODE

    begin
      mod = Module.new do
        module_eval code
      end
      include mod
    rescue SyntaxError
      # SyntaxError means that something is wrong with the
      # specialization code. Just eat the error and don't specialize.
    end
  end
end

class Thread
  MUTEX_FOR_THREAD_EXCLUSIVE = Mutex.new

  Thread.initialize_main_thread(Thread.current)
  ThreadGroup::Default.add Thread.current

  sm = Rubinius::Mirror.reflect Rubinius.signal_thread
  sm.group = ThreadGroup::Default
end

class Time
  MonthValue = {
    'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, 'MAY' => 5, 'JUN' => 6,
    'JUL' => 7, 'AUG' => 8, 'SEP' => 9, 'OCT' =>10, 'NOV' =>11, 'DEC' =>12
  }
end

module Rubinius
  class CodeDB
    class << self
      undef :new
    end
  end

  module FFI
    module Library
      # Once the core library is loaded, we want to raise an error if
      # attempting to attach to a non-existent function.
      def ffi_function_missing(name, *args)
        raise FFI::NotFoundError, "Unable to find foreign function '#{name}'"
      end
    end
  end

  ConstantScope = LexicalScope
end
