class Coerce(object):
    @staticmethod
    def bool(space, w_obj):
        return space.is_true(w_obj)

    @staticmethod
    def symbol(space, w_obj):
        if space.is_kind_of(w_obj, space.w_symbol):
            return space.symbol_w(w_obj)
        else:
            w_str = space.convert_type(w_obj, space.w_string, "to_str",
                                       raise_error=False)
            if w_str is space.w_nil:
                w_inspect_str = space.send(w_obj, "inspect")
                if not space.is_kind_of(w_inspect_str, space.w_string):
                    inspect_str = space.any_to_s(w_obj)
                else:
                    inspect_str = space.str_w(w_inspect_str)
                raise space.error(space.w_TypeError,
                                  "%s is not a symbol" % inspect_str)
            else:
                return space.str_w(w_str)

    @staticmethod
    def int(space, w_obj):
        if space.is_kind_of(w_obj, space.w_fixnum):
            return space.int_w(w_obj)
        else:
            return space.int_w(
                space.convert_type(w_obj, space.w_integer, "to_int"))

    @staticmethod
    def bigint(space, w_obj):
        return space.bigint_w(
            space.convert_type(w_obj, space.w_integer, "to_int"))

    @staticmethod
    def float(space, w_obj):
        if space.is_kind_of(w_obj, space.w_float):
            return space.float_w(w_obj)
        else:
            return space.float_w(space.send(w_obj, "Float", [w_obj]))

    @staticmethod
    def strictfloat(space, w_obj):
        if not space.is_kind_of(w_obj, space.w_numeric):
            clsname = w_obj.getclass(space).name
            raise space.error(space.w_TypeError,
                              "can't convert %s into Float" % clsname)
        return Coerce.float(space, w_obj)

    @staticmethod
    def str(space, w_obj):
        if (space.is_kind_of(w_obj, space.w_string) or
                space.is_kind_of(w_obj, space.w_symbol)):
            return space.str_w(w_obj)
        else:
            return space.str_w(
                space.convert_type(w_obj, space.w_string, "to_str"))

    @staticmethod
    def path(space, w_obj):
        w_string = space.convert_type(w_obj, space.w_string, "to_path",
                                      raise_error=False)
        if w_string is space.w_nil:
            w_string = space.convert_type(w_obj, space.w_string, "to_str")
        return space.str0_w(w_string)

    @staticmethod
    def array(space, w_obj):
        if not space.is_kind_of(w_obj, space.w_array):
            w_obj = space.convert_type(w_obj, space.w_array, "to_ary")
        return space.listview(w_obj)

    @staticmethod
    def hash(space, w_obj):
        if not space.is_kind_of(w_obj, space.w_hash):
            return space.convert_type(w_obj, space.w_hash, "to_hash")
        return w_obj class RubyError(Exception):
    def __init__(self, w_value):
        self.w_value = w_value

    def __str__(self):
        return "<RubyError: %s>" % self.w_value def error_for_oserror(space, exc):
    return error_for_errno(space, exc.errno) def error_for_errno(space, errno):
    try:
        name = _errno_for_oserror_map[errno]
    except KeyError:
        w_type = space.w_SystemCallError
    else:
        w_type = space.find_const(
            space.find_const(space.w_object, "Errno"), name)
    return space.error(
        w_type,
        os.strerror(errno),
        [space.newint(errno)]
    ) class ModuleDef(object):
    def __init__(self, name):
        self.name = name
        self.methods = {}

        self.singleton_methods = {}
        self.setup_module_func = None

    def __deepcopy__(self, memo):
        return self

    def method(self, __name, **argspec):
        name = __name

        def adder(func):
            self.methods[name] = (func, argspec)
            return func
        return adder

    def function(self, __name, **argspec):
        name = __name

        def adder(func):
            # TODO: should be private, once we have visibility
            self.methods[name] = (func, argspec)
            self.singleton_methods[name] = (func, argspec)
            return func
        return adder

    def setup_module(self, func):
        self.setup_module_func = func
        return func def check_frozen(param="self"):
    def inner(func):
        code = func.__code__
        space_idx = code.co_varnames.index("space")
        obj_idx = code.co_varnames.index(param)

        @functools.wraps(func)
        def wrapper(*args):
            space = args[space_idx]
            w_obj = args[obj_idx]
            if space.is_true(w_obj.get_flag(space, "frozen?")):
                klass = space.getclass(w_obj)
                raise space.error(
                    space.w_RuntimeError,
                    "can't modify frozen %s" % klass.name)
            return func(*args)
        wrapper.__wraps__ = func
        return wrapper
    return inner class Process(object):
    moduledef = ModuleDef("Process")

    @moduledef.setup_module
    def setup_module(space, w_mod):
        if rtime.HAS_CLOCK_GETTIME:
            for name in rtime.ALL_DEFINED_CLOCKS:
                space.set_const(w_mod, name, space.newint(getattr(rtime, name)))
        else:
            space.set_const(
                w_mod,
                "CLOCK_PROCESS_CPUTIME_ID",
                space.newint(CLOCK_PROCESS_CPUTIME_ID)
            )

    @moduledef.function("euid")
    def method_euid(self, space):
        return space.newint(intmask(geteuid()))

    @moduledef.function("pid")
    def method_pid(self, space):
        return space.newint(os.getpid())

    @moduledef.function("waitpid", pid="int")
    def method_waitpid(self, space, pid=-1):
        try:
            pid, status = os.waitpid(pid, 0)
        except OSError as e:
            raise error_for_oserror(space, e)
        status = WEXITSTATUS(status)
        w_status = space.send(
            space.find_const(self, "Status"),
            "new",
            [space.newint(pid), space.newint(status)]
        )
        space.globals.set(space, "$?", w_status)
        return space.newint(pid)

    @moduledef.function("exit", status="int")
    def method_exit(self, space, status=0):
        raise space.error(space.w_SystemExit, "exit", [space.newint(status)])

    @moduledef.function("exit!", status="int")
    def method_exit_bang(self, space, status=0):
        os._exit(status)

    @moduledef.function("fork")
    def method_fork(self, space, block):
        pid = fork()
        if pid == 0:
            if block is not None:
                space.invoke_block(block, [])
                space.send(self, "exit")
            else:
                return space.w_nil
        else:
            return space.newint(pid)

    @moduledef.function("times")
    def method_times(self, space):
        tms = space.find_const(
            space.find_const(space.w_object, "Struct"),
            "Tms"
        )
        return space.send(
            tms,
            "new",
            [space.newfloat(t) for t in list(os.times()[0:4])]
        )

    @moduledef.function("kill")
    def method_kill(self, space, w_signal, args_w):
        if not args_w:
            raise space.error(space.w_ArgumentError,
                "wrong number of arguments (%d for at least 2)" % (len(args_w) + 1)
            )
        if space.is_kind_of(w_signal, space.w_fixnum):
            sig = space.int_w(w_signal)
        else:
            s = Coerce.str(space, w_signal)
            if s.startswith("SIG"):
                s = s[len("SIG"):]
            try:
                sig = SIGNALS[s]
            except KeyError:
                raise space.error(space.w_ArgumentError,
                    "unsupported name `SIG%s'" % s
                )

        if sig < 0:
            for w_arg in args_w:
                pid = Coerce.int(space, w_arg)
                try:
                    killpg(pid, -sig)
                except OSError as e:
                    raise error_for_oserror(space, e)
        else:
            for w_arg in args_w:
                pid = Coerce.int(space, w_arg)
                try:
                    kill(pid, sig)
                except OSError as e:
                    raise error_for_oserror(space, e)
        return space.newint(len(args_w))

    @moduledef.function("clock_gettime", clockid="int")
    def method_clock_gettime(self, space, clockid, args_w):
        if len(args_w) > 1:
            raise space.error(space.w_ArgumentError,
                "wrong number of arguments (given %d, expected 1..2)"
            )
        if len(args_w) == 1:
            unit = Coerce.symbol(space, args_w[0])
        else:
            unit = "float_second"
        if rtime.HAS_CLOCK_GETTIME:
            with lltype.scoped_alloc(rtime.TIMESPEC) as a:
                if rtime.c_clock_gettime(clockid, a) == 0:
                    sec = rffi.getintfield(a, 'c_tv_sec')
                    nsec = rffi.getintfield(a, 'c_tv_nsec')
                else:
                    raise error_for_oserror(space, OSError(
                        errno.EINVAL, "clock_gettime")
                    )
        elif clockid == CLOCK_PROCESS_CPUTIME_ID:
            r = rtime.clock()
            sec = int(r)
            nsec = r * 1000000000
        else:
            raise error_for_oserror(space, OSError(
                errno.EINVAL, "clock_gettime")
            )
        if unit == "float_second":
            return space.newfloat(sec + nsec * 0.000000001)
        elif unit == "float_millisecond":
            return space.newfloat(sec * 1000 + nsec * 0.000001)
        elif unit == "float_microsecond":
            return space.newfloat(sec * 1000000 + nsec * 0.001)
        elif unit == "second":
            return space.newint(int(sec))
        elif unit == "millisecond":
            return space.newint(int(sec) * 1000)
        elif unit == "microsecond":
            return space.newint(sec * 1000000)
        elif unit == "nanosecond":
            return space.newint(sec * 1000000000 + int(nsec))
        else:
            raise space.error(space.w_ArgumentError,
                "unexpected unit: %s" % unit
            ) class W_BindingObject(W_Object):
    classdef = ClassDef("Binding", W_Object.classdef)
    _immutable_fields_ = ["names[*]", "cells[*]", "w_self", "lexical_scope"]

    def __init__(self, space, names, cells, w_self, lexical_scope):
        W_Object.__init__(self, space)
        self.names = names
        self.cells = cells
        self.w_self = w_self
        self.lexical_scope = lexical_scope

    classdef.undefine_allocator()

    @classdef.method("eval", source="str")
    def method_eval(self, space, source):
        symtable = SymbolTable()
        for name in self.names:
            symtable.cells[name] = symtable.FREEVAR
        bc = space.compile(source, "(eval)", symtable=symtable)
        frame = space.create_frame(bc, w_self=self.w_self, lexical_scope=self.lexical_scope)
        for idx, cell in enumerate(self.cells):
            frame.cells[idx + len(bc.cellvars)] = cell
        with space.getexecutioncontext().visit_frame(frame):
            return space.execute_frame(frame, bc) class W_ExceptionObject(W_Object):
    _attrs_ = ["msg", "frame", "last_instructions", "w_backtrace"]

    classdef = ClassDef("Exception", W_Object.classdef)

    def __init__(self, space, klass=None):
        W_Object.__init__(self, space, klass)
        self.msg = ""
        self.frame = None
        self.w_backtrace = None

    def __str__(self):
        return "%s(%s)" % (self.__class__.__name__, self.msg)

    method_allocate = new_exception_allocate(classdef)

    def copy_instance_vars(self, space, w_other):
        """Copies special instance vars after #copy or #dup"""
        assert isinstance(w_other, W_ExceptionObject)
        W_Object.copy_instance_vars(self, space, w_other)
        self.msg = w_other.msg
        self.frame = w_other.frame
        self.w_backtrace = w_other.w_backtrace

    @classdef.method("initialize")
    def method_initialize(self, space, w_msg=None):
        if w_msg is space.w_nil or w_msg is None:
            msg = space.getclass(self).name
        else:
            msg = space.str_w(space.send(w_msg, "to_s"))
        self.msg = msg

    @classdef.method("to_s")
    def method_to_s(self, space):
        return space.newstr_fromstr(self.msg)

    @classdef.singleton_method("exception")
    def singleton_method_exception(self, space, args_w):
        return space.send(self, "new", args_w)

    @classdef.method("exception")
    def method_exception(self, space, w_string=None):
        if w_string is None:
            return self
        else:
            return space.send(space.getclassfor(self.__class__), "new", [w_string])

    @classdef.method("message")
    def method_message(self, space):
        return space.send(self, "to_s")

    @classdef.method("backtrace")
    def method_backtrace(self, space):
        if self.w_backtrace is not None:
            return self.w_backtrace
        frame = self.frame
        results_w = []
        prev_frame = None
        while frame is not None and frame.has_contents():
            results_w.append(space.newstr_fromstr("%s:%d:in `%s'" % (
                frame.get_filename(),
                frame.get_lineno(prev_frame),
                frame.get_code_name(),
            )))
            prev_frame = frame
            frame = frame.backref()
        return space.newarray(results_w)

    @classdef.method("set_backtrace")
    def method_set_backtrace(self, space, w_backtrace):
        if w_backtrace is space.w_nil:
            self.w_backtrace = w_backtrace
            return w_backtrace
        if space.is_kind_of(w_backtrace, space.w_array):
            for w_obj in space.listview(w_backtrace):
                if not space.is_kind_of(w_obj, space.w_string):
                    raise space.error(space.w_TypeError, "backtrace must be Array of String")
            self.w_backtrace = w_backtrace
            return w_backtrace
        if space.is_kind_of(w_backtrace, space.w_string):
            self.w_backtrace = space.newarray([w_backtrace])
            return self.w_backtrace
        raise space.error(space.w_TypeError, "backtrace must be Array of String")

    @classdef.method("==")
    def method_eq(self, space, w_other):
        if not isinstance(w_other, W_ExceptionObject):
            return space.w_false
        if self is w_other:
            return space.w_true

        w_msg = space.send(self, "message")
        w_backtrace = space.send(self, "backtrace")
        return space.newbool(
            space.is_true(space.send(w_msg, "==", [space.send(w_other, "message")])) and
            space.is_true(space.send(w_backtrace, "==", [space.send(w_other, "backtrace")]))
        ) class W_FunctionObject(W_BaseObject):
    _immutable_fields_ = ["name", "w_class", "visibility?"]

    PUBLIC = 0
    PROTECTED = 1
    PRIVATE = 2
    MODULE_FUNCTION = 3

    def __init__(self, name, w_class=None, visibility=PUBLIC):
        self.name = name
        self.w_class = w_class
        self.visibility = visibility

    def __deepcopy__(self, memo):
        obj = super(W_FunctionObject, self).__deepcopy__(memo)
        obj.name = self.name
        obj.w_class = copy.deepcopy(self.w_class, memo)
        obj.visibility = copy.deepcopy(self.visibility, memo)
        return obj

    def update_visibility(self, visibility):
        self.visibility = visibility

    def arity(self, space):
        return space.newint(0)

    def source_location(self, space):
        return space.w_nil class W_ModuleObject(W_RootObject):
    _immutable_fields_ = ["version?", "included_modules?[*]", "klass?", "name?"]

    classdef = ClassDef("Module", W_RootObject.classdef)

    def __init__(self, space, name, klass=None):
        self.name = name
        self.klass = klass
        self.version = VersionTag()
        self.methods_w = {}
        self.constants_w = {}
        self.class_variables = CellDict()
        self.instance_variables = CellDict()
        self.flags = CellDict()
        self.included_modules = []
        self.descendants = []

    def __deepcopy__(self, memo):
        obj = super(W_ModuleObject, self).__deepcopy__(memo)
        obj.name = self.name
        obj.klass = copy.deepcopy(self.klass, memo)
        obj.version = copy.deepcopy(self.version, memo)
        obj.methods_w = copy.deepcopy(self.methods_w, memo)
        obj.constants_w = copy.deepcopy(self.constants_w, memo)
        obj.class_variables = copy.deepcopy(self.class_variables, memo)
        obj.instance_variables = copy.deepcopy(self.instance_variables, memo)
        obj.flags = copy.deepcopy(self.flags, memo)
        obj.included_modules = copy.deepcopy(self.included_modules, memo)
        obj.descendants = copy.deepcopy(self.descendants, memo)
        return obj

    def getclass(self, space):
        if self.klass is not None:
            return jit.promote(self).klass
        return W_RootObject.getclass(self, space)

    def getsingletonclass(self, space):
        if self.klass is None or not self.klass.is_singleton:
            self.klass = space.newclass(
                "#<Class:%s>" % self.name, self.klass or space.w_module, is_singleton=True, attached=self
            )
        return self.klass

    def mutated(self):
        self.version = VersionTag()

    def define_method(self, space, name, method):
        if (name == "initialize" or name == "initialize_copy" or
            method.visibility == W_FunctionObject.MODULE_FUNCTION):
            method.update_visibility(W_FunctionObject.PRIVATE)
        self.mutated()
        self.methods_w[name] = method
        if not space.bootstrap:
            if isinstance(method, UndefMethod):
                self.method_undefined(space, space.newsymbol(name))
            else:
                self.method_added(space, space.newsymbol(name))

    @jit.unroll_safe
    def find_method(self, space, name):
        method = self._find_method_pure(space, name, self.version)
        if method is None:
            for module in self.included_modules:
                method = module.find_method(space, name)
                if method is not None:
                    return method
        return method

    @jit.unroll_safe
    def find_method_super(self, space, name):
        for module in self.included_modules:
            method = module.find_method(space, name)
            if method is not None:
                return method
        return None

    @jit.elidable
    def _find_method_pure(self, space, method, version):
        return self.methods_w.get(method, None)

    @specialize.argtype(2)
    def methods(self, space, visibility=None, inherit=True):
        methods = {}
        for name, method in self.methods_w.iteritems():
            if (not isinstance(method, UndefMethod) and
                (visibility is None or method.visibility == visibility)):
                methods[name] = None

        if inherit:
            for w_mod in self.included_modules:
                for name in w_mod.methods(space, visibility=visibility):
                    method = self._find_method_pure(space, name, self.version)
                    if method is None or not isinstance(method, UndefMethod):
                        methods[name] = None
        return methods.keys()

    def set_const(self, space, name, w_obj):
        self.mutated()
        self.constants_w[name] = w_obj
        if isinstance(w_obj, W_ModuleObject) and w_obj.name is None and self.name is not None:
            w_obj.set_name_in_scope(space, name, self)

    def find_const(self, space, name, autoload=True):
        w_res = self.find_included_const(space, name, autoload=autoload)
        if w_res is None:
            return space.w_object.find_const(space, name, autoload=autoload)
        else:
            return w_res

    @jit.unroll_safe
    def find_included_const(self, space, name, autoload=True):
        w_res = self.find_local_const(space, name, autoload=autoload)
        if w_res is None:
            for w_mod in self.included_modules:
                w_res = w_mod.find_local_const(space, name, autoload=autoload)
                if w_res is not None:
                    break
        return w_res

    def included_constants(self, space):
        consts = {}
        for const in self.constants_w:
            consts[const] = None
        for w_mod in self.included_modules:
            for const in w_mod.included_constants(space):
                consts[const] = None
        return consts.keys()

    def lexical_constants(self, space):
        consts = {}
        frame = space.getexecutioncontext().gettoprubyframe()
        scope = frame.lexical_scope

        while scope is not None:
            assert isinstance(scope, W_ModuleObject)
            for const in scope.w_mod.constants_w:
                consts[const] = None
            scope = scope.backscope

        return consts.keys()

    def local_constants(self, space):
        return self.constants_w.keys()

    def inherited_constants(self, space):
        return self.local_constants(space)

    def find_local_const(self, space, name, autoload=True):
        w_res = self._find_const_pure(name, self.version)
        if autoload and isinstance(w_res, W_Autoload):
            self.constants_w[name] = None
            try:
                w_res.load()
            finally:
                w_new_res = self.constants_w.get(name, None)
                if not w_res:
                    self.constants_w[name] = w_res
                w_res = w_new_res
            return w_res
        else:
            return w_res

    @jit.elidable
    def _find_const_pure(self, name, version):
        return self.constants_w.get(name, None)

    @jit.unroll_safe
    def set_class_var(self, space, name, w_obj):
        for module in reversed(self.ancestors()):
            assert isinstance(module, W_ModuleObject)
            w_res = module.class_variables.get(space, name)
            if w_res is not None or module is self:
                module.class_variables.set(space, name, w_obj)
                if module is self:
                    for descendant in self.descendants:
                        descendant.remove_class_var(space, name)

    @jit.unroll_safe
    def find_class_var(self, space, name):
        w_res = self.class_variables.get(space, name)
        if w_res is None:
            ancestors = self.ancestors()
            for idx in xrange(1, len(ancestors)):
                module = ancestors[idx]
                assert isinstance(module, W_ModuleObject)
                w_res = module.class_variables.get(space, name)
                if w_res is not None:
                    break
        return w_res

    @jit.unroll_safe
    def remove_class_var(self, space, name):
        self.class_variables.delete(name)
        for descendant in self.descendants:
            descendant.remove_class_var(space, name)

    def set_instance_var(self, space, name, w_value):
        return self.instance_variables.set(space, name, w_value)

    def find_instance_var(self, space, name):
        return self.instance_variables.get(space, name)

    def copy_instance_vars(self, space, w_other):
        assert isinstance(w_other, W_ModuleObject)
        for key in w_other.instance_variables:
            w_value = w_other.instance_variables.get(space, key)
            self.set_instance_var(space, key, w_value)

    def set_flag(self, space, name):
        self.flags.set(space, name, space.w_true)

    def unset_flag(self, space, name):
        self.flags.set(space, name, space.w_false)

    def get_flag(self, space, name):
        return self.flags.get(space, name) or space.w_false

    def ancestors(self, include_singleton=True, include_self=True):
        if include_self:
            return [self] + self.included_modules
        else:
            return self.included_modules[:]

    @jit.unroll_safe
    def is_ancestor_of(self, w_cls):
        if self is w_cls:
            return True
        for w_mod in w_cls.included_modules:
            if self is w_mod:
                return True
        if w_cls.superclass is not None:
            return self.is_ancestor_of(w_cls.superclass)
        return False

    def include_module(self, space, w_mod):
        assert isinstance(w_mod, W_ModuleObject)
        if w_mod not in self.ancestors():
            self.included_modules = [w_mod] + self.included_modules
            w_mod.included(space, self)

    def included(self, space, w_mod):
        self.descendants.append(w_mod)
        if space.respond_to(self, "included"):
            space.send(self, "included", [w_mod])

    def extend_object(self, space, w_mod):
        if self not in w_mod.ancestors():
            self.descendants.append(w_mod)
            w_mod.included_modules = [self] + w_mod.included_modules

    def set_visibility(self, space, names_w, visibility):
        names = [space.symbol_w(w_name) for w_name in names_w]
        if names:
            for name in names:
                self.set_method_visibility(space, name, visibility)
        else:
            self.set_default_visibility(space, visibility)

    def set_default_visibility(self, space, visibility):
        frame = space.getexecutioncontext().gettoprubyframe()
        frame.visibility = visibility

    def set_method_visibility(self, space, name, visibility):
        w_method = self.find_method(space, name)
        if w_method is None or isinstance(w_method, UndefMethod):
            w_method = space.w_object.find_method(space, name)

        if w_method is None or isinstance(w_method, UndefMethod):
            cls_name = space.obj_to_s(self)
            raise space.error(space.w_NameError,
                "undefined method `%s' for class `%s'" % (name, cls_name)
            )
        w_method.update_visibility(visibility)

    def method_added(self, space, w_name):
        space.send(self, "method_added", [w_name])

    def method_undefined(self, space, w_name):
        space.send(self, "method_undefined", [w_name])

    def method_removed(self, space, w_name):
        space.send(self, "method_removed", [w_name])

    def set_name_in_scope(self, space, name, w_scope):
        self.name = space.buildname(name, w_scope)
        for name, w_const in self.constants_w.iteritems():
            if isinstance(w_const, W_ModuleObject):
                w_const.set_name_in_scope(space, name, self)

    @classdef.singleton_method("nesting")
    def singleton_method_nesting(self, space):
        frame = space.getexecutioncontext().gettoprubyframe()
        modules_w = []
        scope = frame.lexical_scope
        while scope is not None:
            modules_w.append(scope.w_mod)
            scope = scope.backscope
        return space.newarray(modules_w)

    @classdef.singleton_method("allocate")
    def method_allocate(self, space):
        return W_ModuleObject(space, None, self)

    @classdef.method("initialize")
    def method_initialize(self, space, block):
        if block is not None:
            space.send(self, "module_exec", [self], block)

    @classdef.method("to_s")
    def method_to_s(self, space):
        name = self.name
        if name is None:
            return space.newstr_fromstr(space.any_to_s(self))
        return space.newstr_fromstr(name)

    @classdef.method("include")
    def method_include(self, space, args_w):
        for w_mod in args_w:
            if type(w_mod) is not W_ModuleObject:
                raise space.error(
                    space.w_TypeError,
                    "wrong argument type %s (expected Module)" % space.obj_to_s(space.getclass(w_mod))
                )

        for w_mod in reversed(args_w):
            space.send(w_mod, "append_features", [self])

        return self

    @classdef.method("include?")
    def method_includep(self, space, w_mod):
        if type(w_mod) is not W_ModuleObject:
            raise space.error(
                space.w_TypeError,
                "wrong argument type %s (expected Module)" % space.obj_to_s(space.getclass(w_mod))
            )
        if w_mod is self:
            return space.w_false
        return space.newbool(w_mod in self.ancestors())

    @classdef.method("append_features")
    def method_append_features(self, space, w_mod):
        if w_mod in self.ancestors():
            raise space.error(space.w_ArgumentError, "cyclic include detected")
        if type(self) is not W_ModuleObject:
            raise space.error(space.w_TypeError, "wrong argument type")
        for module in reversed(self.ancestors()):
            w_mod.include_module(space, module)

    @classdef.method("define_method", name="symbol")
    @check_frozen()
    def method_define_method(self, space, name, w_method=None, block=None):
        if w_method is not None:
            if space.is_kind_of(w_method, space.w_method):
                w_method = space.send(w_method, "unbind")

            if space.is_kind_of(w_method, space.w_unbound_method):
                self.define_method(space, name, DefineMethodMethod(name, w_method))
                return w_method
            elif space.is_kind_of(w_method, space.w_proc):
                assert isinstance(w_method, W_ProcObject)
                self.define_method(space, name, DefineMethodBlock(name, w_method))
                return w_method.copy(space, is_lambda=True)
            else:
                raise space.error(space.w_TypeError,
                    "wrong argument type %s (expected Proc/Method)" % space.obj_to_s(space.getclass(w_method))
                )
        elif block is not None:
            self.define_method(space, name, DefineMethodBlock(name, block))
            return block.copy(space, is_lambda=True)
        else:
            raise space.error(space.w_ArgumentError, "tried to create Proc object without a block")

    @classdef.method("attr_accessor")
    def method_attr_accessor(self, space, args_w):
        self.method_attr_reader(space, args_w)
        self.method_attr_writer(space, args_w)

    @classdef.method("attr_reader")
    def method_attr_reader(self, space, args_w):
        for w_arg in args_w:
            varname = Coerce.symbol(space, w_arg)
            self.define_method(space, varname, AttributeReader("@" + varname))

    @classdef.method("attr_writer")
    def method_attr_writer(self, space, args_w):
        for w_arg in args_w:
            varname = Coerce.symbol(space, w_arg)
            self.define_method(space, varname + "=", AttributeWriter("@" + varname))

    @classdef.method("attr")
    def method_attr(self, space, args_w):
        if len(args_w) == 2 and (args_w[1] is space.w_true or args_w[1] is space.w_false):
            [w_name, w_writable] = args_w
            if space.is_true(w_writable):
                self.method_attr_accessor(space, [w_name])
            else:
                self.method_attr_reader(space, [w_name])
        else:
            self.method_attr_reader(space, args_w)

    @classdef.method("module_function")
    def method_module_function(self, space, args_w):
        if not args_w:
            self.set_default_visibility(space, W_FunctionObject.MODULE_FUNCTION)
            return self
        for w_arg in args_w:
            name = Coerce.symbol(space, w_arg)
            w_method = self.find_method(space, name)
            if w_method is None or isinstance(w_method, UndefMethod):
                cls_name = space.obj_to_s(self)
                raise space.error(space.w_NameError,
                    "undefined method `%s' for class `%s'" % (name, cls_name)
                )
            self.attach_method(space, name, w_method)
            self.set_method_visibility(space, name, W_FunctionObject.PRIVATE)
        return self

    @classdef.method("private_class_method")
    def method_private_class_method(self, space, args_w):
        w_cls = self.getsingletonclass(space)
        return space.send(w_cls, "private", args_w)

    @classdef.method("public_class_method")
    def method_public_class_method(self, space, args_w):
        w_cls = self.getsingletonclass(space)
        return space.send(w_cls, "public", args_w)

    @classdef.method("alias_method", new_name="symbol", old_name="symbol")
    @check_frozen()
    def method_alias_method(self, space, new_name, old_name):
        w_method = self.find_method(space, old_name)
        if w_method is None:
            w_method = space.w_object.find_method(space, old_name)
        if w_method is None or isinstance(w_method, UndefMethod):
            cls_name = space.obj_to_s(self)
            raise space.error(space.w_NameError,
                "undefined method `%s' for class `%s'" % (old_name, cls_name)
            )
        self.define_method(space, new_name, w_method)
        return self

    @classdef.method("ancestors")
    def method_ancestors(self, space):
        return space.newarray(self.ancestors(include_singleton=False))

    @classdef.method("included")
    def method_included(self, space, w_mod):
        # TODO: should be private
        pass

    @classdef.method("extended")
    def method_extended(self, space, w_mod):
        # TODO: should be private
        pass

    @classdef.method("extend_object")
    def method_extend_object(self, space, w_obj):
        if type(self) is not W_ModuleObject:
            raise space.error(space.w_TypeError, "wrong argument type")
        self.extend_object(space, space.getsingletonclass(w_obj))

    @classdef.method("name")
    def method_name(self, space):
        if self.name is None:
            return space.w_nil
        return space.newstr_fromstr(self.name)

    @classdef.method("private")
    def method_private(self, space, args_w):
        self.set_visibility(space, args_w, W_FunctionObject.PRIVATE)
        return self

    @classdef.method("public")
    def method_public(self, space, args_w):
        self.set_visibility(space, args_w, W_FunctionObject.PUBLIC)
        return self

    @classdef.method("protected")
    def method_protected(self, space, args_w):
        self.set_visibility(space, args_w, W_FunctionObject.PROTECTED)
        return self

    @classdef.method("private_constant")
    def method_private_constant(self, space, args_w):
        pass

    @classdef.method("constants")
    def method_constants(self, space, w_inherit=None):
        if self is space.w_module and w_inherit is None:
            consts = {}
            for const in self.lexical_constants(space):
                consts[const] = None
            for const in self.inherited_constants(space):
                consts[const] = None
            return space.newarray([space.newsymbol(n) for n in consts])

        if w_inherit is None or space.is_true(w_inherit):
            return space.newarray([space.newsymbol(n) for n in self.included_constants(space)])
        else:
            return space.newarray([space.newsymbol(n) for n in self.constants_w])

    @classdef.method("const_missing", name="symbol")
    def method_const_missing(self, space, name):
        if self is space.w_object:
            raise space.error(space.w_NameError, "uninitialized constant %s" % (name))
        else:
            self_name = space.obj_to_s(self)
            raise space.error(space.w_NameError, "uninitialized constant %s::%s" % (self_name, name))

    @classdef.method("class_eval", string="str", filename="str")
    @classdef.method("module_eval", string="str", filename="str")
    def method_module_eval(self, space, string=None, filename=None, w_lineno=None, block=None):
        if string is not None and block is not None:
            raise space.error(space.w_ArgumentError, "wrong number of arguments")
        if string is not None:
            if filename is None:
                filename = "module_eval"
            if w_lineno is not None:
                lineno = space.int_w(w_lineno)
            else:
                lineno = 1
            return space.execute(string, self, lexical_scope=StaticScope(self, None), filepath=filename, initial_lineno=lineno)
        elif block is None:
            raise space.error(space.w_ArgumentError, "block not supplied")
        else:
            return space.invoke_block(block.copy(space, w_self=self, lexical_scope=StaticScope(self, block.lexical_scope)), [])

    @classdef.method("const_defined?", const="str", inherit="bool")
    def method_const_definedp(self, space, const, inherit=True):
        space._check_const_name(const)
        if inherit:
            return space.newbool(self.find_const(space, const, autoload=False) is not None)
        else:
            return space.newbool(self.find_local_const(space, const, autoload=False) is not None)

    @classdef.method("const_get", const="symbol", inherit="bool")
    def method_const_get(self, space, const, inherit=True):
        space._check_const_name(const)
        if inherit:
            w_res = self.find_const(space, const)
        else:
            w_res = self.find_local_const(space, const)
        if w_res is None:
            return space.send(self, "const_missing", [space.newsymbol(const)])
        return w_res

    @classdef.method("const_set", const="symbol")
    @check_frozen()
    def method_const_set(self, space, const, w_value):
        space.set_const(self, const, w_value)
        return w_value

    @classdef.method("remove_const", name="str")
    def method_remove_const(self, space, name):
        space._check_const_name(name)
        w_res = self.find_local_const(space, name, autoload=False)
        if w_res is None:
            self_name = space.obj_to_s(self)
            raise space.error(space.w_NameError,
                "uninitialized constant %s::%s" % (self_name, name)
            )
        del self.constants_w[name]
        self.mutated()
        return w_res

    @classdef.method("class_variable_defined?", name="symbol")
    def method_class_variable_definedp(self, space, name):
        return space.newbool(self.find_class_var(space, name) is not None)

    @classdef.method("class_variable_get", name="symbol")
    def method_class_variable_get(self, space, name):
        return space.find_class_var(self, name)

    @classdef.method("class_variable_set", name="symbol")
    @check_frozen()
    def method_class_variable_set(self, space, name, w_value):
        self.set_class_var(space, name, w_value)
        return w_value

    @classdef.method("remove_class_variable", name="symbol")
    def method_remove_class_variable(self, space, name):
        w_value = self.class_variables.get(space, name)
        if w_value is not None:
            self.class_variables.delete(name)
            return w_value
        if self.find_class_var(space, name) is not None:
            raise space.error(space.w_NameError,
                "cannot remove %s for %s" % (name, space.obj_to_s(self))
            )
        raise space.error(space.w_NameError,
            "class variable %s not defined for %s" % (name, space.obj_to_s(self))
        )

    @classdef.method("method_defined?", name="str")
    def method_method_definedp(self, space, name):
        return space.newbool(self.find_method(space, name) is not None)

    @classdef.method("===")
    def method_eqeqeq(self, space, w_obj):
        return space.newbool(self.is_ancestor_of(space.getclass(w_obj)))

    @classdef.method("<=")
    def method_lte(self, space, w_other):
        if not isinstance(w_other, W_ModuleObject):
            raise space.error(space.w_TypeError, "compared with non class/module")
        for w_mod in self.ancestors():
            if w_other is w_mod:
                return space.w_true
        for w_mod in w_other.ancestors():
            if self is w_mod:
                return space.w_false
        return space.w_nil

    @classdef.method("<")
    def method_lt(self, space, w_other):
        if self is w_other:
            return space.w_false
        return space.send(self, "<=", [w_other])

    @classdef.method(">=")
    def method_gte(self, space, w_other):
        if not isinstance(w_other, W_ModuleObject):
            raise space.error(space.w_TypeError, "compared with non class/module")
        return space.send(w_other, "<=", [self])

    @classdef.method(">")
    def method_gt(self, space, w_other):
        if not isinstance(w_other, W_ModuleObject):
            raise space.error(space.w_TypeError, "compared with non class/module")
        if self is w_other:
            return space.w_false
        return space.send(w_other, "<=", [self])

    @classdef.method("<=>")
    def method_comparison(self, space, w_other):
        if not isinstance(w_other, W_ModuleObject):
            return space.w_nil

        if self is w_other:
            return space.newint(0)

        other_is_subclass = space.send(self, "<", [w_other])

        if space.is_true(other_is_subclass):
            return space.newint(-1)
        elif other_is_subclass is space.w_nil:
            return space.w_nil
        else:
            return space.newint(1)

    @classdef.method("instance_method", name="symbol")
    def method_instance_method(self, space, name):
        return space.newmethod(name, self)

    @classdef.method("instance_methods", inherit="bool")
    def method_instance_methods(self, space, inherit=True):
        return space.newarray([
            space.newsymbol(sym)
            for sym in self.methods(space, inherit=inherit)
        ])

    @classdef.method("public_instance_methods", inherit="bool")
    def method_public_instance_methods(self, space, inherit=True):
        return space.newarray([
            space.newsymbol(sym)
            for sym in self.methods(space, visibility=W_FunctionObject.PUBLIC, inherit=inherit)
        ])

    @classdef.method("protected_instance_methods", inherit="bool")
    def method_protected_instance_methods(self, space, inherit=True):
        return space.newarray([
            space.newsymbol(sym)
            for sym in self.methods(space, visibility=W_FunctionObject.PROTECTED, inherit=inherit)
        ])

    @classdef.method("private_instance_methods", inherit="bool")
    def method_private_instance_methods(self, space, inherit=True):
        return space.newarray([
            space.newsymbol(sym)
            for sym in self.methods(space, visibility=W_FunctionObject.PRIVATE, inherit=inherit)
        ])

    @classdef.method("undef_method", name="symbol")
    def method_undef_method(self, space, name):
        w_method = self.find_method(space, name)
        if w_method is None or isinstance(w_method, UndefMethod):
            cls_name = space.obj_to_s(self)
            raise space.error(space.w_NameError,
                "undefined method `%s' for class `%s'" % (name, cls_name)
            )
        self.define_method(space, name, UndefMethod(name))
        return self

    @classdef.method("remove_method", name="symbol")
    @check_frozen()
    def method_remove_method(self, space, name):
        w_method = self._find_method_pure(space, name, self.version)
        if w_method is None or isinstance(w_method, UndefMethod):
            cls_name = space.obj_to_s(self)
            raise space.error(space.w_NameError,
                "method `%s' not defined in %s" % (name, cls_name)
            )
        del self.methods_w[name]
        self.mutated()
        self.method_removed(space, space.newsymbol(name))
        return self

    def method_removed(self, space, w_name):
        space.send(self, "method_removed", [w_name])

    @classdef.method("method_added")
    def method_method_added(self, space, w_name):
        return space.w_nil

    @classdef.method("method_undefined")
    def method_method_undefined(self, space, w_name):
        return space.w_nil

    @classdef.method("method_removed")
    def method_method_removed(self, space, w_name):
        return space.w_nil

    @classdef.method("autoload", name="symbol", path="path")
    def method_autoload(self, space, name, path):
        if len(path) == 0:
            raise space.error(space.w_ArgumentError, "empty file name")
        if not self.find_const(space, name):
            space.set_const(self, name, W_Autoload(space, path))
        return space.w_nil

    @classdef.method("autoload?", name="symbol")
    def method_autoload(self, space, name):
        w_autoload = self.constants_w.get(name, None)
        if isinstance(w_autoload, W_Autoload):
            return space.newstr_fromstr(w_autoload.path)
        else:
            return space.w_nil

    @classdef.method("class_exec")
    @classdef.method("module_exec")
    def method_module_exec(self, space, args_w, block):
        if block is None:
            raise space.error(space.w_LocalJumpError, "no block given")
        return space.invoke_block(
            block.copy(
                space,
                w_self=self,
                lexical_scope=StaticScope(self, None)
            ),
            args_w
        ) class W_RandomObject(W_Object):
    classdef = ClassDef("Random", W_Object.classdef)

    def __init__(self, space, seed=0, klass=None):
        W_Object.__init__(self, space, klass)
        self.w_seed = None
        self.random = Random(abs(seed))

    @classdef.setup_class
    def setup_class(cls, space, w_cls):
        default = space.send(w_cls, "new")
        space.set_const(w_cls, "DEFAULT", default)

    @classdef.singleton_method("allocate")
    def method_allocate(self, space):
        return W_RandomObject(space, 0, self)

    @classdef.method("initialize")
    def method_initialize(self, space, w_seed=None):
        self.srand(space, w_seed)

    @classdef.method("seed")
    def method_seed(self, space):
        return self.w_seed

    def srand(self, space, seed=None):
        previous_seed = self.w_seed
        if seed is None:
            seed = intmask(self._generate_seed())
        else:
            seed = Coerce.int(space, seed)
        self.w_seed = space.newint(seed)
        if previous_seed is None:
            value = space.newfloat(self.random.random())
        else:
            value = previous_seed
        self.random = Random(abs(seed))
        return value

    @classdef.method("rand")
    def method_rand(self, space, w_max=None):
        if w_max is None:
            return space.newfloat(self.random.random())
        elif space.is_kind_of(w_max, space.w_float):
            return self._rand_float(space, w_max)
        elif space.is_kind_of(w_max, space.getclassfor(W_BignumObject)):
            return self._rand_bignum(space, w_max)
        elif space.is_kind_of(w_max, space.getclassfor(W_RangeObject)):
            return self._rand_range(space, w_max)
        else:
            return self._rand_int(space, w_max)

    @classdef.singleton_method("rand")
    def method_singleton_rand(self, space, args_w):
        default = space.find_const(self, "DEFAULT")
        return space.send(default, "rand", args_w)

    def _rand_range(self, space, range):
        random = self.random.random()
        first = space.send(range, "first")
        last = space.send(range, "last")
        if space.is_true(space.send(range, "include?", [last])):
            last = space.send(last, "+", [space.newint(1)])
        if (not space.respond_to(last, "-") or
            not space.respond_to(last, "+")):
                raise space.error(space.w_ArgumentError, "invalid argument")
        diff = space.send(last, "-", [first])
        offset = space.send(diff, "*", [space.newfloat(random)])
        choice = space.send(offset, "+", [first])
        if (not space.is_kind_of(first, space.w_float) and
            not space.is_kind_of(last, space.w_float)):
            choice = space.send(choice, "to_i")
        return choice

    def _rand_float(self, space, float):
        random = self.random.random()
        max = Coerce.float(space, float)
        if max <= 0:
            raise space.error(space.w_ArgumentError, "invalid argument")
        return space.newfloat(random * max)

    def _rand_int(self, space, integer):
        random = self.random.random()
        max = Coerce.int(space, integer)
        if max <= 0:
            raise space.error(space.w_ArgumentError, "invalid argument")
        else:
            return space.newint(int(random * max))

    def _rand_bignum(self, space, bignum):
        random = self.random.random()
        max = Coerce.float(space, bignum)
        if max <= 0:
            raise space.error(space.w_ArgumentError, "invalid argument")
        return space.newbigint_fromfloat(random * max)

    def _generate_seed(self):
        seed = 0
        if os.access('/dev/urandom', os.R_OK):
            file = os.open('/dev/urandom', os.R_OK, 0644)
            bytes = os.read(file, 4)
            os.close(file)
            for b in bytes:
                seed = seed * 0xff + ord(b)
        return seed + int(time.time()) + os.getpid() class W_StringObject(W_Object):
    classdef = ClassDef("String", W_Object.classdef)
    classdef.include_module(Comparable)

    def __init__(self, space, storage, strategy, klass=None):
        W_Object.__init__(self, space, klass)
        self.str_storage = storage
        self.strategy = strategy

    def __deepcopy__(self, memo):
        obj = super(W_StringObject, self).__deepcopy__(memo)
        obj.str_storage = copy.deepcopy(self.str_storage, memo)
        obj.strategy = copy.deepcopy(self.strategy, memo)
        return obj

    @staticmethod
    def newstr_fromstr(space, strvalue):
        strategy = space.fromcache(ConstantStringStrategy)
        storage = strategy.erase(strvalue)
        return W_StringObject(space, storage, strategy)

    @staticmethod
    @jit.look_inside_iff(lambda space, strs_w: jit.isconstant(len(strs_w)))
    def newstr_fromstrs(space, strs_w):
        total_length = 0
        for w_item in strs_w:
            assert isinstance(w_item, W_StringObject)
            total_length += w_item.length()

        storage = newlist_hint(total_length)
        for w_item in strs_w:
            assert isinstance(w_item, W_StringObject)
            w_item.strategy.extend_into(w_item.str_storage, storage)
        return space.newstr_fromchars(storage)

    @staticmethod
    def newstr_fromchars(space, chars):
        strategy = space.fromcache(MutableStringStrategy)
        storage = strategy.erase(chars)
        return W_StringObject(space, storage, strategy)

    def str_w(self, space):
        return self.strategy.str_w(self.str_storage)

    def symbol_w(self, space):
        return self.str_w(space)

    def liststr_w(self, space):
        return self.strategy.liststr_w(self.str_storage)

    def length(self):
        return self.strategy.length(self.str_storage)

    def copy(self, space):
        return W_StringObject(space, self.strategy.copy(self.str_storage), self.strategy)

    def replace(self, space, chars):
        strategy = space.fromcache(MutableStringStrategy)
        self.str_storage = strategy.erase(chars)
        self.strategy = strategy

    def extend(self, space, w_other):
        self.strategy.to_mutable(space, self)
        strategy = self.strategy
        assert isinstance(strategy, MutableStringStrategy)
        storage = strategy.unerase(self.str_storage)
        w_other.strategy.extend_into(w_other.str_storage, storage)

    def clear(self, space):
        self.strategy.to_mutable(space, self)
        self.strategy.clear(self)

    def tr_trans(self, space, source, replacement, squeeze):
        change_made = False
        string = space.str_w(self)
        new_string = []
        is_negative_set = len(source) > 1 and source[0] == "^"
        if is_negative_set:
            source = source[1:]

        trans_table = create_trans_table(source, replacement, is_negative_set)

        if squeeze:
            last_repl = ""
            for char in string:
                repl = trans_table[ord(char)]
                if last_repl == repl:
                    continue
                if repl != char:
                    last_repl = repl
                    if not change_made:
                        change_made = True
                new_string.append(repl)
        else:
            for char in string:
                repl = trans_table[ord(char)]
                if not change_made and repl != char:
                    change_made = True
                new_string.append(repl)

        return new_string if change_made else None

    @classdef.singleton_method("allocate")
    def singleton_method_allocate(self, space):
        strategy = space.fromcache(ConstantStringStrategy)
        storage = strategy.erase("")
        return W_StringObject(space, storage, strategy, self)

    @classdef.method("initialize")
    @check_frozen()
    def method_initialize(self, space, w_s=None):
        if w_s is not None:
            w_s = space.convert_type(w_s, space.w_string, "to_str")
            assert isinstance(w_s, W_StringObject)
            self.strategy = w_s.strategy
            self.str_storage = w_s.strategy.copy(w_s.str_storage)
        return self

    @classdef.method("initialize_copy")
    def method_initialize_copy(self, space, w_other):
        assert isinstance(w_other, W_StringObject)
        self.strategy = w_other.strategy
        self.str_storage = w_other.strategy.copy(w_other.str_storage)
        return self

    @classdef.method("to_str")
    @classdef.method("to_s")
    def method_to_s(self, space):
        return self

    @classdef.method("ord")
    def method_ord(self, space):
        if self.length() == 0:
            raise space.error(space.w_ArgumentError, "empty string")
        return space.newint(ord(self.strategy.getitem(self.str_storage, 0)))

    @classdef.method("inspect")
    def method_inspect(self, space):
        s = space.newstr_fromstr('"%s"' % self.str_w(space))
        space.infect(s, self)
        return s

    @classdef.method("+")
    def method_plus(self, space, w_obj):
        w_other = space.convert_type(w_obj, space.w_string, "to_str")
        assert isinstance(w_other, W_StringObject)
        total_size = self.length() + w_other.length()
        s = space.newstr_fromchars(newlist_hint(total_size))
        s.extend(space, self)
        s.extend(space, w_other)
        space.infect(s, self)
        space.infect(s, w_other)
        return s

    @classdef.method("*", times="int")
    def method_times(self, space, times):
        if times < 0:
            raise space.error(space.w_ArgumentError, "negative argument")
        res = self.strategy.mul(space, self.str_storage, times)
        space.infect(res, self)
        return res

    @classdef.method("<<")
    @classdef.method("concat")
    @check_frozen()
    def method_lshift(self, space, w_other):
        if space.is_kind_of(w_other, space.w_fixnum):
            w_other = space.send(w_other, "chr")
        else:
            w_other = space.convert_type(w_other, space.w_string, "to_str")
        assert isinstance(w_other, W_StringObject)
        self.extend(space, w_other)
        space.infect(self, w_other)
        return self

    @classdef.method("bytesize")
    @classdef.method("size")
    @classdef.method("length")
    def method_length(self, space):
        return space.newint(self.length())

    @classdef.method("hash")
    def method_hash(self, space):
        return space.newint(self.strategy.hash(self.str_storage))

    @classdef.method("[]")
    @classdef.method("slice")
    @classdef.method("byteslice")
    def method_subscript(self, space, w_idx, w_count=None):
        if space.is_kind_of(w_idx, space.w_string):
            if space.send(self, "include?", [w_idx]):
                return w_idx
            else:
                return space.w_nil

        if space.is_kind_of(w_idx, space.w_regexp):
            w_match = space.send(w_idx, "match", [self])
            if w_match is space.w_nil:
                return space.w_nil
            elif not w_count:
                return space.send(w_match, "[]", [space.newint(0)])
            else:
                return space.send(w_match, "[]", [w_count])

        start, end, as_range, nil = space.subscript_access(self.length(), w_idx, w_count=w_count)
        if nil:
            return space.w_nil
        elif as_range:
            assert start >= 0
            assert end >= 0
            return self.strategy.getslice(space, self.str_storage, start, end)
        else:
            return space.newstr_fromstr(self.strategy.getitem(self.str_storage, start))

    @classdef.method("[]=")
    def method_subscript(self, space, w_idx, w_count, w_other=None):
        if w_other is None:
            w_other = w_count
            w_count = None

        other = Coerce.str(space, w_other)
        start_idx = -1
        end_idx = -1
        if space.is_kind_of(w_idx, space.w_string):
            other_str = space.str_w(w_idx)
            start_idx = space.str_w(self).find(other_str)
            end_idx = start_idx + len(other_str)
        elif space.is_kind_of(w_idx, space.w_regexp):
            ctx = w_idx.make_ctx(space.str_w(self))
            if self.search_context(space, ctx):
                if w_count is None:
                    start_idx = ctx.match_start
                    end_idx = ctx.match_end
                elif space.is_kind_of(w_count, space.w_string):
                    raise space.error(
                        space.w_NotImplementedError,
                        "string subscript replace with regexp and named group"
                    )
                else:
                    groupnum = Coerce.int(space, w_count)
                    try:
                        start_idx, end_idx = ctx.span(groupnum)
                    except IndexError:
                        pass
        else:
            start_idx, end_idx, as_range, nil = space.subscript_access(self.length(), w_idx, w_count=w_count)
            if not w_count:
                end_idx = start_idx + 1

        if start_idx < 0 or end_idx < 0:
            raise space.error(space.w_IndexError, "cannot find substring in string to replace")

        self.strategy.to_mutable(space, self)
        self.strategy.delslice(space, self.str_storage, start_idx, end_idx)
        self.strategy.insert(self.str_storage, start_idx, other)
        return self

    @classdef.method("slice!")
    @check_frozen()
    def method_slice_i(self, space, w_idx, w_count=None):
        start, end, as_range, nil = space.subscript_access(self.length(), w_idx, w_count=w_count)
        if nil:
            return space.w_nil
        elif as_range:
            assert start >= 0
            assert end >= 0
            self.strategy.to_mutable(space, self)
            w_string = self.strategy.getslice(space, self.str_storage, start, end)
            self.strategy.delslice(space, self.str_storage, start, end)
            return w_string
        else:
            assert start >= 0
            self.strategy.to_mutable(space, self)
            w_string = self.strategy.getslice(space, self.str_storage, start, start + 1)
            self.strategy.delslice(space, self.str_storage, start, start + 1)
            return w_string

    @classdef.method("<=>")
    def method_comparator(self, space, w_other):
        if isinstance(w_other, W_StringObject):
            s1 = space.str_w(self)
            s2 = space.str_w(w_other)
            if s1 < s2:
                return space.newint(-1)
            elif s1 == s2:
                return space.newint(0)
            elif s1 > s2:
                return space.newint(1)
        else:
            if space.respond_to(w_other, "to_str") and space.respond_to(w_other, "<=>"):
                tmp = space.send(w_other, "<=>", [self])
                if tmp is not space.w_nil:
                    return space.newint(-space.int_w(tmp))
            return space.w_nil

    @classdef.method("to_sym")
    @classdef.method("intern")
    def method_to_sym(self, space):
        return space.newsymbol(space.str_w(self))

    @classdef.method("clear")
    @check_frozen()
    def method_clear(self, space):
        self.clear(space)
        return self

    @classdef.method("ljust", integer="int", padstr="str")
    def method_ljust(self, space, integer, padstr=" "):
        if not padstr:
            raise space.error(space.w_ArgumentError, "zero width padding")
        elif integer <= self.length():
            return self.copy(space)
        else:
            pad_len = integer - self.length() - 1
            assert pad_len >= 0
            chars = []
            chars += space.str_w(self)
            for i in xrange(pad_len / len(padstr)):
                chars += padstr
            chars += padstr[:pad_len % len(padstr) + 1]
            return space.newstr_fromchars(chars)

    @classdef.method("rjust", integer="int", padstr="str")
    def method_rjust(self, space, integer, padstr=" "):
        if not padstr:
            raise space.error(space.w_ArgumentError, "zero width padding")
        elif integer <= self.length():
            return self.copy(space)
        else:
            pad_len = integer - self.length() - 1
            assert pad_len >= 0
            chars = []
            for i in xrange(pad_len / len(padstr)):
                chars += padstr
            chars += padstr[:pad_len % len(padstr) + 1]
            chars += space.str_w(self)
            return space.newstr_fromchars(chars)

    def search_context(self, space, ctx):
        try:
            return rsre_core.search_context(ctx)
        except rsre_core.Error, e:
            raise space.error(space.w_RuntimeError, e.msg)

    @classdef.method("find_string", offset="int")
    @classdef.method("index", offset="int")
    def method_index(self, space, w_sub, offset=0):
        if offset < 0 or offset >= self.length():
            return space.w_nil
        elif space.is_kind_of(w_sub, space.w_string):
            return space.newint(space.str_w(self).find(space.str_w(w_sub), offset))
        elif space.is_kind_of(w_sub, space.w_regexp):
            ctx = w_sub.make_ctx(space.str_w(self), offset=offset)
            if self.search_context(space, ctx):
                return space.newint(ctx.match_start)
            else:
                return space.newint(-1)
        else:
            raise space.error(
                space.w_TypeError,
                "type mismatch: %s given" % space.obj_to_s(space.getclass(w_sub))
            )

    @classdef.method("rindex", end="int")
    def method_rindex(self, space, w_sub, end=0):
        if end < 0:
            end += self.length()
        else:
            end = self.length()
        if end < 0:
            return space.w_nil

        idx = -1
        if space.is_kind_of(w_sub, space.w_string):
            idx = space.str_w(self).rfind(space.str_w(w_sub), 0, end + 1)
        elif space.is_kind_of(w_sub, space.w_regexp):
            ctx = w_sub.make_ctx(space.str_w(self))
            idx = -1
            while self.search_context(space, ctx):
                if ctx.match_start > end:
                    break
                else:
                    idx = ctx.match_start
                ctx.reset(idx + 1)
        else:
            raise space.error(
                space.w_TypeError,
                "type mismatch: %s given" % space.obj_to_s(space.getclass(w_sub))
            )
        if idx < 0:
            return space.w_nil
        else:
            return space.newint(idx)

    @classdef.method("split", limit="int")
    def method_split(self, space, w_sep=None, limit=0):
        if w_sep is None:
            res_w = []
            i = 0
            limit -= 1
            s = space.str_w(self)
            while True:
                while i < len(s):
                    if not s[i].isspace():
                        break
                    i += 1
                else:
                    break
                if limit == 0:
                    j = len(s)
                else:
                    j = i + 1
                    while j < len(s) and not s[j].isspace():
                        j += 1
                    limit -= 1
                res_w.append(space.newstr_fromstr(s[i:j]))
                i = j + 1
            return space.newarray(res_w)
        elif space.is_kind_of(w_sep, space.w_string):
            sep = space.str_w(w_sep)
            if sep:
                return space.newarray([
                    space.newstr_fromstr(s) for s in split(space.str_w(self), sep, limit - 1)
                ])
            else:
                if limit:
                    raise space.error(space.w_NotImplementedError, "String#split with empty string and limit")
                return space.newarray([
                    space.newstr_fromstr(self.strategy.getitem(self.str_storage, i))
                    for i in xrange(self.length())
                ])
        elif space.is_kind_of(w_sep, space.w_regexp):
            results_w = []
            n = 0
            last = 0
            string = space.str_w(self)
            ctx = w_sep.make_ctx(string)
            w_match = w_sep.get_match_result(space, ctx, string, found=True)

            while limit <= 0 or n + 1 < limit:
                if not self.search_context(space, ctx):
                    break
                elif ctx.match_start == ctx.match_end:
                    if ctx.match_start == ctx.end:
                        break
                    results_w.append(space.newstr_fromstr(string[last]))
                    last = ctx.match_end + 1
                else:
                    results_w.append(space.newstr_fromstr(string[last:ctx.match_start]))
                    for num in xrange(1, w_match.size(), 1):
                        begin, end = w_match.get_span(num)
                        begin += last
                        end += last
                        if begin >= 0 and end >= 0:
                            results_w.append(space.newstr_fromstr(string[begin:end]))
                    last = ctx.match_end
                n += 1
                ctx.reset(last)

            if len(string) > last:
                results_w.append(space.newstr_fromstr(string[last:]))
            if limit < 0 or len(results_w) < limit:
                results_w.append(space.newstr_fromstr(""))
            return space.newarray(results_w)

        else:
            raise space.error(
                space.w_TypeError,
                "wrong argument type %s (expected Regexp)" % (
                    space.obj_to_s(space.getclass(w_sep))
                )
            )

    @classdef.method("swapcase!")
    @check_frozen()
    def method_swapcase_i(self, space):
        self.strategy.to_mutable(space, self)
        changed = self.strategy.swapcase(self.str_storage)
        return self if changed else space.w_nil

    @classdef.method("upcase!")
    @check_frozen()
    def method_upcase_i(self, space):
        self.strategy.to_mutable(space, self)
        changed = self.strategy.upcase(self.str_storage)
        return self if changed else space.w_nil

    @classdef.method("downcase!")
    @check_frozen()
    def method_downcase_i(self, space):
        self.strategy.to_mutable(space, self)
        changed = self.strategy.downcase(self.str_storage)
        return self if changed else space.w_nil

    @classdef.method("capitalize!")
    @check_frozen()
    def method_capitalize_i(self, space):
        self.strategy.to_mutable(space, self)
        changed = self.strategy.capitalize(self.str_storage)
        return self if changed else space.w_nil

    def _digits(self, s, i, radix):
        number_seen = False
        while i < len(s):
            c = ord(s[i])
            if c == ord("_") and number_seen:
                i += 1
                continue
            if ord("a") <= c <= ord("z"):
                digit = c - ord("a") + 10
            elif ord("A") <= c <= ord("Z"):
                digit = c - ord("A") + 10
            elif ord("0") <= c <= ord("9"):
                digit = c - ord("0")
            else:
                break
            if digit >= radix:
                break
            number_seen = True
            yield digit
            i += 1

    def to_int(self, s, neg, i, radix):
        val = 0
        for digit in self._digits(s, i, radix):
            val = ovfcheck(val * radix + digit)
        if neg:
            val = -val
        return val

    def to_bigint(self, s, neg, i, radix):
        val = rbigint.fromint(0)
        bigint_radix = rbigint.fromint(radix)
        for digit in self._digits(s, i, radix):
            val = val.mul(bigint_radix).add(rbigint.fromint(digit))
        if neg:
            val = val.neg()
        return val

    @classdef.method("to_i")
    def method_to_i(self, space, w_radix=None):
        if w_radix is None:
            is_radix = False
            radix = 10
        else:
            is_radix = True
            radix = Coerce.int(space, w_radix)
        s = space.str_w(self)
        i = 0
        length = len(s)
        while i < length:
            if not s[i].isspace():
                break
            i += 1
        neg = i < length and s[i] == "-"
        if neg:
            i += 1
        if i < length and s[i] == "+":
            i += 1
        if i < length and s[i] == "0":
            if i + 1 < length:
                try:
                    r = RADIX_MAP[s[i + 1]]
                except KeyError:
                    if radix == 0:
                        radix = 8
                else:
                    if not is_radix or radix == r or radix == 0:
                        radix = r
                        i += 2
        if radix == 0:
            radix = 10
        if not 2 <= radix <= 36:
            raise space.error(space.w_ArgumentError, "invalid radix %d" % radix)
        try:
            value = self.to_int(s, neg, i, radix)
        except OverflowError:
            value = self.to_bigint(s, neg, i, radix)
            return space.newbigint_fromrbigint(value)
        else:
            return space.newint(value)

    @classdef.method("to_f")
    def method_to_f(self, space):
        value = 0.0
        precision = False
        pointer = 0.1
        negative = False
        exponent = False
        multi = 0
        number = False

        for char in space.str_w(self):
            c = ord(char)
            if ord("0") <= c <= ord("9"):
                if exponent:
                    multi = multi * 10 + c - ord("0")
                elif precision:
                    value = value + pointer * (c - ord("0"))
                    pointer /= 10
                else:
                    value = value * 10 + c - ord("0")

                number = True
            elif c == ord("-"):
                if value != 0.0:
                    break
                negative = not negative
            elif c == ord("+"):
                if value != 0.0:
                    break
                pass
            elif c == ord("."):
                if precision or exponent:
                    break
                precision = True
            elif c == ord("e"):
                if exponent:
                    break
                exponent = True
            elif char.isspace():
                if number:
                    break
            else:
                break

        if exponent:
            value = value * math.pow(10, multi)

        return space.newfloat(value)

    @classdef.method("tr", source="str", replacement="str")
    def method_tr(self, space, source, replacement):
        string = self.copy(space)
        new_string = self.tr_trans(space, source, replacement, False)
        return space.newstr_fromchars(new_string) if new_string else string

    @classdef.method("tr!", source="str", replacement="str")
    @check_frozen()
    def method_tr_i(self, space, source, replacement):
        new_string = self.tr_trans(space, source, replacement, False)
        self.replace(space, new_string)
        return self if new_string else space.w_nil

    @classdef.method("tr_s", source="str", replacement="str")
    def method_tr_s(self, space, source, replacement):
        string = self.copy(space)
        new_string = self.tr_trans(space, source, replacement, True)
        return space.newstr_fromchars(new_string) if new_string else string

    @classdef.method("tr_s!", source="str", replacement="str")
    @check_frozen()
    def method_tr_s_i(self, space, source, replacement):
        new_string = self.tr_trans(space, source, replacement, True)
        self.replace(space, new_string)
        return self if new_string else space.w_nil

    @classdef.method("=~")
    def method_match_operator(self, space, w_obj):
        if space.is_kind_of(w_obj, space.w_string):
            raise space.error(space.w_TypeError, "type mismatch: String given")
        else:
            return space.send(w_obj, "=~", [self])

    @classdef.method("%")
    def method_mod(self, space, w_arg):
        w_listarg = space.convert_type(
            w_arg, space.w_array, "to_ary", raise_error=False
        )
        if w_listarg is space.w_nil:
            args_w = [w_arg]
        else:
            args_w = space.listview(w_listarg)
        elements_w = StringFormatter(space.str_w(self), args_w).format(space)
        return space.newstr_fromstrs(elements_w)

    @classdef.method("getbyte", pos="int")
    def method_getbyte(self, space, pos):
        if pos >= self.length() or pos < -self.length():
            return space.w_nil
        if pos < 0:
            pos += self.length()
        ch = self.strategy.getitem(self.str_storage, pos)
        return space.newint(ord(ch))

    @classdef.method("setbyte", pos="int", replacement="int")
    @check_frozen()
    def method_setbyte(self, space, pos, replacement):
        if pos >= self.length() or pos < -self.length():
            raise space.error(space.w_IndexError,
                "index %d out of string" % pos
            )
        if pos < 0:
            pos += self.length()
        self.strategy.to_mutable(space, self)
        self.strategy.replaceitem(self.str_storage, pos, chr(replacement))
        return space.newint(replacement)

    @classdef.method("include?", substr="str")
    def method_includep(self, space, substr):
        return space.newbool(substr in space.str_w(self))

    def scan_string(self, space, w_pattern):
        string = space.str_w(self)
        pattern = space.str_w(w_pattern)
        idx = string.find(pattern, 0)
        while idx >= 0:
            w_match = space.newstr_fromstr(string[idx:idx + len(pattern)])
            space.infect(w_match, self)
            space.infect(w_match, w_pattern)
            yield w_match
            if not pattern:
                idx += 1
            else:
                idx += len(pattern)
            idx = string.find(pattern, idx)

    def scan_regexp(self, space, w_pattern):
        last = -1
        string = space.str_w(self)
        ctx = w_pattern.make_ctx(string)

        while last < len(string) and self.search_context(space, ctx):
            w_matchdata = w_pattern.get_match_result(space, ctx, string, found=True)
            if w_matchdata.size() > 1:
                matches_w = []
                for num in xrange(1, w_matchdata.size(), 1):
                    begin, end = w_matchdata.get_span(num)
                    assert begin >= 0
                    assert end >= 0
                    w_str = space.newstr_fromstr(string[begin:end])
                    space.infect(w_str, self)
                    space.infect(w_str, w_pattern)
                    matches_w.append(w_str)
                w_match = space.newarray(matches_w)
            else:
                w_match = space.newstr_fromstr(string[ctx.match_start:ctx.match_end])
                space.infect(w_match, self)
                space.infect(w_match, w_pattern)

            yield w_match

            if ctx.match_start == ctx.match_end:
                last += 1
            else:
                last = ctx.match_end
            assert last >= 0
            ctx.reset(last)

    def scan_process_result(self, space, w_match, results_w, block):
        if block:
            space.invoke_block(block, [w_match])
        else:
            results_w.append(w_match)

    @classdef.method("scan")
    def method_scan(self, space, w_pattern, block):
        results_w = []
        w_str_pattern = space.convert_type(w_pattern, space.w_string, "to_str", raise_error=False)
        if w_str_pattern is not space.w_nil:
            for w_match in self.scan_string(space, w_str_pattern):
                self.scan_process_result(space, w_match, results_w, block)
        elif space.is_kind_of(w_pattern, space.w_regexp):
            for w_match in self.scan_regexp(space, w_pattern):
                self.scan_process_result(space, w_match, results_w, block)
        else:
            raise space.error(
                space.w_TypeError,
                "wrong argument type %s (expected Regexp)" % (
                    space.obj_to_s(space.getclass(w_pattern))
                )
            )
        if block:
            return self
        else:
            return space.newarray(results_w)

    @classdef.method("gsub")
    def method_gsub(self, space, w_pattern, w_replacement=None, block=None):
        return self.gsub_main(space, w_pattern, w_replacement, block, first_only=False)

    @classdef.method("sub")
    def method_sub(self, space, w_pattern, w_replacement=None, block=None):
        if w_replacement is None and block is None:
            raise space.error(space.w_ArgumentError, "wrong number of arguments")
        return self.gsub_main(space, w_pattern, w_replacement, block, first_only=True)

    def gsub_main(self, space, w_pattern, w_replacement, block, first_only):
        if w_replacement is None and block is None:
            raise space.error(
                space.w_NotImplementedError,
                "gsub enumerator"
            )

        w_hash = None
        replacement = None
        if w_replacement:
            w_hash = space.convert_type(w_replacement, space.w_hash, "to_hash", raise_error=False)
            if w_hash is space.w_nil:
                w_hash = None
                replacement = space.str_w(
                    space.convert_type(w_replacement, space.w_string, "to_str")
                )

        if space.is_kind_of(w_pattern, space.w_regexp):
            return self.gsub_regexp(space, w_pattern, replacement, w_hash, block, first_only)
        elif space.is_kind_of(w_pattern, space.w_string):
            return self.gsub_string(space, w_pattern, replacement, w_hash, block, first_only)
        else:
            raise space.error(
                space.w_TypeError,
                "wrong argument type %s (expected Regexp)" % (
                    space.obj_to_s(space.getclass(w_replacement))
                )
            )

    def gsub_regexp(self, space, w_pattern, replacement, w_hash, block, first_only):
        result = []
        pos = 0
        string = space.str_w(self)
        ctx = w_pattern.make_ctx(string)

        w_matchdata = w_pattern.get_match_result(space, ctx, string, found=True)
        replacement_parts = None
        if replacement is not None and "\\" in replacement:
            replacement_parts = [s for s in replacement.split("\\") if s]

        while pos < len(string) and self.search_context(space, ctx):
            result += string[pos:ctx.match_start]
            if replacement_parts is not None:
                result += self.gsub_regexp_subst_string(
                    space, replacement_parts, w_matchdata, pos
                )
            elif replacement is not None:
                result += replacement
            elif block:
                result += self.gsub_regexp_block(space, block, w_matchdata)
            elif w_hash:
                result += self.gsub_regexp_hash(space, w_hash, w_matchdata)
            pos = ctx.match_end
            ctx.reset(pos)
            if first_only:
                break
        result += string[pos:]
        return space.newstr_fromchars(result)

    def gsub_regexp_subst_string(self, space, parts_w, w_match, pos=0):
        result = []
        string = space.str_w(self)
        result += parts_w[0]
        for s in parts_w[1:]:
            if s[0].isdigit():
                group = int(s[0])
                if group < w_match.size():
                    begin, end = w_match.get_span(group)
                    begin += pos
                    end += pos
                    assert begin >= 0
                    assert end >= 0
                    result += string[begin:end]
                result += s[1:]
            else:
                result += s
        return result

    def gsub_regexp_block(self, space, block, w_match):
        w_arg = space.send(w_match, "[]", [space.newint(0)])
        return self.gsub_yield_block(space, block, w_arg)

    def gsub_regexp_hash(self, space, w_hash, w_match):
        w_arg = space.send(w_match, "[]", [space.newint(0)])
        return self.gsub_lookup_hash(space, w_hash, w_arg)

    def gsub_string(self, space, w_pattern, replacement, w_hash, block, first_only):
        result = []
        pos = 0
        string = space.str_w(self)
        pattern = space.str_w(w_pattern)
        while pos + len(pattern) < len(string):
            idx = string.find(pattern, pos)
            if idx >= 0:
                result += string[pos:idx]
                if replacement is not None:
                    result += replacement
                elif block:
                    result += self.gsub_yield_block(space, block, w_pattern)
                elif w_hash:
                    result += self.gsub_lookup_hash(space, w_hash, w_pattern)
                pos = idx + len(pattern)
            else:
                break
            if first_only:
                break
        result += string[pos:]
        return space.newstr_fromchars(result)

    def gsub_yield_block(self, space, block, w_matchstr):
        w_value = space.invoke_block(block, [w_matchstr])
        return self.gsub_replacement_to_s(space, w_value)

    def gsub_lookup_hash(self, space, w_hash, w_matchstr):
        w_value = space.send(w_hash, "[]", [w_matchstr])
        return self.gsub_replacement_to_s(space, w_value)

    def gsub_replacement_to_s(self, space, w_replacement):
        if space.is_kind_of(w_replacement, space.w_string):
            return space.str_w(w_replacement)
        else:
            w_replacement = space.send(w_replacement, "to_s")
            if space.is_kind_of(w_replacement, space.w_string):
                return space.str_w(w_replacement)
            else:
                return space.any_to_s(w_replacement)

    @classdef.method("chomp!")
    @check_frozen()
    def method_chomp_i(self, space, w_newline=None):
        if w_newline is None:
            w_newline = space.globals.get(space, "$/")
        if w_newline is space.w_nil:
            return space.w_nil
        newline = space.str_w(space.convert_type(w_newline, space.w_string, "to_str"))
        if newline and newline in "\n\r":
            newline = None
        self.strategy.to_mutable(space, self)
        changed = self.strategy.chomp(self.str_storage, newline)
        return self if changed else space.w_nil

    @classdef.method("chop!")
    @check_frozen()
    def method_chop_i(self, space):
        self.strategy.to_mutable(space, self)
        changed = self.strategy.chop(self.str_storage)
        return self if changed else space.w_nil

    @classdef.method("reverse!")
    @check_frozen()
    def method_reverse_i(self, space):
        self.strategy.to_mutable(space, self)
        self.strategy.reverse(self.str_storage)
        return self

    @classdef.method("next!")
    @classdef.method("succ!")
    @check_frozen()
    def method_succ_i(self, space):
        self.strategy.to_mutable(space, self)
        self.strategy.succ(self.str_storage)
        return self

    @classdef.method("insert", index="int", other="str")
    @check_frozen()
    def method_insert(self, space, index, other):
        if index < 0:
            index += self.length() + 1
        if not 0 <= index <= self.length():
            raise space.error(space.w_IndexError,
                "index %d out of string" % index
            )
        self.strategy.to_mutable(space, self)
        self.strategy.insert(self.str_storage, index, other)
        return self

    @classdef.method("prepend", other="str")
    @check_frozen()
    def method_prepend(self, space, other):
        self.strategy.to_mutable(space, self)
        self.strategy.insert(self.str_storage, 0, other)
        return self

    @classdef.method("strip!")
    @check_frozen()
    def method_strip_i(self, space):
        self.strategy.to_mutable(space, self)
        changed = self.strategy.strip(self.str_storage)
        if changed:
            return self
        else:
            return space.w_nil
import errno
import os
import time
from rpython.rlib.objectmodel import compute_identity_hash
from rpython.rlib.rfloat import round_double
from rpython.rlib.streamio import open_file_as_stream
from topaz.coerce import Coerce
from topaz.error import RubyError, error_for_oserror, error_for_errno
from topaz.module import ModuleDef, check_frozen
from topaz.modules.process import Process
from topaz.objects.bindingobject import W_BindingObject
from topaz.objects.exceptionobject import W_ExceptionObject
from topaz.objects.functionobject import W_FunctionObject
from topaz.objects.moduleobject import W_ModuleObject
from topaz.objects.procobject import W_ProcObject
from topaz.objects.randomobject import W_RandomObject
from topaz.objects.stringobject import W_StringObject
from topaz.scope import StaticScope
        from topaz.interpreter import Throw
        from topaz.interpreter import Throw

    @moduledef.method("lambda")
    def function_lambda(self, space, block):
        if block is None:
            block = space.getexecutioncontext().gettoprubyframe().block
        if block is None:
            raise space.error(space.w_ArgumentError,
                "tried to create lambda object without a block"
            )
        else:
            return block.copy(space, is_lambda=True)

    @moduledef.method("proc")
    def function_proc(self, space, block):
        if block is None:
            block = space.getexecutioncontext().gettoprubyframe().block
        if block is None:
            raise space.error(space.w_ArgumentError,
                "tried to create Proc object without a block"
            )
        return block.copy(space)

    @staticmethod
    def find_feature(space, path):
        assert path is not None
        if os.path.isfile(path):
            return path
        if not path.endswith(".rb"):
            path += ".rb"

        if not (path.startswith("/") or path.startswith("./") or path.startswith("../")):
            w_load_path = space.globals.get(space, "$LOAD_PATH")
            for w_base in space.listview(w_load_path):
                base = Coerce.path(space, w_base)
                full = os.path.join(base, path)
                if os.path.isfile(full):
                    path = os.path.join(base, path)
                    break
        return path

    @staticmethod
    def load_feature(space, path, orig_path, wrap=False):
        if not os.path.exists(path):
            raise space.error(space.w_LoadError, orig_path)

        try:
            f = open_file_as_stream(path, buffering=0)
            try:
                contents = f.readall()
            finally:
                f.close()
        except OSError as e:
            raise error_for_oserror(space, e)

        if wrap:
            lexical_scope = StaticScope(space.newmodule("Anonymous"), None)
        else:
            lexical_scope = None
        space.execute(contents, filepath=path, lexical_scope=lexical_scope)

    @moduledef.function("require", path="path")
    def function_require(self, space, path):
        assert path is not None
        orig_path = path
        path = Kernel.find_feature(space, path)

        w_loaded_features = space.globals.get(space, '$"')
        w_already_loaded = space.send(
            w_loaded_features, "include?", [space.newstr_fromstr(path)]
        )
        if space.is_true(w_already_loaded):
            return space.w_false

        Kernel.load_feature(space, path, orig_path)
        w_loaded_features.method_lshift(space, space.newstr_fromstr(path))
        return space.w_true

    @moduledef.function("load", path="path", wrap="bool")
    def function_load(self, space, path, wrap=False):
        assert path is not None
        orig_path = path
        path = Kernel.find_feature(space, path)
        Kernel.load_feature(space, path, orig_path, wrap=wrap)
        return space.w_true

    @moduledef.method("fail")
    @moduledef.method("raise")
    def method_raise(self, space, w_str_or_exception=None, w_string=None, w_array=None):
        w_exception = None
        if w_str_or_exception is None:
            w_exception = space.globals.get(space, "$!") or space.w_nil
            if w_exception is space.w_nil:
                w_exception = space.w_RuntimeError
        elif isinstance(w_str_or_exception, W_StringObject):
            w_exception = space.w_RuntimeError
            w_string = w_str_or_exception
        else:
            w_exception = w_str_or_exception

        if not space.respond_to(w_exception, "exception"):
            raise space.error(space.w_TypeError,
                "exception class/object expected"
            )

        if w_string is not None:
            w_exc = space.send(w_exception, "exception", [w_string])
        else:
            w_exc = space.send(w_exception, "exception")

        if w_array is not None:
            raise space.error(
                space.w_NotImplementedError,
                "custom backtrace for Kernel#raise"
            )

        if not isinstance(w_exc, W_ExceptionObject):
            raise space.error(space.w_TypeError,
                "exception object expected"
            )


        raise RubyError(w_exc)